mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-10-12 02:08:45 +00:00
GS/HW: ROV support.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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> "
|
||||
|
||||
@@ -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
@@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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{};
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ struct PipelineSelectorMTL
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(PipelineSelectorMTL) == 24);
|
||||
static_assert(sizeof(PipelineSelectorMTL) == 28);
|
||||
|
||||
template <>
|
||||
struct std::hash<PipelineSelectorMTL>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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 = {};
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 = ⅈ
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user