GS/HW: ROV support.

This commit is contained in:
TJnotJT
2026-06-03 03:26:24 +02:00
committed by lightningterror
parent 5369d6b38e
commit bbcd5ec25e
47 changed files with 2051 additions and 427 deletions
+186 -49
View File
@@ -50,6 +50,12 @@
#define PS_AA1_TRIANGLE_SW_Z 3
#endif
#ifndef PS_ROV_DEPTH_NONE
#define PS_ROV_DEPTH_NONE 0
#define PS_ROV_DEPTH_READ_WRITE 1
#define PS_ROV_DEPTH_READ_ONLY 2
#endif
#ifndef PS_FST
#define PS_IIP 0
#define PS_FST 0
@@ -111,6 +117,8 @@
#define PS_TEX_IS_FB 0
#define PS_AA1 0
#define PS_ABE 0
#define PS_ROV_COLOR 0
#define PS_ROV_DEPTH 0
#endif
#ifndef VS_EXPAND_NONE
@@ -132,6 +140,12 @@
#define SW_DEPTH (NEEDS_DEPTH_FOR_AFAIL || NEEDS_DEPTH_FOR_ZTST || NEEDS_DEPTH_FOR_AA1)
#define ZWRITE (PS_ZFLOOR || PS_ZCLAMP || SW_DEPTH)
#define PS_RETURN_COLOR_ROV (!PS_NO_COLOR && PS_ROV_COLOR)
#define PS_RETURN_COLOR (!PS_NO_COLOR && !PS_ROV_COLOR)
#define PS_RETURN_DEPTH_ROV (PS_ROV_DEPTH == PS_ROV_DEPTH_READ_WRITE)
#define PS_RETURN_DEPTH (ZWRITE && !PS_ROV_DEPTH)
#define PS_ROV_EARLYDEPTHSTENCIL (PS_ROV_COLOR && !PS_ROV_DEPTH && !ZWRITE)
struct VS_INPUT
{
float2 st : TEXCOORD0;
@@ -178,22 +192,27 @@ struct PS_INPUT
#ifdef PIXEL_SHADER
struct PS_OUTPUT
struct PS_OUTPUT_REAL
{
#define NUM_RTS 0
#if !PS_NO_COLOR
#if PS_DATE == 1 || PS_DATE == 2
float c : SV_Target;
#else
float4 c0 : SV_Target0;
#undef NUM_RTS
#define NUM_RTS 1
#if !PS_NO_COLOR1
float4 c1 : SV_Target1;
#if PS_RETURN_COLOR
#if PS_DATE == 1 || PS_DATE == 2
float c : SV_Target;
#else
float4 c0 : SV_Target0;
#undef NUM_RTS
#define NUM_RTS 1
#if !PS_NO_COLOR1
float4 c1 : SV_Target1;
#endif
#endif
#endif
#endif
#endif
#if ZWRITE
#if PS_RETURN_DEPTH
// In DX12 we do depth feedback loops with a color copy.
#if SW_DEPTH && PS_NO_COLOR1 && DX12
#if NUM_RTS > 0
@@ -208,16 +227,45 @@ struct PS_OUTPUT
float depth : SV_Depth;
#endif
#endif
#undef NUM_RTS
};
struct PS_OUTPUT
{
#if !PS_NO_COLOR
#if PS_DATE == 1 || PS_DATE == 2
float c;
#else
float4 c0;
#if !PS_NO_COLOR1
float4 c1;
#endif
#endif
#endif
};
Texture2D<float4> Texture : register(t0);
Texture2D<float4> Palette : register(t1);
#if !PS_ROV_COLOR
Texture2D<float4> RtTexture : register(t2);
#endif
Texture2D<float> PrimMinTexture : register(t3);
#if !PS_ROV_DEPTH
Texture2D<float> DepthTexture : register(t4);
#endif
SamplerState TextureSampler : register(s0);
#if PS_ROV_COLOR
RasterizerOrderedTexture2D<unorm float4> RtTextureRov : register(u0);
static float4 rov_rt_value;
#endif
#if PS_ROV_DEPTH
RasterizerOrderedTexture2D<float> DepthTextureRov : register(u1);
static float rov_depth_value;
#endif
#ifdef DX12
cbuffer cb1 : register(b1)
#else
@@ -244,6 +292,38 @@ cbuffer cb1
float RcpScaleFactor;
};
float4 RtLoad(int2 xy)
{
#if PS_ROV_COLOR
return rov_rt_value;
#else
return RtTexture.Load(int3(int2(xy), 0));
#endif
}
float DepthLoad(int2 xy)
{
#if PS_ROV_DEPTH
return rov_depth_value;
#else
return DepthTexture.Load(int3(int2(xy), 0));
#endif
}
void RtWrite(int2 xy, float4 c)
{
#if PS_ROV_COLOR
RtTextureRov[xy] = c;
#endif
}
void DepthWrite(int2 xy, float d)
{
#if PS_ROV_DEPTH
DepthTextureRov[xy] = d;
#endif
}
#if (PS_AUTOMATIC_LOD != 1) && (PS_MANUAL_LOD == 1)
float manual_lod(float uv_w)
{
@@ -407,7 +487,7 @@ float4 sample_c_af(float2 uv, float uv_w)
float4 sample_c(float2 uv, float uv_w, int2 xy)
{
#if PS_TEX_IS_FB == 1
return RtTexture.Load(int3(int2(xy), 0));
return RtLoad(xy);
#elif PS_REGION_RECT == 1
return Texture.Load(int3(int2(uv), 0));
#else
@@ -602,7 +682,7 @@ float4x4 sample_4p(uint4 u)
uint fetch_raw_depth(int2 xy)
{
#if PS_TEX_IS_FB == 1
float4 col = RtTexture.Load(int3(xy, 0));
float4 col = RtLoad(xy);
#else
float4 col = Texture.Load(int3(xy, 0));
#endif
@@ -612,7 +692,7 @@ uint fetch_raw_depth(int2 xy)
float4 fetch_raw_color(int2 xy)
{
#if PS_TEX_IS_FB == 1
return RtTexture.Load(int3(xy, 0));
return RtLoad(xy);
#else
return Texture.Load(int3(xy, 0));
#endif
@@ -621,7 +701,7 @@ float4 fetch_raw_color(int2 xy)
float4 fetch_c(int2 uv)
{
#if PS_TEX_IS_FB == 1
return RtTexture.Load(int3(uv, 0));
return RtLoad(uv);
#else
return Texture.Load(int3(uv, 0));
#endif
@@ -1027,7 +1107,7 @@ void ps_fbmask(inout float4 C, float2 pos_xy)
if (PS_FBMASK)
{
float multi = PS_COLCLIP_HW ? 65535.0f : 255.0f;
float4 RT = trunc(RtTexture.Load(int3(pos_xy, 0)) * multi + 0.1f);
float4 RT = trunc(RtLoad(int2(pos_xy)) * multi + 0.1f);
C = (float4)(((uint4)C & ~FbMask) | ((uint4)RT & FbMask));
}
}
@@ -1103,7 +1183,7 @@ void ps_blend(inout float4 Color, inout float4 As_rgba, float2 pos_xy)
As_rgba.rgb = (float3)1.0f;
}
float4 RT = SW_BLEND_NEEDS_RT ? RtTexture.Load(int3(pos_xy, 0)) : (float4)0.0f;
float4 RT = SW_BLEND_NEEDS_RT ? RtLoad(int2(pos_xy)) : (float4)0.0f;
if (PS_SHUFFLE && SW_BLEND_NEEDS_RT)
{
@@ -1236,20 +1316,49 @@ void ps_blend(inout float4 Color, inout float4 As_rgba, float2 pos_xy)
}
}
PS_OUTPUT ps_main(PS_INPUT input)
#if PS_ROV_EARLYDEPTHSTENCIL
[earlydepthstencil]
#endif
#if PS_ROV_COLOR || PS_ROV_DEPTH
#define DISCARD rov_discard = true
#else
#define DISCARD discard
#endif
#if (PS_RETURN_COLOR || PS_RETURN_DEPTH)
PS_OUTPUT_REAL ps_main(PS_INPUT input)
#else
void ps_main(PS_INPUT input)
#endif
{
// Must floor before depth testing.
#if PS_ZFLOOR
input.p.z = floor(input.p.z * exp2(32.0f)) * exp2(-32.0f);
#endif
#if PS_ZTST == ZTST_GEQUAL
if (input.p.z < DepthTexture.Load(int3(input.p.xy, 0)).r)
discard;
#elif PS_ZTST == ZTST_GREATER
if (input.p.z <= DepthTexture.Load(int3(input.p.xy, 0)).r)
discard;
#if PS_ROV_COLOR
rov_rt_value = RtTextureRov[input.p.xy];
#endif
#if PS_ROV_DEPTH
rov_depth_value = DepthTextureRov[input.p.xy];
#endif
#if PS_ROV_COLOR || PS_ROV_DEPTH
bool rov_discard = false;
#endif
// Use ROV discard macro for since we cannot do
// conditional discard based on value read from ROV.
#if PS_ZTST == ZTST_GEQUAL
if (input.p.z < DepthLoad(input.p.xy))
DISCARD;
#elif PS_ZTST == ZTST_GREATER
if (input.p.z <= DepthLoad(input.p.xy))
DISCARD;
#endif
float4 C = ps_color(input);
#if PS_AA1
@@ -1267,13 +1376,11 @@ PS_OUTPUT ps_main(PS_INPUT input)
bool atst_pass = atst(C);
#if PS_AFAIL == AFAIL_KEEP
#if PS_ATST != PS_ATST_NONE && PS_AFAIL == AFAIL_KEEP
if (!atst_pass)
discard;
#endif
PS_OUTPUT output;
if (PS_SCANMSK & 2)
{
// fail depth test on prohibited lines
@@ -1284,7 +1391,7 @@ PS_OUTPUT ps_main(PS_INPUT input)
float4 alpha_blend = (float4)0.0f;
if (SW_AD_TO_HW)
{
float4 RT = PS_RTA_CORRECTION ? trunc(RtTexture.Load(int3(input.p.xy, 0)) * 128.0f + 0.1f) : trunc(RtTexture.Load(int3(input.p.xy, 0)) * 255.0f + 0.1f);
float4 RT = PS_RTA_CORRECTION ? trunc(RtLoad(input.p.xy) * 128.0f + 0.1f) : trunc(RtLoad(input.p.xy) * 255.0f + 0.1f);
alpha_blend = (float4)(RT.a / 128.0f);
}
else
@@ -1308,9 +1415,9 @@ PS_OUTPUT ps_main(PS_INPUT input)
#if PS_WRITE_RG == 1
// Pseudo 16 bits access.
float rt_a = RtTexture.Load(int3(input.p.xy, 0)).g;
float rt_a = RtLoad(input.p.xy).g;
#else
float rt_a = RtTexture.Load(int3(input.p.xy, 0)).a;
float rt_a = RtLoad(input.p.xy).a;
#endif
#if (PS_DATE & 3) == 1
@@ -1329,9 +1436,8 @@ PS_OUTPUT ps_main(PS_INPUT input)
#endif
#endif
if (bad)
discard;
if (bad)
discard;
#endif
#if PS_DATE == 3
@@ -1342,6 +1448,8 @@ PS_OUTPUT ps_main(PS_INPUT input)
discard;
#endif
PS_OUTPUT output;
// Get first primitive that will write a failling alpha value
#if PS_DATE == 1
// DATM == 0
@@ -1427,32 +1535,32 @@ PS_OUTPUT ps_main(PS_INPUT input)
alpha_blend.a = float(atst_pass);
#endif
// Output color scaling
#if !PS_NO_COLOR
output.c0.a = PS_RTA_CORRECTION ? C.a / 128.0f : C.a / 255.0f;
output.c0.rgb = PS_COLCLIP_HW ? float3(C.rgb / 65535.0f) : C.rgb / 255.0f;
#if !PS_NO_COLOR1
output.c1 = alpha_blend;
#endif
#endif // !PS_NO_COLOR
// Alpha test with feedback
#if PS_AFAIL == AFAIL_FB_ONLY
if (!atst_pass)
input.p.z = DepthTexture.Load(int3(input.p.xy, 0)).r;
input.p.z = DepthLoad(input.p.xy);
#elif PS_AFAIL == AFAIL_ZB_ONLY
if (!atst_pass)
output.c0 = RtTexture.Load(int3(input.p.xy, 0));
output.c0 = RtLoad(input.p.xy);
#elif PS_AFAIL == AFAIL_RGB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z
if (!atst_pass)
{
output.c0.a = RtTexture.Load(int3(input.p.xy, 0)).a;
output.c0.a = RtLoad(input.p.xy).a;
#if PS_AFAIL == AFAIL_RGB_ONLY_SW_Z
input.p.z = DepthTexture.Load(int3(input.p.xy, 0)).r;
input.p.z = DepthLoad(input.p.xy);
#endif
}
#endif
#endif // !PS_NO_COLOR
#endif // PS_DATE != 1/2
#if PS_ZCLAMP
@@ -1461,18 +1569,47 @@ PS_OUTPUT ps_main(PS_INPUT input)
#if PS_AA1 == PS_AA1_TRIANGLE_SW_Z
if (!bool(input.interior))
input.p.z = DepthTexture.Load(int3(input.p.xy, 0)).r; // No depth update for triangle edges.
input.p.z = DepthLoad(input.p.xy); // No depth update for triangle edges.
#endif
#if ZWRITE
#if SW_DEPTH && PS_NO_COLOR1 && DX12
// Output color clone for feedback as well as real depth.
output.depth_color = input.p.z;
#endif
output.depth = input.p.z;
#if (PS_RETURN_COLOR || PS_RETURN_DEPTH)
// Output struct with the actual system values output semantics.
PS_OUTPUT_REAL output_real;
#endif
return output;
// Color write back
#if PS_RETURN_COLOR
#if PS_DATE == 1 || PS_DATE == 2
output_real.c = output.c;
#else
output_real.c0 = output.c0;
#if !PS_NO_COLOR1
output_real.c1 = output.c1;
#endif
#endif
#elif PS_RETURN_COLOR_ROV
output.c0 = (rov_discard | (FbMask == 0xFFu)) ? RtLoad(input.p.xy) : output.c0;
RtWrite(input.p.xy, output.c0);
#endif
// Depth write back
#if PS_RETURN_DEPTH
output_real.depth = input.p.z;
#if SW_DEPTH && PS_NO_COLOR1 && DX12
// Output color clone for feedback.
output_real.depth_color = input.p.z;
#endif
#elif PS_RETURN_DEPTH_ROV
#if SW_DEPTH
input.p.z = rov_discard ? DepthLoad(input.p.xy) : input.p.z;
#endif
DepthWrite(input.p.xy, input.p.z);
#endif
#if (PS_RETURN_COLOR || PS_RETURN_DEPTH)
return output_real;
#endif
}
#endif // PIXEL_SHADER
+128 -40
View File
@@ -40,6 +40,12 @@
#define PS_AA1_TRIANGLE_SW_Z 3
#endif
#ifndef PS_ROV_DEPTH_NONE
#define PS_ROV_DEPTH_NONE 0
#define PS_ROV_DEPTH_READ_WRITE 1
#define PS_ROV_DEPTH_READ_ONLY 2
#endif
// TEX_COORD_DEBUG output the uv coordinate as color. It is useful
// to detect bad sampling due to upscaling
//#define TEX_COORD_DEBUG
@@ -59,11 +65,17 @@
#define NEEDS_DEPTH_FOR_ZTST (PS_ZTST == ZTST_GEQUAL || PS_ZTST == ZTST_GREATER)
#define NEEDS_DEPTH_FOR_AA1 (PS_AA1 == PS_AA1_TRIANGLE_SW_Z)
#define NEEDS_RT (NEEDS_RT_EARLY || NEEDS_RT_FOR_AFAIL || (!PS_PRIMID_INIT && (PS_FBMASK || SW_BLEND_NEEDS_RT || SW_AD_TO_HW)) || PS_COLOR_FEEDBACK)
#define NEEDS_RT (NEEDS_RT_EARLY || NEEDS_RT_FOR_AFAIL || (!PS_PRIMID_INIT && (PS_FBMASK || SW_BLEND_NEEDS_RT || SW_AD_TO_HW)))
#define NEEDS_TEX (PS_TFX != 4)
#define SW_DEPTH (NEEDS_DEPTH_FOR_AFAIL || NEEDS_DEPTH_FOR_ZTST || NEEDS_DEPTH_FOR_AA1)
#define ZWRITE (SW_DEPTH || PS_ZCLAMP || PS_ZFLOOR)
#define PS_RETURN_COLOR_ROV (!PS_NO_COLOR && PS_ROV_COLOR)
#define PS_RETURN_COLOR (!PS_NO_COLOR && !PS_ROV_COLOR)
#define PS_RETURN_DEPTH_ROV (PS_ROV_DEPTH == PS_ROV_DEPTH_READ_WRITE)
#define PS_RETURN_DEPTH (ZWRITE && !PS_ROV_DEPTH)
#define PS_ROV_EARLYDEPTHSTENCIL (PS_ROV_COLOR && !PS_ROV_DEPTH && !ZWRITE)
layout(std140, binding = 0) uniform cb21
{
vec3 FogColor;
@@ -121,28 +133,42 @@ in SHADER
#if defined(GL_EXT_shader_framebuffer_fetch)
#undef TARGET_0_QUALIFIER
#define TARGET_0_QUALIFIER inout
#define LAST_FRAG_COLOR SV_Target0
#define LAST_FRAG_COLOR o_col0
#elif defined(GL_ARM_shader_framebuffer_fetch)
#define LAST_FRAG_COLOR gl_LastFragColorARM
#endif
#endif
#if !PS_NO_COLOR && !PS_NO_COLOR1
// Same buffer but 2 colors for dual source blending
layout(location = 0, index = 0) TARGET_0_QUALIFIER vec4 SV_Target0;
layout(location = 0, index = 1) out vec4 SV_Target1;
#elif !PS_NO_COLOR
layout(location = 0) TARGET_0_QUALIFIER vec4 SV_Target0;
#if PS_RETURN_COLOR
#if !PS_NO_COLOR && !PS_NO_COLOR1
// Same buffer but 2 colors for dual source blending
layout(location = 0, index = 0) TARGET_0_QUALIFIER vec4 o_col0;
layout(location = 0, index = 1) out vec4 o_col1;
#elif !PS_NO_COLOR
layout(location = 0) TARGET_0_QUALIFIER vec4 o_col0;
#endif
#elif PS_RETURN_COLOR_ROV
vec4 o_col0;
#endif
// Depth feedback mode 2 is for depth as color.
// Use FB fetch for the feedback if it's available.
#if SW_DEPTH && PS_NO_COLOR1 && (DEPTH_FEEDBACK_SUPPORT == 2)
#if HAS_FRAMEBUFFER_FETCH
layout(location = 1) inout float SV_Target1;
#else
layout(location = 1) out float SV_Target1;
#if HAS_FRAMEBUFFER_FETCH
layout(location = 1) inout float o_col1;
#else
layout(location = 1) out float o_col1;
#endif
#endif
#if PS_ROV_COLOR
layout(binding = 0, rgba8) uniform restrict coherent image2D RtImageRov;
vec4 rov_rt_value = vec4(0.0f, 0.0f, 0.0f, 1.0f);
#endif
#if PS_ROV_DEPTH
layout(binding = 1, r32f) uniform restrict coherent image2D DepthImageRov;
float rov_depth_value = 0.0f;
#endif
#if NEEDS_TEX
@@ -161,17 +187,19 @@ layout(binding = 3) uniform sampler2D img_prim_min;
// Depth feedback mode 1 binds depth buffer directly as a texture.
// Depth feedback mode 2 (depth as color) can use FB fetch for the feedback,
// in which case we don't need to explicitly bind depth as a texture.
#if (DEPTH_FEEDBACK_SUPPORT == 1 || (DEPTH_FEEDBACK_SUPPORT == 2 && !HAS_FRAMEBUFFER_FETCH)) && SW_DEPTH
#if (DEPTH_FEEDBACK_SUPPORT == 1 || (DEPTH_FEEDBACK_SUPPORT == 2 && !HAS_FRAMEBUFFER_FETCH)) && SW_DEPTH && !PS_ROV_DEPTH
layout(binding = 4) uniform sampler2D DepthSampler;
#endif
#if ZWRITE && PS_HAS_CONSERVATIVE_DEPTH && !SW_DEPTH
#if ZWRITE && PS_HAS_CONSERVATIVE_DEPTH && !SW_DEPTH && !PS_ROV_DEPTH
layout(depth_less) out float gl_FragDepth;
#endif
vec4 sample_from_rt()
{
#if !NEEDS_RT
#if PS_ROV_COLOR
return rov_rt_value;
#elif !NEEDS_RT
return vec4(0.0);
#elif HAS_FRAMEBUFFER_FETCH
return LAST_FRAG_COLOR;
@@ -180,14 +208,16 @@ vec4 sample_from_rt()
#endif
}
vec4 sample_from_depth()
float sample_from_depth()
{
#if !SW_DEPTH
return vec4(0.0);
#if PS_ROV_DEPTH
return rov_depth_value;
#elif !SW_DEPTH
return 0.0f;
#elif HAS_FRAMEBUFFER_FETCH && (DEPTH_FEEDBACK_SUPPORT == 2)
return SV_Target1;
return o_col1;
#else
return texelFetch(DepthSampler, ivec2(gl_FragCoord.xy), 0);
return texelFetch(DepthSampler, ivec2(gl_FragCoord.xy), 0).r;
#endif
}
@@ -1183,6 +1213,20 @@ float As = As_rgba.a;
#endif
}
#if (PS_ROV_COLOR || PS_ROV_DEPTH) && (USE_ARB_FSI || USE_NV_FSI)
layout(pixel_interlock_ordered) in;
#endif
#if PS_ROV_EARLYDEPTHSTENCIL
layout(early_fragment_tests) in;
#endif
#if PS_ROV_COLOR || PS_ROV_DEPTH
#define DISCARD rov_discard = true
#else
#define DISCARD discard
#endif
void ps_main()
{
float input_z = gl_FragCoord.z;
@@ -1192,18 +1236,40 @@ void ps_main()
input_z = floor(input_z * exp2(32.0f)) * exp2(-32.0f);
#endif
#if PS_ROV_COLOR || PS_ROV_DEPTH
#if USE_ARB_FSI
beginInvocationInterlockARB();
#elif USE_NV_FSI
beginInvocationInterlockNV();
#elif USE_INTEL_FSI
beginFragmentShaderOrderingINTEL();
#endif
#endif
#if PS_ROV_COLOR
rov_rt_value = imageLoad(RtImageRov, ivec2(gl_FragCoord.xy));
#endif
#if PS_ROV_DEPTH
rov_depth_value = imageLoad(DepthImageRov, ivec2(gl_FragCoord.xy)).r;
#endif
#if PS_ROV_COLOR || PS_ROV_DEPTH
bool rov_discard = false;
#endif
#if PS_ZTST == ZTST_GEQUAL
if (input_z < sample_from_depth().r)
discard;
if (input_z < sample_from_depth())
DISCARD;
#elif PS_ZTST == ZTST_GREATER
if (input_z <= sample_from_depth().r)
discard;
if (input_z <= sample_from_depth())
DISCARD;
#endif
#if PS_SCANMSK & 2
// fail depth test on prohibited lines
if ((int(gl_FragCoord.y) & 1) == (PS_SCANMSK & 1))
discard;
DISCARD;
#endif
#if PS_DATE >= 5
@@ -1232,7 +1298,7 @@ void ps_main()
#endif
if (bad) {
discard;
DISCARD;
}
#endif
@@ -1243,7 +1309,7 @@ void ps_main()
// the bad alpha value so we must keep it.
if (gl_PrimitiveID > stencil_ceil) {
discard;
DISCARD;
}
#endif
@@ -1264,9 +1330,9 @@ void ps_main()
bool atst_pass = atst(C);
#if PS_AFAIL == AFAIL_KEEP
#if PS_ATST != PS_ATST_NONE && PS_AFAIL == AFAIL_KEEP
if (!atst_pass)
discard;
DISCARD;
#endif
#if SW_AD_TO_HW
@@ -1293,12 +1359,12 @@ void ps_main()
#if PS_DATE == 1
// DATM == 0
// Pixel with alpha equal to 1 will failed (128-255)
SV_Target0 = (C.a > 127.5f) ? vec4(gl_PrimitiveID) : vec4(0x7FFFFFFF);
o_col0 = (C.a > 127.5f) ? vec4(gl_PrimitiveID) : vec4(0x7FFFFFFF);
return;
#elif PS_DATE == 2
// DATM == 1
// Pixel with alpha equal to 0 will failed (0-127)
SV_Target0 = (C.a < 127.5f) ? vec4(gl_PrimitiveID) : vec4(0x7FFFFFFF);
o_col0 = (C.a < 127.5f) ? vec4(gl_PrimitiveID) : vec4(0x7FFFFFFF);
return;
#endif
@@ -1372,7 +1438,7 @@ void ps_main()
// Alpha test with feedback
#if PS_AFAIL == AFAIL_FB_ONLY
if (!atst_pass)
input_z = sample_from_depth().r;
input_z = sample_from_depth();
#elif PS_AFAIL == AFAIL_ZB_ONLY
if (!atst_pass)
C = sample_from_rt();
@@ -1381,17 +1447,17 @@ void ps_main()
{
C.a = sample_from_rt().a;
#if PS_AFAIL == AFAIL_RGB_ONLY_SW_Z
input_z = sample_from_depth().r;
input_z = sample_from_depth();
#endif
}
#endif
// Warning: do not write SV_Target0 until the end since the value might be needed for
// Warning: do not write o_col0 until the end since the value might be needed for
// FB fetch in sample_from_rt().
SV_Target0 = C;
o_col0 = C;
#if !PS_NO_COLOR1
SV_Target1 = alpha_blend;
o_col1 = alpha_blend;
#endif
#endif
@@ -1401,18 +1467,40 @@ void ps_main()
#if PS_AA1 == PS_AA1_TRIANGLE_SW_Z
if (!bool(PSin.interior))
input_z = sample_from_depth().r; // No depth update for triangle edges.
input_z = sample_from_depth(); // No depth update for triangle edges.
#endif
#if ZWRITE
// Writing back color (result already written to o_col0 for non-ROV)
#if PS_RETURN_COLOR_ROV
bvec4 discard_channels = bvec4(uvec4(rov_discard) | uvec4(equal(FbMask, uvec4(0xFFu))));
o_col0 = mix(o_col0, sample_from_rt(), discard_channels);
imageStore(RtImageRov, ivec2(gl_FragCoord.xy), o_col0);
#endif
// Writing back depth
#if PS_RETURN_DEPTH
#if SW_DEPTH && PS_NO_COLOR1 && (DEPTH_FEEDBACK_SUPPORT == 2)
// Depth as color write. For depth as color feedback we write to both
// color copy and real depth to avoid having to copy back to real depth.
// Warning: do not write SV_Target1 until the end since the value might
// Warning: do not write o_col1 until the end since the value might
// be needed for FB fetch in sample_from_depth().
SV_Target1 = input_z;
o_col1 = input_z;
#endif
// Standard depth write.
gl_FragDepth = input_z;
#elif PS_RETURN_DEPTH_ROV
input_z = rov_discard ? sample_from_depth() : input_z;
imageStore(DepthImageRov, ivec2(gl_FragCoord.xy), vec4(input_z, 0, 0, 1.0f));
#endif
#if (PS_ROV_COLOR || PS_ROV_DEPTH)
#if USE_ARB_FSI
endInvocationInterlockARB();
#elif USE_NV_FSI
endInvocationInterlockNV();
#endif
// No end invocation for Intel fragment shader ordering.
#endif
}
+108 -29
View File
@@ -522,6 +522,12 @@ void main()
#define PS_AA1_TRIANGLE_SW_Z 3
#endif
#ifndef PS_ROV_DEPTH_NONE
#define PS_ROV_DEPTH_NONE 0
#define PS_ROV_DEPTH_READ_WRITE 1
#define PS_ROV_DEPTH_READ_ONLY 2
#endif
#ifndef PS_FST
#define PS_FST 0
#define PS_WMS 0
@@ -574,6 +580,8 @@ void main()
#define PS_NO_COLOR1 0
#define PS_DATE 0
#define PS_TEX_IS_FB 0
#define PS_ROV_COLOR 0
#define PS_ROV_DEPTH 0
#endif
#define SW_BLEND (PS_BLEND_A || PS_BLEND_B || PS_BLEND_D)
@@ -586,7 +594,13 @@ void main()
#define PS_FEEDBACK_LOOP_IS_NEEDED_RT (PS_TEX_IS_FB == 1 || AFAIL_NEEDS_RT || PS_FBMASK || SW_BLEND_NEEDS_RT || SW_AD_TO_HW || (PS_DATE >= 5))
#define PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH (AFAIL_NEEDS_DEPTH || ZTST_NEEDS_DEPTH || AA1_NEEDS_DEPTH)
#define ZWRITE (PS_ZCLAMP || PS_ZFLOOR || PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH)
#define ZWRITE (PS_ZCLAMP || PS_ZFLOOR || SW_DEPTH || PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH)
#define PS_RETURN_COLOR_ROV (!PS_NO_COLOR && PS_ROV_COLOR)
#define PS_RETURN_COLOR (!PS_NO_COLOR && !PS_ROV_COLOR)
#define PS_RETURN_DEPTH_ROV (PS_ROV_DEPTH == PS_ROV_DEPTH_READ_WRITE)
#define PS_RETURN_DEPTH (ZWRITE && !PS_ROV_DEPTH)
#define PS_ROV_EARLYDEPTHSTENCIL (PS_ROV_COLOR && !PS_ROV_DEPTH && !ZWRITE)
#define NEEDS_TEX (PS_TFX != 4)
@@ -625,11 +639,27 @@ layout(location = 0) in VSOutput
flat uint interior; // 1 for triangle interior; 0 for edge;
} vsIn;
#if !PS_NO_COLOR && !PS_NO_COLOR1
layout(location = 0, index = 0) out vec4 o_col0;
layout(location = 0, index = 1) out vec4 o_col1;
#elif !PS_NO_COLOR
layout(location = 0) out vec4 o_col0;
#if PS_RETURN_COLOR
#if !PS_NO_COLOR1
layout(location = 0, index = 0) out vec4 o_col0;
layout(location = 0, index = 1) out vec4 o_col1;
#elif !PS_NO_COLOR
layout(location = 0) out vec4 o_col0;
#endif
#elif PS_RETURN_COLOR_ROV
vec4 o_col0;
#endif
#if PS_ROV_COLOR
layout(set = 1, binding = 5, rgba8) uniform restrict coherent image2D RtImageRov;
vec4 rov_rt_value;
vec4 sample_from_rt() { return rov_rt_value; }
#endif
#if PS_ROV_DEPTH
layout(set = 1, binding = 6, r32f) uniform restrict coherent image2D DepthImageRov;
float rov_depth_value;
float sample_from_depth() { return rov_depth_value; }
#endif
#if NEEDS_TEX
@@ -639,27 +669,27 @@ layout(set = 1, binding = 1) uniform texture2D Palette;
#if PS_FEEDBACK_LOOP_IS_NEEDED_RT || PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH
#if defined(DISABLE_TEXTURE_BARRIER) || defined(HAS_FEEDBACK_LOOP_LAYOUT)
#if PS_FEEDBACK_LOOP_IS_NEEDED_RT
#if (PS_FEEDBACK_LOOP_IS_NEEDED_RT && !PS_ROV_COLOR)
layout(set = 1, binding = 2) uniform texture2D RtSampler;
vec4 sample_from_rt() { return texelFetch(RtSampler, ivec2(gl_FragCoord.xy), 0); }
#endif
#if PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH
#if (PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH && !PS_ROV_DEPTH)
layout(set = 1, binding = 4) uniform texture2D DepthSampler;
vec4 sample_from_depth() { return texelFetch(DepthSampler, ivec2(gl_FragCoord.xy), 0); }
float sample_from_depth() { return texelFetch(DepthSampler, ivec2(gl_FragCoord.xy), 0).r; }
#endif
#else
// Must consider each case separately since the input attachment indices must be consecutive.
#if PS_FEEDBACK_LOOP_IS_NEEDED_RT && PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH
#if (PS_FEEDBACK_LOOP_IS_NEEDED_RT && !PS_ROV_COLOR) && (PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH && !PS_ROV_DEPTH)
layout(input_attachment_index = 0, set = 1, binding = 2) uniform subpassInput RtSampler;
layout(input_attachment_index = 1, set = 1, binding = 4) uniform subpassInput DepthSampler;
vec4 sample_from_rt() { return subpassLoad(RtSampler); }
vec4 sample_from_depth() { return subpassLoad(DepthSampler); }
#elif PS_FEEDBACK_LOOP_IS_NEEDED_RT
float sample_from_depth() { return subpassLoad(DepthSampler).r; }
#elif (PS_FEEDBACK_LOOP_IS_NEEDED_RT && !PS_ROV_COLOR)
layout(input_attachment_index = 0, set = 1, binding = 2) uniform subpassInput RtSampler;
vec4 sample_from_rt() { return subpassLoad(RtSampler); }
#elif PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH
#elif (PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH && !PS_ROV_DEPTH)
layout(input_attachment_index = 0, set = 1, binding = 4) uniform subpassInput DepthSampler;
vec4 sample_from_depth() { return subpassLoad(DepthSampler); }
float sample_from_depth() { return subpassLoad(DepthSampler).r; }
#endif
#endif
#endif
@@ -1604,7 +1634,7 @@ void ps_blend(inout vec4 Color, inout vec4 As_rgba)
#elif PS_BLEND_MIX == 1
Color.rgb = ((A - B) * C_clamped + D) - (124.0f/256.0f);
#else
Color.rgb = trunc((A - B) * C + D);
Color.rgb = trunc((A - B) * C + D);
#endif
#if PS_BLEND_HW == 1
@@ -1678,6 +1708,20 @@ void ps_blend(inout vec4 Color, inout vec4 As_rgba)
#endif
}
#if PS_ROV_COLOR || PS_ROV_DEPTH
layout(pixel_interlock_ordered) in;
#endif
#if PS_ROV_EARLYDEPTHSTENCIL
layout(early_fragment_tests) in;
#endif
#if PS_ROV_COLOR || PS_ROV_DEPTH
#define DISCARD rov_discard = true
#else
#define DISCARD discard
#endif
void main()
{
float input_z = gl_FragCoord.z;
@@ -1687,18 +1731,34 @@ void main()
input_z = floor(input_z * exp2(32.0f)) * exp2(-32.0f);
#endif
#if PS_ROV_COLOR || PS_ROV_DEPTH
beginInvocationInterlockARB();
#endif
#if PS_ROV_COLOR
rov_rt_value = imageLoad(RtImageRov, ivec2(gl_FragCoord.xy));
#endif
#if PS_ROV_DEPTH
rov_depth_value = imageLoad(DepthImageRov, ivec2(gl_FragCoord.xy)).r;
#endif
#if PS_ROV_COLOR || PS_ROV_DEPTH
bool rov_discard = gl_HelperInvocation;
#endif
#if PS_ZTST == ZTST_GEQUAL
if (input_z < sample_from_depth().r)
discard;
if (input_z < sample_from_depth())
DISCARD;
#elif PS_ZTST == ZTST_GREATER
if (input_z <= sample_from_depth().r)
discard;
if (input_z <= sample_from_depth())
DISCARD;
#endif
#if PS_SCANMSK & 2
// fail depth test on prohibited lines
if ((int(gl_FragCoord.y) & 1) == (PS_SCANMSK & 1))
discard;
DISCARD;
#endif
#if PS_DATE >= 5
@@ -1726,7 +1786,7 @@ void main()
#endif
if (bad) {
discard;
DISCARD;
}
#endif // PS_DATE >= 5
@@ -1737,7 +1797,7 @@ void main()
// the bad alpha value so we must keep it.
if (gl_PrimitiveID > stencil_ceil) {
discard;
DISCARD;
}
#endif
@@ -1758,9 +1818,10 @@ void main()
bool atst_pass = atst(C);
#if PS_AFAIL == AFAIL_KEEP
if (!atst_pass)
discard;
#if PS_ATST != PS_ATST_NONE && PS_AFAIL == AFAIL_KEEP
if (!atst_pass) {
DISCARD;
}
#endif
#if SW_AD_TO_HW
@@ -1852,6 +1913,7 @@ void main()
alpha_blend.a = float(atst_pass);
#endif
// Output color scaling
#if !PS_NO_COLOR
#if PS_RTA_CORRECTION
o_col0.a = C.a / 128.0f;
@@ -1870,7 +1932,7 @@ void main()
// Alpha test with feedback
#if PS_AFAIL == AFAIL_FB_ONLY
if (!atst_pass)
input_z = sample_from_depth().r;
input_z = sample_from_depth();
#elif PS_AFAIL == AFAIL_ZB_ONLY
if (!atst_pass)
o_col0 = sample_from_rt();
@@ -1879,7 +1941,7 @@ void main()
{
o_col0.a = sample_from_rt().a;
#if PS_AFAIL == AFAIL_RGB_ONLY_SW_Z
input_z = sample_from_depth().r;
input_z = sample_from_depth();
#endif
}
#endif
@@ -1891,11 +1953,28 @@ void main()
#if PS_AA1 == PS_AA1_TRIANGLE_SW_Z
if (!bool(vsIn.interior))
input_z = sample_from_depth().r; // No depth update for triangle edges.
input_z = sample_from_depth(); // No depth update for triangle edges.
#endif
#if ZWRITE
// Writing back color (result already written to o_col0 for non-ROV)
#if PS_RETURN_COLOR_ROV
bvec4 discard_channels = bvec4(uvec4(rov_discard) | uvec4(equal(FbMask, uvec4(0xFFu))));
o_col0 = mix(o_col0, sample_from_rt(), discard_channels);
imageStore(RtImageRov, ivec2(gl_FragCoord.xy), o_col0);
#endif
// Writing back depth
#if PS_RETURN_DEPTH
gl_FragDepth = input_z;
#elif PS_RETURN_DEPTH_ROV
input_z = rov_discard ? sample_from_depth() : input_z;
imageStore(DepthImageRov, ivec2(gl_FragCoord.xy), vec4(input_z, 0, 0, 1.0f));
#endif
#if PS_ROV_COLOR || PS_ROV_DEPTH
endInvocationInterlockARB();
#endif
#endif // PS_DATE
}
+12
View File
@@ -91,6 +91,9 @@ static double s_last_barriers = 0;
static double s_last_copies = 0;
static double s_last_uploads = 0;
static double s_last_readbacks = 0;
static double s_last_depth_copies_rov = 0;
static double s_last_draws_rov = 0;
static double s_last_barriers_rov = 0;
static u64 s_total_internal_draws = 0;
static u64 s_total_draws = 0;
static u64 s_total_render_passes = 0;
@@ -98,6 +101,9 @@ static u64 s_total_barriers = 0;
static u64 s_total_copies = 0;
static u64 s_total_uploads = 0;
static u64 s_total_readbacks = 0;
static u64 s_total_depth_copies_rov = 0;
static u64 s_total_draws_rov = 0;
static u64 s_total_barriers_rov = 0;
static u32 s_total_frames = 0;
static u32 s_total_drawn_frames = 0;
@@ -287,6 +293,9 @@ void Host::BeginPresentFrame()
update_stat(GSPerfMon::TextureCopies, s_total_copies, s_last_copies);
update_stat(GSPerfMon::TextureUploads, s_total_uploads, s_last_uploads);
update_stat(GSPerfMon::Readbacks, s_total_readbacks, s_last_readbacks);
update_stat(GSPerfMon::DepthCopiesROV, s_total_depth_copies_rov, s_last_depth_copies_rov);
update_stat(GSPerfMon::DrawCallsROV, s_total_draws_rov, s_last_draws_rov);
update_stat(GSPerfMon::BarriersROV, s_total_barriers_rov, s_last_barriers_rov);
const bool idle_frame = s_total_frames && (last_draws == s_total_internal_draws && last_uploads == s_total_uploads);
@@ -913,6 +922,9 @@ void GSRunner::DumpStats()
Console.WriteLn(fmt::format("@HWSTAT@ Copies: {} (avg {})", s_total_copies, static_cast<u64>(std::ceil(s_total_copies / static_cast<double>(s_total_drawn_frames)))));
Console.WriteLn(fmt::format("@HWSTAT@ Uploads: {} (avg {})", s_total_uploads, static_cast<u64>(std::ceil(s_total_uploads / static_cast<double>(s_total_drawn_frames)))));
Console.WriteLn(fmt::format("@HWSTAT@ Readbacks: {} (avg {})", s_total_readbacks, static_cast<u64>(std::ceil(s_total_readbacks / static_cast<double>(s_total_drawn_frames)))));
Console.WriteLn(fmt::format("@HWSTAT@ Depth Copies (ROV): {} (avg {})", s_total_depth_copies_rov, static_cast<u64>(std::ceil(s_total_depth_copies_rov / static_cast<double>(s_total_drawn_frames)))));
Console.WriteLn(fmt::format("@HWSTAT@ Draws Calls (ROV): {} (avg {})", s_total_draws_rov, static_cast<u64>(std::ceil(s_total_draws_rov / static_cast<double>(s_total_drawn_frames)))));
Console.WriteLn(fmt::format("@HWSTAT@ Barriers (ROV): {} (avg {})", s_total_barriers_rov, static_cast<u64>(std::ceil(s_total_barriers_rov / static_cast<double>(s_total_drawn_frames)))));
if (s_perf_enable)
{
Console.WriteLn(fmt::format("@HWSTAT@ Minimum Frame Time: {:.3f} ms ({:.3f} FPS)", PerformanceMetrics::GetMinimumFrameTime(), 1000.0f / PerformanceMetrics::GetMinimumFrameTime()));
@@ -126,6 +126,13 @@
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="rovBarriersVK">
<property name="text">
<string>ROV Barriers Vulkan</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="6" column="0">
@@ -353,6 +360,7 @@
<tabstop>gsDumpCompression</tabstop>
<tabstop>texturePreloading</tabstop>
<tabstop>exclusiveFullscreenControl</tabstop>
<tabstop>rovBarriersVK</tabstop>
<tabstop>useBlitSwapChain</tabstop>
<tabstop>extendedUpscales</tabstop>
<tabstop>disableMailboxPresentation</tabstop>
@@ -72,6 +72,13 @@
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="rov">
<property name="text">
<string>ROV</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="enableHWFixes">
<property name="text">
<string>Manual Hardware Renderer Fixes</string>
@@ -243,6 +250,7 @@
<tabstop>mipmapping</tabstop>
<tabstop>accurateAlphaTest</tabstop>
<tabstop>hwAA1</tabstop>
<tabstop>rov</tabstop>
<tabstop>enableHWFixes</tabstop>
</tabstops>
<resources/>
@@ -113,6 +113,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_hw.mipmapping, "EmuCore/GS", "hw_mipmap", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_hw.accurateAlphaTest, "EmuCore/GS", "HWAccurateAlphaTest", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_hw.hwAA1, "EmuCore/GS", "HWAA1", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_hw.rov, "EmuCore/GS", "HWROV", false);
SettingWidgetBinder::BindWidgetToIntSetting(
sif, m_hw.blending, "EmuCore/GS", "accurate_blending_unit", static_cast<int>(AccBlendLevel::Basic));
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_hw.enableHWFixes, "EmuCore/GS", "UserHacks", false);
@@ -239,6 +240,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_advanced.palFrameRate, "EmuCore/GS", "FrameRatePAL", 50.00f);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_advanced.spinCPUDuringReadbacks, "EmuCore/GS", "HWSpinCPUForReadbacks", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_advanced.spinGPUDuringReadbacks, "EmuCore/GS", "HWSpinGPUForReadbacks", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_advanced.rovBarriersVK, "EmuCore/GS", "HWROVBarriersVK", false);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_advanced.texturePreloading, "EmuCore/GS", "texture_preloading", static_cast<int>(TexturePreloadingLevel::Off));
setTabVisible(m_advanced_tab, QtHost::ShouldShowAdvancedSettings());
@@ -497,6 +499,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
dialog()->registerWidgetHelp(
m_hw.hwAA1, tr("AA1"), tr("Unchecked"), tr("Enables AA1 (PS2 antialiasing), which some games require to render correctly. This may result in a heavy performance penalty."));
dialog()->registerWidgetHelp(
m_hw.rov, tr("ROV"), tr("Unchecked"), tr("Enables ROV (Rasterizer Ordered View), which allows feedback loops to be executed with fewer draw calls. Can improve performance in feedback heavy games with higher accuracy settings."));
dialog()->registerWidgetHelp(
m_hw.textureFiltering, tr("Texture Filtering"), tr("Bilinear (PS2)"),
tr("Changes what filtering algorithm is used to map textures to surfaces.<br> "
+3
View File
@@ -793,6 +793,9 @@ struct Pcsx2Config
HWMipmap : 1,
HWAccurateAlphaTest : 1,
HWAA1 : 1,
HWROV : 1,
HWROVLogging : 1,
HWROVBarriersVK : 1,
ManualUserHacks : 1,
UserHacks_AlignSpriteX : 1,
UserHacks_CPUFBConversion : 1,
+30 -10
View File
@@ -701,16 +701,36 @@ void GSgetStats(SmallStringBase& info)
}
else
{
info.format("{} HW | {} PRIM | {} DRW | {} DRWC | {} BAR | {} RP | {} RB | {} TC | {} TU",
api_name,
(int)pm.Get(GSPerfMon::Prim),
(int)pm.Get(GSPerfMon::Draw),
(int)std::ceil(pm.Get(GSPerfMon::DrawCalls)),
(int)std::ceil(pm.Get(GSPerfMon::Barriers)),
(int)std::ceil(pm.Get(GSPerfMon::RenderPasses)),
(int)std::ceil(pm.Get(GSPerfMon::Readbacks)),
(int)std::ceil(pm.Get(GSPerfMon::TextureCopies)),
(int)std::ceil(pm.Get(GSPerfMon::TextureUploads)));
if (!GSConfig.HWROV)
{
info.format("{} HW | {} PRIM | {} DRW | {} DRWC | {} BAR | {} RP | {} RB | {} TC | {} TU",
api_name,
(int)pm.Get(GSPerfMon::Prim),
(int)pm.Get(GSPerfMon::Draw),
(int)std::ceil(pm.Get(GSPerfMon::DrawCalls)),
(int)std::ceil(pm.Get(GSPerfMon::Barriers)),
(int)std::ceil(pm.Get(GSPerfMon::RenderPasses)),
(int)std::ceil(pm.Get(GSPerfMon::Readbacks)),
(int)std::ceil(pm.Get(GSPerfMon::TextureCopies)),
(int)std::ceil(pm.Get(GSPerfMon::TextureUploads)));
}
else
{
// Add ROV stats along standard stats.
info.format("{} HW | {} PRIM | {} DRW | {}/{} DRWC | {}/{} BAR | {} RP | {} RB | {}/{} TC | {} TU",
api_name,
(int)pm.Get(GSPerfMon::Prim),
(int)pm.Get(GSPerfMon::Draw),
(int)std::ceil(pm.Get(GSPerfMon::DrawCalls)),
(int)std::ceil(pm.Get(GSPerfMon::DrawCallsROV)),
(int)std::ceil(pm.Get(GSPerfMon::Barriers)),
(int)std::ceil(pm.Get(GSPerfMon::BarriersROV)),
(int)std::ceil(pm.Get(GSPerfMon::RenderPasses)),
(int)std::ceil(pm.Get(GSPerfMon::Readbacks)),
(int)std::ceil(pm.Get(GSPerfMon::TextureCopies)),
(int)std::ceil(pm.Get(GSPerfMon::DepthCopiesROV)),
(int)std::ceil(pm.Get(GSPerfMon::TextureUploads)));
}
}
}
+5
View File
@@ -46,10 +46,15 @@
#define GL_POP() g_gs_device->PopDebugGroup()
#define GL_INS(...) g_gs_device->InsertDebugMessage(GSDevice::DebugMessageCategory::Message, __VA_ARGS__)
#define GL_PERF(...) g_gs_device->InsertDebugMessage(GSDevice::DebugMessageCategory::Performance, __VA_ARGS__)
#define GL_ROV(...) \
if (GSConfig.HWROVLogging) \
Console.Warning(__VA_ARGS__); \
g_gs_device->InsertDebugMessage(GSDevice::DebugMessageCategory::Message, __VA_ARGS__);
#else
#define GL_PUSH_(...) (void)(0)
#define GL_PUSH(...) (void)(0)
#define GL_POP() (void)(0)
#define GL_INS(...) (void)(0)
#define GL_PERF(...) (void)(0)
#define GL_ROV(...) (void)(0)
#endif
+3
View File
@@ -23,6 +23,9 @@ public:
SyncPoint,
Barriers,
RenderPasses,
DepthCopiesROV, // Overlaps with regular texture copies.
DrawCallsROV, // Overlaps with regular draw calls.
BarriersROV, // Overlaps with regular barriers.
CounterLast,
// Reused counters for HW.
+2 -2
View File
@@ -203,8 +203,8 @@ enum GS_AFAIL
enum GS_ALPHA_BITS
{
ALPHA_ABC_CS = 0,
ALPHA_ABC_CD = 1,
ALPHA_ABD_CS = 0,
ALPHA_ABD_CD = 1,
ALPHA_C_AS = 0,
ALPHA_C_AD = 1,
ALPHA_C_FIX = 2,
+32 -7
View File
@@ -4226,7 +4226,7 @@ bool GSState::TrianglesAreQuads(bool shuffle_check)
}
template<u32 primclass>
GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlist, bool save_bbox, float bbox_scale)
GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlist, bool save_bbox, float bbox_scale, u32* max_size)
{
const GSVector4i xyof = m_context->scissor.xyof.xyxy();
@@ -4274,7 +4274,14 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
if (primclass == GS_TRIANGLE_CLASS && m_quad_check_valid && m_are_quads && !using_aa1)
{
// The triangles-are-quads check already ensures that there is no overlap.
m_drawlist.push_back(m_index->tail / n);
if (save_drawlist)
{
m_drawlist.push_back(m_index->tail / n);
}
else if (max_size)
{
*max_size = 1;
}
if (save_bbox)
{
const GSVector4i draw_area = GSVector4i(m_vt.m_min.p.upld(m_vt.m_max.p) * GSVector4(16.0f)) + xyof;
@@ -4286,6 +4293,7 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
PRIM_OVERLAP overlap = PRIM_OVERLAP_NO;
bool check_quads = (primclass == GS_TRIANGLE_CLASS) && !using_aa1;
u32 drawlist_size = 0;
u32 i = 0;
u32 skip = 0; // Number of indices to skip if we have the bbox from the previous iteration.
@@ -4683,6 +4691,17 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
{
m_drawlist.push_back((j - i) / n); // Prim count
}
else if (max_size)
{
// If the max size pointer is passed it means we just want to peek at
// the drawlist size up to the given limit to avoid unecessary work.
drawlist_size++;
if (drawlist_size >= *max_size)
{
*max_size = drawlist_size;
return drawlist_size > 0 ? PRIM_OVERLAP_YES : PRIM_OVERLAP_UNKNOW;
}
}
else if (j < count)
{
return PRIM_OVERLAP_YES; // Early exit if not saving drawlist.
@@ -4696,21 +4715,27 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
all = bbox;
i = j;
}
if (max_size)
{
*max_size = drawlist_size;
}
return overlap;
}
GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlist(bool save_drawlist, bool save_bbox, float bbox_scale)
GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlist(bool save_drawlist, bool save_bbox, float bbox_scale, u32* max_size)
{
switch (m_vt.m_primclass)
{
case GS_POINT_CLASS:
return GetPrimitiveOverlapDrawlistImpl<GS_POINT_CLASS>(save_drawlist, save_bbox, bbox_scale);
return GetPrimitiveOverlapDrawlistImpl<GS_POINT_CLASS>(save_drawlist, save_bbox, bbox_scale, max_size);
case GS_LINE_CLASS:
return GetPrimitiveOverlapDrawlistImpl<GS_LINE_CLASS>(save_drawlist, save_bbox, bbox_scale);
return GetPrimitiveOverlapDrawlistImpl<GS_LINE_CLASS>(save_drawlist, save_bbox, bbox_scale, max_size);
case GS_TRIANGLE_CLASS:
return GetPrimitiveOverlapDrawlistImpl<GS_TRIANGLE_CLASS>(save_drawlist, save_bbox, bbox_scale);
return GetPrimitiveOverlapDrawlistImpl<GS_TRIANGLE_CLASS>(save_drawlist, save_bbox, bbox_scale, max_size);
case GS_SPRITE_CLASS:
return GetPrimitiveOverlapDrawlistImpl<GS_SPRITE_CLASS>(save_drawlist, save_bbox, bbox_scale);
return GetPrimitiveOverlapDrawlistImpl<GS_SPRITE_CLASS>(save_drawlist, save_bbox, bbox_scale, max_size);
default:
pxFail("Invalid primclass."); // Impossible.
return PRIM_OVERLAP_UNKNOW;
+4 -2
View File
@@ -533,8 +533,10 @@ public:
bool TrianglesAreQuadsImpl();
bool TrianglesAreQuads(bool shuffle_check = false);
template <u32 primclass>
PRIM_OVERLAP GetPrimitiveOverlapDrawlistImpl(bool save_drawlist = false, bool save_bbox = false, float bbox_scale = 1.0f);
PRIM_OVERLAP GetPrimitiveOverlapDrawlist(bool save_drawlist = false, bool save_bbox = false, float bbox_scale = 1.0f);
PRIM_OVERLAP GetPrimitiveOverlapDrawlistImpl(bool save_drawlist = false, bool save_bbox = false,
float bbox_scale = 1.0f, u32* max_size = nullptr);
PRIM_OVERLAP GetPrimitiveOverlapDrawlist(bool save_drawlist = false, bool save_bbox = false,
float bbox_scale = 1.0f, u32* max_size = nullptr);
PRIM_OVERLAP PrimitiveOverlap(bool save_drawlist = false);
template <u32 primclass, bool fst>
void GetPrimitiveOverlapDrawlistTextureBBoxImpl(float bbox_scale = 1.0f);
+38 -3
View File
@@ -693,6 +693,12 @@ void GSDevice::Recycle(GSTexture* t)
t->SetLastFrameUsed(m_frame);
t->ClearUnorderedAccess();
#ifdef PCSX2_DEVBUILD
t->SetDebugName("");
#endif
FastList<GSTexture*>& pool = m_pool[!t->IsTexture()];
pool.push_front(t);
m_pool_memory_usage += t->GetMemUsage();
@@ -776,6 +782,18 @@ GSTexture* GSDevice::CreateDepthStencil(const GSVector2i& size, bool clear, bool
clear, !prefer_reuse);
}
GSTexture* GSDevice::CreateDepthColor(int w, int h, bool clear, bool prefer_reuse)
{
return FetchSurface(GSTexture::Type::RenderTarget, w, h, 1, GSTexture::Format::DepthColor,
clear, !prefer_reuse);
}
GSTexture* GSDevice::CreateDepthColor(const GSVector2i& size, bool clear, bool prefer_reuse)
{
return FetchSurface(GSTexture::Type::RenderTarget, size.x, size.y, 1, GSTexture::Format::DepthColor,
clear, !prefer_reuse);
}
GSTexture* GSDevice::CreateTexture(int w, int h, int mipmap_levels, GSTexture::Format format, bool prefer_reuse /* = false */)
{
pxAssert(mipmap_levels != 0 && (mipmap_levels < 0 || mipmap_levels <= GetMipmapLevelsForSize(w, h)));
@@ -1486,9 +1504,9 @@ static const char* GetSetDATMName(SetDATM datm)
return "Unknown";
}
static const char* GetPSAA1Name(u32 aa1)
static const char* GetPSAA1Name(GSHWDrawConfig::PS_AA1 aa1)
{
switch (static_cast<GSHWDrawConfig::PS_AA1>(aa1))
switch (aa1)
{
case GSHWDrawConfig::PS_AA1::NONE: return "NONE";
case GSHWDrawConfig::PS_AA1::LINE: return "LINE";
@@ -1509,6 +1527,17 @@ static const char* GetTexHazardName(u32 tex_hazard)
return "Unknown";
}
static const char* GetPSROVDepthname(GSHWDrawConfig::PS_ROV_DEPTH rov_depth)
{
switch (rov_depth)
{
case GSHWDrawConfig::PS_ROV_DEPTH::NONE: return "NONE";
case GSHWDrawConfig::PS_ROV_DEPTH::READ_ONLY: return "READ_ONLY";
case GSHWDrawConfig::PS_ROV_DEPTH::READ_WRITE: return "READ_WRITE";
}
return "Unknown";
}
static void DumpPSSelector(DrawConfigWriter& out, const GSHWDrawConfig::PSSelector& ps)
{
out.WriteLn("aem_fmt: {}", ps.aem_fmt);
@@ -1564,9 +1593,13 @@ static void DumpPSSelector(DrawConfigWriter& out, const GSHWDrawConfig::PSSelect
out.WriteLn("point_sampler: {}", ps.point_sampler);
out.WriteLn("region_rect: {}", ps.region_rect);
out.WriteLn("scanmsk: {} ({})", GSUtil::GetSCANMSKName(ps.scanmsk), ps.scanmsk);
out.WriteLn("aa1: {} ({})", static_cast<u32>(ps.aa1), GetPSAA1Name(static_cast<u32>(ps.aa1)));
out.WriteLn("aa1: {} ({})", GetPSAA1Name(ps.aa1), static_cast<u32>(ps.aa1));
out.WriteLn("abe: {}", static_cast<u32>(ps.abe));
out.WriteLn("sw_aniso: {}", ps.sw_aniso);
out.WriteLn("rov_color: {}", ps.rov_color);
out.WriteLn("rov_depth: {} ({})", GetPSROVDepthname(ps.rov_depth), static_cast<u32>(ps.rov_depth));
out.WriteLn("ztst: {} ({})", GSUtil::GetZTSTName(ps.ztst), static_cast<u32>(ps.ztst));
out.WriteLn("zfloor: {}", static_cast<u32>(ps.zfloor));
}
static void DumpVSSelector(DrawConfigWriter& out, const GSHWDrawConfig::VSSelector& vs)
@@ -1690,6 +1723,8 @@ static void DumpConfig(DrawConfigWriter& out, const GSHWDrawConfig& conf,
out.WriteLn("topology: {} ({})", GetTopologyName(conf.topology), static_cast<u32>(conf.topology));
out.WriteLn("require_one_barrier: {}", conf.require_one_barrier);
out.WriteLn("require_full_barrier: {}", conf.require_full_barrier);
DumpVector4(out, "drawarea", conf.drawarea);
DumpVector4(out, "samplearea", conf.samplearea);
out.WriteLn("tex_hazard: {}", GetTexHazardName(conf.tex_hazard));
out.WriteLn("destination_alpha: {} ({})", GetDestinationAlphaModeName(conf.destination_alpha), static_cast<u32>(conf.destination_alpha));
+65 -2
View File
@@ -666,6 +666,7 @@ struct alignas(16) GSHWDrawConfig
using PS_ATST = GSShader::PS_ATST;
using PS_AFAIL = GSShader::PS_AFAIL;
using PS_AA1 = GSShader::PS_AA1;
using PS_ROV_DEPTH = GSShader::PS_ROV_DEPTH;
#pragma pack(push, 1)
struct VSSelector
{
@@ -786,12 +787,16 @@ struct alignas(16) GSHWDrawConfig
// Anisotropic filtering
u32 sw_aniso : 5;
// ROVs
u32 rov_color : 1;
PS_ROV_DEPTH rov_depth : 2;
};
struct
{
u64 key_lo;
u32 key_hi;
u64 key_hi;
};
};
__fi PSSelector() : key_lo(0), key_hi(0) {}
@@ -800,6 +805,21 @@ struct alignas(16) GSHWDrawConfig
__fi bool operator!=(const PSSelector& rhs) const { return (key_lo != rhs.key_lo || key_hi != rhs.key_hi); }
__fi bool operator<(const PSSelector& rhs) const { return (key_lo < rhs.key_lo || key_hi < rhs.key_hi); }
__fi bool IsSWBlending() const
{
return blend_a || blend_b || blend_d;
}
__fi bool IsZTesting() const
{
return ztst == ZTST_GEQUAL || ztst == ZTST_GREATER;
}
__fi bool IsAlphaTesting() const
{
return atst != PS_ATST::NONE;
}
__fi bool IsFeedbackLoopRT() const
{
const u32 sw_blend_bits = blend_a | blend_b | blend_d;
@@ -816,6 +836,11 @@ struct alignas(16) GSHWDrawConfig
return afail_needs_depth || ztst_needs_depth || aa1_needs_depth;
}
__fi bool HasShaderDiscard() const
{
return (IsAlphaTesting() && afail == PS_AFAIL::KEEP) || scanmsk || date || IsZTesting();
}
/// Disables color output from the pixel shader, this is done when all channels are masked.
__fi void DisableColorOutput()
{
@@ -844,9 +869,39 @@ struct alignas(16) GSHWDrawConfig
{
aa1 = PS_AA1::TRIANGLE;
}
if (rov_depth == PS_ROV_DEPTH::READ_WRITE)
{
rov_depth = PS_ROV_DEPTH::READ_ONLY;
}
}
__fi bool HasColorOutput() const
{
return !no_color;
}
__fi bool HasDepthOutput() const
{
return zfloor || zclamp || IsFeedbackLoopDepth() || (rov_depth == PS_ROV_DEPTH::READ_WRITE);
}
__fi bool HasColorROV() const
{
return rov_color != 0;
}
__fi bool HasDepthROV() const
{
return rov_depth == PS_ROV_DEPTH::READ_ONLY || rov_depth == PS_ROV_DEPTH::READ_WRITE;
}
__fi bool HasDepthROVWrite() const
{
return rov_depth == PS_ROV_DEPTH::READ_WRITE;
}
};
static_assert(sizeof(PSSelector) == 12, "PSSelector is 12 bytes");
static_assert(sizeof(PSSelector) == 16, "PSSelector is 12 bytes");
#pragma pack(pop)
struct PSSelectorHash
{
@@ -1255,6 +1310,11 @@ struct alignas(16) GSHWDrawConfig
{
return ps.IsFeedbackLoopDepth() || (tex_hazard == TEX_HAZARD_DEPTH);
}
bool IsBlending()
{
return blend.enable || blend_multi_pass.enable || ps.IsSWBlending();
}
// Dumping
static void DumpConfig(const std::string& path, const GSHWDrawConfig& conf,
@@ -1330,6 +1390,7 @@ public:
bool test_and_sample_depth: 1; ///< Supports concurrently binding the depth-stencil buffer for sampling and depth testing.
bool depth_feedback : 1; ///< Depth feedback loops can be done with DS directly (otherwise need to copy to separate RT). Implies `feedback_loops`.
bool aa1 : 1; ///< Supports the GS AA1 feature.
bool rov : 1; ///< Supports rasterizer ordered views for both depth and color.
FeatureSupport()
{
memset(this, 0, sizeof(*this));
@@ -1563,9 +1624,11 @@ public:
GSTexture* CreateRenderTarget(int w, int h, GSTexture::Format format, bool clear = true, bool prefer_reuse = true);
GSTexture* CreateDepthStencil(int w, int h, bool clear = true, bool prefer_reuse = true);
GSTexture* CreateDepthColor(int w, int h, bool clear = true, bool prefer_reuse = true);
GSTexture* CreateTexture(int w, int h, int mipmap_levels, GSTexture::Format format, bool prefer_reuse = false);
GSTexture* CreateRenderTarget(const GSVector2i& size, GSTexture::Format format, bool clear = true, bool prefer_reuse = true);
GSTexture* CreateDepthStencil(const GSVector2i& size, bool clear = true, bool prefer_reuse = true);
GSTexture* CreateDepthColor(const GSVector2i& size, bool clear = true, bool prefer_reuse = true);
GSTexture* CreateTexture(const GSVector2i& size, int mipmap_levels, GSTexture::Format format, bool prefer_reuse = false);
GSTexture* CreateCompatible(GSTexture* tex, bool clear = true, bool prefer_reuse = true);
GSTexture* CreateCompatible(GSTexture* tex, const GSVector2i& size, bool clear = true, bool prefer_reuse = true);
@@ -56,4 +56,11 @@ enum class PS_AA1 : uint32_t
TRIANGLE_SW_Z = 3, ///< AA1 triangles with software Z discard
};
enum class PS_ROV_DEPTH : uint32_t
{
NONE = 0,
READ_WRITE = 1,
READ_ONLY = 2,
};
} // namespace GSShader
+14
View File
@@ -73,6 +73,13 @@ protected:
bool m_needs_mipmaps_generated = true;
ClearValue m_clear_value = {};
// For GL/DX11 since they don't track layouts.
// Used heuristically to decide whether to use ROV for a draw.
bool m_unordered_access = false;
#ifdef PCSX2_DEVBUILD
std::string m_debug_name;
#endif
public:
GSTexture();
virtual ~GSTexture();
@@ -87,6 +94,7 @@ public:
#ifdef PCSX2_DEVBUILD
virtual void SetDebugName(std::string_view name) = 0;
const std::string& GetDebugName() { return m_debug_name; }
#endif
bool Save(const std::string& fn);
@@ -173,6 +181,12 @@ public:
// Typical size of a RGBA texture
u32 GetMemUsage() const { return m_size.x * m_size.y * (m_format == Format::UNorm8 ? 1 : 4); }
// The unordered access flag is sticky, so once it's set we keep using ROV for the target
// until it's recycled/destroyed. Only used for DX11/GL, which don't track actual resource layout/state.
virtual bool IsUnorderedAccess() const { return m_unordered_access; }
void UseUnorderedAccess() { m_unordered_access = true; }
void ClearUnorderedAccess() { m_unordered_access = false; }
// Helper routines for formats/types
static bool IsCompressedFormat(Format format) { return (format >= Format::BC1 && format <= Format::BC7); }
};
@@ -65,6 +65,7 @@ bool D3D11ShaderCache::Open(D3D_FEATURE_LEVEL feature_level, bool debug)
m_shader_model = D3D::ShaderModel::SM41;
break;
case D3D_FEATURE_LEVEL_11_0:
case D3D_FEATURE_LEVEL_11_1:
m_shader_model = D3D::ShaderModel::SM50;
break;
default:
+104 -26
View File
@@ -109,7 +109,8 @@ bool GSDevice11::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
wil::com_ptr_nothrow<IDXGIAdapter1> dxgi_adapter = D3D::GetAdapterByName(m_dxgi_factory.get(), GSConfig.Adapter);
static constexpr std::array<D3D_FEATURE_LEVEL, 2> requested_feature_levels = {{
static constexpr std::array<D3D_FEATURE_LEVEL, 3> requested_feature_levels = {{
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_0,
}};
@@ -592,11 +593,18 @@ bool GSDevice11::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
Host::OSD_WARNING_DURATION);
}
// 1x1 dummy texture.
m_null_texture = CreateSurface(GSTexture::Type::RenderTarget, 1, 1, 1, GSTexture::Format::Color);
if (!m_null_texture)
return false;
return true;
}
void GSDevice11::Destroy()
{
delete m_null_texture;
GSDevice::Destroy();
DestroySwapChain();
DestroyTimestampQueries();
@@ -683,6 +691,12 @@ void GSDevice11::SetFeatures(IDXGIAdapter1* adapter)
m_conservative_depth = (m_feature_level >= D3D_FEATURE_LEVEL_11_0);
m_rgba16_unorm_hw_blend = IsTextureFormatHWBlendable(m_dev.get(), DXGI_FORMAT_R16G16B16A16_UNORM);
m_uav_texture = (m_feature_level >= D3D_FEATURE_LEVEL_11_0);
D3D11_FEATURE_DATA_D3D11_OPTIONS2 options2{};
m_dev->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS2, &options2, sizeof(options2));
m_features.rov = m_uav_texture && options2.ROVsSupported;
// Let the user know if said features are available.
Console.WriteLnFmt("D3D11: DXTn Texture Compression: {}", m_features.dxt_textures ? "Supported" : "Not Supported");
Console.WriteLnFmt("D3D11: BC6/7 Texture Compression: {}", m_features.bptc_textures ? "Supported" : "Not Supported");
@@ -1322,6 +1336,8 @@ GSTexture* GSDevice11::CreateSurface(GSTexture::Type type, int width, int height
{
case GSTexture::Type::RenderTarget:
desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
if (m_uav_texture)
desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS;
break;
case GSTexture::Type::DepthStencil:
desc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
@@ -1331,6 +1347,7 @@ GSTexture* GSDevice11::CreateSurface(GSTexture::Type type, int width, int height
desc.MiscFlags = (levels > 1 && !GSTexture::IsCompressedFormat(format)) ? D3D11_RESOURCE_MISC_GENERATE_MIPS : 0;
break;
case GSTexture::Type::RWTexture:
pxAssert(m_uav_texture);
desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE;
break;
default:
@@ -1482,7 +1499,7 @@ void GSDevice11::DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTextur
// ps
PSSetShaderResource(0, sTex);
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
PSSetSamplerState(filter == Biln ? m_convert.ln.get() : m_convert.pt.get());
PSSetShader(ps, ps_cb);
@@ -1547,7 +1564,7 @@ void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
// ps
PSSetShaderResource(0, sTex);
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
PSSetSamplerState(filter == Biln ? m_convert.ln.get() : m_convert.pt.get());
PSSetShader(m_present.ps[static_cast<u32>(shader)].get(), m_present.ps_cb.get());
@@ -1698,7 +1715,7 @@ void GSDevice11::DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rect
IASetIndexBuffer(m_ib.get());
CommitClear(rects[0].src);
PSSetShaderResource(0, rects[0].src);
PSSetShaderResource(TEXTURE_TEXTURE, rects[0].src);
PSSetSamplerState(rects[0].filter == Biln ? m_convert.ln.get() : m_convert.pt.get());
OMSetBlendState(m_convert.bs[rects[0].wmask.wrgba].get(), 0.0f);
@@ -1921,6 +1938,8 @@ void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstant
sm.AddMacro("PS_AA1", static_cast<u32>(sel.aa1));
sm.AddMacro("PS_ABE", sel.abe);
sm.AddMacro("PS_ANISOTROPIC_FILTERING", sel.sw_aniso);
sm.AddMacro("PS_ROV_COLOR", sel.rov_color);
sm.AddMacro("PS_ROV_DEPTH", static_cast<u32>(sel.rov_depth));
wil::com_ptr_nothrow<ID3D11PixelShader> ps = m_shader_cache.GetPixelShader(m_dev.get(), m_tfx_source, sm.GetPtr(), "ps_main");
i = m_ps.try_emplace(sel, std::move(ps)).first;
@@ -2339,7 +2358,7 @@ void GSDevice11::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSV
// ps
PSSetShaderResource(0, rt);
PSSetShaderResource(TEXTURE_TEXTURE, rt);
PSSetSamplerState(m_convert.pt.get());
PSSetShader(GetConvertShader(SetDATMShader(datm)), nullptr);
@@ -2707,10 +2726,16 @@ void GSDevice11::OMSetBlendState(ID3D11BlendState* bs, u8 bf)
}
}
void GSDevice11::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor, ID3D11DepthStencilView* read_only_dsv)
void GSDevice11::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, GSTexture* rt_uav_tex, GSTexture* ds_uav_tex,
const GSVector4i* scissor, ID3D11DepthStencilView* read_only_dsv)
{
if (!(rt || rt_uav_tex) && ds_uav_tex)
rt_uav_tex = m_null_texture; // Fill in the first UAV slot with the null texture.
ID3D11RenderTargetView* rtv = nullptr;
ID3D11DepthStencilView* dsv = nullptr;
ID3D11UnorderedAccessView* rt_uav = nullptr;
ID3D11UnorderedAccessView* ds_uav = nullptr;
if (rt)
{
@@ -2722,9 +2747,21 @@ void GSDevice11::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector
CommitClear(ds);
dsv = read_only_dsv ? read_only_dsv : *static_cast<GSTexture11*>(ds);
}
if (rt_uav_tex)
{
CommitClear(rt_uav_tex);
rt_uav = *static_cast<GSTexture11*>(rt_uav_tex);
}
if (ds_uav_tex)
{
CommitClear(ds_uav_tex);
ds_uav = *static_cast<GSTexture11*>(ds_uav_tex);
}
const bool changed = (m_state.rtv != rtv || m_state.dsv != dsv);
g_perfmon.Put(GSPerfMon::RenderPasses, static_cast<double>(changed));
const bool changed = (m_state.rtv != rtv || m_state.dsv != dsv || m_state.rt_uav != rt_uav || m_state.ds_uav != ds_uav);
if (changed)
g_perfmon.Put(GSPerfMon::RenderPasses, 1.0);
if (m_state.rtv != rtv)
{
@@ -2744,15 +2781,49 @@ void GSDevice11::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector
m_state.dsv = dsv;
m_state.current_ds = ds;
}
if (m_state.rt_uav != rt_uav)
{
if (m_state.rt_uav)
m_state.rt_uav->Release();
if (rt_uav)
rt_uav->AddRef();
m_state.rt_uav = rt_uav;
m_state.current_rt_uav = rt_uav_tex;
}
if (m_state.ds_uav != ds_uav)
{
if (m_state.ds_uav)
m_state.ds_uav->Release();
if (ds_uav)
ds_uav->AddRef();
m_state.ds_uav = ds_uav;
m_state.current_ds_uav = ds_uav_tex;
}
PSUnbindConflictingSRVs(m_state.current_rt, read_only_dsv ? nullptr : m_state.current_ds);
PSUnbindConflictingSRVs(m_state.current_rt_uav, m_state.current_ds_uav);
if (changed)
m_ctx->OMSetRenderTargets(1, &rtv, dsv);
if (rt || ds)
{
const GSVector2i size = rt ? rt->GetSize() : ds->GetSize();
// OM targets and UAVs share the same namespace in DX11, so we need to pack them into contiguous slots.
u32 num_rtvs = rt ? 1 : 0;
u32 num_uavs = 0;
ID3D11UnorderedAccessView* uavs[2];
if (rt_uav)
uavs[num_uavs++] = rt_uav;
if (ds_uav)
uavs[num_uavs++] = ds_uav;
ID3D11RenderTargetView* rtvs[] = { rtv };
m_ctx->OMSetRenderTargetsAndUnorderedAccessViews(num_rtvs, rtvs, dsv, num_rtvs, num_uavs, uavs, nullptr);
}
if (rt || ds || rt_uav_tex || ds_uav_tex)
{
const GSVector2i size =
rt ? rt->GetSize() :
ds ? ds->GetSize() :
(rt_uav_tex && rt_uav_tex != m_null_texture) ? rt_uav_tex->GetSize() :
ds_uav_tex->GetSize();
SetViewport(size);
SetScissor(scissor ? *scissor : GSVector4i::loadh(size));
}
@@ -2815,6 +2886,10 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
{
const GSVector2i rtsize = (config.rt ? config.rt : config.ds)->GetSize();
GSTexture* colclip_rt = g_gs_device->GetColorClipTexture();
GSTexture* draw_rt = config.ps.HasColorROV() ? nullptr : config.rt;
GSTexture* draw_ds = config.ps.HasDepthROV() ? nullptr : config.ds;
GSTexture* draw_rt_rov = config.ps.HasColorROV() ? config.rt : nullptr;
GSTexture* draw_ds_rov = config.ps.HasDepthROV() ? config.ds : nullptr;
GSTexture* draw_rt_clone = nullptr;
GSTexture* draw_ds_clone = nullptr;
GSTexture* primid_texture = nullptr;
@@ -2865,6 +2940,8 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy();
StretchRect(config.rt, sRect, colclip_rt, dRect, ShaderConvert::COLCLIP_INIT, Nearest);
}
draw_rt = colclip_rt ? colclip_rt : draw_rt;
}
// Destination Alpha Setup
@@ -2950,7 +3027,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
const OMBlendSelector blend(GSHWDrawConfig::ColorMaskSelector(1),
GSHWDrawConfig::BlendState(true, CONST_ONE, CONST_ONE, 3 /* MIN */, CONST_ONE, CONST_ZERO, false, 0));
SetupOM(dss, blend, 0);
OMSetRenderTargets(primid_texture, config.ds, &config.scissor, read_only_dsv);
OMSetRenderTargets(primid_texture, config.ds, nullptr, nullptr, &config.scissor, read_only_dsv);
SetRenderHWShaderResources(config, nullptr);
Draw(config);
@@ -2960,16 +3037,14 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
}
// Avoid changing framebuffer just to switch from rt+depth to rt and vice versa.
GSTexture* draw_rt = colclip_rt ? colclip_rt : config.rt;
GSTexture* draw_ds = config.ds;
// Make sure no tex is bound as both rtv and srv at the same time.
// All conflicts should've been taken care of by PSUnbindConflictingSRVs.
if (!draw_rt && draw_ds && m_state.rtv && m_state.current_rt && m_state.rtv == *static_cast<GSTexture11*>(m_state.current_rt) &&
if (!(draw_rt || draw_rt_rov) && draw_ds && m_state.rtv && m_state.current_rt && m_state.rtv == *static_cast<GSTexture11*>(m_state.current_rt) &&
m_state.current_ds == draw_ds && config.tex != m_state.current_rt && m_state.current_rt->GetSize() == draw_ds->GetSize())
{
draw_rt = m_state.current_rt;
}
else if (!draw_ds && draw_rt && m_state.dsv && m_state.current_ds && m_state.dsv == *static_cast<GSTexture11*>(m_state.current_ds) &&
else if (!(draw_ds || draw_ds_rov) && draw_rt && m_state.dsv && m_state.current_ds && m_state.dsv == *static_cast<GSTexture11*>(m_state.current_ds) &&
m_state.current_rt == draw_rt && config.tex != m_state.current_ds && m_state.current_ds->GetSize() == draw_rt->GetSize())
{
draw_ds = m_state.current_ds;
@@ -2977,7 +3052,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
const bool rt_feedbackloop_pass1 = config.IsFeedbackLoopRT(config.ps);
const bool rt_feedbackloop_pass2 = config.IsFeedbackLoopRT(config.alpha_second_pass.ps);
if (draw_rt && (((config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
if (draw_rt && !draw_rt_rov && (((config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
(rt_feedbackloop_pass1 || rt_feedbackloop_pass2))))
{
// Requires a copy of the RT.
@@ -2989,7 +3064,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
const bool ds_feedbackloop_pass1 = config.IsFeedbackLoopDepth(config.ps);
const bool ds_feedbackloop_pass2 = config.IsFeedbackLoopDepth(config.alpha_second_pass.ps);
if (draw_ds && m_features.depth_feedback && (config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
if (draw_ds && !draw_ds_rov && m_features.depth_feedback && (config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
(ds_feedbackloop_pass1 || ds_feedbackloop_pass2))
{
// Requires a copy of the DS.
@@ -2999,7 +3074,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
Console.Warning("D3D11: Failed to allocate temp texture for DS copy.");
}
OMSetRenderTargets(draw_rt, draw_ds, &config.scissor, read_only_dsv);
OMSetRenderTargets(draw_rt, draw_ds, draw_rt_rov, draw_ds_rov, &config.scissor, read_only_dsv);
SetRenderHWShaderResources(config, primid_texture);
SetupOM(config.depth, OMBlendSelector(config.colormask, config.blend), config.blend.constant);
@@ -3008,7 +3083,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
m_ctx->ClearDepthStencilView(*static_cast<GSTexture11*>(draw_ds), D3D11_CLEAR_STENCIL, 0.0f, 1);
SendHWDraw(config, rt_feedbackloop_pass1 ? draw_rt_clone : nullptr, draw_rt, ds_feedbackloop_pass1 ? draw_ds_clone : nullptr, draw_ds,
config.require_one_barrier, config.require_full_barrier);
config.require_one_barrier, config.require_full_barrier, rtsize);
if (config.blend_multi_pass.enable)
{
@@ -3036,7 +3111,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
const bool one_barrier = config.alpha_second_pass.require_one_barrier && m_features.multidraw_fb_copy;
SetupOM(config.alpha_second_pass.depth, OMBlendSelector(config.alpha_second_pass.colormask, config.blend), config.blend.constant);
SendHWDraw(config, rt_feedbackloop_pass2 ? draw_rt_clone : nullptr, draw_rt, ds_feedbackloop_pass2 ? draw_ds_clone : nullptr, draw_ds,
one_barrier, config.alpha_second_pass.require_full_barrier);
one_barrier, config.alpha_second_pass.require_full_barrier, rtsize);
}
if (colclip_rt)
@@ -3110,7 +3185,7 @@ void GSDevice11::FeedbackCopyAndBind(const GSHWDrawConfig& config,
void GSDevice11::SendHWDraw(const GSHWDrawConfig& config,
GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds,
const bool one_barrier, const bool full_barrier)
const bool one_barrier, const bool full_barrier, GSVector2i rtsize)
{
#ifdef PCSX2_DEVBUILD
if ((one_barrier || full_barrier) && !(config.IsFeedbackLoopRT(config.ps) || config.IsFeedbackLoopDepth(config.ps))) [[unlikely]]
@@ -3149,6 +3224,9 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config,
}
Draw(config);
if (config.ps.HasColorROV() || config.ps.HasDepthROV())
g_perfmon.Put(GSPerfMon::DrawCallsROV, 1);
}
void GSDevice11::SetRenderHWShaderResources(const GSHWDrawConfig& config, GSTexture* primid_texture)
@@ -3156,13 +3234,13 @@ void GSDevice11::SetRenderHWShaderResources(const GSHWDrawConfig& config, GSText
if (config.tex && config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_NONE)
{
CommitClear(config.tex);
PSSetShaderResource(0, config.tex);
PSSetShaderResource(TEXTURE_TEXTURE, config.tex);
}
if (config.pal)
{
CommitClear(config.pal);
PSSetShaderResource(1, config.pal);
PSSetShaderResource(TEXTURE_PALETTE, config.pal);
}
if (primid_texture)
PSSetShaderResource(3, primid_texture);
PSSetShaderResource(TEXTURE_PRIMID, primid_texture);
}
+15 -2
View File
@@ -83,6 +83,11 @@ public:
private:
enum : u32
{
TEXTURE_TEXTURE = 0,
TEXTURE_PALETTE = 1,
TEXTURE_RT = 2,
TEXTURE_PRIMID = 3,
TEXTURE_DEPTH = 4,
MAX_TEXTURES = 5,
MAX_SAMPLERS = 1,
VERTEX_BUFFER_SIZE = 32 * 1024 * 1024,
@@ -129,6 +134,8 @@ private:
wil::com_ptr_nothrow<ID3D11ShaderResourceView> m_expand_vb_srv;
wil::com_ptr_nothrow<ID3D11ShaderResourceView> m_expand_ib_vs_srv;
GSTexture* m_null_texture;
D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_10_0;
u32 m_vb_pos = 0; // bytes
u32 m_ib_pos = 0; // indices/sizeof(u16)
@@ -141,6 +148,7 @@ private:
bool m_is_exclusive_fullscreen = false;
bool m_conservative_depth = false;
bool m_rgba16_unorm_hw_blend = false;
bool m_uav_texture = false;
struct
{
@@ -166,8 +174,12 @@ private:
u8 bf;
ID3D11RenderTargetView* rtv;
ID3D11DepthStencilView* dsv;
ID3D11UnorderedAccessView* rt_uav;
ID3D11UnorderedAccessView* ds_uav;
GSTexture* current_rt;
GSTexture* current_ds;
GSTexture* current_rt_uav;
GSTexture* current_ds_uav;
} m_state;
std::array<std::array<wil::com_ptr_nothrow<ID3D11Query>, 3>, NUM_TIMESTAMP_QUERIES> m_timestamp_queries = {};
@@ -371,7 +383,8 @@ public:
void OMSetDepthStencilState(ID3D11DepthStencilState* dss, u8 sref);
void OMSetBlendState(ID3D11BlendState* bs, u8 bf);
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor = nullptr, ID3D11DepthStencilView* read_only_dsv = nullptr);
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, GSTexture* rt_uav = nullptr, GSTexture* ds_uav = nullptr,
const GSVector4i* scissor = nullptr, ID3D11DepthStencilView* read_only_dsv = nullptr);
void SetViewport(const GSVector2i& viewport);
void SetScissor(const GSVector4i& scissor);
@@ -390,7 +403,7 @@ public:
const GSVector4i& copyarea, const GSVector4i& samplearea);
void SendHWDraw(const GSHWDrawConfig& config,
GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds,
const bool one_barrier, const bool full_barrier);
const bool one_barrier, const bool full_barrier, GSVector2i rtsize);
void SetRenderHWShaderResources(const GSHWDrawConfig& config, GSTexture* primid_texture);
void ClearSamplerCache() override;
+2
View File
@@ -102,6 +102,8 @@ void GSTexture11::SetDebugName(std::string_view name)
GSDevice11::SetD3DDebugObjectName(m_srv.get(), fmt::format("{} SRV", name));
if (m_rtv)
GSDevice11::SetD3DDebugObjectName(m_rtv.get(), fmt::format("{} RTV", name));
m_debug_name = name;
}
#endif
-1
View File
@@ -19,7 +19,6 @@ class GSTexture11 final : public GSTexture
wil::com_ptr_nothrow<ID3D11UnorderedAccessView> m_uav;
wil::com_ptr_nothrow<ID3D11DepthStencilView> m_read_only_dsv;
D3D11_TEXTURE2D_DESC m_desc;
public:
explicit GSTexture11(wil::com_ptr_nothrow<ID3D11Texture2D> texture, const D3D11_TEXTURE2D_DESC& desc,
GSTexture::Type type, GSTexture::Format format);
+25
View File
@@ -354,6 +354,31 @@ u32 D3D12::RootSignatureBuilder::AddDescriptorTable(
return index;
}
// Allows using ranges of non-contiguous shader registers in a single descriptor table.
u32 D3D12::RootSignatureBuilder::AddDescriptorTableMultiRange(u32 num_ranges,
D3D12_DESCRIPTOR_RANGE_TYPE* rt, u32* start_shader_reg, u32* num_shader_regs, D3D12_SHADER_VISIBILITY visibility)
{
const u32 index = m_desc.NumParameters++;
const u32 dr_index = m_num_descriptor_ranges;
m_num_descriptor_ranges += num_ranges;
for (u32 i = 0; i < num_ranges; i++)
{
m_descriptor_ranges[dr_index + i].RangeType = rt[i];
m_descriptor_ranges[dr_index + i].NumDescriptors = num_shader_regs[i];
m_descriptor_ranges[dr_index + i].BaseShaderRegister = start_shader_reg[i];
m_descriptor_ranges[dr_index + i].RegisterSpace = 0;
m_descriptor_ranges[dr_index + i].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
}
m_params[index].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
m_params[index].DescriptorTable.pDescriptorRanges = &m_descriptor_ranges[dr_index];
m_params[index].DescriptorTable.NumDescriptorRanges = num_ranges;
m_params[index].ShaderVisibility = visibility;
return index;
}
#ifdef PCSX2_DEVBUILD
#include "common/StringUtil.h"
+2
View File
@@ -37,6 +37,8 @@ namespace D3D12
u32 AddSRVParameter(u32 shader_reg, D3D12_SHADER_VISIBILITY visibility);
u32 AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE rt, u32 start_shader_reg, u32 num_shader_regs,
D3D12_SHADER_VISIBILITY visibility);
u32 AddDescriptorTableMultiRange(u32 num_ranges, D3D12_DESCRIPTOR_RANGE_TYPE* rt, u32* start_shader_reg,
u32* num_shader_regs, D3D12_SHADER_VISIBILITY visibility);
private:
D3D12_ROOT_SIGNATURE_DESC m_desc{};
+200 -84
View File
@@ -397,17 +397,6 @@ bool GSDevice12::CreateDescriptorHeaps()
return false;
}
// Allocate null SRV descriptor for unbound textures.
constexpr D3D12_SHADER_RESOURCE_VIEW_DESC null_srv_desc = {
DXGI_FORMAT_R8G8B8A8_UNORM, D3D12_SRV_DIMENSION_TEXTURE2D, D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING};
if (!m_descriptor_heap_manager.Allocate(&m_null_srv_descriptor))
{
pxFailRel("Failed to allocate null descriptor");
return false;
}
m_device->CreateShaderResourceView(nullptr, &null_srv_desc, m_null_srv_descriptor.cpu_handle);
return true;
}
@@ -1449,6 +1438,10 @@ bool GSDevice12::CheckFeatures(const u32& vendor_id)
m_enhanced_barriers = false;
}
D3D12_FEATURE_DATA_D3D12_OPTIONS options{};
m_device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS));
m_features.rov = options.ROVsSupported;
return true;
}
@@ -1505,30 +1498,30 @@ void GSDevice12::Draw(const GSHWDrawConfig& config)
}
void GSDevice12::LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_format, DXGI_FORMAT* srv_format,
DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format) const
DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format, DXGI_FORMAT* uav_format) const
{
static constexpr std::array<std::array<DXGI_FORMAT, 4>, static_cast<int>(GSTexture::Format::Last) + 1>
static constexpr std::array<std::array<DXGI_FORMAT, 5>, static_cast<int>(GSTexture::Format::Last) + 1>
s_format_mapping = {{
{DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // Invalid
{DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_UNKNOWN}, // Color
DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UNORM}, // Color
{DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM,
DXGI_FORMAT_UNKNOWN}, // ColorHQ
DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R10G10B10A2_UNORM}, // ColorHQ
{DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT,
DXGI_FORMAT_UNKNOWN}, // ColorHDR
DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16G16B16A16_FLOAT}, // ColorHDR
{DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R16G16B16A16_UNORM,
DXGI_FORMAT_UNKNOWN}, // ColorClip
DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16G16B16A16_UNORM}, // ColorClip
{DXGI_FORMAT_D32_FLOAT_S8X24_UINT, DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS, DXGI_FORMAT_UNKNOWN,
DXGI_FORMAT_D32_FLOAT_S8X24_UINT}, // DepthStencil
{DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_UNKNOWN}, // DepthColor
{DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_UNKNOWN}, // UNorm8
{DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_UNKNOWN}, // UInt16
{DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_UNKNOWN}, // UInt32
{DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_UNKNOWN}, // Int32
{DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC1
{DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC2
{DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC3
{DXGI_FORMAT_BC7_UNORM, DXGI_FORMAT_BC7_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC7
DXGI_FORMAT_D32_FLOAT_S8X24_UINT, DXGI_FORMAT_R32_FLOAT}, // DepthStencil
{DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32_FLOAT}, // DepthColor
{DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_A8_UNORM}, // UNorm8
{DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16_UINT}, // UInt16
{DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32_UINT}, // UInt32
{DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32_FLOAT}, // Int32
{DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC1
{DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC2
{DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC3
{DXGI_FORMAT_BC7_UNORM, DXGI_FORMAT_BC7_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC7
}};
const auto& mapping = s_format_mapping[static_cast<int>(format)];
@@ -1540,14 +1533,17 @@ void GSDevice12::LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_f
*rtv_format = mapping[2];
if (dsv_format)
*dsv_format = mapping[3];
if (uav_format)
*uav_format = mapping[4];
}
GSTexture* GSDevice12::CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format)
{
DXGI_FORMAT dxgi_format, srv_format, rtv_format, dsv_format;
LookupNativeFormat(format, &dxgi_format, &srv_format, &rtv_format, &dsv_format);
DXGI_FORMAT dxgi_format, srv_format, rtv_format, dsv_format, uav_format;
LookupNativeFormat(format, &dxgi_format, &srv_format, &rtv_format, &dsv_format, &uav_format);
const DXGI_FORMAT uav_format = (type == GSTexture::Type::RWTexture) ? dxgi_format : DXGI_FORMAT_UNKNOWN;
if (type != GSTexture::Type::RWTexture && type != GSTexture::Type::RenderTarget)
uav_format = DXGI_FORMAT_UNKNOWN; // We don't need the UAV descriptor.
std::unique_ptr<GSTexture12> tex(GSTexture12::Create(type, format, width, height, levels,
dxgi_format, srv_format, rtv_format, dsv_format, uav_format));
@@ -2446,24 +2442,23 @@ void GSDevice12::VSSetIndexBuffer(const void* index, size_t count)
}
void GSDevice12::OMSetRenderTargets(GSTexture* rt, GSTexture* ds_as_rt, GSTexture* ds, const GSVector4i& scissor,
bool depth_read)
bool depth_read, const GSVector2i& viewport_size)
{
GSTexture12* d12Rt = static_cast<GSTexture12*>(rt);
GSTexture12* d12DsRt = static_cast<GSTexture12*>(ds_as_rt);
GSTexture12* d12Ds = static_cast<GSTexture12*>(ds);
pxAssert(d12Rt || d12DsRt || d12Ds);
if (m_current_render_target != d12Rt || m_current_depth_render_target != d12DsRt || m_current_depth_target != d12Ds ||
m_current_depth_read_only != depth_read)
// Check if framebuffer changed
if (m_current_render_target != d12Rt || m_current_depth_render_target != d12DsRt ||
m_current_depth_target != d12Ds || m_current_depth_read_only != depth_read)
{
// framebuffer change
EndRenderPass();
}
else if (InRenderPass())
{
// Framebuffer unchanged, but check for clears. Have to restart render pass, unlike Vulkan.
// We'll take care of issuing the actual clear there, because we have to start one anyway.
for (GSTexture12* tex : std::array{d12Rt, d12DsRt, d12Ds})
for (GSTexture12* tex : std::array{ d12Rt, d12DsRt, d12Ds })
{
if (tex && tex->GetState() != GSTexture::State::Dirty)
{
@@ -2491,7 +2486,8 @@ void GSDevice12::OMSetRenderTargets(GSTexture* rt, GSTexture* ds_as_rt, GSTextur
}
// This is used to set/initialize the framebuffer for tfx rendering.
const GSVector2i size = d12Rt ? d12Rt->GetSize() : (d12DsRt ? d12DsRt->GetSize() : d12Ds->GetSize());
const GSVector2i size = d12Rt ? d12Rt->GetSize() :
(d12DsRt ? d12DsRt->GetSize() : (d12Ds ? d12Ds->GetSize() : viewport_size));
const D3D12_VIEWPORT vp{0.0f, 0.0f, static_cast<float>(size.x), static_cast<float>(size.y), 0.0f, 1.0f};
SetViewport(vp);
@@ -2608,8 +2604,8 @@ GSDevice12::ComPtr<ID3DBlob> GSDevice12::GetUtilityPixelShader(const std::string
bool GSDevice12::CreateNullTexture()
{
m_null_texture =
GSTexture12::Create(GSTexture::Type::Texture, GSTexture::Format::Color, 1, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN);
GSTexture12::Create(GSTexture::Type::RenderTarget, GSTexture::Format::Color, 1, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UNORM);
if (!m_null_texture)
return false;
@@ -2689,9 +2685,13 @@ bool GSDevice12::CreateRootSignatures()
rsb.AddCBVParameter(1, D3D12_SHADER_VISIBILITY_PIXEL);
rsb.AddSRVParameter(0, D3D12_SHADER_VISIBILITY_VERTEX);
rsb.AddSRVParameter(5, D3D12_SHADER_VISIBILITY_VERTEX);
rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 2, D3D12_SHADER_VISIBILITY_PIXEL); // Source / Palette
rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 2, D3D12_SHADER_VISIBILITY_PIXEL); // Source (t0) / Palette (t2)
rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 0, NUM_TFX_SAMPLERS, D3D12_SHADER_VISIBILITY_PIXEL);
rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 2, 3, D3D12_SHADER_VISIBILITY_PIXEL); // RT / PrimID / Depth
// RT (t2) / PrimID (t3) / Depth (t4) / RT UAV (u0) / Depth UAV (u1)
D3D12_DESCRIPTOR_RANGE_TYPE rt_types[2] = { D3D12_DESCRIPTOR_RANGE_TYPE_SRV, D3D12_DESCRIPTOR_RANGE_TYPE_UAV };
u32 rt_start_regs[2] = { 2, 0 };
u32 rt_num_regs[2] = { 3, 2 };
rsb.AddDescriptorTableMultiRange(2, rt_types, rt_start_regs, rt_num_regs, D3D12_SHADER_VISIBILITY_PIXEL);
rsb.Add32BitConstants(2, sizeof(m_vs_pc_cache) / sizeof(u32), D3D12_SHADER_VISIBILITY_VERTEX);
if (!(m_tfx_root_signature = rsb.Create()))
return false;
@@ -2728,7 +2728,7 @@ bool GSDevice12::CompileConvertPipelines()
GSTexture::Format format = shader.OutputFormat();
DXGI_FORMAT dxgi_format;
LookupNativeFormat(format, nullptr, nullptr, &dxgi_format, nullptr);
LookupNativeFormat(format, nullptr, nullptr, &dxgi_format, nullptr, nullptr);
if (shader.DATMConvertShader() || shader.DepthOutput())
{
@@ -3067,7 +3067,6 @@ void GSDevice12::DestroyResources()
m_shader_cache.Close();
m_descriptor_heap_manager.Free(&m_null_srv_descriptor);
m_timestamp_query_buffer.reset();
m_timestamp_query_allocation.reset();
m_sampler_heap_manager.Destroy();
@@ -3178,6 +3177,8 @@ const ID3DBlob* GSDevice12::GetTFXPixelShader(const GSHWDrawConfig::PSSelector&
sm.AddMacro("PS_AA1", static_cast<u32>(sel.aa1));
sm.AddMacro("PS_ABE", sel.abe);
sm.AddMacro("PS_ANISOTROPIC_FILTERING", sel.sw_aniso);
sm.AddMacro("PS_ROV_COLOR", sel.rov_color);
sm.AddMacro("PS_ROV_DEPTH", static_cast<u32>(sel.rov_depth));
ComPtr<ID3DBlob> ps(m_shader_cache.GetPixelShader(m_tfx_source, sm.GetPtr(), "ps_main"));
it = m_tfx_pixel_shaders.emplace(sel, std::move(ps)).first;
@@ -3216,11 +3217,11 @@ GSDevice12::ComPtr<ID3D12PipelineState> GSDevice12::CreateTFXPipeline(const Pipe
if (p.rt)
{
const GSTexture::Format format = IsDATEModePrimIDInit(p.ps.date) ?
GSTexture::Format::PrimID :
(p.ps.colclip_hw ? GSTexture::Format::ColorClip : GSTexture::Format::Color);
GSTexture::Format::PrimID :
(p.ps.colclip_hw ? GSTexture::Format::ColorClip : GSTexture::Format::Color);
DXGI_FORMAT native_format;
LookupNativeFormat(format, nullptr, nullptr, &native_format, nullptr);
LookupNativeFormat(format, nullptr, nullptr, &native_format, nullptr, nullptr);
gpb.SetRenderTarget(num_rts++, native_format);
}
if (p.ds_as_rt)
@@ -3335,8 +3336,10 @@ bool GSDevice12::BindDrawPipeline(const PipelineSelector& p)
void GSDevice12::InitializeState()
{
for (u32 i = 0; i < NUM_TOTAL_TFX_TEXTURES; i++)
for (u32 i = 0; i < TEXTURE_RT_UAV; i++)
m_tfx_textures[i] = m_null_texture->GetSRVDescriptor();
for (u32 i = TEXTURE_RT_UAV; i < NUM_TOTAL_TFX_TEXTURES; i++)
m_tfx_textures[i] = m_null_texture->GetUAVDescriptor();
m_tfx_sampler_sel = GSHWDrawConfig::SamplerSelector::Point().key;
InvalidateCachedState();
@@ -3475,25 +3478,30 @@ void GSDevice12::SetStencilRef(u8 ref)
m_dirty_flags |= DIRTY_FLAG_STENCIL_REF;
}
void GSDevice12::PSSetShaderResource(int i, GSTexture* sr, bool check_state, bool feedback)
void GSDevice12::PSSetShaderResource(int i, GSTexture* sr, bool check_state, ResourceType type)
{
pxAssert(i < NUM_TFX_TEXTURES + NUM_TFX_RT_TEXTURES + NUM_TFX_UAV_TEXTURES);
D3D12DescriptorHandle handle;
if (sr)
{
const GSTexture12::ResourceState state = GetResourceState(type);
GSTexture12* dtex = static_cast<GSTexture12*>(sr);
if (check_state)
{
if (dtex->GetResourceState() != GSTexture12::ResourceState::PixelShaderResource && InRenderPass())
if (dtex->GetResourceState() != state && InRenderPass())
{
GL_INS("Ending render pass due to resource transition");
EndRenderPass();
}
dtex->CommitClear();
dtex->TransitionToState(GSTexture12::ResourceState::PixelShaderResource);
dtex->TransitionToState(state);
}
dtex->SetUseFenceCounter(GetCurrentFenceValue());
handle = (feedback && !m_enhanced_barriers) ? dtex->GetFBLDescriptor() : dtex->GetSRVDescriptor();
handle = GetResourceDescriptor(dtex, type);
}
else
{
@@ -3504,7 +3512,7 @@ void GSDevice12::PSSetShaderResource(int i, GSTexture* sr, bool check_state, boo
return;
m_tfx_textures[i] = handle;
m_dirty_flags |= (i < 2) ? DIRTY_FLAG_TFX_TEXTURES : DIRTY_FLAG_TFX_RT_TEXTURES;
m_dirty_flags |= (i < NUM_TFX_TEXTURES) ? DIRTY_FLAG_TFX_TEXTURES : DIRTY_FLAG_TFX_RT_TEXTURES;
}
void GSDevice12::PSSetSampler(GSHWDrawConfig::SamplerSelector sel)
@@ -3517,6 +3525,68 @@ void GSDevice12::PSSetSampler(GSHWDrawConfig::SamplerSelector sel)
m_dirty_flags |= DIRTY_FLAG_TFX_SAMPLERS;
}
void GSDevice12::PSSetUnorderedAccess(GSTexture* rt, GSTexture* ds, bool write_rt, bool write_ds)
{
GSTexture12* d12Rt = static_cast<GSTexture12*>(rt);
GSTexture12* d12Ds = static_cast<GSTexture12*>(ds);
GSTexture12* oldD12Rt = m_tfx_textures_uav[0] != m_null_texture.get() ? m_tfx_textures_uav[0] : nullptr;
GSTexture12* oldD12Ds = m_tfx_textures_uav[1] != m_null_texture.get() ? m_tfx_textures_uav[1] : nullptr;
if (!(d12Rt || d12Ds || oldD12Rt || oldD12Ds))
return;
pxAssert(!(d12Rt || d12Ds) || m_features.rov);
if (d12Rt)
{
PSSetShaderResource(TEXTURE_RT_UAV, d12Rt, true, ResourceType::UAV);
// Unbind conflicting RT texture
PSSetShaderResource(TEXTURE_RT, nullptr, false);
// Unbind conflicting source texture
if (m_tfx_textures[TEXTURE_TEXTURE] == d12Rt->GetSRVDescriptor() ||
m_tfx_textures[TEXTURE_TEXTURE] == d12Rt->GetFBLDescriptor())
{
PSSetShaderResource(TEXTURE_TEXTURE, nullptr, false);
}
if (write_rt)
{
d12Rt->SetState(GSTexture::State::Dirty);
}
}
else
{
// Unbind to avoid conflicts with OM targets.
PSSetShaderResource(TEXTURE_RT_UAV, nullptr, false);
}
if (d12Ds)
{
PSSetShaderResource(TEXTURE_DEPTH_UAV, d12Ds, true, ResourceType::UAV);
// Unbind conflicting depth texture
PSSetShaderResource(TEXTURE_DEPTH, nullptr, false);
// Unbind conflicting source texture
if (m_tfx_textures[TEXTURE_TEXTURE] == d12Ds->GetSRVDescriptor())
{
PSSetShaderResource(TEXTURE_TEXTURE, nullptr, false);
}
if (write_ds)
{
d12Ds->SetState(GSTexture::State::Dirty);
}
}
else
{
// Unbind to avoid conflicts with OM targets.
PSSetShaderResource(TEXTURE_DEPTH_UAV, nullptr, false);
}
}
void GSDevice12::SetUtilityRootSignature()
{
if (m_current_root_signature == RootSignature::Utility)
@@ -3578,7 +3648,8 @@ void GSDevice12::SetUtilityPushConstants(const void* data, u32 size)
void GSDevice12::UnbindTexture(GSTexture12* tex)
{
for (u32 i = 0; i < NUM_TOTAL_TFX_TEXTURES; i++)
// Source / palette
for (u32 i = 0; i <= TEXTURE_PALETTE; i++)
{
if (m_tfx_textures[i] == tex->GetSRVDescriptor() || m_tfx_textures[i] == tex->GetFBLDescriptor())
{
@@ -3586,6 +3657,28 @@ void GSDevice12::UnbindTexture(GSTexture12* tex)
m_dirty_flags |= DIRTY_FLAG_TFX_TEXTURES;
}
}
// RT / primid / depth
for (u32 i = TEXTURE_RT; i <= TEXTURE_DEPTH; i++)
{
if (m_tfx_textures[i] == tex->GetSRVDescriptor() || m_tfx_textures[i] == tex->GetFBLDescriptor())
{
m_tfx_textures[i] = m_null_texture->GetSRVDescriptor();
m_dirty_flags |= DIRTY_FLAG_TFX_RT_TEXTURES;
}
}
// RT UAV / depth UAV
for (u32 i = TEXTURE_RT_UAV; i <= TEXTURE_DEPTH_UAV; i++)
{
if (m_tfx_textures_uav[i - TEXTURE_RT_UAV] == tex)
{
m_tfx_textures_uav[i - TEXTURE_RT_UAV] = nullptr;
m_tfx_textures[i] = m_null_texture->GetUAVDescriptor();
m_dirty_flags |= DIRTY_FLAG_TFX_RT_TEXTURES;
}
}
if (m_current_render_target == tex)
{
EndRenderPass();
@@ -3714,7 +3807,7 @@ void GSDevice12::BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE color_b
if (color_begin == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR)
{
LookupNativeFormat(m_current_render_target->GetFormat(), nullptr,
&rt[num_rts].BeginningAccess.Clear.ClearValue.Format, nullptr, nullptr);
&rt[num_rts].BeginningAccess.Clear.ClearValue.Format, nullptr, nullptr, nullptr);
GSVector4::store<false>(rt[num_rts].BeginningAccess.Clear.ClearValue.Color, clear_color);
}
num_rts++;
@@ -3737,7 +3830,7 @@ void GSDevice12::BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE color_b
if (depth_begin == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR)
{
LookupNativeFormat(m_current_depth_target->GetFormat(), nullptr, nullptr, nullptr,
&ds.DepthBeginningAccess.Clear.ClearValue.Format);
&ds.DepthBeginningAccess.Clear.ClearValue.Format, nullptr);
ds.DepthBeginningAccess.Clear.ClearValue.DepthStencil.Depth = clear_depth;
}
ds.StencilEndingAccess.Type = stencil_end;
@@ -3745,7 +3838,7 @@ void GSDevice12::BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE color_b
if (stencil_begin == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR)
{
LookupNativeFormat(m_current_depth_target->GetFormat(), nullptr, nullptr, nullptr,
&ds.StencilBeginningAccess.Clear.ClearValue.Format);
&ds.StencilBeginningAccess.Clear.ClearValue.Format, nullptr);
ds.StencilBeginningAccess.Clear.ClearValue.DepthStencil.Stencil = clear_stencil;
}
}
@@ -3847,6 +3940,10 @@ __ri void GSDevice12::ApplyBaseState(u32 flags, ID3D12GraphicsCommandList* cmdli
{
cmdlist->OMSetRenderTargets(0, nullptr, FALSE, m_current_depth_read_only ? &m_current_depth_target->GetReadDepthViewDescriptor().cpu_handle : &m_current_depth_target->GetWriteDescriptor().cpu_handle);
}
else
{
cmdlist->OMSetRenderTargets(0, nullptr, FALSE, nullptr);
}
}
}
@@ -3925,7 +4022,7 @@ bool GSDevice12::ApplyTFXState(bool already_execed)
if (flags & DIRTY_FLAG_TFX_RT_TEXTURES)
{
if (!GetTextureGroupDescriptors(&m_tfx_rt_textures_handle_gpu, m_tfx_textures.data() + 2, 3))
if (!GetTextureGroupDescriptors(&m_tfx_rt_textures_handle_gpu, m_tfx_textures.data() + 2, NUM_TFX_RT_TEXTURES + NUM_TFX_UAV_TEXTURES))
{
ExecuteCommandListAndRestartRenderPass(false, "Ran out of TFX RT descriptor descriptor groups");
return ApplyTFXState(true);
@@ -4161,10 +4258,14 @@ void GSDevice12::FeedbackBarrier(const GSTexture12* texture)
void GSDevice12::RenderHW(GSHWDrawConfig& config)
{
GSTexture12* colclip_rt = static_cast<GSTexture12*>(g_gs_device->GetColorClipTexture());
GSTexture12* draw_rt = static_cast<GSTexture12*>(config.rt);
GSTexture12* draw_ds = static_cast<GSTexture12*>(config.ds);
GSTexture12* draw_rt = config.ps.HasColorROV() ? nullptr : static_cast<GSTexture12*>(config.rt);
GSTexture12* draw_ds = config.ps.HasDepthROV() ? nullptr : static_cast<GSTexture12*>(config.ds);
GSTexture12* draw_rt_rov = config.ps.HasColorROV() ? static_cast<GSTexture12*>(config.rt) : nullptr;
GSTexture12* draw_ds_rov = config.ps.HasDepthROV() ? static_cast<GSTexture12*>(config.ds) : nullptr;
GSTexture12* draw_rt_clone = nullptr;
const bool feedback = draw_rt && (config.require_one_barrier || (config.require_full_barrier && m_features.texture_barrier) || (config.tex && config.tex == config.rt));
// Align the render area to 128x128, hopefully avoiding render pass restarts for small render area changes (e.g. Ratchet and Clank).
const GSVector2i rtsize(config.rt ? config.rt->GetSize() : config.ds->GetSize());
@@ -4200,6 +4301,9 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
Recycle(colclip_rt);
g_gs_device->SetColorClipTexture(nullptr);
colclip_rt = nullptr;
draw_rt = config.ps.HasColorROV() ? nullptr : static_cast<GSTexture12*>(config.rt);
}
else
{
@@ -4314,18 +4418,18 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
{
// avoid restarting the render pass just to switch from rt+depth to rt and vice versa
// keep the depth even if doing colclip hw draws, because the next draw will probably re-enable depth
if (!draw_rt && m_current_render_target && config.tex != m_current_render_target &&
m_current_render_target->GetSize() == draw_ds->GetSize())
if (!(draw_rt || draw_rt_rov) && m_current_render_target && config.tex != m_current_render_target &&
draw_ds && m_current_render_target->GetSize() == draw_ds->GetSize())
{
draw_rt = m_current_render_target;
m_pipeline_selector.rt = true;
}
else if (!draw_ds && m_current_depth_target && config.tex != m_current_depth_target &&
m_current_depth_target->GetSize() == draw_rt->GetSize())
{
draw_ds = m_current_depth_target;
m_pipeline_selector.ds = true;
}
}
else if (!(draw_ds || draw_ds_rov) && m_current_depth_target && config.tex != m_current_depth_target &&
draw_rt && m_current_depth_target->GetSize() == draw_rt->GetSize())
{
draw_ds = m_current_depth_target;
m_pipeline_selector.ds = true;
}
GSTexture12* draw_ds_as_rt = static_cast<GSTexture12*>(m_ds_as_rt);
@@ -4377,10 +4481,13 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
else
Console.Warning("D3D12: Failed to allocate temp texture for RT copy.");
}
PSSetUnorderedAccess(draw_rt_rov, draw_ds_rov, config.ps.HasColorOutput(), config.ps.HasDepthROVWrite());
// For depth testing and sampling, use a read only dsv, otherwise use a write dsv
OMSetRenderTargets(draw_rt, draw_ds_as_rt, draw_ds, config.scissor,
config.tex && (config.tex == config.ds || config.ps.IsFeedbackLoopDepth()) && !config.depth.zwe);
config.tex && (config.tex == draw_ds || config.ps.IsFeedbackLoopDepth()) && !config.depth.zwe && !config.ps.HasDepthROV(),
config.rt ? config.rt->GetSize() : config.ds->GetSize());
// DX12 equivalent of vkCmdClearAttachments for StencilOne
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::StencilOne)
@@ -4435,14 +4542,15 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
// Restore original scissor, not sure if needed since the render pass has already been started. But to be safe.
OMSetRenderTargets(draw_rt, nullptr, draw_ds, config.scissor);
}
// VB/IB upload, if we did DATE setup and it's not colclip hw this has already been done
SetPrimitiveTopology(s_primitive_topology_mapping[static_cast<u8>(config.topology)]);
if (!date_image || colclip_rt)
UploadHWDrawVerticesAndIndices(config);
// now we can do the actual draw
SendHWDraw(pipe, config, draw_rt, draw_ds_as_rt, feedback_rt, feedback_depth,
config.require_one_barrier, config.require_full_barrier);
SendHWDraw(pipe, config, draw_rt, draw_ds_as_rt, draw_rt_rov, draw_ds_rov,
feedback_rt, feedback_depth, config.require_one_barrier, config.require_full_barrier);
// blend second pass
if (config.blend_multi_pass.enable)
@@ -4472,8 +4580,9 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
pipe.cms = config.alpha_second_pass.colormask;
pipe.dss = config.alpha_second_pass.depth;
pipe.bs = config.blend;
SendHWDraw(pipe, config, draw_rt, draw_ds_as_rt, feedback_rt, feedback_depth,
config.alpha_second_pass.require_one_barrier, config.alpha_second_pass.require_full_barrier);
SendHWDraw(pipe, config, draw_rt, draw_ds_as_rt, draw_rt_rov, draw_ds_rov,
feedback_rt, feedback_depth, config.alpha_second_pass.require_one_barrier,
config.alpha_second_pass.require_full_barrier);
}
if (date_image)
@@ -4516,9 +4625,13 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
}
void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig& config, GSTexture12* draw_rt,
GSTexture12* draw_ds, const bool feedback_rt, const bool feedback_depth, const bool one_barrier,
const bool full_barrier)
GSTexture12* draw_ds, GSTexture12* draw_rt_rov, GSTexture12* draw_ds_rov,
const bool feedback_rt, const bool feedback_depth,
const bool one_barrier, const bool full_barrier)
{
// Should not be mixing ROVs with barriers.
pxAssert(!(draw_rt_rov || draw_ds_rov) || !(one_barrier || full_barrier));
const int n_barriers = static_cast<int>(feedback_rt) + static_cast<int>(feedback_depth);
if (!m_features.texture_barrier) [[unlikely]]
@@ -4535,11 +4648,11 @@ void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig&
Console.Warning("D3D12: Possible unnecessary barrier detected.");
#endif
if ((one_barrier || full_barrier) && feedback_rt)
PSSetShaderResource(2, draw_rt, false, true);
PSSetShaderResource(2, draw_rt, false, ResourceType::FBL);
if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_RT)
PSSetShaderResource(0, draw_rt, false, true);
PSSetShaderResource(0, draw_rt, false, ResourceType::FBL);
if ((one_barrier || full_barrier) && feedback_depth)
PSSetShaderResource(4, draw_ds, false, true);
PSSetShaderResource(4, draw_ds, false, ResourceType::FBL);
if (full_barrier)
{
@@ -4580,6 +4693,9 @@ void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig&
if (BindDrawPipeline(pipe))
Draw(config);
if (config.ps.HasColorROV() || config.ps.HasDepthROV())
g_perfmon.Put(GSPerfMon::DrawCallsROV, 1);
}
void GSDevice12::UpdateHWPipelineSelector(GSHWDrawConfig& config)
@@ -4587,14 +4703,14 @@ void GSDevice12::UpdateHWPipelineSelector(GSHWDrawConfig& config)
m_pipeline_selector.vs.key = config.vs.key;
m_pipeline_selector.ps.key_hi = config.ps.key_hi;
m_pipeline_selector.ps.key_lo = config.ps.key_lo;
m_pipeline_selector.dss.key = config.depth.key;
m_pipeline_selector.bs.key = config.blend.key;
m_pipeline_selector.dss.key = config.ps.HasDepthROV() ? GSHWDrawConfig::DepthStencilSelector::NoDepth().key : config.depth.key;
m_pipeline_selector.bs.key = config.ps.HasColorROV() ? GSHWDrawConfig::BlendState().key : config.blend.key;
m_pipeline_selector.bs.constant = 0; // don't dupe states with different alpha values
m_pipeline_selector.cms.key = config.colormask.key;
m_pipeline_selector.cms.key = config.ps.HasColorROV() ? GSHWDrawConfig::ColorMaskSelector().key : config.colormask.key;
m_pipeline_selector.topology = static_cast<u32>(config.topology);
m_pipeline_selector.rt = config.rt != nullptr;
m_pipeline_selector.ds = config.ds != nullptr;
m_pipeline_selector.ds_as_rt = m_ds_as_rt != nullptr;
m_pipeline_selector.rt = config.rt != nullptr && !config.ps.HasColorROV();
m_pipeline_selector.ds = config.ds != nullptr && !config.ps.HasDepthROV();
m_pipeline_selector.ds_as_rt = m_ds_as_rt != nullptr && !config.ps.HasDepthROV();
}
void GSDevice12::UploadHWDrawVerticesAndIndices(GSHWDrawConfig& config)
+52 -12
View File
@@ -56,6 +56,44 @@ public:
D3D12_RESOURCE_DESC desc;
};
enum class ResourceType
{
SRV, // Shader resource view
FBL, // Feedback loop
UAV, // Unordered access
};
D3D12DescriptorHandle GetResourceDescriptor(GSTexture12* tex, ResourceType type) const
{
switch (type)
{
case ResourceType::SRV:
return tex->GetSRVDescriptor();
case ResourceType::FBL:
return m_enhanced_barriers ? tex->GetSRVDescriptor() : tex->GetFBLDescriptor();
case ResourceType::UAV:
return tex->GetUAVDescriptor();
default:
pxFailRel("Impossible.");
return D3D12DescriptorHandle{ 0, 0 };
}
}
static constexpr GSTexture12::ResourceState GetResourceState(ResourceType type)
{
switch (type)
{
case ResourceType::SRV:
case ResourceType::FBL:
return GSTexture12::ResourceState::PixelShaderResource;
case ResourceType::UAV:
return GSTexture12::ResourceState::PixelShaderUAV;
default:
pxFailRel("Impossible.");
return static_cast<GSTexture12::ResourceState>(-1);
}
}
__fi IDXGIAdapter1* GetAdapter() const { return m_adapter.get(); }
__fi ID3D12Device* GetDevice() const { return m_device.get(); }
__fi ID3D12CommandQueue* GetCommandQueue() const { return m_command_queue.get(); }
@@ -96,7 +134,6 @@ public:
D3D12DescriptorHeapManager& GetRTVHeapManager() { return m_rtv_heap_manager; }
D3D12DescriptorHeapManager& GetDSVHeapManager() { return m_dsv_heap_manager; }
D3D12DescriptorHeapManager& GetSamplerHeapManager() { return m_sampler_heap_manager; }
const D3D12DescriptorHandle& GetNullSRVDescriptor() const { return m_null_srv_descriptor; }
D3D12StreamBuffer& GetTextureStreamBuffer() { return m_texture_stream_buffer; }
// Root signature access.
@@ -198,7 +235,6 @@ private:
D3D12DescriptorHeapManager m_rtv_heap_manager;
D3D12DescriptorHeapManager m_dsv_heap_manager;
D3D12DescriptorHeapManager m_sampler_heap_manager;
D3D12DescriptorHandle m_null_srv_descriptor;
D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_11_0;
@@ -224,14 +260,13 @@ public:
GSHWDrawConfig::VSSelector vs;
GSHWDrawConfig::DepthStencilSelector dss;
GSHWDrawConfig::ColorMaskSelector cms;
u8 pad;
__fi bool operator==(const PipelineSelector& p) const { return BitEqual(*this, p); }
__fi bool operator!=(const PipelineSelector& p) const { return !BitEqual(*this, p); }
__fi PipelineSelector() { std::memset(this, 0, sizeof(*this)); }
};
static_assert(sizeof(PipelineSelector) == 24, "Pipeline selector is 24 bytes");
static_assert(sizeof(PipelineSelector) == 32, "Pipeline selector is 32 bytes");
struct PipelineSelectorHash
{
@@ -282,11 +317,14 @@ public:
TEXTURE_RT = 2,
TEXTURE_PRIMID = 3,
TEXTURE_DEPTH = 4,
TEXTURE_RT_UAV = 5,
TEXTURE_DEPTH_UAV = 6,
NUM_TFX_CONSTANT_BUFFERS = 2,
NUM_TFX_TEXTURES = 2,
NUM_TFX_RT_TEXTURES = 3,
NUM_TOTAL_TFX_TEXTURES = NUM_TFX_TEXTURES + NUM_TFX_RT_TEXTURES,
NUM_TFX_UAV_TEXTURES = 2,
NUM_TOTAL_TFX_TEXTURES = NUM_TFX_TEXTURES + NUM_TFX_RT_TEXTURES + NUM_TFX_UAV_TEXTURES,
NUM_TFX_SAMPLERS = 1,
NUM_UTILITY_TEXTURES = 1,
NUM_UTILITY_SAMPLERS = 1,
@@ -385,7 +423,7 @@ private:
std::string m_tfx_source;
void LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_format, DXGI_FORMAT* srv_format,
DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format) const;
DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format, DXGI_FORMAT* uav_format) const;
u32 GetSwapChainBufferCount() const;
bool CreateSwapChain();
@@ -513,11 +551,12 @@ public:
void IASetIndexBuffer(const void* index, size_t count);
void VSSetIndexBuffer(const void* index, size_t count);
void PSSetShaderResource(int i, GSTexture* sr, bool check_state, bool feedback = false);
void PSSetShaderResource(int i, GSTexture* sr, bool check_state, ResourceType type = ResourceType::SRV);
void PSSetSampler(GSHWDrawConfig::SamplerSelector sel);
void PSSetUnorderedAccess(GSTexture* rt, GSTexture* ds, bool write_rt, bool write_ds);
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, GSTexture* ds_as_rt, const GSVector4i& scissor,
bool depth_read = false);
bool depth_read = false, const GSVector2i& viewport_size = {});
void SetVSConstantBuffer(const GSHWDrawConfig::VSConstantBuffer& cb);
void SetPSConstantBuffer(const GSHWDrawConfig::PSConstantBuffer& cb);
@@ -526,8 +565,8 @@ public:
void RenderHW(GSHWDrawConfig& config) override;
void SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig& config, GSTexture12* draw_rt,
GSTexture12* draw_ds, const bool feedback_rt, const bool feedback_depth, const bool one_barrier,
const bool full_barrier);
GSTexture12* draw_ds, GSTexture12* draw_rt_rov, GSTexture12* draw_ds_rov,
const bool feedback_rt, const bool feedback_depth, const bool one_barrier, const bool full_barrier);
void UpdateHWPipelineSelector(GSHWDrawConfig& config);
void UploadHWDrawVerticesAndIndices(GSHWDrawConfig& config);
@@ -593,8 +632,8 @@ private:
DIRTY_FLAG_PS_CONSTANT_BUFFER_BINDING = (1 << 6),
DIRTY_FLAG_VS_VERTEX_BUFFER_BINDING = (1 << 7),
DIRTY_FLAG_VS_INDEX_BUFFER_BINDING = (1 << 8),
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE = (1 << 9),
DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE = (1 << 10),
DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE = (1 << 9),
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE = (1 << 10),
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE_2 = (1 << 11),
DIRTY_FLAG_VS_PUSH_CONSTANTS = (1 << 12),
@@ -657,6 +696,7 @@ private:
std::array<D3D12_GPU_VIRTUAL_ADDRESS, NUM_TFX_CONSTANT_BUFFERS> m_tfx_constant_buffers{};
std::array<D3D12DescriptorHandle, NUM_TOTAL_TFX_TEXTURES> m_tfx_textures{};
std::array<GSTexture12*, NUM_TFX_UAV_TEXTURES> m_tfx_textures_uav{};
D3D12DescriptorHandle m_tfx_sampler;
u32 m_tfx_sampler_sel = 0;
D3D12DescriptorHandle m_tfx_textures_handle_gpu;
+36 -22
View File
@@ -110,6 +110,10 @@ void GSTexture12::Destroy(bool defer)
}
m_write_descriptor_type = WriteDescriptorType::None;
#ifdef PCSX2_DEVBUILD
m_debug_name.clear();
#endif
}
// For use with non-simultaneous textures only.
@@ -203,6 +207,7 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
desc.desc1.Flags = (levels > 1 && !IsCompressedFormat(format)) ? D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET :
D3D12_RESOURCE_FLAG_NONE;
state = ResourceState::CopyDst;
pxAssert(uav_format == DXGI_FORMAT_UNKNOWN);
}
break;
@@ -217,6 +222,10 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
desc.desc1.Layout = D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE;
optimized_clear_value.Format = rtv_format;
state = ResourceState::RenderTarget;
if (uav_format != DXGI_FORMAT_UNKNOWN)
{
desc.desc1.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
}
}
break;
@@ -227,6 +236,7 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
desc.desc1.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
optimized_clear_value.Format = dsv_format;
state = ResourceState::DepthWriteStencil;
pxAssert(uav_format == DXGI_FORMAT_UNKNOWN);
}
break;
@@ -235,6 +245,8 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
pxAssert(levels == 1);
allocationDesc.Flags |= D3D12MA::ALLOCATION_FLAG_COMMITTED;
state = ResourceState::PixelShaderResource;
pxAssert(uav_format != DXGI_FORMAT_UNKNOWN);
desc.desc1.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
}
break;
@@ -242,9 +254,6 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
return {};
}
if (uav_format != DXGI_FORMAT_UNKNOWN)
desc.desc1.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
wil::com_ptr_nothrow<ID3D12Resource> resource;
wil::com_ptr_nothrow<ID3D12Resource> resource_fbl;
wil::com_ptr_nothrow<D3D12MA::Allocation> allocation;
@@ -330,6 +339,12 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
dev->GetDescriptorHeapManager().Free(&srv_descriptor);
return {};
}
if (uav_format != DXGI_FORMAT_UNKNOWN && !CreateUAVDescriptor(resource.get(), uav_format, &uav_descriptor))
{
dev->GetRTVHeapManager().Free(&write_descriptor);
dev->GetDescriptorHeapManager().Free(&srv_descriptor);
return {};
}
}
break;
@@ -350,26 +365,15 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
}
break;
default:
break;
}
if (uav_format != DXGI_FORMAT_UNKNOWN && !CreateUAVDescriptor(resource.get(), dsv_format, &uav_descriptor))
{
switch (write_descriptor_type)
case Type::RWTexture:
{
case WriteDescriptorType::RTV:
dev->GetRTVHeapManager().Free(&write_descriptor);
break;
case WriteDescriptorType::DSV:
dev->GetDSVHeapManager().Free(&ro_dsv_descriptor);
dev->GetDSVHeapManager().Free(&write_descriptor);
break;
default:
break;
if (uav_format != DXGI_FORMAT_UNKNOWN && !CreateUAVDescriptor(resource.get(), uav_format, &uav_descriptor))
{
dev->GetDescriptorHeapManager().Free(&srv_descriptor);
return {};
}
}
dev->GetDescriptorHeapManager().Free(&srv_descriptor);
return {};
break;
}
// Feedback descriptor used with legacy barriers
@@ -442,7 +446,7 @@ std::unique_ptr<GSTexture12> GSTexture12::Adopt(wil::com_ptr_nothrow<ID3D12Resou
if (uav_format != DXGI_FORMAT_UNKNOWN)
{
if (!CreateUAVDescriptor(resource.get(), srv_format, &uav_descriptor))
if (!CreateUAVDescriptor(resource.get(), uav_format, &uav_descriptor))
{
switch (write_descriptor_type)
{
@@ -796,6 +800,8 @@ void GSTexture12::SetDebugName(std::string_view name)
return;
D3D12::SetObjectName(m_resource.get(), name);
m_debug_name = name;
}
#endif
@@ -1137,6 +1143,14 @@ void GSTexture12::TransitionSubresourceToState(const D3D12CommandList& cmdlist,
cmdlist.list4->ResourceBarrier(num_barriers, barriers);
}
// Count as a UAV barrier if we transition to/from UAV.
if (IsRenderTargetOrDepthStencil() &&
(before_state == ResourceState::PixelShaderUAV || after_state == ResourceState::PixelShaderUAV))
{
g_perfmon.Put(GSPerfMon::Barriers, 1);
g_perfmon.Put(GSPerfMon::BarriersROV, 1);
}
}
void GSTexture12::CommitClear()
+2
View File
@@ -56,6 +56,8 @@ public:
__fi ID3D12Resource* GetResource() const { return m_resource.get(); }
__fi ID3D12Resource* GetFBLResource() const { return m_resource_fbl.get(); }
virtual bool IsUnorderedAccess() const override { return GetResourceState() == ResourceState::PixelShaderUAV; }
void* GetNativeHandle() const override;
bool Update(const GSVector4i& r, const void* data, int pitch, int layer = 0) override;
+398 -5
View File
@@ -6825,6 +6825,13 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, DATEOptio
m_conf.ps.blend_d = 2;
}
// Save in case needed for ROV setup.
m_optimized_blend.A = m_conf.ps.blend_a;
m_optimized_blend.B = m_conf.ps.blend_b;
m_optimized_blend.C = m_conf.ps.blend_c;
m_optimized_blend.D = m_conf.ps.blend_d;
m_optimized_blend.FIX = AFIX;
// TODO: blend_ad_alpha_masked, as well as other blend cases can be optimized on dx11/dx12/gl to use
// blend multipass more which might be faster, vk likely won't benefit as barriers are already fast.
@@ -7478,6 +7485,388 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, DATEOptio
}
}
// In certain cases using a ROV with depth or color will force the other one
// because of how color and depth interact via testing.
__fi void GSRendererHW::GetForcedROVUsage(bool& rov_color, bool& rov_depth)
{
// Cannot force if both are already enabled or disabled.
if (rov_color == rov_depth)
return;
// If depth and color have feedback and one uses ROV, the other must also.
// We currently don't have a way of using barriers in one and ROV in the other.
if ((m_conf.ps.IsFeedbackLoopRT() && rov_depth) || (m_conf.ps.IsFeedbackLoopDepth() && rov_color))
{
GL_INS("ROV: Feedback compatibility forces color and depth ROV");
rov_color = true;
rov_depth = true;
}
// If we use color ROV with discard or the pixel shader writes to depth,
// we cannot use early depth stencil, so must use depth ROV with feedback.
// Same applies in reverse for depth ROV forcing color ROV with feedback.
const bool color_write = (m_conf.colormask.wrgba != 0);
const bool depth_test = m_cached_ctx.DepthRead();
// Separate flag for DATE since they are many methods and the interaction with ROV is not clear.
const bool date = m_cached_ctx.TEST.DATE;
if (m_conf.ds && rov_color && (m_conf.ps.HasShaderDiscard() || m_conf.ps.HasDepthOutput() || date))
{
GL_INS("ROV: Color ROV with shader discard/depth write forces depth ROV");
rov_depth = true;
}
else if (m_conf.rt && color_write && rov_depth && (m_conf.ps.HasShaderDiscard() || depth_test || date))
{
GL_INS("ROV: Depth ROV with shader discard forces color ROV");
rov_color = true;
}
}
void GSRendererHW::DetermineROVUsage(GSTextureCache::Target* rt, GSTextureCache::Target* ds)
{
const GSDevice::FeatureSupport& features = g_gs_device->Features();
if (!(GSConfig.HWROV && features.rov))
return;
GL_PUSH("HW: ROV Setup");
if (features.framebuffer_fetch)
{
GL_INS("ROV: Disabled because have FB-fetch");
return;
}
if (rt && rt->m_texture == m_conf.tex && !m_conf.ps.tex_is_fb)
{
GL_INS("ROV: Disabled because tex is RT and not sampling from current pixel");
return;
}
if (ds && ds->m_texture == m_conf.tex)
{
GL_INS("ROV: Disabled because tex is depth");
return;
}
const bool color_write = rt && m_conf.colormask.wrgba != 0;
const bool depth_write = ds && m_cached_ctx.DepthWrite();
const u32 colormask = GSUtil::GetChannelMask(m_cached_ctx.FRAME.PSM) & m_conf.colormask.wrgba;
const bool colormask_needs_rt = colormask != 0xF;
const u32 ate = m_cached_ctx.TEST.ATE;
const u32 atst = m_cached_ctx.TEST.ATST;
const u32 afail = m_cached_ctx.TEST.AFAIL;
const bool afail_needs_rt = ate && ((afail == AFAIL_ZB_ONLY) || (afail == AFAIL_RGB_ONLY));
const bool afail_needs_depth = ate && ((afail == AFAIL_FB_ONLY) || (afail == AFAIL_RGB_ONLY));
const bool blend = m_conf.IsBlending();
const bool blend_needs_rt = blend &&
(m_optimized_blend.A == ALPHA_ABD_CD || m_optimized_blend.B == ALPHA_ABD_CD ||
m_optimized_blend.C == ALPHA_C_AD || m_optimized_blend.D == ALPHA_ABD_CD);
const bool two_pass_alpha = GSHWDrawConfig::HasAlphaTestSecondPass(m_conf.alpha_test);
const bool date = m_conf.destination_alpha != GSHWDrawConfig::DestinationAlphaMode::Off;
const bool ztst = m_cached_ctx.DepthRead();
const bool full_barrier = m_conf.require_full_barrier;
// Heuristically determine what ROVs would be needed to eliminate passes based on the current config.
const bool multipass_color = (full_barrier && m_conf.ps.IsFeedbackLoopRT()) ||
two_pass_alpha ||
m_conf.blend_multi_pass.enable;
const bool multipass_depth = (full_barrier && m_conf.ps.IsFeedbackLoopDepth()) || two_pass_alpha;
// If already ROV, just continue the usage.
const bool color_is_rov = rt && rt->m_texture->IsUnorderedAccess();
const bool depth_is_rov = ds && ds->m_texture->IsDepthColor();
bool use_rov_color = (color_write && multipass_color) || color_is_rov;
bool use_rov_depth = (depth_write && multipass_depth) || depth_is_rov;
// In certain cases, ROV in color or depth will force ROV in the other for correctness.
GetForcedROVUsage(use_rov_color, use_rov_depth);
// Get the number of barriers that would be used with the current config.
u32 barriers = 1;
if (full_barrier)
{
if (m_drawlist.size() > 0)
{
barriers = static_cast<u32>(m_drawlist.size()); // Already computed
}
else
{
barriers = 2; // Tells drawlist computation to stop after reaching 2.
GetPrimitiveOverlapDrawlist(false, false, 1.0f, &barriers);
}
}
const u32 multiplier = m_conf.alpha_second_pass.enable ? 2 : 1; // Alpha second pass doubles the barriers.
barriers *= multiplier;
// Heuristic: only activate ROV if we save at least one draw call by doing so.
const bool activate = (use_rov_color != color_is_rov || use_rov_depth != depth_is_rov) && barriers >= 2;
if (!color_is_rov && !depth_is_rov && !activate)
{
// Not enough barriers or no feedback to activate, and ROV is not already active.
GL_ROV("No ROV usage: Draw=%05lld | C=%016p | D=%016p | BAR=%d.",
s_n, rt ? rt->m_texture : nullptr, ds ? ds->m_texture : nullptr, barriers);
return;
}
if (activate)
{
GL_ROV("ROV activated: Draw=%05lld | C=%016p | D=%016p | BAR=%d | C=[%d=>%d] | D=[%d=>%d].",
s_n, rt ? rt->m_texture : nullptr, ds ? ds->m_texture : nullptr, barriers,
color_is_rov, use_rov_color, depth_is_rov, use_rov_depth);
}
else
{
GL_ROV("ROV continued: Draw=%05lld | C=%016p | D=%016p | BAR=%d | C=%d | D=%d.",
s_n, rt ? rt->m_texture : nullptr, ds ? ds->m_texture : nullptr, barriers,
color_is_rov, depth_is_rov);
}
GL_INS("ROV: Color ROV %s / depth ROV %s",
use_rov_color ? "enabled" : "disabled", use_rov_depth ? "enabled" : "disabled");
// Set unordered access flag for backends that don't explicitly track resource state (DX11/GL).
// Used only for heuristic above. Not used for depth, since we check for depth color instead.
if (use_rov_color)
rt->m_texture->UseUnorderedAccess();
// Do the actual pipeline config.
ConfigureROV(use_rov_color, use_rov_depth);
}
void GSRendererHW::ConfigureROV(bool color_rov, bool depth_rov)
{
// Do the actual config for depth.
if (depth_rov)
{
m_conf.depth = GSHWDrawConfig::DepthStencilSelector::NoDepth(); // Disable real depth.
const bool depth_write = m_cached_ctx.DepthWrite();
GL_INS("ROV: Using %s depth ROV", depth_write ? "read/write" : "read-only");
ConfigureDepthFeedback(true);
m_conf.ps.rov_depth = depth_write ? GSHWDrawConfig::PS_ROV_DEPTH::READ_WRITE : GSHWDrawConfig::PS_ROV_DEPTH::READ_ONLY;
}
// Do the actual config for color.
if (color_rov)
{
// FbMask setup
if (m_conf.colormask.wrgba != 0)
{
const GSVector4i fbmask = GSVector4i(m_conf.colormask.wr ? 0 : 0xFF, m_conf.colormask.wg ? 0 : 0xFF,
m_conf.colormask.wb ? 0 : 0xFF, m_conf.colormask.wa ? 0 : 0xFF);
if (!m_conf.ps.fbmask)
{
// Don't enable FB mask emulation, just use the mask for ROV.
m_conf.cb_ps.FbMask = fbmask;
}
else
{
m_conf.cb_ps.FbMask |= fbmask;
}
GL_INS("ROV: FbMask={ R=%x, G=%x, B=%x, A=%x }",
m_conf.cb_ps.FbMask.r, m_conf.cb_ps.FbMask.g, m_conf.cb_ps.FbMask.b, m_conf.cb_ps.FbMask.a);
}
else
{
m_conf.ps.no_color = true;
}
// Blend setup
if (m_conf.IsBlending())
{
GL_INS("ROV: Using SW blend%s", m_conf.blend.enable ? " and disabling HW" : "");
m_conf.ps.blend_a = m_optimized_blend.A;
m_conf.ps.blend_b = m_optimized_blend.B;
m_conf.ps.blend_c = m_optimized_blend.C;
m_conf.ps.blend_d = m_optimized_blend.D;
if (m_conf.ps.blend_c == ALPHA_C_FIX)
m_conf.cb_ps.TA_MaxDepth_Af.a = m_optimized_blend.FIX / 128.0f;
// Disable HW or mixed blend or multipass blend
m_conf.blend = {};
m_conf.ps.blend_hw = false;
m_conf.ps.blend_mix = false;
m_conf.blend_multi_pass = {};
if (!m_conf.ps.no_color1)
{
// We should never need dual source with SW blend
GL_INS("ROV: Disabling dual source blending");
m_conf.ps.no_color1 = true;
}
// Only needed with HW blend.
m_conf.ps.round_inv = false;
m_conf.ps.a_masked = false;
}
// Dither setup
if (m_conf.ps.dither)
{
// Only needed with HW blend.
m_conf.ps.dither_adjust = false;
}
// Destination alpha test setup
if (m_conf.destination_alpha != GSHWDrawConfig::DestinationAlphaMode::Off)
{
GL_INS("ROV: Using DATE Full%s",
(m_conf.destination_alpha != GSHWDrawConfig::DestinationAlphaMode::Full) ? " and replace current method" : "");
if (m_conf.destination_alpha != GSHWDrawConfig::DestinationAlphaMode::Full)
{
m_conf.destination_alpha = GSHWDrawConfig::DestinationAlphaMode::Full;
m_conf.depth.date = false; // Don't use stencil with ROV
m_conf.depth.date_one = false; // Don't use stencil with ROV
m_conf.ps.date = 5 + m_cached_ctx.TEST.DATM; // Shader discard DATM.
m_conf.datm = static_cast<SetDATM>(0); // Not needed
}
}
// Colclip setup
if (m_conf.ps.colclip_hw)
{
// Remove HW colclip texture if needed
GL_INS("ROV: Replacing colclip HW with SW");
const bool has_colclip_texture = g_gs_device->GetColorClipTexture() != nullptr;
m_conf.ps.colclip_hw = 0;
m_conf.ps.colclip = true;
m_conf.colclip_mode = has_colclip_texture ? GSHWDrawConfig::ColClipMode::EarlyResolve : GSHWDrawConfig::ColClipMode::NoModify;
}
// PABE setup
const bool PABE = m_draw_env->PABE.PABE && GetAlphaMinMax().min < 128;
if (m_conf.IsBlending() && PABE && !m_conf.ps.pabe)
{
GL_INS("ROV: Enabling PABE");
m_conf.ps.pabe = true;
}
// Alpha test setup. KEEP will already be fine.
if (m_cached_ctx.TEST.ATE && m_conf.alpha_test != GSHWDrawConfig::AlphaTestMode::KEEP)
{
GL_INS("ROV: Using SW feedback alpha test%s", m_conf.alpha_second_pass.enable ?
" and disabling alpha second pass" : "");
m_conf.alpha_test = GSHWDrawConfig::AlphaTestMode::FEEDBACK;
GSHWDrawConfig::PS_ATST ps_atst;
float ps_aref;
GetAlphaTestConfigPS(m_cached_ctx.TEST.ATST, m_cached_ctx.TEST.AREF, false, ps_atst, ps_aref);
m_conf.ps.atst = ps_atst;
m_conf.ps.afail = static_cast<GSHWDrawConfig::PS_AFAIL>(m_cached_ctx.TEST.AFAIL);
if (m_cached_ctx.DepthWrite() && m_cached_ctx.TEST.AFAIL == AFAIL_RGB_ONLY)
{
pxAssert(depth_rov); // Should have enabled depth ROV for depth feedback loop.
m_conf.ps.afail = PS_AFAIL::RGB_ONLY_SW_Z;
}
m_conf.cb_ps.FogColor_AREF.a = ps_aref;
GL_INS("ROV: Using ATST=%d, AFAIL=%d, AREF=%.2f", ps_atst, static_cast<u32>(m_conf.ps.afail), ps_aref);
if (m_conf.alpha_second_pass.enable)
{
m_conf.alpha_second_pass = {};
}
}
m_conf.ps.rov_color = true;
}
// Remove regular barriers.
if (color_rov || depth_rov)
{
m_conf.require_full_barrier = false;
m_conf.require_one_barrier = false;
}
}
static void CopyDepthTextureROV(GSTexture* src, GSTexture* dst)
{
switch (src->GetState())
{
case GSTexture::State::Cleared:
g_gs_device->ClearDepth(dst, src->GetClearDepth());
break;
case GSTexture::State::Invalidated:
g_gs_device->InvalidateRenderTarget(dst);
break;
case GSTexture::State::Dirty:
g_gs_device->StretchRectAuto(src, dst, Nearest);
break;
}
// These stats are counted both as part of ROV and non-ROV stats.
g_perfmon.Put(GSPerfMon::DepthCopiesROV, 1.0);
g_perfmon.Put(GSPerfMon::DrawCallsROV, 1.0);
};
void GSRendererHW::ConvertDepthFormatROV(GSTextureCache::Target* ds)
{
if (!ds)
return;
GSTexture* ds_tex_old = m_conf.ds;
GSTexture* ds_tex_new = nullptr;
// Convert depth to depth color or vice versa if needed.
bool depth_to_color;
if (m_conf.ps.HasDepthROV() && !ds_tex_old->IsDepthColor())
{
depth_to_color = true;
ds_tex_new = g_gs_device->CreateDepthColor(ds_tex_old->GetSize(), false, true);
}
else if (!m_conf.ps.HasDepthROV() && ds_tex_old->IsDepthColor())
{
depth_to_color = false;
ds_tex_new = g_gs_device->CreateDepthStencil(ds->m_texture->GetSize(), false, true);
}
else
{
return;
}
GL_PUSH("HW: Convert %s for ROV.", depth_to_color ? "DepthStencil -> DepthColor" : "DepthColor -> DepthStencil");
CopyDepthTextureROV(ds_tex_old, ds_tex_new);
#if PCSX2_DEVBUILD
ds_tex_new->SetDebugName(ds->m_texture->GetDebugName());
#endif
// Fix up the texture cache.
if (ds->m_texture == ds_tex_old)
{
GL_CACHE("HW: Replaced texture for DS @ 0x%04x", ds->m_TEX0.TBP0);
ds->m_texture = ds_tex_new;
}
else
{
// Must be the temporary Z.
pxAssert(g_texture_cache->GetTemporaryZ() == ds_tex_old);
GL_CACHE("HW: Replaced texture for temporary Z @ 0x%04x", g_texture_cache->GetTemporaryZInfo().ZBP);
g_texture_cache->SetTemporaryZ(ds_tex_new);
}
g_gs_device->Recycle(ds_tex_old);
m_conf.ds = ds_tex_new;
}
__ri static constexpr bool IsRedundantClamp(u8 clamp, u32 clamp_min, u32 clamp_max, u32 tsize)
{
// Don't shader sample when the clamp/repeat is configured to the texture size.
@@ -8780,14 +9169,14 @@ void GSRendererHW::EmulateAlphaTestSecondPass()
}
// Setup barriers and/or SW depth testing for depth feedback.
void GSRendererHW::ConfigureDepthFeedback()
void GSRendererHW::ConfigureDepthFeedback(bool rov_depth)
{
if (m_conf.ps.IsFeedbackLoopDepth())
if (m_conf.ps.IsFeedbackLoopDepth() || rov_depth)
{
const GSDevice::FeatureSupport& features = g_gs_device->Features();
// We need barriers for the feedback.
if (features.feedback_loops())
// We need barriers for the feedback (except with ROV).
if (features.feedback_loops() && !rov_depth)
{
m_conf.require_one_barrier |= (m_prim_overlap == PRIM_OVERLAP_NO);
m_conf.require_full_barrier |= (m_prim_overlap != PRIM_OVERLAP_NO);
@@ -9037,6 +9426,10 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
m_conf.ps.rta_correction = rt->m_rt_alpha_scale;
}
// Call before computing the full drawlist in case ROV is used and we don't need it.
DetermineROVUsage(rt, ds);
ConvertDepthFormatROV(ds);
// Barriers must be determined before indices are modified via HandleProvokingVertexFirst/SetupIA.
DetermineBarriers(rt, tex);
@@ -9065,7 +9458,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
SetupIA(rtscale, vs_scale_x, vs_scale_y, m_channel_shuffle_width != 0, no_rt);
if (m_conf.ds && m_conf.ps.IsFeedbackLoopDepth() && !g_gs_device->Features().depth_feedback)
if (m_conf.ds && m_conf.ps.IsFeedbackLoopDepth() && !g_gs_device->Features().depth_feedback && !m_conf.ps.HasDepthROV())
{
GL_PUSH("HW: Creating temporary R32 RT for depth feedback");
+8 -1
View File
@@ -236,7 +236,7 @@ private:
static void GetAlphaTestConfigPS(const u32 atst, const u8 aref, const bool invert_test, PS_ATST& ps_atst_out, float& aref_out);
void EmulateAlphaTest(DATEOptions& date_options);
void EmulateAlphaTestSecondPass();
void ConfigureDepthFeedback();
void ConfigureDepthFeedback(bool rov_depth = false);
void CalculateAlphaRange(GSTextureCache::Target* rt, GSTextureCache::Target* ds, DATEOptions& date_options,
int& blend_alpha_min, int& blend_alpha_max, int& rt_new_alpha_min, int& rt_new_alpha_max);
@@ -253,6 +253,11 @@ private:
const GSVector2i& unscaled_size, float& vs_scale_x, float& vs_scale_y);
void DetermineBarriers(GSTextureCache::Target* rt, GSTextureCache::Source* tex);
void GetForcedROVUsage(bool& color_cov, bool& depth_rov); // Whether having color or depth with the current config forces the other.
void DetermineROVUsage(GSTextureCache::Target* rt, GSTextureCache::Target* ds); // Heuristics to determine whether to enable/disable ROV
void ConfigureROV(bool color_rov, bool depth_rov); // Actual config for ROV
void ConvertDepthFormatROV(GSTextureCache::Target* ds); // Convert between depth and depth color if needed.
void SetTCOffset();
bool NextDrawColClip() const;
bool IsPossibleChannelShuffle() const;
@@ -330,6 +335,8 @@ private:
GSVector2i m_lod = {}; // Min & Max level of detail
GIFRegALPHA m_optimized_blend = {}; // Save for ROV setup
GSHWDrawConfig m_conf = {};
HWCachedCtx m_cached_ctx;
+1 -1
View File
@@ -101,7 +101,7 @@ struct PipelineSelectorMTL
}
};
static_assert(sizeof(PipelineSelectorMTL) == 24);
static_assert(sizeof(PipelineSelectorMTL) == 28);
template <>
struct std::hash<PipelineSelectorMTL>
+16 -2
View File
@@ -38,14 +38,21 @@ namespace GLState
GSTextureOGL* ds_as_rt = nullptr;
GSTextureOGL* ds = nullptr;
GSTextureOGL* rt_uav = nullptr;
GSTextureOGL* ds_uav = nullptr;
bool rt_written;
bool ds_as_rt_written;
bool ds_written;
bool rt_uav_written;
bool ds_uav_written;
u32 draw_buffers;
GLuint tex_unit[8];
GLuint64 tex_handle[8];
GLuint image_unit[8];
u32 UpdateDrawBuffers()
{
@@ -83,13 +90,20 @@ namespace GLState
ds_as_rt = nullptr;
ds = nullptr;
rt_uav = nullptr;
ds_uav = nullptr;
rt_written = false;
ds_as_rt_written = false;
ds_written = false;
rt_uav_written = false;
ds_uav_written = false;
draw_buffers = 0;
std::fill(std::begin(tex_unit), std::end(tex_unit), 0);
std::fill(std::begin(tex_handle), std::end(tex_handle), 0);
std::fill(std::begin(image_unit), std::end(image_unit), 0);
}
} // namespace GLState
+8 -1
View File
@@ -42,14 +42,21 @@ namespace GLState
extern GSTextureOGL* ds_as_rt; // Depth-Stencil as color
extern GSTextureOGL* ds; // Depth-Stencil
extern GSTextureOGL* rt_uav; // Color UAV (storage image)
extern GSTextureOGL* ds_uav; // Depth UAV (storage image)
extern u32 draw_buffers; // Number of color attachments to framebuffer.
extern bool rt_written; // Render Target written
extern bool ds_as_rt_written; // Depth Stencil as RT written
extern bool ds_written; // Depth Stencil written
extern bool rt_uav_written; // RT UAV written
extern bool ds_uav_written; // Depth UAV written
extern GLuint tex_unit[8]; // shader input texture
extern GLuint64 tex_handle[8]; // shader input texture
extern GLuint image_unit[8]; // shader UAV texture
extern u32 UpdateDrawBuffers();
extern void Clear();
+157 -44
View File
@@ -650,7 +650,7 @@ bool GSDeviceOGL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
// Basic to ensure structures are correctly packed
static_assert(sizeof(VSSelector) == 1, "Wrong VSSelector size");
static_assert(sizeof(PSSelector) == 12, "Wrong PSSelector size");
static_assert(sizeof(PSSelector) == 16, "Wrong PSSelector size");
static_assert(sizeof(PSSamplerSelector) == 1, "Wrong PSSamplerSelector size");
static_assert(sizeof(OMDepthStencilSelector) == 1, "Wrong OMDepthStencilSelector size");
static_assert(sizeof(OMColorMaskSelector) == 1, "Wrong OMColorMaskSelector size");
@@ -901,6 +901,23 @@ bool GSDeviceOGL::CheckFeatures()
m_features.aa1 = GSConfig.HWAA1 && m_features.vs_expand && m_features.feedback_loops();
m_features.rov = (GLAD_GL_ARB_fragment_shader_interlock || GLAD_GL_NV_fragment_shader_interlock ||
GLAD_GL_INTEL_fragment_shader_ordering) && GLAD_GL_ARB_shader_image_load_store && GLAD_GL_VERSION_4_5;
m_features.rov &= !m_features.framebuffer_fetch; // Prefer FB fetch over ROV.
m_features.rov &= GSConfig.HWROV;
#if PCSX2_DEVBUILD
// Force enable even without FSI extensions (works for debugging on Mesa).
m_features.rov = GSConfig.HWROV && GLAD_GL_ARB_shader_image_load_store && GLAD_GL_VERSION_4_5;
#endif
if (GSConfig.HWROV && !m_features.rov)
{
Console.Warning("GL: ROV is disabled because but the needed extensions are not present or framebuffer fetch is being used.");
}
return true;
}
@@ -1332,8 +1349,9 @@ void GSDeviceOGL::CommitClear(GSTexture* t, bool use_write_fbo)
if (use_write_fbo)
{
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
(t->GetType() == GSTexture::Type::RenderTarget) ? GL_COLOR_ATTACHMENT0 :
(m_features.framebuffer_fetch ? GL_DEPTH_ATTACHMENT : GL_DEPTH_STENCIL_ATTACHMENT),
(t->GetType() == GSTexture::Type::RenderTarget) ?
GL_COLOR_ATTACHMENT0 :
(m_features.framebuffer_fetch ? GL_DEPTH_ATTACHMENT : GL_DEPTH_STENCIL_ATTACHMENT),
GL_TEXTURE_2D, 0, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLState::fbo);
}
@@ -1439,9 +1457,14 @@ std::string GSDeviceOGL::GenGlslHeader(const std::string_view entry, GLenum type
{
std::string header;
// Intel's GL driver doesn't like the readonly qualifier with 3.3 GLSL.
if (m_features.vs_expand && GLAD_GL_VERSION_4_3)
if (m_features.rov)
{
// FSI extensions need 4.5 GLSL
header = "#version 450 core\n";
}
else if (m_features.vs_expand && GLAD_GL_VERSION_4_3)
{
// Intel's GL driver doesn't like the readonly qualifier with 3.3 GLSL.
header = "#version 430 core\n";
}
else
@@ -1501,6 +1524,38 @@ std::string GSDeviceOGL::GenGlslHeader(const std::string_view entry, GLenum type
pxAssert(0);
}
if (m_features.rov && type == GL_FRAGMENT_SHADER)
{
header += "#extension GL_ARB_shader_image_load_store : require\n";
if (GLAD_GL_ARB_fragment_shader_interlock)
{
header += "#extension GL_ARB_fragment_shader_interlock : require\n";
header += "#define USE_ARB_FSI 1\n";
header += "#define USE_NV_FSI 0\n";
header += "#define USE_INTEL_FSI 0\n";
}
else if (GLAD_GL_NV_fragment_shader_interlock)
{
header += "#extension GL_NV_fragment_shader_interlock : require\n";
header += "#define USE_ARB_FSI 0\n";
header += "#define USE_NV_FSI 1\n";
header += "#define USE_INTEL_FSI 0\n";
}
else if (GLAD_GL_INTEL_fragment_shader_ordering)
{
header += "#extension GL_INTEL_fragment_shader_ordering : require\n";
header += "#define USE_ARB_FSI 0\n";
header += "#define USE_NV_FSI 0\n";
header += "#define USE_INTEL_FSI 1\n";
}
}
else
{
header += "#define USE_ARB_FSI 0\n";
header += "#define USE_NV_FSI 0\n";
header += "#define USE_INTEL_FSI 0\n";
}
// Don't remove this, the recursive macro breaks some Intel drivers.
if (entry != "main")
{
@@ -1522,7 +1577,7 @@ std::string GSDeviceOGL::GetVSSource(VSSelector sel)
std::string macro = fmt::format("#define VS_FST {}\n", static_cast<u32>(sel.fst))
+ fmt::format("#define VS_IIP {}\n", static_cast<u32>(sel.iip))
+ fmt::format("#define VS_POINT_SIZE {}\n", static_cast<u32>(sel.point_size))
+ fmt::format("#define VS_EXPAND {}\n", static_cast<int>(sel.expand));
+ fmt::format("#define VS_EXPAND {}\n", static_cast<int>(sel.expand));
std::string src = GenGlslHeader("vs_main", GL_VERTEX_SHADER, macro);
src += m_shader_tfx_vgs;
@@ -1592,7 +1647,9 @@ std::string GSDeviceOGL::GetPSSource(const PSSelector& sel)
+ fmt::format("#define PS_ZTST {}\n", sel.ztst)
+ fmt::format("#define PS_AA1 {}\n", static_cast<u32>(sel.aa1))
+ fmt::format("#define PS_ABE {}\n", sel.abe)
+ fmt::format("#define PS_ANISOTROPIC_FILTERING {}", sel.sw_aniso)
+ fmt::format("#define PS_ANISOTROPIC_FILTERING {}\n", sel.sw_aniso)
+ fmt::format("#define PS_ROV_COLOR {}\n", sel.rov_color)
+ fmt::format("#define PS_ROV_DEPTH {}\n", static_cast<u32>(sel.rov_depth))
;
std::string src = GenGlslHeader("ps_main", GL_FRAGMENT_SHADER, macro);
@@ -1619,7 +1676,7 @@ void GSDeviceOGL::BlitRect(GSTexture* sTex, const GSVector4i& r, const GSVector2
OMSetDepthStencilState(m_convert.dss);
OMSetBlendState();
OMSetColorMaskState();
PSSetShaderResource(0, sTex);
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
PSSetSamplerState(filter == Biln ? m_convert.ln : m_convert.pt);
DrawStretchRect(float_r / (GSVector4(sTex->GetSize()).xyxy()), float_r, dsize);
@@ -1722,7 +1779,7 @@ void GSDeviceOGL::DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTextu
// Texture
// ************************************
PSSetShaderResource(0, sTex);
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
PSSetSamplerState(filter == Biln ? m_convert.ln : m_convert.pt);
// ************************************
@@ -1757,7 +1814,7 @@ void GSDeviceOGL::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture
OMSetBlendState(false);
OMSetColorMaskState();
PSSetShaderResource(0, sTex);
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
PSSetSamplerState(filter == Biln ? m_convert.ln : m_convert.pt);
// Flip y axis only when we render in the backbuffer
@@ -1784,7 +1841,7 @@ void GSDeviceOGL::UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX,
OMSetColorMaskState();
OMSetRenderTargets(dTex, nullptr, nullptr);
PSSetShaderResource(0, sTex);
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
PSSetSamplerState(m_convert.pt);
const GSVector4 dRect(0, 0, dSize, 1);
@@ -1808,7 +1865,7 @@ void GSDeviceOGL::ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 off
OMSetColorMaskState();
OMSetRenderTargets(dTex, nullptr, nullptr);
PSSetShaderResource(0, sTex);
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
PSSetSamplerState(m_convert.pt);
const GSVector4 dRect(0, 0, dTex->GetWidth(), dTex->GetHeight());
@@ -1832,7 +1889,7 @@ void GSDeviceOGL::FilteredDownsampleTexture(GSTexture* sTex, GSTexture* dTex, u3
OMSetColorMaskState();
OMSetRenderTargets(dTex, nullptr, nullptr);
PSSetShaderResource(0, sTex);
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
PSSetSamplerState(m_convert.pt);
//const GSVector4 dRect = GSVector4(dTex->GetRect());
@@ -1958,7 +2015,7 @@ void GSDeviceOGL::DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rec
m_vertex_stream_buffer->Unmap(vcount * sizeof(GSVertexPT1));
m_index_stream_buffer->Unmap(icount * sizeof(u16));
PSSetShaderResource(0, rects[0].src);
PSSetShaderResource(TEXTURE_TEXTURE, rects[0].src);
PSSetSamplerState(rects[0].filter == Biln ? m_convert.ln : m_convert.pt);
OMSetColorMaskState(rects[0].wmask);
DrawIndexedPrimitive();
@@ -2149,7 +2206,7 @@ void GSDeviceOGL::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GS
// Texture
PSSetShaderResource(0, rt);
PSSetShaderResource(TEXTURE_TEXTURE, rt);
PSSetSamplerState(m_convert.pt);
DrawPrimitive();
@@ -2233,6 +2290,45 @@ void GSDeviceOGL::IASetPrimitiveTopology(GLenum topology)
m_draw_topology = topology;
}
void GSDeviceOGL::PSBindImage(GLuint image_unit, GSTexture* tex)
{
GLuint id = tex ? static_cast<GSTextureOGL*>(tex)->GetID() : 0;
if (GLState::image_unit[image_unit] != id)
{
GLState::image_unit[image_unit] = id;
glBindImageTexture(image_unit, id, 0, GL_FALSE, 0, GL_READ_WRITE,
tex ? static_cast<GSTextureOGL*>(tex)->GetGLFormat() : GL_R32F);
}
}
void GSDeviceOGL::PSSetUnorderedAccess(GSTexture* rt, GSTexture* ds)
{
const bool rt_changed = (rt != GLState::rt_uav);
const bool ds_changed = (ds != GLState::ds_uav);
GLState::rt_uav_written = false;
GLState::ds_uav_written = false;
PSBindImage(IMAGE_RT_ROV, rt);
if (rt)
{
CommitClear(rt, false);
GLState::rt_uav_written = rt_changed;
PSSetShaderResource(TEXTURE_RT, nullptr); // Unbind to avoid conflicts.
}
PSBindImage(IMAGE_DEPTH_ROV, ds);
if (ds)
{
pxAssert(ds->IsDepthColor());
CommitClear(ds, false);
GLState::ds_uav_written = ds_changed;
PSSetShaderResource(TEXTURE_DEPTH, nullptr); // Unbind to avoid conflicts.
}
}
void GSDeviceOGL::PSSetShaderResource(int i, GSTexture* sr)
{
pxAssert(i < static_cast<int>(std::size(GLState::tex_unit)));
@@ -2308,7 +2404,7 @@ bool GSDeviceOGL::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, con
prog.Uniform4uiv(1, &constants[4]);
prog.Uniform2iv(2, reinterpret_cast<const s32*>(&constants[8]));
PSSetShaderResource(0, sTex);
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
glBindImageTexture(0, static_cast<GSTextureOGL*>(dTex)->GetID(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8);
static const int threadGroupWorkRegionDim = 16;
@@ -2524,6 +2620,10 @@ void GSDeviceOGL::OMUnbindTexture(GSTextureOGL* tex)
OMAttachDsAsRt();
if (GLState::ds == tex)
OMAttachDs();
if (GLState::rt_uav == tex)
PSBindImage(IMAGE_RT_ROV, nullptr);
if (GLState::ds_uav == tex)
PSBindImage(IMAGE_DEPTH_ROV, nullptr);
}
void GSDeviceOGL::OMSetBlendState(bool enable, GLenum src_factor, GLenum dst_factor, GLenum op,
@@ -2570,7 +2670,8 @@ void GSDeviceOGL::OMSetBlendState(bool enable, GLenum src_factor, GLenum dst_fac
}
}
void GSDeviceOGL::OMSetRenderTargets(GSTexture* rt, GSTexture* ds_as_rt, GSTexture* ds, const GSVector4i* scissor)
void GSDeviceOGL::OMSetRenderTargets(GSTexture* rt, GSTexture* ds_as_rt, GSTexture* ds, const GSVector4i* scissor,
const GSVector2i* viewport)
{
const bool rt_changed = (rt != GLState::rt);
const bool ds_as_rt_changed = (ds_as_rt != GLState::ds_as_rt);
@@ -2613,12 +2714,9 @@ void GSDeviceOGL::OMSetRenderTargets(GSTexture* rt, GSTexture* ds_as_rt, GSTextu
else
OMAttachDs();
if (rt || ds_as_rt || ds)
{
const GSVector2i size = rt ? rt->GetSize() : (ds_as_rt ? ds_as_rt->GetSize() : ds->GetSize());
SetViewport(size);
SetScissor(scissor ? *scissor : GSVector4i::loadh(size));
}
const GSVector2i size = rt ? rt->GetSize() : (ds_as_rt ? ds_as_rt->GetSize() : (ds ? ds->GetSize() : *viewport));
SetViewport(size);
SetScissor(scissor ? *scissor : GSVector4i::loadh(size));
if (draw_buffers != GLState::UpdateDrawBuffers())
{
@@ -2626,6 +2724,13 @@ void GSDeviceOGL::OMSetRenderTargets(GSTexture* rt, GSTexture* ds_as_rt, GSTextu
static constexpr GLenum target[2] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
glDrawBuffers(GLState::draw_buffers, target);
}
if (!(rt || ds_as_rt || ds))
{
// Need to set dimensions to avoid an incomplete FBO if we're doing a pure ROV draw.
glNamedFramebufferParameteri(m_fbo, GL_FRAMEBUFFER_DEFAULT_WIDTH, size.x);
glNamedFramebufferParameteri(m_fbo, GL_FRAMEBUFFER_DEFAULT_HEIGHT, size.y);
}
}
void GSDeviceOGL::SetViewport(const GSVector2i& viewport)
@@ -2706,6 +2811,11 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
const GSVector2i rtsize = (config.rt ? config.rt : config.ds)->GetSize();
GSTexture* colclip_rt = g_gs_device->GetColorClipTexture();
GSTexture* draw_rt = config.ps.HasColorROV() ? nullptr : config.rt;
GSTexture* draw_ds = config.ps.HasDepthROV() ? nullptr : config.ds;
GSTexture* draw_ds_as_rt = config.ps.HasDepthROV() ? nullptr : m_ds_as_rt;
GSTexture* draw_rt_rov = config.ps.HasColorROV() ? config.rt : nullptr;
GSTexture* draw_ds_rov = config.ps.HasDepthROV() ? config.ds : nullptr;
GSTexture* draw_rt_clone = nullptr;
GSTexture* draw_ds_clone = nullptr;
GSTexture* primid_texture = nullptr;
@@ -2762,6 +2872,8 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy();
StretchRect(config.rt, sRect, colclip_rt, dRect, ShaderConvert::COLCLIP_INIT, Nearest);
}
draw_rt = colclip_rt ? colclip_rt : config.rt;
}
// Destination Alpha Setup
@@ -2820,13 +2932,13 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
IASetPrimitiveTopology(topology);
if (config.tex && (m_features.texture_barrier || config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_NONE))
PSSetShaderResource(0, config.tex);
PSSetShaderResource(TEXTURE_TEXTURE, config.tex);
if (config.pal)
PSSetShaderResource(1, config.pal);
PSSetShaderResource(TEXTURE_PALETTE, config.pal);
if (m_features.texture_barrier && (config.require_one_barrier || config.require_full_barrier))
PSSetShaderResource(2, colclip_rt ? colclip_rt : config.rt);
PSSetShaderResource(TEXTURE_RT, colclip_rt ? colclip_rt : config.rt);
if (m_features.texture_barrier && (config.require_one_barrier || config.require_full_barrier) && config.ps.IsFeedbackLoopDepth())
PSSetShaderResource(4, m_features.depth_feedback ? config.ds : m_ds_as_rt);
PSSetShaderResource(TEXTURE_DEPTH, m_features.depth_feedback ? config.ds : m_ds_as_rt);
SetupSampler(config.sampler);
@@ -2896,10 +3008,10 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
psel.ps.date = 3;
config.alpha_second_pass.ps.date = 3;
SetupPipeline(psel);
PSSetShaderResource(3, primid_texture);
PSSetShaderResource(TEXTURE_PRIMID, primid_texture);
}
if (m_ds_as_rt)
if (draw_ds_as_rt)
{
// We must clear the blend equation of any dual source blending factors or
// it may interact badly with MRTs, even if blending is disabled.
@@ -2918,25 +3030,21 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
OMSetBlendState();
}
GSTexture* draw_rt = colclip_rt ? colclip_rt : config.rt;
GSTexture* draw_ds_as_rt = m_ds_as_rt;
GSTexture* draw_ds = config.ds;
// Clear texture binding when it's bound to RT or DS.
if (!config.tex && ((draw_rt && static_cast<GSTextureOGL*>(draw_rt)->GetID() == GLState::tex_unit[0]) ||
(draw_ds && static_cast<GSTextureOGL*>(draw_ds)->GetID() == GLState::tex_unit[0])))
PSSetShaderResource(0, nullptr);
PSSetShaderResource(TEXTURE_TEXTURE, nullptr);
// Avoid changing framebuffer just to switch from rt+depth to rt and vice versa.
bool fb_optimization_needs_barrier = false;
if (!draw_rt && GLState::rt && GLState::ds == draw_ds && config.tex != GLState::rt &&
GLState::rt->GetSize() == draw_ds->GetSize() && !draw_ds_as_rt)
if (!(draw_rt || draw_rt_rov) && GLState::rt && GLState::ds == draw_ds && config.tex != GLState::rt &&
draw_ds && GLState::rt->GetSize() == draw_ds->GetSize() && !draw_ds_as_rt)
{
draw_rt = GLState::rt;
fb_optimization_needs_barrier = !GLState::rt_written;
}
else if (!draw_ds && GLState::ds && GLState::rt == draw_rt && config.tex != GLState::ds &&
GLState::ds->GetSize() == draw_rt->GetSize() && !draw_ds_as_rt)
else if (!(draw_ds || draw_ds_rov) && GLState::ds && GLState::rt == draw_rt && config.tex != GLState::ds &&
draw_rt && GLState::ds->GetSize() == draw_rt->GetSize() && !draw_ds_as_rt)
{
draw_ds = GLState::ds;
fb_optimization_needs_barrier = !GLState::ds_written;
@@ -2953,7 +3061,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
const bool rt_feedbackloop_pass1 = config.IsFeedbackLoopRT(config.ps);
const bool rt_feedbackloop_pass2 = config.IsFeedbackLoopRT(config.alpha_second_pass.ps);
if (draw_rt && !m_features.texture_barrier && (((config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
if (draw_rt && !draw_rt_rov && !m_features.texture_barrier && (((config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
(rt_feedbackloop_pass1 || rt_feedbackloop_pass2))))
{
// Requires a copy of the RT.
@@ -2965,7 +3073,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
const bool ds_feedbackloop_pass1 = config.ps.IsFeedbackLoopDepth();
const bool ds_feedbackloop_pass2 = config.alpha_second_pass.ps.IsFeedbackLoopDepth();
if (draw_ds && !m_features.texture_barrier && m_features.depth_feedback &&
if (draw_ds && !draw_ds_rov && !m_features.texture_barrier && m_features.depth_feedback &&
(config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) && (ds_feedbackloop_pass1 || ds_feedbackloop_pass2))
{
// Requires a copy of the DS.
@@ -2975,7 +3083,9 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
Console.Warning("GL: Failed to allocate temp texture for DS copy.");
}
OMSetRenderTargets(draw_rt, draw_ds_as_rt, draw_ds, &config.scissor);
PSSetUnorderedAccess(draw_rt_rov, draw_ds_rov);
OMSetRenderTargets(draw_rt, draw_ds_as_rt, draw_ds, &config.scissor, &rtsize);
OMSetColorMaskState(config.colormask);
SetupOM(config.depth);
@@ -2993,7 +3103,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
}
SendHWDraw(config, rt_feedbackloop_pass1 ? draw_rt_clone : nullptr, draw_rt, ds_feedbackloop_pass1 ? draw_ds_clone : nullptr, draw_ds,
config.require_one_barrier, config.require_full_barrier);
config.require_one_barrier, config.require_full_barrier, rtsize);
if (config.blend_multi_pass.enable)
{
@@ -3041,7 +3151,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
const bool one_barrier = config.alpha_second_pass.require_one_barrier && m_features.feedback_loops();
SetupOM(config.alpha_second_pass.depth);
SendHWDraw(config, rt_feedbackloop_pass2 ? draw_rt_clone : nullptr, draw_rt, ds_feedbackloop_pass2 ? draw_ds_clone : nullptr, draw_ds,
one_barrier, config.alpha_second_pass.require_full_barrier);
one_barrier, config.alpha_second_pass.require_full_barrier, rtsize);
}
if (colclip_rt)
@@ -3114,7 +3224,7 @@ void GSDeviceOGL::FeedbackCopyAndBind(const GSHWDrawConfig& config,
void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config,
GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds,
const bool one_barrier, const bool full_barrier)
const bool one_barrier, const bool full_barrier, const GSVector2i& rtsize)
{
#ifdef PCSX2_DEVBUILD
if ((one_barrier || full_barrier) && !(config.IsFeedbackLoopRT(config.ps) || config.IsFeedbackLoopDepth(config.ps))) [[unlikely]]
@@ -3171,6 +3281,9 @@ void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config,
}
Draw(config);
if (config.ps.HasColorROV() || config.ps.HasDepthROV())
g_perfmon.Put(GSPerfMon::DrawCallsROV, 1);
}
// Note: used as a callback of DebugMessageCallback. Don't change the signature
+22 -3
View File
@@ -115,6 +115,21 @@ public:
using OMDepthStencilSelector = GSHWDrawConfig::DepthStencilSelector;
using OMColorMaskSelector = GSHWDrawConfig::ColorMaskSelector;
enum TextureUnit : u32
{
TEXTURE_TEXTURE,
TEXTURE_PALETTE,
TEXTURE_RT,
TEXTURE_PRIMID,
TEXTURE_DEPTH,
};
enum ImageUnit : u32
{
IMAGE_RT_ROV,
IMAGE_DEPTH_ROV,
};
struct alignas(16) ProgramSelector
{
PSSelector ps;
@@ -124,7 +139,7 @@ public:
__fi bool operator==(const ProgramSelector& p) const { return BitEqual(*this, p); }
__fi bool operator!=(const ProgramSelector& p) const { return !BitEqual(*this, p); }
};
static_assert(sizeof(ProgramSelector) == 16, "Program selector is 16 bytes");
static_assert(sizeof(ProgramSelector) == 32, "Program selector is 32 bytes");
struct ProgramSelectorHash
{
@@ -284,6 +299,8 @@ private:
void OMAttachDs(GSTexture* ds = nullptr);
void OMSetFBO(GLuint fbo);
void PSBindImage(GLuint image_unit, GSTexture* img);
void DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2i& ds);
void SetIndexBuffer(std::unique_ptr<GLStreamBuffer>& buffer, const void* index, size_t count);
@@ -369,7 +386,7 @@ public:
const GSVector4i& copyarea, const GSVector4i& samplearea);
void SendHWDraw(const GSHWDrawConfig& config,
GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds,
const bool one_barrier, const bool full_barrier);
const bool one_barrier, const bool full_barrier, const GSVector2i& rtsize);
void SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSVector4i& bbox);
void VSSetUniformBuffer(GSHWDrawConfig::VSConstantBuffer& cb);
@@ -382,6 +399,7 @@ public:
void IASetIndexBuffer(const void* index, size_t count);
void VSSetIndexBuffer(const void* index, size_t count);
void PSSetUnorderedAccess(GSTexture* rt, GSTexture* ds);
void PSSetShaderResource(int i, GSTexture* sr);
void PSSetSamplerState(GLuint ss);
void ClearSamplerCache() override;
@@ -389,7 +407,8 @@ public:
void OMSetDepthStencilState(GSDepthStencilOGL* dss);
void OMSetBlendState(bool enable = false, GLenum src_factor = GL_ONE, GLenum dst_factor = GL_ZERO, GLenum op = GL_FUNC_ADD,
GLenum src_factor_alpha = GL_ONE, GLenum dst_factor_alpha = GL_ZERO, bool is_constant = false, u8 constant = 0);
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds_as_rt, GSTexture* ds, const GSVector4i* scissor = nullptr);
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds_as_rt, GSTexture* ds, const GSVector4i* scissor = nullptr,
const GSVector2i* viewport = nullptr);
void OMSetColorMaskState(OMColorMaskSelector sel = OMColorMaskSelector());
void OMUnbindTexture(GSTextureOGL* tex);
+17 -16
View File
@@ -30,34 +30,33 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
m_type = type;
m_texture_id = 0;
m_mipmap_levels = 1;
int gl_fmt = 0;
// Bunch of constant parameter
switch (m_format)
{
// 1 Channel integer
case Format::PrimID:
gl_fmt = GL_R32F;
m_gl_format = GL_R32F;
m_int_format = GL_RED;
m_int_type = GL_INT;
m_int_shift = 2;
break;
case Format::UInt32:
gl_fmt = GL_R32UI;
m_gl_format = GL_R32UI;
m_int_format = GL_RED_INTEGER;
m_int_type = GL_UNSIGNED_INT;
m_int_shift = 2;
break;
case Format::UInt16:
gl_fmt = GL_R16UI;
m_gl_format = GL_R16UI;
m_int_format = GL_RED_INTEGER;
m_int_type = GL_UNSIGNED_SHORT;
m_int_shift = 1;
break;
// 1 Channel normalized
// 1 Channel normalized
case Format::UNorm8:
gl_fmt = GL_R8;
m_gl_format = GL_R8;
m_int_format = GL_RED;
m_int_type = GL_UNSIGNED_BYTE;
m_int_shift = 0;
@@ -65,7 +64,7 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
// 1 channel float
case Format::DepthColor:
gl_fmt = GL_R32F;
m_gl_format = GL_R32F;
m_int_format = GL_RED;
m_int_type = GL_FLOAT;
m_int_shift = 2;
@@ -75,7 +74,7 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
case Format::Color:
case Format::ColorHQ:
case Format::ColorHDR:
gl_fmt = GL_RGBA8;
m_gl_format = GL_RGBA8;
m_int_format = GL_RGBA;
m_int_type = GL_UNSIGNED_BYTE;
m_int_shift = 2;
@@ -83,7 +82,7 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
// 4 channel float
case Format::ColorClip:
gl_fmt = GL_RGBA16;
m_gl_format = GL_RGBA16;
m_int_format = GL_RGBA;
m_int_type = GL_UNSIGNED_SHORT;
m_int_shift = 3;
@@ -94,14 +93,14 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
{
if (!g_gs_device->Features().framebuffer_fetch)
{
gl_fmt = GL_DEPTH32F_STENCIL8;
m_gl_format = GL_DEPTH32F_STENCIL8;
m_int_format = GL_DEPTH_STENCIL;
m_int_type = GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
m_int_shift = 3; // 4 bytes for depth + 4 bytes for stencil by texels
}
else
{
gl_fmt = GL_DEPTH_COMPONENT32F;
m_gl_format = GL_DEPTH_COMPONENT32F;
m_int_format = GL_DEPTH_COMPONENT;
m_int_type = GL_FLOAT;
m_int_shift = 2;
@@ -110,28 +109,28 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
break;
case Format::BC1:
gl_fmt = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
m_gl_format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
m_int_format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
m_int_type = GL_UNSIGNED_BYTE;
m_int_shift = 1;
break;
case Format::BC2:
gl_fmt = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
m_gl_format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
m_int_format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
m_int_type = GL_UNSIGNED_BYTE;
m_int_shift = 1;
break;
case Format::BC3:
gl_fmt = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
m_gl_format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
m_int_format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
m_int_type = GL_UNSIGNED_BYTE;
m_int_shift = 1;
break;
case Format::BC7:
gl_fmt = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
m_gl_format = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
m_int_format = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
m_int_type = GL_UNSIGNED_BYTE;
m_int_shift = 1;
@@ -157,7 +156,7 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
glTextureParameteri(m_texture_id, GL_TEXTURE_SWIZZLE_A, GL_RED);
}
glTextureStorage2D(m_texture_id, m_mipmap_levels, gl_fmt, m_size.x, m_size.y);
glTextureStorage2D(m_texture_id, m_mipmap_levels, m_gl_format, m_size.x, m_size.y);
}
GSTextureOGL::~GSTextureOGL()
@@ -335,6 +334,8 @@ void GSTextureOGL::SetDebugName(std::string_view name)
if (glObjectLabel)
glObjectLabel(GL_TEXTURE, m_texture_id, static_cast<GLsizei>(name.length()), static_cast<const GLchar*>(name.data()));
m_debug_name = name;
}
#endif
+3
View File
@@ -22,6 +22,7 @@ private:
u32 m_map_offset = 0;
// internal opengl format/type/alignment
GLenum m_gl_format = 0;
GLenum m_int_format = 0;
GLenum m_int_type = 0;
u32 m_int_shift = 0;
@@ -33,6 +34,7 @@ public:
__fi GLenum GetIntFormat() const { return m_int_format; }
__fi GLenum GetIntType() const { return m_int_type; }
__fi u32 GetIntShift() const { return m_int_shift; }
__fi u32 GetGLFormat() const { return m_gl_format; }
void* GetNativeHandle() const override;
@@ -55,6 +57,7 @@ public:
}
__fi u32 GetID() { return m_texture_id; }
__fi u32 GetGLFormat() { return m_gl_format; }
};
class GSDownloadTextureOGL final : public GSDownloadTexture
+219 -37
View File
@@ -444,6 +444,8 @@ bool GSDeviceVK::SelectDeviceExtensions(ExtensionList* extension_list, bool enab
enable_surface && SupportsExtension(VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME, false);
#endif
m_optional_extensions.vk_ext_fragment_shader_interlock = SupportsExtension(VK_EXT_FRAGMENT_SHADER_INTERLOCK_EXTENSION_NAME, false);
return true;
}
@@ -459,6 +461,7 @@ bool GSDeviceVK::SelectDeviceFeatures()
m_device_features.fragmentStoresAndAtomics = available_features.fragmentStoresAndAtomics;
m_device_features.textureCompressionBC = available_features.textureCompressionBC;
m_device_features.geometryShader = available_features.geometryShader;
m_device_features.fragmentStoresAndAtomics = available_features.fragmentStoresAndAtomics;
return true;
}
@@ -628,6 +631,8 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
// VK_EXT_swapchain_maintenance1 types/enums are aliases of VK_KHR_swapchain_maintenance1 types/enums.
VkPhysicalDeviceSwapchainMaintenance1FeaturesKHR swapchain_maintenance1_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_KHR};
VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT fragment_shader_interlock_ext_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT};
if (m_optional_extensions.vk_ext_provoking_vertex)
{
@@ -654,6 +659,11 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
swapchain_maintenance1_feature.swapchainMaintenance1 = VK_TRUE;
Vulkan::AddPointerToChain(&device_info, &swapchain_maintenance1_feature);
}
if (m_optional_extensions.vk_ext_fragment_shader_interlock)
{
fragment_shader_interlock_ext_feature.fragmentShaderPixelInterlock = VK_TRUE;
Vulkan::AddPointerToChain(&device_info, &fragment_shader_interlock_ext_feature);
}
VkResult res = vkCreateDevice(m_physical_device, &device_info, nullptr, &m_device);
if (res != VK_SUCCESS)
@@ -724,6 +734,8 @@ bool GSDeviceVK::ProcessDeviceExtensions()
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_KHR, nullptr, VK_TRUE};
VkPhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT attachment_feedback_loop_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_FEATURES_EXT};
VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT fragment_shader_interlock_ext_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT };
// add in optional feature structs
if (m_optional_extensions.vk_ext_provoking_vertex)
@@ -736,6 +748,8 @@ bool GSDeviceVK::ProcessDeviceExtensions()
Vulkan::AddPointerToChain(&features2, &attachment_feedback_loop_feature);
if (m_optional_extensions.vk_swapchain_maintenance1)
Vulkan::AddPointerToChain(&features2, &swapchain_maintenance1_feature);
if (m_optional_extensions.vk_ext_fragment_shader_interlock)
Vulkan::AddPointerToChain(&features2, &fragment_shader_interlock_ext_feature);
// query
vkGetPhysicalDeviceFeatures2(m_physical_device, &features2);
@@ -813,6 +827,9 @@ bool GSDeviceVK::ProcessDeviceExtensions()
m_optional_extensions.vk_swapchain_maintenance1 &=
(swapchain_maintenance1_feature.swapchainMaintenance1 == VK_TRUE);
m_optional_extensions.vk_ext_fragment_shader_interlock &=
(fragment_shader_interlock_ext_feature.fragmentShaderPixelInterlock == VK_TRUE);
Console.WriteLn(
"VK_EXT_provoking_vertex is %s", m_optional_extensions.vk_ext_provoking_vertex ? "supported" : "NOT supported");
Console.WriteLn(
@@ -830,6 +847,8 @@ bool GSDeviceVK::ProcessDeviceExtensions()
m_optional_extensions.vk_khr_driver_properties ? "supported" : "NOT supported");
Console.WriteLn("VK_EXT_attachment_feedback_loop_layout is %s",
m_optional_extensions.vk_ext_attachment_feedback_loop_layout ? "supported" : "NOT supported");
Console.WriteLn("VK_EXT_fragment_shader_interlock is %s",
m_optional_extensions.vk_ext_fragment_shader_interlock ? "supported" : "NOT supported");
return true;
}
@@ -2100,6 +2119,12 @@ bool GSDeviceVK::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
return false;
}
if ((m_null_framebuffer = GSTextureVK::CreateNullFramebuffer()) == VK_NULL_HANDLE)
{
Host::ReportErrorAsync("GS", "Failed to create dummy framebuffer");
return false;
}
{
std::optional<std::string> shader = ReadShaderSource("shaders/vulkan/tfx.glsl");
if (!shader.has_value())
@@ -2743,6 +2768,9 @@ bool GSDeviceVK::CheckFeatures()
m_max_texture_size = m_device_properties.limits.maxImageDimension2D;
m_features.rov = m_optional_extensions.vk_ext_fragment_shader_interlock &&
m_device_features.fragmentStoresAndAtomics;
return true;
}
@@ -2997,7 +3025,7 @@ void GSDeviceVK::DoMultiStretchRects(
const MultiStretchRect* rects, u32 num_rects, GSTextureVK* dTex, ShaderConvertSelector shader)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
// Set up vertices first.
const u32 vertex_reserve_size = num_rects * 4 * sizeof(GSVertexPT1);
const u32 index_reserve_size = num_rects * 6 * sizeof(u16);
@@ -3487,14 +3515,15 @@ void GSDeviceVK::VSSetIndexBuffer(const void* index, size_t count)
}
void GSDeviceVK::OMSetRenderTargets(
GSTexture* rt, GSTexture* ds, const GSVector4i& scissor, FeedbackLoopFlag feedback_loop)
GSTexture* rt, GSTexture* ds, const GSVector4i& scissor, FeedbackLoopFlag feedback_loop,
const GSVector2i& viewport_size)
{
GSTextureVK* vkRt = static_cast<GSTextureVK*>(rt);
GSTextureVK* vkDs = static_cast<GSTextureVK*>(ds);
pxAssert(vkRt || vkDs);
if (m_current_render_target != vkRt || m_current_depth_target != vkDs ||
m_current_framebuffer_feedback_loop != feedback_loop)
m_current_framebuffer_feedback_loop != feedback_loop ||
m_current_framebuffer == VK_NULL_HANDLE)
{
// framebuffer change or feedback loop enabled/disabled
EndRenderPass();
@@ -3506,12 +3535,16 @@ void GSDeviceVK::OMSetRenderTargets(
(feedback_loop & FeedbackLoopFlag_ReadAndWriteRT) != 0,
(feedback_loop & (FeedbackLoopFlag_ReadAndWriteDepth | FeedbackLoopFlag_ReadDepth)) != 0);
}
else
else if (vkDs)
{
pxAssert(!(feedback_loop & FeedbackLoopFlag_ReadAndWriteRT));
m_current_framebuffer = vkDs->GetLinkedFramebuffer(
nullptr, false, (feedback_loop & (FeedbackLoopFlag_ReadAndWriteDepth | FeedbackLoopFlag_ReadDepth)) != 0);
}
else
{
m_current_framebuffer = m_null_framebuffer;
}
}
else if (InRenderPass())
{
@@ -3656,7 +3689,7 @@ void GSDeviceVK::OMSetRenderTargets(
}
// This is used to set/initialize the framebuffer for tfx rendering.
const GSVector2i size = vkRt ? vkRt->GetSize() : vkDs->GetSize();
const GSVector2i size = (vkRt ? vkRt->GetSize() : (vkDs ? vkDs->GetSize() : viewport_size));
const VkViewport vp{0.0f, 0.0f, static_cast<float>(size.x), static_cast<float>(size.y), 0.0f, 1.0f};
SetViewport(vp);
@@ -3732,6 +3765,11 @@ static void AddShaderHeader(std::stringstream& ss)
ss << "#define DISABLE_TEXTURE_BARRIER 1\n";
if (features.texture_barrier && dev->UseFeedbackLoopLayout())
ss << "#define HAS_FEEDBACK_LOOP_LAYOUT 1\n";
if (features.rov)
{
ss << "#extension GL_ARB_fragment_shader_interlock : require\n";
ss << "#extension GL_ARB_shader_image_load_store : require\n";
}
}
static void AddShaderStageMacro(std::stringstream& ss, bool vs, bool gs, bool fs)
@@ -3905,6 +3943,8 @@ bool GSDeviceVK::CreatePipelineLayouts()
(m_features.texture_barrier && !UseFeedbackLoopLayout()) ? VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT :
VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
1, VK_SHADER_STAGE_FRAGMENT_BIT);
dslb.AddBinding(TFX_TEXTURE_RT_ROV, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
dslb.AddBinding(TFX_TEXTURE_DEPTH_ROV, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
if ((m_tfx_texture_ds_layout = dslb.Create(dev)) == VK_NULL_HANDLE)
return false;
Vulkan::SetObjectName(dev, m_tfx_texture_ds_layout, "TFX texture descriptor layout");
@@ -4739,6 +4779,11 @@ void GSDeviceVK::DestroyResources()
if (m_utility_ds_layout != VK_NULL_HANDLE)
vkDestroyDescriptorSetLayout(m_device, m_utility_ds_layout, nullptr);
if (m_null_framebuffer != VK_NULL_HANDLE)
{
vkDestroyFramebuffer(m_device, m_null_framebuffer, nullptr);
}
if (m_null_texture)
{
m_null_texture->Destroy(false);
@@ -4870,7 +4915,8 @@ VkShaderModule GSDeviceVK::GetTFXFragmentShader(const GSHWDrawConfig::PSSelector
AddMacro(ss, "PS_AA1", static_cast<u32>(sel.aa1));
AddMacro(ss, "PS_ABE", sel.abe);
AddMacro(ss, "PS_ANISOTROPIC_FILTERING", sel.sw_aniso);
AddMacro(ss, "PS_ROV_COLOR", sel.rov_color);
AddMacro(ss, "PS_ROV_DEPTH", static_cast<u32>(sel.rov_depth));
ss << m_tfx_source;
VkShaderModule mod = g_vulkan_shader_cache->GetFragmentShader(ss.str());
@@ -5223,7 +5269,95 @@ void GSDeviceVK::SetLineWidth(float width)
m_dirty_flags |= DIRTY_FLAG_LINE_WIDTH;
}
void GSDeviceVK::PSSetShaderResource(int i, GSTexture* sr, bool check_state)
void GSDeviceVK::PSSetUnorderedAccess(GSTexture* rt, GSTexture* ds, bool write_rt, bool write_ds)
{
GSTextureVK* vkRt = static_cast<GSTextureVK*>(rt);
GSTextureVK* vkDs = static_cast<GSTextureVK*>(ds);
GSTextureVK* oldVkRt = m_tfx_textures[TFX_TEXTURE_RT_ROV] != m_null_texture.get() ?
m_tfx_textures[TFX_TEXTURE_RT_ROV] : nullptr;
GSTextureVK* oldVkDs = m_tfx_textures[TFX_TEXTURE_DEPTH_ROV] != m_null_texture.get() ?
m_tfx_textures[TFX_TEXTURE_DEPTH_ROV] : nullptr;
if (!(vkRt || vkDs || oldVkRt || oldVkDs))
return;
pxAssert(!(vkRt || vkDs) || m_features.rov);
if (vkRt)
{
PSSetShaderResource(TFX_TEXTURE_RT_ROV, vkRt, true, ResourceType::UAV);
// Unbind conflicting RT texture
PSSetShaderResource(TFX_TEXTURE_RT, nullptr, false);
// Unbind conflicting source texture
if (m_tfx_textures[TFX_TEXTURE_TEXTURE] == vkRt)
{
PSSetShaderResource(TFX_TEXTURE_TEXTURE, nullptr, false);
}
if (write_rt)
{
vkRt->SetState(GSTexture::State::Dirty);
}
}
else
{
// Unbind to avoid conflicts with OM targets.
PSSetShaderResource(TFX_TEXTURE_RT_ROV, nullptr, false);
}
if (vkDs)
{
PSSetShaderResource(TFX_TEXTURE_DEPTH_ROV, vkDs, true, ResourceType::UAV);
// Unbind conflicting depth texture
PSSetShaderResource(TFX_TEXTURE_DEPTH, nullptr, false);
// Unbind conflicting source texture
if (m_tfx_textures[TFX_TEXTURE_TEXTURE] == vkDs)
{
PSSetShaderResource(TFX_TEXTURE_TEXTURE, nullptr, false);
}
if (write_ds)
{
vkDs->SetState(GSTexture::State::Dirty);
}
}
else
{
// Unbind to avoid conflicts with OM targets.
PSSetShaderResource(TFX_TEXTURE_DEPTH_ROV, nullptr, false);
}
if (GSConfig.HWROVBarriersVK)
{
// This is to fix issues with some systems that seem to require barriers even with FSI.
// Adds a barriers to the UAVs before every draw. Can result in a large performance hit.
if ((vkRt || vkDs) && InRenderPass())
{
GL_INS("Ending render pass due to UAV barrier");
EndRenderPass();
}
for (GSTextureVK* tex : std::array{ vkRt, vkDs })
{
if (tex)
{
if (InRenderPass())
{
GL_INS("Ending render pass due to UAV barrier");
EndRenderPass();
}
// The subresources version does the barrier even when the before/after layout
// are the same since we want a memory barrier here, not just a layout change.
tex->TransitionSubresourcesToLayout(GetCurrentCommandBuffer(), 0, 1, tex->GetLayout(), tex->GetLayout());
}
}
}
}
void GSDeviceVK::PSSetShaderResource(int i, GSTexture* sr, bool check_state, ResourceType type)
{
GSTextureVK* vkTex = static_cast<GSTextureVK*>(sr);
bool update_layout = false;
@@ -5231,15 +5365,20 @@ void GSDeviceVK::PSSetShaderResource(int i, GSTexture* sr, bool check_state)
{
if (check_state)
{
if (vkTex->GetLayout() != GSTextureVK::Layout::ShaderReadOnly && InRenderPass())
{
GL_INS("Ending render pass due to resource transition");
EndRenderPass();
update_layout = true;
}
const GSTextureVK::Layout layout = GetResourceLayout(type);
vkTex->CommitClear();
vkTex->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
if (vkTex->GetLayout() != layout)
{
update_layout = true;
if (InRenderPass())
{
GL_INS("Ending render pass due to resource transition");
EndRenderPass();
}
vkTex->TransitionToLayout(layout);
}
}
vkTex->SetUseFenceCounter(GetCurrentFenceCounter());
}
@@ -5567,6 +5706,16 @@ bool GSDeviceVK::ApplyTFXState(bool already_execed)
m_tfx_textures[TFX_TEXTURE_DEPTH]->GetVkLayout());
}
}
if (flags & DIRTY_FLAG_TFX_TEXTURE_RT_ROV)
{
dsub.AddImageDescriptorWrite(VK_NULL_HANDLE, TFX_TEXTURE_RT_ROV, m_tfx_textures[TFX_TEXTURE_RT_ROV]->GetView(),
m_tfx_textures[TFX_TEXTURE_RT_ROV]->GetVkLayout(), true);
}
if (flags & DIRTY_FLAG_TFX_TEXTURE_DEPTH_ROV)
{
dsub.AddImageDescriptorWrite(VK_NULL_HANDLE, TFX_TEXTURE_DEPTH_ROV, m_tfx_textures[TFX_TEXTURE_DEPTH_ROV]->GetView(),
m_tfx_textures[TFX_TEXTURE_DEPTH_ROV]->GetVkLayout(), true);
}
dsub.PushUpdate(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_tfx_pipeline_layout, TFX_DESCRIPTOR_SET_TEXTURES);
}
@@ -5744,8 +5893,10 @@ GSTextureVK* GSDeviceVK::SetupPrimitiveTrackingDATE(GSHWDrawConfig& config)
void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
{
const GSVector2i rtsize(config.rt ? config.rt->GetSize() : config.ds->GetSize());
GSTextureVK* draw_rt = static_cast<GSTextureVK*>(config.rt);
GSTextureVK* draw_ds = static_cast<GSTextureVK*>(config.ds);
GSTextureVK* draw_rt = config.ps.HasColorROV() ? nullptr : static_cast<GSTextureVK*>(config.rt);
GSTextureVK* draw_ds = config.ps.HasDepthROV() ? nullptr : static_cast<GSTextureVK*>(config.ds);
GSTextureVK* draw_rt_rov = config.ps.HasColorROV() ? static_cast<GSTextureVK*>(config.rt) : nullptr;
GSTextureVK* draw_ds_rov = config.ps.HasDepthROV() ? static_cast<GSTextureVK*>(config.ds) : nullptr;
GSTextureVK* draw_rt_clone = nullptr;
GSTextureVK* colclip_rt = static_cast<GSTextureVK*>(g_gs_device->GetColorClipTexture());
@@ -5756,11 +5907,11 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
// bind textures before checking the render pass, in case we need to transition them
if (config.tex)
{
PSSetShaderResource(0, config.tex, config.tex != config.rt && config.tex != config.ds);
PSSetShaderResource(TFX_TEXTURE_TEXTURE, config.tex, config.tex != config.rt && config.tex != config.ds);
PSSetSampler(config.sampler);
}
if (config.pal)
PSSetShaderResource(1, config.pal, true);
PSSetShaderResource(TFX_TEXTURE_PALETTE, config.pal, true);
if (config.blend.constant_enable)
SetBlendConstants(config.blend.constant);
@@ -5839,6 +5990,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
g_gs_device->SetColorClipTexture(nullptr);
colclip_rt = nullptr;
draw_rt = config.ps.HasColorROV() ? nullptr : static_cast<GSTextureVK*>(config.rt);
}
else
{
@@ -5927,25 +6079,31 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
// colclip hw requires blitting.
EndRenderPass();
}
else if (InRenderPass() && (m_current_render_target == draw_rt || m_current_depth_target == draw_ds))
else if (InRenderPass() &&
((draw_rt && m_current_render_target == draw_rt) ||
(draw_ds && m_current_depth_target == draw_ds)))
{
// avoid restarting the render pass just to switch from rt+depth to rt and vice versa
// keep the depth even if doing colclip hw draws, because the next draw will probably re-enable depth
if (!draw_rt && m_current_render_target && config.tex != m_current_render_target &&
m_current_render_target->GetSize() == draw_ds->GetSize())
// keep the depth even if doing colclip hw draws, because the next draw will probably re-enable depth.
if (!(draw_rt || draw_rt_rov) && m_current_render_target && config.tex != m_current_render_target &&
draw_ds && m_current_render_target->GetSize() == draw_ds->GetSize())
{
draw_rt = m_current_render_target;
m_pipeline_selector.rt = true;
}
else if (!draw_ds && m_current_depth_target && config.tex != m_current_depth_target &&
m_current_depth_target->GetSize() == draw_rt->GetSize())
else if (!(draw_ds || draw_ds_rov) && m_current_depth_target && config.tex != m_current_depth_target &&
draw_rt && m_current_depth_target->GetSize() == draw_rt->GetSize())
{
draw_ds = m_current_depth_target;
m_pipeline_selector.ds = true;
}
// Prefer keeping feedback loop enabled, that way we're not constantly restarting render passes
pipe.feedback_loop_flags |= m_current_framebuffer_feedback_loop;
if (draw_rt)
pipe.feedback_loop_flags |= m_current_framebuffer_feedback_loop & FeedbackLoopFlag_ReadAndWriteRT;
if (draw_ds)
pipe.feedback_loop_flags |= (m_current_framebuffer_feedback_loop &
(FeedbackLoopFlag_ReadAndWriteDepth | FeedbackLoopFlag_ReadDepth));
}
if (draw_rt && ((config.require_one_barrier && (config.IsFeedbackLoopRT(config.ps) || config.IsFeedbackLoopRT(config.alpha_second_pass.ps)))) && !m_features.texture_barrier)
@@ -5992,25 +6150,46 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
Console.Warning("VK: Failed to allocate temp texture for RT copy.");
}
OMSetRenderTargets(draw_rt, draw_ds, config.scissor, static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags));
if (pipe.IsRTFeedbackLoop())
if (pipe.IsRTFeedbackLoop() && draw_rt)
{
pxAssertMsg(m_features.texture_barrier, "Texture barriers enabled");
PSSetShaderResource(TFX_TEXTURE_RT, draw_rt, false);
if (m_tfx_textures[TFX_TEXTURE_RT_ROV] == draw_rt)
{
// Unbind to avoid conflicts.
PSSetShaderResource(TFX_TEXTURE_RT_ROV, nullptr, false);
m_dirty_flags |= static_cast<u32>(DIRTY_FLAG_TFX_TEXTURE_RT_ROV);
}
}
else if (!draw_rt_clone)
{
PSSetShaderResource(TFX_TEXTURE_RT, nullptr, false); // Unbind to avoid layout conflicts
// Unbind to avoid conflicts with framebuffer
PSSetShaderResource(TFX_TEXTURE_RT, nullptr, false);
}
if (pipe.IsDepthFeedbackLoop())
if (pipe.IsDepthFeedbackLoop() && draw_ds)
{
pxAssertMsg(m_features.texture_barrier, "Texture barriers enabled");
PSSetShaderResource(TFX_TEXTURE_DEPTH, draw_ds, false);
if (m_tfx_textures[TFX_TEXTURE_DEPTH_ROV] == draw_ds)
{
// Unbind to avoid conflicts.
PSSetShaderResource(TFX_TEXTURE_DEPTH_ROV, nullptr, false);
m_dirty_flags |= static_cast<u32>(DIRTY_FLAG_TFX_TEXTURE_DEPTH_ROV);
}
}
else
{
PSSetShaderResource(TFX_TEXTURE_DEPTH, nullptr, false); // Unbind to avoid layout conflicts
// Unbind to avoid conflicts with framebuffer
PSSetShaderResource(TFX_TEXTURE_DEPTH, nullptr, false);
}
PSSetUnorderedAccess(draw_rt_rov, draw_ds_rov, config.ps.HasColorOutput(), config.ps.HasDepthROVWrite());
OMSetRenderTargets(draw_rt, draw_ds, config.scissor, static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags), rtsize);
// Begin render pass if new target or out of the area.
if (!InRenderPass())
{
@@ -6188,13 +6367,13 @@ void GSDeviceVK::UpdateHWPipelineSelector(GSHWDrawConfig& config, PipelineSelect
pipe.vs.key = config.vs.key;
pipe.ps.key_hi = config.ps.key_hi;
pipe.ps.key_lo = config.ps.key_lo;
pipe.dss.key = config.depth.key;
pipe.bs.key = config.blend.key;
pipe.dss.key = config.ps.HasDepthROV() ? GSHWDrawConfig::DepthStencilSelector::NoDepth().key : config.depth.key;
pipe.bs.key = config.ps.HasColorROV() ? GSHWDrawConfig::BlendState().key : config.blend.key;
pipe.bs.constant = 0; // don't dupe states with different alpha values
pipe.cms.key = config.colormask.key;
pipe.cms.key = config.ps.HasColorROV() ? GSHWDrawConfig::ColorMaskSelector().key : config.colormask.key;
pipe.topology = static_cast<u32>(config.topology);
pipe.rt = config.rt != nullptr;
pipe.ds = config.ds != nullptr;
pipe.rt = config.rt != nullptr && !config.ps.HasColorROV();
pipe.ds = config.ds != nullptr && !config.ps.HasDepthROV();
pipe.line_width = config.line_expand;
pipe.feedback_loop_flags = FeedbackLoopFlag_None;
if (m_features.texture_barrier && (config.require_one_barrier || config.require_full_barrier))
@@ -6205,7 +6384,7 @@ void GSDeviceVK::UpdateHWPipelineSelector(GSHWDrawConfig& config, PipelineSelect
if (config.IsFeedbackLoopDepth(config.ps))
pipe.feedback_loop_flags |= FeedbackLoopFlag_ReadAndWriteDepth;
}
if (!(pipe.feedback_loop_flags & FeedbackLoopFlag_ReadAndWriteDepth))
if (pipe.ds && !(pipe.feedback_loop_flags & FeedbackLoopFlag_ReadAndWriteDepth))
{
pipe.feedback_loop_flags |= (config.tex && config.tex == config.ds) ? FeedbackLoopFlag_ReadDepth : FeedbackLoopFlag_None;
}
@@ -6339,4 +6518,7 @@ void GSDeviceVK::SendHWDraw(const GSHWDrawConfig& config, GSTextureVK* draw_rt,
}
Draw(config);
if (config.ps.HasColorROV() || config.ps.HasDepthROV())
g_perfmon.Put(GSPerfMon::DrawCallsROV, 1);
}
+46 -19
View File
@@ -46,6 +46,7 @@ public:
bool vk_khr_driver_properties : 1;
bool vk_khr_shader_non_semantic_info : 1;
bool vk_ext_attachment_feedback_loop_layout : 1;
bool vk_ext_fragment_shader_interlock : 1;
};
// Global state accessors
@@ -61,8 +62,8 @@ public:
// The interaction between raster order attachment access and fbfetch is unclear.
__fi bool UseFeedbackLoopLayout() const
{
return (m_optional_extensions.vk_ext_attachment_feedback_loop_layout &&
!m_optional_extensions.vk_ext_rasterization_order_attachment_access);
return m_optional_extensions.vk_ext_attachment_feedback_loop_layout &&
!m_optional_extensions.vk_ext_rasterization_order_attachment_access;
}
// Helpers for getting constants
@@ -301,6 +302,25 @@ public:
FeedbackLoopFlag_ReadAndWriteDepth = 4,
};
enum class ResourceType
{
SRV, // Shader resource view (read only)
UAV, // Unordered access (read/write)
};
static constexpr GSTextureVK::Layout GetResourceLayout(ResourceType type)
{
switch (type)
{
default:
pxFailRel("Impossible.");
case ResourceType::SRV:
return GSTextureVK::Layout::ShaderReadOnly;
case ResourceType::UAV:
return GSTextureVK::Layout::ReadWriteImage;
}
}
struct alignas(8) PipelineSelector
{
GSHWDrawConfig::PSSelector ps;
@@ -334,7 +354,7 @@ public:
__fi bool IsDepthFeedbackLoop() const { return ((feedback_loop_flags & FeedbackLoopFlag_ReadAndWriteDepth) != 0); }
__fi bool IsTestingAndSamplingDepth() const { return ((feedback_loop_flags & (FeedbackLoopFlag_ReadDepth | FeedbackLoopFlag_ReadAndWriteDepth)) != 0); }
};
static_assert(sizeof(PipelineSelector) == 24, "Pipeline selector is 24 bytes");
static_assert(sizeof(PipelineSelector) == 32, "Pipeline selector is 32 bytes");
struct PipelineSelectorHash
{
@@ -368,6 +388,8 @@ public:
TFX_TEXTURE_RT,
TFX_TEXTURE_PRIMID,
TFX_TEXTURE_DEPTH,
TFX_TEXTURE_RT_ROV,
TFX_TEXTURE_DEPTH_ROV,
NUM_TFX_TEXTURES
};
@@ -586,11 +608,12 @@ public:
void IASetIndexBuffer(const void* index, size_t count);
void VSSetIndexBuffer(const void* index, size_t count);
void PSSetShaderResource(int i, GSTexture* sr, bool check_state);
void PSSetUnorderedAccess(GSTexture* rt, GSTexture* ds, bool write_rt, bool write_ds);
void PSSetShaderResource(int i, GSTexture* sr, bool check_state, ResourceType type = ResourceType::SRV);
void PSSetSampler(GSHWDrawConfig::SamplerSelector sel);
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i& scissor,
FeedbackLoopFlag feedback_loop = FeedbackLoopFlag_None);
FeedbackLoopFlag feedback_loop = FeedbackLoopFlag_None, const GSVector2i& viewport_size = {});
void SetVSConstantBuffer(const GSHWDrawConfig::VSConstantBuffer& cb);
void SetPSConstantBuffer(const GSHWDrawConfig::PSConstantBuffer& cb);
@@ -654,28 +677,31 @@ public:
private:
enum DIRTY_FLAG : u32
{
DIRTY_FLAG_TFX_TEXTURE_0 = (1 << 0), // 0, 1, 2, 3, 4
DIRTY_FLAG_TFX_UBO = (1 << 5),
DIRTY_FLAG_UTILITY_TEXTURE = (1 << 6),
DIRTY_FLAG_BLEND_CONSTANTS = (1 << 7),
DIRTY_FLAG_LINE_WIDTH = (1 << 8),
DIRTY_FLAG_INDEX_BUFFER = (1 << 9),
DIRTY_FLAG_VIEWPORT = (1 << 10),
DIRTY_FLAG_SCISSOR = (1 << 11),
DIRTY_FLAG_PIPELINE = (1 << 12),
DIRTY_FLAG_VS_CONSTANT_BUFFER = (1 << 13),
DIRTY_FLAG_PS_CONSTANT_BUFFER = (1 << 14),
DIRTY_FLAG_VS_PUSH_CONSTANTS = (1 << 15),
DIRTY_FLAG_TFX_TEXTURE_0 = (1 << 0), // 0, 1, 2, 3, 4, 5, 6
DIRTY_FLAG_TFX_UBO = (1 << 7),
DIRTY_FLAG_UTILITY_TEXTURE = (1 << 8),
DIRTY_FLAG_BLEND_CONSTANTS = (1 << 9),
DIRTY_FLAG_LINE_WIDTH = (1 << 10),
DIRTY_FLAG_INDEX_BUFFER = (1 << 11),
DIRTY_FLAG_VIEWPORT = (1 << 12),
DIRTY_FLAG_SCISSOR = (1 << 13),
DIRTY_FLAG_PIPELINE = (1 << 14),
DIRTY_FLAG_VS_CONSTANT_BUFFER = (1 << 15),
DIRTY_FLAG_PS_CONSTANT_BUFFER = (1 << 16),
DIRTY_FLAG_VS_PUSH_CONSTANTS = (1 << 17),
DIRTY_FLAG_TFX_TEXTURE_TEX = (DIRTY_FLAG_TFX_TEXTURE_0 << 0),
DIRTY_FLAG_TFX_TEXTURE_PALETTE = (DIRTY_FLAG_TFX_TEXTURE_0 << 1),
DIRTY_FLAG_TFX_TEXTURE_RT = (DIRTY_FLAG_TFX_TEXTURE_0 << 2),
DIRTY_FLAG_TFX_TEXTURE_PRIMID = (DIRTY_FLAG_TFX_TEXTURE_0 << 3),
DIRTY_FLAG_TFX_TEXTURE_DEPTH = (DIRTY_FLAG_TFX_TEXTURE_0 << 4),
DIRTY_FLAG_TFX_TEXTURE_RT_ROV = (DIRTY_FLAG_TFX_TEXTURE_0 << 5),
DIRTY_FLAG_TFX_TEXTURE_DEPTH_ROV = (DIRTY_FLAG_TFX_TEXTURE_0 << 6),
DIRTY_FLAG_TFX_TEXTURES = DIRTY_FLAG_TFX_TEXTURE_TEX | DIRTY_FLAG_TFX_TEXTURE_PALETTE |
DIRTY_FLAG_TFX_TEXTURE_RT | DIRTY_FLAG_TFX_TEXTURE_PRIMID |
DIRTY_FLAG_TFX_TEXTURE_DEPTH,
DIRTY_FLAG_TFX_TEXTURE_DEPTH | DIRTY_FLAG_TFX_TEXTURE_RT_ROV |
DIRTY_FLAG_TFX_TEXTURE_DEPTH_ROV,
DIRTY_BASE_STATE = DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_PIPELINE | DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR |
DIRTY_FLAG_BLEND_CONSTANTS | DIRTY_FLAG_LINE_WIDTH,
@@ -716,7 +742,7 @@ private:
float m_current_line_width = 1.0f;
u8 m_blend_constant_color = 0;
std::array<const GSTextureVK*, NUM_TFX_TEXTURES> m_tfx_textures{};
std::array<GSTextureVK*, NUM_TFX_TEXTURES> m_tfx_textures{};
VkSampler m_tfx_sampler = VK_NULL_HANDLE;
u32 m_tfx_sampler_sel = 0;
VkDescriptorSet m_tfx_ubo_descriptor_set = VK_NULL_HANDLE;
@@ -732,6 +758,7 @@ private:
VkPipeline m_current_pipeline = VK_NULL_HANDLE;
std::unique_ptr<GSTextureVK> m_null_texture;
VkFramebuffer m_null_framebuffer;
// current pipeline selector - we save this in the struct to avoid re-zeroing it every draw
PipelineSelector m_pipeline_selector = {};
+39 -3
View File
@@ -11,6 +11,24 @@
#include "common/Console.h"
#include "common/BitUtils.h"
VkFramebuffer GSTextureVK::CreateNullFramebuffer()
{
const VkRenderPass rp = GSDeviceVK::GetInstance()->GetRenderPass(
VK_FORMAT_UNDEFINED,
VK_FORMAT_UNDEFINED,
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, false, false);
if (!rp)
return VK_NULL_HANDLE;
Vulkan::FramebufferBuilder fbb;
fbb.SetSize(16384, 16384, 1);
fbb.SetRenderPass(rp);
return fbb.Create(GSDeviceVK::GetInstance()->GetDevice());
}
static constexpr const VkComponentMapping s_identity_swizzle{VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY};
@@ -101,7 +119,7 @@ std::unique_ptr<GSTextureVK> GSTextureVK::Create(Type type, Format format, int w
pxAssert(levels == 1);
ici.usage =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT |
(GSDeviceVK::GetInstance()->UseFeedbackLoopLayout() ? VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT
: VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT);
}
@@ -239,6 +257,10 @@ void GSTextureVK::Destroy(bool defer)
m_image = VK_NULL_HANDLE;
m_allocation = VK_NULL_HANDLE;
}
#ifdef PCSX2_DEVBUILD
m_debug_name.clear();
#endif
}
VkImageLayout GSTextureVK::GetVkLayout() const
@@ -523,6 +545,8 @@ void GSTextureVK::SetDebugName(std::string_view name)
Vulkan::SetObjectName(GSDeviceVK::GetInstance()->GetDevice(), m_image, "%.*s", static_cast<int>(name.size()), name.data());
Vulkan::SetObjectName(GSDeviceVK::GetInstance()->GetDevice(), m_view, "%.*s", static_cast<int>(name.size()), name.data());
m_debug_name = name;
}
#endif
@@ -547,13 +571,17 @@ void GSTextureVK::CommitClear(VkCommandBuffer cmdbuf)
const VkImageSubresourceRange srr = {VK_IMAGE_ASPECT_DEPTH_BIT, 0u, 1u, 0u, 1u};
vkCmdClearDepthStencilImage(cmdbuf, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &cv, 1, &srr);
}
else
else if (IsRenderTarget())
{
alignas(16) VkClearColorValue cv;
GSVector4::store<true>(cv.float32, GetClearForFormat());
const VkImageSubresourceRange srr = {VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u};
vkCmdClearColorImage(cmdbuf, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &cv, 1, &srr);
}
else
{
pxFailRel("Illegal texture type for clear.");
}
SetState(GSTexture::State::Dirty);
}
@@ -582,7 +610,7 @@ void GSTextureVK::TransitionSubresourcesToLayout(
VkCommandBuffer command_buffer, int start_level, int num_levels, Layout old_layout, Layout new_layout)
{
VkImageAspectFlags aspect;
if (m_type == Type::DepthStencil)
if (IsDepthStencil())
{
aspect = g_gs_device->Features().stencil_buffer ? (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) :
VK_IMAGE_ASPECT_DEPTH_BIT;
@@ -754,6 +782,14 @@ void GSTextureVK::TransitionSubresourcesToLayout(
break;
}
vkCmdPipelineBarrier(command_buffer, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, &barrier);
// Count as a UAV barrier if we transition to/from UAV.
if (IsRenderTargetOrDepthStencil() &&
(old_layout == Layout::ReadWriteImage || new_layout == Layout::ReadWriteImage))
{
g_perfmon.Put(GSPerfMon::Barriers, 1);
g_perfmon.Put(GSPerfMon::BarriersROV, 1);
}
}
VkFramebuffer GSTextureVK::GetFramebuffer(bool feedback_loop)
+4
View File
@@ -42,6 +42,8 @@ public:
__fi VkImage GetImage() const { return m_image; }
__fi VkImageView GetView() const { return m_view; }
__fi Layout GetLayout() const { return m_layout; }
virtual bool IsUnorderedAccess() const override { return GetLayout() == Layout::ReadWriteImage; }
__fi VkFormat GetVkFormat() const { return m_vk_format; }
VkImageLayout GetVkLayout() const;
@@ -70,6 +72,8 @@ public:
void TransitionSubresourcesToLayout(
VkCommandBuffer command_buffer, int start_level, int num_levels, Layout old_layout, Layout new_layout);
static VkFramebuffer CreateNullFramebuffer();
/// Framebuffers are lazily allocated.
VkFramebuffer GetFramebuffer(bool feedback_loop);
+2 -2
View File
@@ -733,7 +733,7 @@ void Vulkan::DescriptorSetUpdateBuilder::PushUpdate(
}
void Vulkan::DescriptorSetUpdateBuilder::AddImageDescriptorWrite(VkDescriptorSet set, u32 binding, VkImageView view,
VkImageLayout layout /*= VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL*/)
VkImageLayout layout /*= VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL*/, bool storage_image /*= false*/)
{
pxAssert(m_num_writes < MAX_WRITES && m_num_image_infos < MAX_IMAGE_INFOS);
@@ -747,7 +747,7 @@ void Vulkan::DescriptorSetUpdateBuilder::AddImageDescriptorWrite(VkDescriptorSet
dw.dstSet = set;
dw.dstBinding = binding;
dw.descriptorCount = 1;
dw.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
dw.descriptorType = storage_image ? VK_DESCRIPTOR_TYPE_STORAGE_IMAGE : VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
dw.pImageInfo = &ii;
}
+1 -1
View File
@@ -241,7 +241,7 @@ namespace Vulkan
bool clear = true);
void AddImageDescriptorWrite(VkDescriptorSet set, u32 binding, VkImageView view,
VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, bool storage_image = false);
void AddSamplerDescriptorWrite(VkDescriptorSet set, u32 binding, VkSampler sampler);
void AddSamplerDescriptorWrites(VkDescriptorSet set, u32 binding, const VkSampler* samplers, u32 num_samplers);
void AddCombinedImageSamplerDescriptorWrite(VkDescriptorSet set, u32 binding, VkImageView view,
+6
View File
@@ -916,6 +916,12 @@ __ri void ImGuiManager::DrawSettingsOverlay(float scale, float margin, float spa
if (GSConfig.HWAA1)
APPEND("AA1 ");
if (GSConfig.HWROV)
APPEND("ROV ");
if (GSConfig.HWROVBarriersVK)
APPEND("RBVK ");
// deliberately test global and print local here for auto values
if (EmuConfig.GS.TextureFiltering != BiFiltering::PS2)
APPEND("BF={} ", static_cast<unsigned>(GSConfig.TextureFiltering));
+6
View File
@@ -755,6 +755,9 @@ Pcsx2Config::GSOptions::GSOptions()
HWAccurateAlphaTest = false;
HWAA1 = false;
UseDebugBlend = false;
HWROV = false;
HWROVLogging = false;
HWROVBarriersVK = false;
ManualUserHacks = false;
UserHacks_AlignSpriteX = false;
@@ -1043,6 +1046,9 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
SettingsWrapBitBool(HWAccurateAlphaTest);
SettingsWrapBitBool(HWAA1);
SettingsWrapBitBool(UseDebugBlend);
SettingsWrapBitBool(HWROV);
SettingsWrapBitBool(HWROVLogging);
SettingsWrapBitBool(HWROVBarriersVK);
SettingsWrapIntEnumEx(AccurateBlendingUnit, "accurate_blending_unit");
SettingsWrapIntEnumEx(TextureFiltering, "filter");
SettingsWrapIntEnumEx(TexturePreloading, "texture_preloading");
+1 -1
View File
@@ -3,4 +3,4 @@
/// Version number for GS and other shaders. Increment whenever any of the contents of the
/// shaders change, to invalidate the cache.
static constexpr u32 SHADER_CACHE_VERSION = 98; // Last changed in PR 14477
static constexpr u32 SHADER_CACHE_VERSION = 99; // Last changed in PR 14503