GS/HW: Support HW AA1 for lines/triangles.

Credits: concept by TellowKrinkle.
This commit is contained in:
TJnotJT
2026-03-25 18:49:46 -04:00
committed by lightningterror
parent ed796ee3db
commit 2ae8f1f23f
29 changed files with 1277 additions and 189 deletions
+157 -16
View File
@@ -43,6 +43,13 @@
#define PS_ATST_NOTEQUAL 4
#endif
#ifndef PS_AA1_NONE
#define PS_AA1_NONE 0
#define PS_AA1_LINE 1
#define PS_AA1_TRIANGLE 2
#define PS_AA1_TRIANGLE_SW_Z 3
#endif
#ifndef PS_FST
#define PS_IIP 0
#define PS_FST 0
@@ -102,6 +109,17 @@
#define PS_NO_COLOR1 0
#define PS_DATE 0
#define PS_TEX_IS_FB 0
#define PS_AA1 0
#define PS_ABE 0
#endif
#ifndef VS_EXPAND_NONE
#define VS_EXPAND_NONE 0
#define VS_EXPAND_POINT 1
#define VS_EXPAND_LINE 2
#define VS_EXPAND_SPRITE 3
#define VS_EXPAND_LINE_AA1 4
#define VS_EXPAND_TRIANGLE_AA1 5
#endif
#define SW_BLEND (PS_BLEND_A || PS_BLEND_B || PS_BLEND_D)
@@ -110,7 +128,8 @@
#define NEEDS_RT_FOR_AFAIL (PS_AFAIL == AFAIL_ZB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z)
#define NEEDS_DEPTH_FOR_AFAIL (PS_AFAIL == AFAIL_FB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z)
#define NEEDS_DEPTH_FOR_ZTST (PS_ZTST == ZTST_GEQUAL || PS_ZTST == ZTST_GREATER)
#define SW_DEPTH (NEEDS_DEPTH_FOR_AFAIL || NEEDS_DEPTH_FOR_ZTST)
#define NEEDS_DEPTH_FOR_AA1 (PS_AA1 == PS_AA1_TRIANGLE_SW_Z)
#define SW_DEPTH (NEEDS_DEPTH_FOR_AFAIL || NEEDS_DEPTH_FOR_ZTST || NEEDS_DEPTH_FOR_AA1)
#define ZWRITE (PS_ZFLOOR || PS_ZCLAMP || SW_DEPTH)
struct VS_INPUT
@@ -135,6 +154,9 @@ struct VS_OUTPUT
#else
nointerpolation float4 c : COLOR0;
#endif
float inv_cov : COLOR1; // We use the inverse to make it simpler to interpolate.
nointerpolation uint interior : COLOR2; // 1 for triangle interior; 0 for edge;
};
struct PS_INPUT
@@ -147,6 +169,8 @@ struct PS_INPUT
#else
nointerpolation float4 c : COLOR0;
#endif
float inv_cov : COLOR1; // We use the inverse to make it simpler to interpolate.
nointerpolation uint interior : COLOR2; // 1 for triangle interior; 0 for edge;
#if (PS_DATE >= 1 && PS_DATE <= 3) || GS_FORWARD_PRIMID
uint primid : SV_PrimitiveID;
#endif
@@ -1077,7 +1101,15 @@ PS_OUTPUT ps_main(PS_INPUT input)
#endif
float4 C = ps_color(input);
#if PS_FIXED_ONE_A
#if PS_AA1
float cov = clamp(1.0f - abs(input.inv_cov), 0.0f, 1.0f);
#if PS_ABE
if (floor(C.a) == 128.0f) // The coverage is only used if the fragment alpha is 128.
C.a = 128.0f * cov;
#else
C.a = 128.0f * cov;
#endif
#elif PS_FIXED_ONE_A
// AA (Fixed one) will output a coverage of 1.0 as alpha
C.a = 128.0f;
#endif
@@ -1279,6 +1311,11 @@ PS_OUTPUT ps_main(PS_INPUT input)
input.p.z = min(input.p.z, MaxDepthPS);
#endif
#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.
#endif
#if ZWRITE
#if SW_DEPTH && PS_NO_COLOR1 && DX12
// Output color clone for feedback as well as real depth.
@@ -1310,7 +1347,19 @@ cbuffer cb0
float2 TextureOffset;
float2 PointSize;
uint MaxDepth;
uint BaseVertex; // Only used in DX11.
uint _cb0_pad0;
};
#ifdef DX12
cbuffer cb2 : register(b2)
#else
cbuffer cb2
#endif
{
uint BaseVertex;
uint BaseIndex;
uint _cb2_pad0;
uint _cb2_pad1;
};
VS_OUTPUT vs_main(VS_INPUT input)
@@ -1362,10 +1411,14 @@ VS_OUTPUT vs_main(VS_INPUT input)
output.c = input.c;
output.t.z = input.f.r;
// Silence compiler warnings; should be optimized out when not needed.
output.inv_cov = 0.0f;
output.interior = 0;
return output;
}
#if VS_EXPAND != 0
#if VS_EXPAND != VS_EXPAND_NONE
struct VS_RAW_INPUT
{
@@ -1379,14 +1432,19 @@ struct VS_RAW_INPUT
};
StructuredBuffer<VS_RAW_INPUT> vertices : register(t0);
StructuredBuffer<uint> IndexBuffer : register(t5);
uint load_index(uint _i)
{
uint i = _i + BaseIndex;
// i is even => load lower 16 bits; i odd => load upper 16 bits.
uint shift = (i & 1) << 4;
return (IndexBuffer.Load(i >> 1) >> shift) & 0xFFFF;
}
VS_INPUT load_vertex(uint index)
{
#ifdef DX12
VS_RAW_INPUT raw = vertices.Load(index);
#else
VS_RAW_INPUT raw = vertices.Load(BaseVertex + index);
#endif
VS_INPUT vert;
vert.st = raw.ST;
@@ -1401,7 +1459,7 @@ VS_INPUT load_vertex(uint index)
VS_OUTPUT vs_main_expand(uint vid : SV_VertexID)
{
#if VS_EXPAND == 1 // Point
#if VS_EXPAND == VS_EXPAND_POINT
VS_OUTPUT vtx = vs_main(load_vertex(vid >> 2));
@@ -1410,7 +1468,13 @@ VS_OUTPUT vs_main_expand(uint vid : SV_VertexID)
return vtx;
#elif VS_EXPAND == 2 // Line
#elif (VS_EXPAND == VS_EXPAND_LINE) || (VS_EXPAND == VS_EXPAND_LINE_AA1)
// The difference between EXPAND_LINE and EXPAND_LINE_AA1
// is that EXPAND_LINE expands in the perpendicular direction while
// EXPAND_LINE_AA1 expands in the Y direction for shallow lines (X dominant)
// and the X direction for steep lines (Y dominant).
// EXPAND_LINE_AA1 also adds coverage to the output.
uint vid_base = vid >> 2;
bool is_bottom = vid & 2;
@@ -1419,20 +1483,31 @@ VS_OUTPUT vs_main_expand(uint vid : SV_VertexID)
VS_OUTPUT vtx = vs_main(load_vertex(vid_base));
VS_OUTPUT other = vs_main(load_vertex(vid_other));
float2 line_vector = normalize(vtx.p.xy - other.p.xy);
float2 line_normal = float2(line_vector.y, -line_vector.x);
float2 line_width = (line_normal * PointSize) / 2;
// line_normal is inverted for bottom point
float2 offset = (is_bottom ^ is_right) ? line_width : -line_width;
// Use bottom minus top for delta regardless of which vertex we are expanding.
float2 line_delta = is_bottom ? (vtx.p.xy - other.p.xy) : (other.p.xy - vtx.p.xy);
float2 line_vector = normalize(line_delta);
#if VS_EXPAND == VS_EXPAND_LINE
float2 line_expand = float2(line_vector.y, -line_vector.x);
#elif VS_EXPAND == VS_EXPAND_LINE_AA1
// Expand in y direction for shallow lines and x direction for steep lines.
line_delta /= VertexScale;
float2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? float2(0.0f, 2.0f) : float2(2.0f, 0.0f);
#endif
float2 line_width = (line_expand * PointSize) / 2;
float2 offset = is_right ? line_width : -line_width;
vtx.p.xy += offset;
#if VS_EXPAND == VS_EXPAND_LINE_AA1
vtx.inv_cov = is_right ? 1.0f : -1.0f;
#endif
// Lines will be run as (0 1 2) (1 2 3)
// This means that both triangles will have a point based off the top line point as their first point
// So we don't have to do anything for !IIP
return vtx;
#elif VS_EXPAND == 3 // Sprite
#elif VS_EXPAND == VS_EXPAND_SPRITE
// Sprite points are always in pairs
uint vid_base = vid >> 1;
@@ -1455,6 +1530,72 @@ VS_OUTPUT vs_main_expand(uint vid : SV_VertexID)
return vtx;
#elif VS_EXPAND == VS_EXPAND_TRIANGLE_AA1
// Triangles with AA1 are expanded as follows:
// - Vertices 0-2: Interior of triangle (1 triangle).
// - Vertices 3-8: First edge expanded (2 triangles).
// - Vertices 9-14: Second edge expanded (2 triangles).
// - Vertices 15-20: Third edge expanded (2 triangles).
uint prim_id = vid / 21;
uint prim_offset = vid - 21 * prim_id; // range: 0-20
bool interior = prim_offset < 3;
VS_OUTPUT vtx;
if (interior)
{
vtx = vs_main(load_vertex(load_index(3 * prim_id + prim_offset)));
vtx.inv_cov = 0.0f; // Full coverage
vtx.interior = 1;
}
else
{
// Vertex indices for this edge. We need all 3 for determining exterior/interior.
uint prim_offset_edges = prim_offset - 3; // range: 0-17
uint i0 = prim_offset_edges / 6;
uint i1 = (i0 >= 2) ? i0 - 2 : i0 + 1;
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
uint edge_offset = prim_offset_edges - 6 * i0; // range: 0-5
// Note: order of top/bottom, inside/outside order is arbitrary,
// as long as it assembles into two triangles forming a quad.
bool is_bottom = (2 <= edge_offset) && (edge_offset <= 4);
bool is_outside = edge_offset & 1;
vtx = vs_main(load_vertex(load_index(3 * prim_id + i0)));
VS_OUTPUT other = vs_main(load_vertex(load_index(3 * prim_id + i1)));
VS_OUTPUT opposite = vs_main(load_vertex(load_index(3 * prim_id + i2)));
// Similar expansion to line AA1 except instead of expanding on both sides of
// the line we expand on on the side towards the outside of the triangle.
float2 line_delta = vtx.p.xy - other.p.xy;
float2 line_normal = normalize(float2(line_delta.y, -line_delta.x));
float2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? float2(0.0f, 2.0f) : float2(2.0f, 0.0f);
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(opposite.p.xy - vtx.p.xy, line_normal) >= 0.0f))
{
// Expand direction point towards the interior so flip it.
line_expand = -line_expand;
}
float2 line_width = (line_expand * PointSize) / 2;
if (is_bottom)
vtx = other;
if (is_outside)
{
vtx.p.xy += line_width;
vtx.inv_cov = 1.0f; // No coverage
}
else
{
vtx.inv_cov = 0.0f; // Full coverage
}
vtx.interior = 0;
}
return vtx;
#endif
}
+27 -3
View File
@@ -33,6 +33,13 @@
#define PS_ATST_NOTEQUAL 4
#endif
#ifndef PS_AA1_NONE
#define PS_AA1_NONE 0
#define PS_AA1_LINE 1
#define PS_AA1_TRIANGLE 2
#define PS_AA1_TRIANGLE_SW_Z 3
#endif
// TEX_COORD_DEBUG output the uv coordinate as color. It is useful
// to detect bad sampling due to upscaling
//#define TEX_COORD_DEBUG
@@ -50,10 +57,11 @@
#define NEEDS_RT_FOR_AFAIL (PS_AFAIL == PS_ZB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z)
#define NEEDS_DEPTH_FOR_AFAIL (PS_AFAIL == AFAIL_FB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z)
#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_TEX (PS_TFX != 4)
#define SW_DEPTH (NEEDS_DEPTH_FOR_AFAIL || NEEDS_DEPTH_FOR_ZTST)
#define SW_DEPTH (NEEDS_DEPTH_FOR_AFAIL || NEEDS_DEPTH_FOR_ZTST || NEEDS_DEPTH_FOR_AA1)
#define ZWRITE (SW_DEPTH || PS_ZCLAMP || PS_ZFLOOR)
layout(std140, binding = 0) uniform cb21
@@ -97,6 +105,9 @@ in SHADER
#else
flat vec4 c;
#endif
float inv_cov; // We use the inverse to make it simpler to interpolate.
flat uint interior; // 1 for triangle interior; 0 for edge;
} PSin;
#define TARGET_0_QUALIFIER out
@@ -1099,7 +1110,15 @@ void ps_main()
vec4 C = ps_color();
#if PS_FIXED_ONE_A
#if PS_AA1
float cov = clamp(1.0f - abs(PSin.inv_cov), 0.0f, 1.0f);
#if PS_ABE
if (floor(C.a) == 128.0f) // The coverage is only used if the fragment alpha is 128.
C.a = 128.0f * cov;
#else
C.a = 128.0f * cov;
#endif
#elif PS_FIXED_ONE_A
// AA (Fixed one) will output a coverage of 1.0 as alpha
C.a = 128.0f;
#endif
@@ -1224,7 +1243,7 @@ void ps_main()
#elif (PS_AFAIL == AFAIL_RGB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z)
if (!atst_pass)
{
C.a = sample_from_rt().a;
C.a = sample_from_rt().a;
#if PS_AFAIL == AFAIL_RGB_ONLY_SW_Z
input_z = sample_from_depth().r;
#endif
@@ -1244,6 +1263,11 @@ void ps_main()
input_z = min(input_z, MaxDepthPS);
#endif
#if PS_AA1 == PS_AA1_TRIANGLE_SW_Z
if (!bool(PSin.interior))
input_z = sample_from_depth().r; // No depth update for triangle edges.
#endif
#if ZWRITE
#if SW_DEPTH && PS_NO_COLOR1 && (DEPTH_FEEDBACK_SUPPORT == 2)
// Depth as color write. For depth as color feedback we write to both
+116 -10
View File
@@ -18,6 +18,15 @@ layout(std140, binding = 1) uniform cb20
#ifdef VERTEX_SHADER
#ifndef VS_EXPAND_NONE
#define VS_EXPAND_NONE 0
#define VS_EXPAND_POINT 1
#define VS_EXPAND_LINE 2
#define VS_EXPAND_SPRITE 3
#define VS_EXPAND_LINE_AA1 4
#define VS_EXPAND_TRIANGLE_AA1 5
#endif
out SHADER
{
vec4 t_float;
@@ -27,11 +36,13 @@ out SHADER
#else
flat vec4 c;
#endif
float inv_cov; // We use the inverse to make it simpler to interpolate.
uint interior; // 1 for triangle interior; 0 for edge;
} VSout;
const float exp_min32 = exp2(-32.0f);
#if VS_EXPAND == 0
#if VS_EXPAND == VS_EXPAND_NONE
layout(location = 0) in vec2 i_st;
layout(location = 2) in vec4 i_c;
@@ -98,10 +109,23 @@ struct RawVertex
uint FOG;
};
layout(std140, binding = 4) uniform cb22
{
uint BaseVertex;
uint BaseIndex;
uint pad_cb22_0;
uint pad_cb22_1;
};
layout(std140, binding = 2) readonly buffer VertexBuffer {
RawVertex vertex_buffer[];
};
// Warning: use std430 instead of std140 so that the ints are tightly packed.
layout(std430, binding = 3) readonly buffer IndexBuffer {
uint index_buffer[];
};
struct ProcessedVertex
{
vec4 p;
@@ -110,9 +134,17 @@ struct ProcessedVertex
vec4 c;
};
uint load_index(uint _i)
{
uint i = _i + BaseIndex;
// i is even => load lower 16 bits; i odd => load upper 16 bits.
uint shift = (i & 1) << 4;
return (index_buffer[i >> 1] >> shift) & 0xFFFF;
}
ProcessedVertex load_vertex(uint index)
{
RawVertex rvtx = vertex_buffer[index];
RawVertex rvtx = vertex_buffer[BaseVertex + index];
vec2 i_st = rvtx.ST;
vec4 i_c = vec4(uvec4(bitfieldExtract(rvtx.RGBA, 0, 8), bitfieldExtract(rvtx.RGBA, 8, 8),
@@ -156,14 +188,14 @@ void main()
uint vid = uint(gl_VertexID);
#if VS_EXPAND == 1 // Point
#if VS_EXPAND == VS_EXPAND_POINT
vtx = load_vertex(vid >> 2);
vtx.p.x += ((vid & 1u) != 0u) ? PointSize.x : 0.0f;
vtx.p.y += ((vid & 2u) != 0u) ? PointSize.y : 0.0f;
#elif VS_EXPAND == 2 // Line
#elif (VS_EXPAND == VS_EXPAND_LINE) || (VS_EXPAND == VS_EXPAND_LINE_AA1)
uint vid_base = vid >> 2;
bool is_bottom = (vid & 2u) != 0u;
@@ -172,18 +204,29 @@ void main()
vtx = load_vertex(vid_base);
ProcessedVertex other = load_vertex(vid_other);
vec2 line_vector = normalize(vtx.p.xy - other.p.xy);
vec2 line_normal = vec2(line_vector.y, -line_vector.x);
vec2 line_width = (line_normal * PointSize) / 2;
// line_normal is inverted for bottom point
vec2 offset = ((uint(is_bottom) ^ uint(is_right)) != 0u) ? line_width : -line_width;
// Use bottom minus top for delta regardless of which vertex we are expanding.
vec2 line_delta = is_bottom ? (vtx.p.xy - other.p.xy) : (other.p.xy - vtx.p.xy);
vec2 line_vector = normalize(line_delta);
#if VS_EXPAND == VS_EXPAND_LINE
vec2 line_expand = vec2(line_vector.y, -line_vector.x);
#elif VS_EXPAND == VS_EXPAND_LINE_AA1
// Expand in y direction for shallow lines and x direction for steep lines.
line_delta /= VertexScale;
vec2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? vec2(0.0f, 2.0f) : vec2(2.0f, 0.0f);
#endif
vec2 line_width = (line_expand * PointSize) / 2;
vec2 offset = is_right ? line_width : -line_width;
vtx.p.xy += offset;
#if VS_EXPAND == VS_EXPAND_LINE_AA1
VSout.inv_cov = is_right ? 1.0f : -1.0f;
#endif
// Lines will be run as (0 1 2) (1 2 3)
// This means that both triangles will have a point based off the top line point as their first point
// So we don't have to do anything for !IIP
#elif VS_EXPAND == 3 // Sprite
#elif VS_EXPAND == VS_EXPAND_SPRITE
// Sprite points are always in pairs
uint vid_base = vid >> 1;
@@ -204,6 +247,69 @@ void main()
vtx.t_float.y = is_bottom ? lt.t_float.y : vtx.t_float.y;
vtx.t_int.yw = is_bottom ? lt.t_int.yw : vtx.t_int.yw;
#elif VS_EXPAND == VS_EXPAND_TRIANGLE_AA1
// Triangles with AA1 are expanded as follows:
// - Vertices 0-2: Interior of triangle (1 triangle).
// - Vertices 3-8: First edge expanded (2 triangles).
// - Vertices 9-14: Second edge expanded (2 triangles).
// - Vertices 15-20: Third edge expanded (2 triangles).
uint prim_id = vid / 21;
uint prim_offset = vid - 21 * prim_id; // range: 0-20
bool interior = prim_offset < 3;
if (interior)
{
vtx = load_vertex(load_index(3 * prim_id + prim_offset));
VSout.inv_cov = 0.0f; // Full coverage
VSout.interior = 1;
}
else
{
// Vertex indices for this edge. We need all 3 for determining exterior/interior.
uint prim_offset_edges = prim_offset - 3; // range: 0-17
uint i0 = prim_offset_edges / 6;
uint i1 = (i0 >= 2) ? i0 - 2 : i0 + 1;
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
uint edge_offset = prim_offset_edges - 6 * i0; // range: 0-5
// Note: order of top/bottom, inside/outside order is arbitrary,
// as long as it assembles into two triangles forming a quad.
bool is_bottom = (2 <= edge_offset) && (edge_offset <= 4);
bool is_outside = (edge_offset & 1) != 0;
vtx = load_vertex(load_index(3 * prim_id + i0));
ProcessedVertex other = load_vertex(load_index(3 * prim_id + i1));
ProcessedVertex opposite = load_vertex(load_index(3 * prim_id + i2));
// Similar expansion to line AA1 except instead of expanding on both sides of
// the line we expand on on the side towards the outside of the triangle.
vec2 line_delta = vtx.p.xy - other.p.xy;
vec2 line_normal = normalize(vec2(line_delta.y, -line_delta.x));
vec2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? vec2(0.0f, 2.0f) : vec2(2.0f, 0.0f);
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(opposite.p.xy - vtx.p.xy, line_normal) >= 0.0f))
{
// Expand direction point towards the interior so flip it.
line_expand = -line_expand;
}
vec2 line_width = (line_expand * PointSize) / 2;
if (is_bottom)
vtx = other;
if (is_outside)
{
vtx.p.xy += line_width;
VSout.inv_cov = 1.0f; // No coverage
}
else
{
VSout.inv_cov = 0.0f; // Full coverage
}
VSout.interior = 0;
}
#endif
gl_Position = vtx.p;
+145 -15
View File
@@ -7,6 +7,15 @@
#if defined(VERTEX_SHADER)
#ifndef VS_EXPAND_NONE
#define VS_EXPAND_NONE 0
#define VS_EXPAND_POINT 1
#define VS_EXPAND_LINE 2
#define VS_EXPAND_SPRITE 3
#define VS_EXPAND_LINE_AA1 4
#define VS_EXPAND_TRIANGLE_AA1 5
#endif
layout(std140, set = 0, binding = 0) uniform cb0
{
vec2 VertexScale;
@@ -28,9 +37,12 @@ layout(location = 0) out VSOutput
#else
flat vec4 c;
#endif
float inv_cov; // We use the inverse to make it simpler to interpolate.
uint interior; // 1 for triangle interior; 0 for edge;
} vsOut;
#if VS_EXPAND == 0
#if VS_EXPAND == VS_EXPAND_NONE
layout(location = 0) in vec2 a_st;
layout(location = 1) in uvec4 a_c;
@@ -99,10 +111,23 @@ struct RawVertex
uint FOG;
};
layout(push_constant) uniform cb2
{
uint BaseVertex;
uint BaseIndex;
uint pad_cb2_0;
uint pad_cb2_1;
};
layout(std140, set = 0, binding = 2) readonly buffer VertexBuffer {
RawVertex vertex_buffer[];
};
// Warning: use std430 instead of std140 so that the ints are tightly packed.
layout(std430, set = 0, binding = 3) readonly buffer IndexBuffer {
uint index_buffer[];
};
struct ProcessedVertex
{
vec4 p;
@@ -111,9 +136,17 @@ struct ProcessedVertex
vec4 c;
};
uint load_index(uint _i)
{
uint i = _i + BaseIndex;
// i is even => load lower 16 bits; i odd => load upper 16 bits.
uint shift = (i & 1) << 4;
return (index_buffer[i >> 1] >> shift) & 0xFFFF;
}
ProcessedVertex load_vertex(uint index)
{
RawVertex rvtx = vertex_buffer[index];
RawVertex rvtx = vertex_buffer[BaseVertex + index];
vec2 a_st = rvtx.ST;
uvec4 a_c = uvec4(bitfieldExtract(rvtx.RGBA, 0, 8), bitfieldExtract(rvtx.RGBA, 8, 8),
@@ -161,14 +194,14 @@ void main()
ProcessedVertex vtx;
uint vid = uint(gl_VertexIndex);
#if VS_EXPAND == 1 // Point
#if VS_EXPAND == VS_EXPAND_POINT
vtx = load_vertex(vid >> 2);
vtx.p.x += ((vid & 1u) != 0u) ? PointSize.x : 0.0f;
vtx.p.y += ((vid & 2u) != 0u) ? PointSize.y : 0.0f;
#elif VS_EXPAND == 2 // Line
#elif (VS_EXPAND == VS_EXPAND_LINE) || (VS_EXPAND == VS_EXPAND_LINE_AA1)
uint vid_base = vid >> 2;
@@ -179,18 +212,29 @@ void main()
vtx = load_vertex(vid_base);
ProcessedVertex other = load_vertex(vid_other);
vec2 line_vector = normalize(vtx.p.xy - other.p.xy);
vec2 line_normal = vec2(line_vector.y, -line_vector.x);
vec2 line_width = (line_normal * PointSize) / 2;
// line_normal is inverted for bottom point
vec2 offset = ((uint(is_bottom) ^ uint(is_right)) != 0u) ? line_width : -line_width;
// Use bottom minus top for delta regardless of which vertex we are expanding.
vec2 line_delta = is_bottom ? (vtx.p.xy - other.p.xy) : (other.p.xy - vtx.p.xy);
vec2 line_vector = normalize(line_delta);
#if VS_EXPAND == VS_EXPAND_LINE
vec2 line_expand = vec2(line_vector.y, -line_vector.x);
#elif VS_EXPAND == VS_EXPAND_LINE_AA1
// Expand in y direction for shallow lines and x direction for steep lines.
line_delta /= VertexScale;
vec2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? vec2(0.0f, 2.0f) : vec2(2.0f, 0.0f);
#endif
vec2 line_width = (line_expand * PointSize) / 2;
vec2 offset = is_right ? line_width : -line_width;
vtx.p.xy += offset;
#if VS_EXPAND == VS_EXPAND_LINE_AA1
vsOut.inv_cov = is_right ? 1.0f : -1.0f;
#endif
// Lines will be run as (0 1 2) (1 2 3)
// This means that both triangles will have a point based off the top line point as their first point
// So we don't have to do anything for !IIP
#elif VS_EXPAND == 3 // Sprite
#elif VS_EXPAND == VS_EXPAND_SPRITE
// Sprite points are always in pairs
uint vid_base = vid >> 1;
@@ -211,6 +255,69 @@ void main()
vtx.t.y = is_bottom ? lt.t.y : vtx.t.y;
vtx.ti.yw = is_bottom ? lt.ti.yw : vtx.ti.yw;
#elif VS_EXPAND == VS_EXPAND_TRIANGLE_AA1
// Triangles with AA1 are expanded as follows:
// - Vertices 0-2: Interior of triangle (1 triangle).
// - Vertices 3-8: First edge expanded (2 triangles).
// - Vertices 9-14: Second edge expanded (2 triangles).
// - Vertices 15-20: Third edge expanded (2 triangles).
uint prim_id = vid / 21;
uint prim_offset = vid - 21 * prim_id; // range: 0-20
bool interior = prim_offset < 3;
if (interior)
{
vtx = load_vertex(load_index(3 * prim_id + prim_offset));
vsOut.inv_cov = 0.0f; // Full coverage
vsOut.interior = 1;
}
else
{
// Vertex indices for this edge. We need all 3 for determining exterior/interior.
uint prim_offset_edges = prim_offset - 3; // range: 0-17
uint i0 = prim_offset_edges / 6;
uint i1 = (i0 >= 2) ? i0 - 2 : i0 + 1;
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
uint edge_offset = prim_offset_edges - 6 * i0; // range: 0-5
// Note: order of top/bottom, inside/outside order is arbitrary,
// as long as it assembles into two triangles forming a quad.
bool is_bottom = (2 <= edge_offset) && (edge_offset <= 4);
bool is_outside = (edge_offset & 1) != 0;
vtx = load_vertex(load_index(3 * prim_id + i0));
ProcessedVertex other = load_vertex(load_index(3 * prim_id + i1));
ProcessedVertex opposite = load_vertex(load_index(3 * prim_id + i2));
// Similar expansion to line AA1 except instead of expanding on both sides of
// the line we expand on on the side towards the outside of the triangle.
vec2 line_delta = vtx.p.xy - other.p.xy;
vec2 line_normal = normalize(vec2(line_delta.y, -line_delta.x));
vec2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? vec2(0.0f, 2.0f) : vec2(2.0f, 0.0f);
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(opposite.p.xy - vtx.p.xy, line_normal) >= 0.0f))
{
// Expand direction point towards the interior so flip it.
line_expand = -line_expand;
}
vec2 line_width = (line_expand * PointSize) / 2;
if (is_bottom)
vtx = other;
if (is_outside)
{
vtx.p.xy += line_width;
vsOut.inv_cov = 1.0f; // No coverage
}
else
{
vsOut.inv_cov = 0.0f; // Full coverage
}
vsOut.interior = 0;
}
#endif
gl_Position = vtx.p;
@@ -267,6 +374,13 @@ void main()
#define PS_ATST_NOTEQUAL 4
#endif
#ifndef PS_AA1_NONE
#define PS_AA1_NONE 0
#define PS_AA1_LINE 1
#define PS_AA1_TRIANGLE 2
#define PS_AA1_TRIANGLE_SW_Z 3
#endif
#ifndef PS_FST
#define PS_FST 0
#define PS_WMS 0
@@ -327,9 +441,10 @@ void main()
#define AFAIL_NEEDS_RT (PS_AFAIL == AFAIL_ZB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z)
#define AFAIL_NEEDS_DEPTH (PS_AFAIL == AFAIL_FB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z)
#define ZTST_NEEDS_DEPTH (PS_ZTST == ZTST_GEQUAL || PS_ZTST == ZTST_GREATER)
#define AA1_NEEDS_DEPTH (PS_AA1 == PS_AA1_TRIANGLE_SW_Z)
#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) || AFAIL_NEEDS_RT)
#define PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH (AFAIL_NEEDS_DEPTH || ZTST_NEEDS_DEPTH)
#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 NEEDS_TEX (PS_TFX != 4)
@@ -365,6 +480,8 @@ layout(location = 0) in VSOutput
#else
flat vec4 c;
#endif
float inv_cov; // We use the inverse to make it simpler to interpolate.
flat uint interior; // 1 for triangle interior; 0 for edge;
} vsIn;
#if !PS_NO_COLOR && !PS_NO_COLOR1
@@ -1346,7 +1463,15 @@ void main()
vec4 C = ps_color();
#if PS_FIXED_ONE_A
#if PS_AA1
float cov = clamp(1.0f - abs(vsIn.inv_cov), 0.0f, 1.0f);
#if PS_ABE
if (floor(C.a) == 128.0f) // The coverage is only used if the fragment alpha is 128.
C.a = 128.0f * cov;
#else
C.a = 128.0f * cov;
#endif
#elif PS_FIXED_ONE_A
// AA (Fixed one) will output a coverage of 1.0 as alpha
C.a = 128.0f;
#endif
@@ -1472,7 +1597,7 @@ void main()
#elif (PS_AFAIL == AFAIL_RGB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z)
if (!atst_pass)
{
o_col0.a = sample_from_rt().a;
o_col0.a = sample_from_rt().a;
#if PS_AFAIL == AFAIL_RGB_ONLY_SW_Z
input_z = sample_from_depth().r;
#endif
@@ -1483,7 +1608,12 @@ void main()
#if PS_ZCLAMP
input_z = min(input_z, MaxDepthPS);
#endif
#if PS_AA1 == PS_AA1_TRIANGLE_SW_Z
if (!bool(vsIn.interior))
input_z = sample_from_depth().r; // No depth update for triangle edges.
#endif
#if ZWRITE
gl_FragDepth = input_z;
#endif
@@ -65,6 +65,13 @@
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="hwAA1">
<property name="text">
<string>AA1</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="enableHWFixes">
<property name="text">
<string>Manual Hardware Renderer Fixes</string>
@@ -235,6 +242,7 @@
<tabstop>blending</tabstop>
<tabstop>mipmapping</tabstop>
<tabstop>accurateAlphaTest</tabstop>
<tabstop>hwAA1</tabstop>
<tabstop>enableHWFixes</tabstop>
</tabstops>
<resources/>
@@ -112,6 +112,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_hw.dithering, "EmuCore/GS", "dithering_ps2", 2);
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::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);
@@ -492,6 +493,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
dialog()->registerWidgetHelp(
m_hw.accurateAlphaTest, tr("Accurate Alpha Test"), tr("Unchecked"), tr("Enables accurate alpha testing, which some games require to render correctly. This may require more draw calls and result in a speed penalty."));
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.textureFiltering, tr("Texture Filtering"), tr("Bilinear (PS2)"),
tr("Changes what filtering algorithm is used to map textures to surfaces.<br> "
+1
View File
@@ -792,6 +792,7 @@ struct Pcsx2Config
Mipmap : 1,
HWMipmap : 1,
HWAccurateAlphaTest: 1,
HWAA1 : 1,
ManualUserHacks : 1,
UserHacks_AlignSpriteX : 1,
UserHacks_CPUFBConversion : 1,
+33 -12
View File
@@ -4269,6 +4269,10 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
const u16* RESTRICT index = m_index->buff;
const u32 count = m_index->tail;
// Since adjacent triangles overlap at the edges with AA1, we cannot combine
// such triangles, so disable some barrier optimizations.
const bool using_aa1 = IsCoverageAlphaSupported();
const auto GetPoint = [&](int i) -> GSVector4i {
if constexpr (primclass == GS_SPRITE_CLASS || primclass == GS_POINT_CLASS)
return GSVector4i(v[i].m[1]).upl16(); // Optimize out using the indices.
@@ -4280,7 +4284,7 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
// Allows faster comparison than using O(n^2) for full pairwise intersections.
// Check Virtua Fighter for example.
if (primclass == GS_TRIANGLE_CLASS && m_quad_check_valid && m_are_quads)
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);
@@ -4293,7 +4297,7 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
}
PRIM_OVERLAP overlap = PRIM_OVERLAP_NO;
bool check_quads = (primclass == GS_TRIANGLE_CLASS);
bool check_quads = (primclass == GS_TRIANGLE_CLASS) && !using_aa1;
u32 i = 0;
u32 skip = 0; // Number of indices to skip if we have the bbox from the previous iteration.
@@ -4643,20 +4647,20 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
};
// First check: see if the triangles are part of a triangle strip.
if (!got_bbox)
if (!got_bbox && !using_aa1)
{
got_bbox = CheckTriangleStrips(j, skip, bbox, saved_tristrip);
}
// Second check: see if the triangles are part of triangle fan.
if (!got_bbox)
if (!got_bbox && !using_aa1)
{
got_bbox = CheckTriangleQuads.template operator()<1>(j, skip, bbox);
}
// Third check: see if a pair of triangles are an axis-aligned quad.
// This doesn't require indices to match like the tristrip check.
if (!got_bbox && check_quads)
if (!got_bbox && check_quads && !using_aa1)
{
got_bbox = GetBBoxAxisAlignedTriangles(j, skip, bbox);
@@ -4670,6 +4674,10 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
got_bbox = GetBBox(j, skip, bbox);
}
// Expand for AA1 edges overlapping.
if (using_aa1)
bbox = bbox.ExpandOne();
// Avoid degenerate bbox.
bbox = bbox.FixDegenerate();
@@ -4729,7 +4737,7 @@ GSState::PRIM_OVERLAP GSState::PrimitiveOverlap(bool save_drawlist)
if (m_vertex->next < 4)
return PRIM_OVERLAP_NO;
if (m_vt.m_primclass == GS_TRIANGLE_CLASS)
if (m_vt.m_primclass == GS_TRIANGLE_CLASS && !IsCoverageAlphaSupported())
return (m_index->tail == 6 && TrianglesAreQuads()) ? PRIM_OVERLAP_NO : PRIM_OVERLAP_UNKNOW;
else if (m_vt.m_primclass != GS_SPRITE_CLASS)
return PRIM_OVERLAP_UNKNOW; // maybe, maybe not
@@ -6424,12 +6432,15 @@ void GSState::CalcAlphaMinMax(const int tex_alpha_min, const int tex_alpha_max)
// Limit max to 255 as we send 500 when we don't know, makes calculating 24/16bit easier.
int min = tex_alpha_min, max = std::min(tex_alpha_max, 255);
if (IsCoverageAlpha())
if (IsCoverageAlphaFixedOne())
{
// HW renderer doesn't currently support AA, so its min is 128.
// If we add AA support to the HW renderer, this will need to be changed.
// (Will probably only be supported with ROV/FBFetch so we would want to check for that.)
min = GSIsHardwareRenderer() ? 128 : 0;
// HW renderer doesn't support AA1, assume alpha is constant 128.
min = 128;
max = 128;
}
else if (IsCoverageAlphaSupported())
{
min = 0;
max = 128;
}
else
@@ -6735,7 +6746,17 @@ bool GSState::IsMipMapActive()
bool GSState::IsCoverageAlpha()
{
return !PRIM->ABE && PRIM->AA1 && (m_vt.m_primclass == GS_LINE_CLASS || m_vt.m_primclass == GS_TRIANGLE_CLASS);
return PRIM->AA1 && (m_vt.m_primclass == GS_LINE_CLASS || m_vt.m_primclass == GS_TRIANGLE_CLASS);
}
bool GSState::IsCoverageAlphaFixedOne()
{
return IsCoverageAlpha() && !PRIM->ABE && !IsCoverageAlphaSupported();
}
bool GSState::IsCoverageAlphaSupported()
{
return false;
}
GIFRegTEX0 GSState::GetTex0Layer(u32 lod)
+2
View File
@@ -224,6 +224,8 @@ protected:
bool IsMipMapDraw();
bool IsMipMapActive();
bool IsCoverageAlpha();
bool IsCoverageAlphaFixedOne();
virtual bool IsCoverageAlphaSupported();
void CalcAlphaMinMax(const int tex_min, const int tex_max);
void CorrectATEAlphaMinMax(const u32 atst, const int aref);
+8
View File
@@ -157,6 +157,14 @@ public:
};
}
BoundingOct ExpandOne() const
{
return {
bbox0 + GSVector4i(-1, -1, 1, 1),
bbox1 + GSVector4i(-1, -1, 1, 1),
};
}
const GSVector4i& ToBBox()
{
return bbox0;
+20 -4
View File
@@ -1150,10 +1150,12 @@ static const char* GetVSExpandName(GSHWDrawConfig::VSExpand vsexpand)
{
switch (vsexpand)
{
case GSHWDrawConfig::VSExpand::None: return "None";
case GSHWDrawConfig::VSExpand::Point: return "Point";
case GSHWDrawConfig::VSExpand::Line: return "Line";
case GSHWDrawConfig::VSExpand::Sprite: return "Sprite";
case GSHWDrawConfig::VSExpand::None: return "None";
case GSHWDrawConfig::VSExpand::Point: return "Point";
case GSHWDrawConfig::VSExpand::Line: return "Line";
case GSHWDrawConfig::VSExpand::Sprite: return "Sprite";
case GSHWDrawConfig::VSExpand::LineAA1: return "LineAA1";
case GSHWDrawConfig::VSExpand::TriangleAA1: return "TriangleAA1";
}
return "Unknown";
}
@@ -1395,6 +1397,18 @@ static const char* GetSetDATMName(SetDATM datm)
return "Unknown";
}
static const char* GetPSAA1Name(u32 aa1)
{
switch (static_cast<GSHWDrawConfig::PS_AA1>(aa1))
{
case GSHWDrawConfig::PS_AA1::NONE: return "NONE";
case GSHWDrawConfig::PS_AA1::LINE: return "LINE";
case GSHWDrawConfig::PS_AA1::TRIANGLE: return "TRIANGLE";
case GSHWDrawConfig::PS_AA1::TRIANGLE_SW_Z: return "TRIANGLE_SW_Z";
}
return "Unknown";
}
static void DumpPSSelector(DrawConfigWriter& out, const GSHWDrawConfig::PSSelector& ps)
{
out.WriteLn("aem_fmt: {}", ps.aem_fmt);
@@ -1451,6 +1465,8 @@ 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("abe: {}", static_cast<u32>(ps.abe));
}
static void DumpVSSelector(DrawConfigWriter& out, const GSHWDrawConfig::VSSelector& vs)
+73 -6
View File
@@ -304,6 +304,7 @@ struct alignas(16) GSHWDrawConfig
using VSExpand = GSShader::VSExpand;
using PS_ATST = GSShader::PS_ATST;
using PS_AFAIL = GSShader::PS_AFAIL;
using PS_AA1 = GSShader::PS_AA1;
#pragma pack(push, 1)
struct VSSelector
{
@@ -315,8 +316,8 @@ struct alignas(16) GSHWDrawConfig
u8 tme : 1;
u8 iip : 1;
u8 point_size : 1; ///< Set when points need to be expanded without VS expanding.
VSExpand expand : 2;
u8 _free : 2;
VSExpand expand : 3;
u8 _free : 1;
};
u8 key;
};
@@ -324,7 +325,10 @@ struct alignas(16) GSHWDrawConfig
VSSelector(u8 k): key(k) {}
/// Returns true if the fixed index buffer should be used.
__fi bool UseExpandIndexBuffer() const { return (expand == VSExpand::Point || expand == VSExpand::Sprite); }
__fi bool UseFixedExpandIndexBuffer() const { return (expand == VSExpand::Point || expand == VSExpand::Sprite); }
/// Return true if the index buffer should be bound as a vertex shader resource.
__fi bool UseVSExpandIndexBuffer() const { return (expand == VSExpand::TriangleAA1); }
};
static_assert(sizeof(VSSelector) == 1, "VSSelector is a single byte");
@@ -415,6 +419,10 @@ struct alignas(16) GSHWDrawConfig
// Scan mask
u32 scanmsk : 2;
// AA1
PS_AA1 aa1 : 2; // Pixel shader AA1 primitive. Must be used in conjunction with VS AA1 expand.
u32 abe : 1; // Alpha blend enabled. Currently only used for emulating AA1/ABE interaction.
};
struct
@@ -441,7 +449,8 @@ struct alignas(16) GSHWDrawConfig
{
const bool afail_needs_depth = afail == PS_AFAIL::FB_ONLY || afail == PS_AFAIL::RGB_ONLY_SW_Z;
const bool ztst_needs_depth = ztst == ZTST_GEQUAL || ztst == ZTST_GREATER;
return afail_needs_depth || ztst_needs_depth;
const bool aa1_needs_depth = aa1 == PS_AA1::TRIANGLE_SW_Z;
return afail_needs_depth || ztst_needs_depth || aa1_needs_depth;
}
/// Disables color output from the pixel shader, this is done when all channels are masked.
@@ -459,6 +468,20 @@ struct alignas(16) GSHWDrawConfig
// disable both outputs.
no_color = no_color1 = 1;
}
/// Disables depth output from the pixel shader.
__fi void DisableDepthOutput()
{
if (afail == PS_AFAIL::RGB_ONLY_SW_Z)
{
afail = PS_AFAIL::RGB_ONLY;
}
if (aa1 == PS_AA1::TRIANGLE_SW_Z)
{
aa1 = PS_AA1::TRIANGLE;
}
}
};
static_assert(sizeof(PSSelector) == 12, "PSSelector is 12 bytes");
#pragma pack(pop)
@@ -618,6 +641,46 @@ struct alignas(16) GSHWDrawConfig
return true;
}
};
struct alignas(16) VSPushConstants
{
u32 base_vertex;
u32 base_index;
u32 _pad0;
u32 _pad1;
__fi VSPushConstants()
{
memset(static_cast<void*>(this), 0, sizeof(*this));
}
__fi VSPushConstants(const VSPushConstants& other)
{
memcpy(static_cast<void*>(this), static_cast<const void*>(&other), sizeof(*this));
}
__fi VSPushConstants& operator=(const VSPushConstants& other)
{
new (this) VSPushConstants(other);
return *this;
}
__fi bool operator==(const VSPushConstants& other) const
{
return BitEqual(*this, other);
}
__fi bool operator!=(const VSPushConstants& other) const
{
return !(*this == other);
}
__fi bool Update(const VSPushConstants& other)
{
if (*this == other)
return false;
memcpy(static_cast<void*>(this), static_cast<const void*>(&other), sizeof(*this));
return true;
}
};
static_assert(sizeof(VSPushConstants) == 16, "VSPushConstants wrong size");
struct alignas(16) PSConstantBuffer
{
GSVector4 FogColor_AREF;
@@ -776,8 +839,8 @@ struct alignas(16) GSHWDrawConfig
AlphaTestMode alpha_test;
DestinationAlphaMode destination_alpha;
SetDATM datm : 2;
bool line_expand : 1;
SetDATM datm;
bool line_expand;
struct AlphaPass
{
@@ -824,9 +887,12 @@ static inline u32 GetExpansionFactor(GSHWDrawConfig::VSExpand expand)
{
case GSHWDrawConfig::VSExpand::Point:
case GSHWDrawConfig::VSExpand::Line:
case GSHWDrawConfig::VSExpand::LineAA1:
return 4;
case GSHWDrawConfig::VSExpand::Sprite:
return 2;
case GSHWDrawConfig::VSExpand::TriangleAA1:
return 7;
default:
return 1;
}
@@ -889,6 +955,7 @@ public:
bool cas_sharpening : 1; ///< Supports sufficient functionality for contrast adaptive sharpening.
bool test_and_sample_depth: 1; ///< Supports concurrently binding the depth-stencil buffer for sampling and depth testing.
DepthFeedbackSupport depth_feedback : 2; ///< Support for depth feedback loops.
bool aa1 : 1; ///< Supports the GS AA1 feature.
FeatureSupport()
{
memset(this, 0, sizeof(*this));
+14 -4
View File
@@ -11,10 +11,12 @@ namespace GSShader {
enum class VSExpand : uint8_t
{
None = 0,
Point = 1,
Line = 2,
Sprite = 3,
None = 0,
Point = 1,
Line = 2,
Sprite = 3,
LineAA1 = 4,
TriangleAA1 = 5,
};
enum class PS_ATST : uint32_t
@@ -37,4 +39,12 @@ enum class PS_AFAIL : uint32_t
RGB_ONLY_SW_Z = 5, ///< RGB only with software Z discard
};
enum class PS_AA1 : uint32_t
{
NONE = 0, ///< No AA1
LINE = 1, ///< AA1 lines
TRIANGLE = 2, ///< AA1 triangles
TRIANGLE_SW_Z = 3, ///< AA1 triangles with software Z discard
};
} // namespace GSShader
+155 -11
View File
@@ -414,6 +414,30 @@ bool GSDevice11::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
}
}
if (m_features.aa1)
{
bd.ByteWidth = INDEX_BUFFER_SIZE;
bd.BindFlags = D3D11_BIND_SHADER_RESOURCE;
bd.StructureByteStride = sizeof(u32);
bd.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
if (FAILED(m_dev->CreateBuffer(&bd, nullptr, m_expand_ib_vs.put())))
{
Console.Error("D3D11: Failed to create vertex shader index buffer.");
return false;
}
const CD3D11_SHADER_RESOURCE_VIEW_DESC expand_ib_vs_srv_desc(
D3D11_SRV_DIMENSION_BUFFER, DXGI_FORMAT_UNKNOWN, 0, INDEX_BUFFER_SIZE / sizeof(u32));
if (FAILED(m_dev->CreateShaderResourceView(m_expand_ib_vs.get(), &expand_ib_vs_srv_desc, m_expand_ib_vs_srv.put())))
{
Console.Error("D3D11: Failed to create vertex shader index buffer SRV.");
return false;
}
m_ctx->VSSetShaderResources(5, 1, m_expand_ib_vs_srv.addressof());
}
// rasterizer
memset(&rd, 0, sizeof(rd));
@@ -467,6 +491,18 @@ bool GSDevice11::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
memset(&bd, 0, sizeof(bd));
bd.ByteWidth = sizeof(GSHWDrawConfig::VSConstantBuffer);
bd.Usage = D3D11_USAGE_DEFAULT;
bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
if (FAILED(m_dev->CreateBuffer(&bd, nullptr, m_vs_pc.put())))
{
Console.Error("D3D11: Failed to create vertex shader push constant buffer.");
return false;
}
memset(&bd, 0, sizeof(bd));
bd.ByteWidth = sizeof(GSHWDrawConfig::PSConstantBuffer);
bd.Usage = D3D11_USAGE_DEFAULT;
bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
@@ -477,6 +513,8 @@ bool GSDevice11::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
return false;
}
VSSetPushConstants(0, 0, true); // Avoid undefined data.
// create layout
{
@@ -563,6 +601,7 @@ void GSDevice11::Destroy()
m_vs.clear();
m_vs_cb.reset();
m_vs_pc.reset();
m_gs.clear();
m_ps.clear();
m_ps_cb.reset();
@@ -615,6 +654,7 @@ void GSDevice11::SetFeatures(IDXGIAdapter1* adapter)
m_features.vs_expand = (!GSConfig.DisableVertexShaderExpand && m_feature_level >= D3D_FEATURE_LEVEL_11_0);
m_features.cas_sharpening = (m_feature_level >= D3D_FEATURE_LEVEL_11_0);
m_features.test_and_sample_depth = (m_feature_level >= D3D_FEATURE_LEVEL_11_0);
m_features.aa1 = GSConfig.HWAA1 && m_features.vs_expand && (m_features.depth_feedback != GSDevice::DepthFeedbackSupport::None);
m_max_texture_size = (m_feature_level >= D3D_FEATURE_LEVEL_11_0) ?
D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION :
@@ -1153,6 +1193,24 @@ void GSDevice11::DrawIndexedPrimitive(int offset, int count)
m_ctx->DrawIndexed(count, m_index.start + offset, m_vertex.start);
}
void GSDevice11::DrawIndexedPrimitiveVSExpand(int offset, int count, bool vs_indexing, int vs_indexing_expansion)
{
pxAssert(offset + count <= (int)m_index.count);
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
PSUpdateShaderState(true, true);
if (vs_indexing)
{
VSSetPushConstants(m_vertex.start, m_index.start + offset);
m_ctx->Draw(count * vs_indexing_expansion, 0);
}
else
{
VSSetPushConstants(m_vertex.start);
m_ctx->DrawIndexed(count, m_index.start + offset, 0);
}
}
void GSDevice11::CommitClear(GSTexture* t)
{
GSTexture11* T = static_cast<GSTexture11*>(t);
@@ -1833,6 +1891,8 @@ void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstant
sm.AddMacro("PS_NO_COLOR", sel.no_color);
sm.AddMacro("PS_NO_COLOR1", sel.no_color1);
sm.AddMacro("PS_ZTST", sel.ztst);
sm.AddMacro("PS_AA1", static_cast<u32>(sel.aa1));
sm.AddMacro("PS_ABE", sel.abe);
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;
@@ -2303,12 +2363,7 @@ void GSDevice11::IAUnmapVertexBuffer(u32 stride, u32 count)
{
m_ctx->Unmap(m_vb.get(), 0);
if (m_state.vb_stride != stride)
{
m_state.vb_stride = stride;
const UINT vb_offset = 0;
m_ctx->IASetVertexBuffers(0, 1, m_vb.addressof(), &stride, &vb_offset);
}
IASetVertexBuffer(m_vb.get(), stride);
m_vertex.count = count;
}
@@ -2325,6 +2380,16 @@ bool GSDevice11::IASetVertexBuffer(const void* vertex, u32 stride, u32 count)
return true;
}
void GSDevice11::IASetVertexBuffer(ID3D11Buffer* buffer, u32 stride)
{
if (m_state.vb != buffer || stride != m_state.vb_stride)
{
const UINT zero = 0;
m_ctx->IASetVertexBuffers(0, 1, &buffer, buffer ? &stride : &zero, &zero);
m_state.vb = buffer;
}
}
bool GSDevice11::IASetExpandVertexBuffer(const void* vertex, u32 stride, u32 count)
{
const u32 size = stride * count;
@@ -2444,6 +2509,66 @@ void GSDevice11::VSSetShader(ID3D11VertexShader* vs, ID3D11Buffer* vs_cb)
}
}
void GSDevice11::VSSetPushConstants(u32 base_vertex, u32 base_index, bool force_update)
{
GSHWDrawConfig::VSPushConstants vs_pc;
vs_pc.base_vertex = base_vertex;
vs_pc.base_index = base_index;
if (m_vs_pc_cache.Update(vs_pc) || force_update)
{
m_ctx->UpdateSubresource(m_vs_pc.get(), 0, NULL, &vs_pc, 0, 0);
}
if (m_state.vs_pc != m_vs_pc.get())
{
m_state.vs_pc = m_vs_pc.get();
m_ctx->VSSetConstantBuffers(1, 1, &m_state.vs_pc);
}
}
u16* GSDevice11::VSMapIndexBuffer(u32 count)
{
if (count > (INDEX_BUFFER_SIZE / sizeof(u16)))
return nullptr;
D3D11_MAP type = D3D11_MAP_WRITE_NO_OVERWRITE;
m_index.start = m_expand_ib_vs_pos;
m_expand_ib_vs_pos += count;
if (m_expand_ib_vs_pos > (INDEX_BUFFER_SIZE / sizeof(u16)))
{
m_index.start = 0;
m_expand_ib_vs_pos = count;
type = D3D11_MAP_WRITE_DISCARD;
}
D3D11_MAPPED_SUBRESOURCE m;
if (FAILED(m_ctx->Map(m_expand_ib_vs.get(), 0, type, 0, &m)))
return nullptr;
return static_cast<u16*>(m.pData) + m_index.start;
}
void GSDevice11::VSUnmapIndexBuffer(u32 count)
{
m_ctx->Unmap(m_expand_ib_vs.get(), 0);
m_index.count = count;
}
bool GSDevice11::VSSetIndexBuffer(const void* index, u32 count)
{
u16* map = VSMapIndexBuffer(count);
if (!map)
return false;
std::memcpy(map, index, count * sizeof(u16));
VSUnmapIndexBuffer(count);
return true;
}
void GSDevice11::PSSetShaderResource(int i, GSTexture* sr)
{
// Update local state only, PSUpdateShaderState updates gpu state.
@@ -2755,8 +2880,6 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
Console.Error("D3D11: Failed to upload structured vertices (%u)", config.nverts);
return;
}
config.cb_vs.max_depth.y = m_vertex.start;
}
else
{
@@ -2767,12 +2890,17 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
}
}
if (config.vs.UseExpandIndexBuffer())
if (config.vs.UseFixedExpandIndexBuffer())
{
IASetIndexBuffer(m_expand_ib.get());
m_index.start = 0;
m_index.count = config.nindices;
}
else if (config.vs.UseVSExpandIndexBuffer())
{
VSSetIndexBuffer(config.indices, config.nindices);
IASetVertexBuffer(nullptr, 0); // Unbind the vertex buffer to prevent unwanted fetches.
}
else
{
if (!IASetIndexBuffer(config.indices, config.nindices))
@@ -2937,6 +3065,21 @@ 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 vs_expand = config.vs.expand != GSHWDrawConfig::VSExpand::None;
const bool vs_indexing = config.vs.UseVSExpandIndexBuffer();
const u32 vs_indexing_expansion = GetExpansionFactor(config.vs.expand);
auto Draw = [&](int offset, int count) {
if (vs_expand)
{
DrawIndexedPrimitiveVSExpand(offset, count, vs_indexing, vs_indexing_expansion);
}
else
{
DrawIndexedPrimitive(offset, count);
}
};
if (draw_rt_clone || draw_ds_clone)
{
#ifdef PCSX2_DEVBUILD
@@ -2977,7 +3120,8 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config,
const GSVector4i original_bbox = (*config.drawlist_bbox)[n].rintersect(config.drawarea);
CopyAndBind(ProcessCopyArea(rtsize, original_bbox));
DrawIndexedPrimitive(p, count);
Draw(p, count);
p += count;
}
@@ -2988,5 +3132,5 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config,
CopyAndBind(ProcessCopyArea(rtsize, config.drawarea));
}
DrawIndexedPrimitive();
Draw(0, m_index.count);
}
+14 -1
View File
@@ -125,12 +125,15 @@ private:
wil::com_ptr_nothrow<ID3D11Buffer> m_ib;
wil::com_ptr_nothrow<ID3D11Buffer> m_expand_vb;
wil::com_ptr_nothrow<ID3D11Buffer> m_expand_ib;
wil::com_ptr_nothrow<ID3D11Buffer> m_expand_ib_vs;
wil::com_ptr_nothrow<ID3D11ShaderResourceView> m_expand_vb_srv;
wil::com_ptr_nothrow<ID3D11ShaderResourceView> m_expand_ib_vs_srv;
D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_10_0;
u32 m_vb_pos = 0; // bytes
u32 m_ib_pos = 0; // indices/sizeof(u32)
u32 m_ib_pos = 0; // indices/sizeof(u16)
u32 m_structured_vb_pos = 0; // bytes
u32 m_expand_ib_vs_pos = 0; // indices/sizeof(u16)
bool m_allow_tearing_supported = false;
bool m_using_flip_model_swap_chain = true;
@@ -146,6 +149,7 @@ private:
ID3D11Buffer* index_buffer;
ID3D11VertexShader* vs;
ID3D11Buffer* vs_cb;
ID3D11Buffer* vs_pc;
std::array<ID3D11ShaderResourceView*, MAX_TEXTURES> ps_pending_srv;
std::array<ID3D11ShaderResourceView*, MAX_TEXTURES> ps_current_srv;
ID3D11PixelShader* ps;
@@ -154,6 +158,7 @@ private:
std::array<ID3D11SamplerState*, MAX_SAMPLERS> ps_current_ss;
GSVector2i viewport;
GSVector4i scissor;
ID3D11Buffer* vb;
u32 vb_stride;
ID3D11DepthStencilState* dss;
u8 sref;
@@ -246,6 +251,7 @@ private:
std::unordered_map<u32, GSVertexShader11> m_vs;
wil::com_ptr_nothrow<ID3D11Buffer> m_vs_cb;
wil::com_ptr_nothrow<ID3D11Buffer> m_vs_pc;
std::unordered_map<u32, wil::com_ptr_nothrow<ID3D11GeometryShader>> m_gs;
std::unordered_map<PSSelector, wil::com_ptr_nothrow<ID3D11PixelShader>, GSHWDrawConfig::PSSelectorHash> m_ps;
wil::com_ptr_nothrow<ID3D11Buffer> m_ps_cb;
@@ -256,6 +262,7 @@ private:
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
GSHWDrawConfig::VSPushConstants m_vs_pc_cache;
D3D11ShaderCache m_shader_cache;
std::string m_tfx_source;
@@ -297,6 +304,7 @@ public:
void DrawPrimitive();
void DrawIndexedPrimitive();
void DrawIndexedPrimitive(int offset, int count);
void DrawIndexedPrimitiveVSExpand(int offset, int count, bool vs_indexing = false, int vs_indexing_expansion = 1);
void PushDebugGroup(const char* fmt, ...) override;
void PopDebugGroup() override;
@@ -323,6 +331,7 @@ public:
void* IAMapVertexBuffer(u32 stride, u32 count);
void IAUnmapVertexBuffer(u32 stride, u32 count);
bool IASetVertexBuffer(const void* vertex, u32 stride, u32 count);
void IASetVertexBuffer(ID3D11Buffer* buffer, u32 stride);
bool IASetExpandVertexBuffer(const void* vertex, u32 stride, u32 count);
u16* IAMapIndexBuffer(u32 count);
@@ -334,6 +343,10 @@ public:
void IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY topology);
void VSSetShader(ID3D11VertexShader* vs, ID3D11Buffer* vs_cb);
void VSSetPushConstants(u32 base_vertex, u32 base_index = 0, bool force_update = false);
u16* VSMapIndexBuffer(u32 count);
void VSUnmapIndexBuffer(u32 count);
bool VSSetIndexBuffer(const void* indices, u32 count);
void PSSetShaderResource(int i, GSTexture* sr);
void PSSetShader(ID3D11PixelShader* ps, ID3D11Buffer* ps_cb);
+99 -24
View File
@@ -549,6 +549,7 @@ bool GSDevice12::ExecuteCommandList(WaitType wait_for_completion)
// Flush stream buffers to GPU memory
m_vertex_stream_buffer.FlushMemory();
m_index_stream_buffer.FlushMemory();
m_expand_index_stream_buffer.FlushMemory();
m_vertex_constant_buffer.FlushMemory();
m_pixel_constant_buffer.FlushMemory();
@@ -603,6 +604,9 @@ bool GSDevice12::ExecuteCommandList(WaitType wait_for_completion)
if (wait_for_completion != WaitType::None)
WaitForFence(res.ready_fence_value, wait_for_completion == WaitType::Spin);
// Push constants need to be refreshed each command list.
m_dirty_flags |= DIRTY_FLAG_VS_PUSH_CONSTANTS;
return true;
}
@@ -1410,6 +1414,8 @@ bool GSDevice12::CheckFeatures(const u32& vendor_id)
{
m_features.depth_feedback = GSDevice::DepthFeedbackSupport::None;
}
m_features.aa1 = GSConfig.HWAA1 && m_features.vs_expand && (m_features.depth_feedback != GSDevice::DepthFeedbackSupport::None);
m_features.dxt_textures = SupportsTextureFormat(DXGI_FORMAT_BC1_UNORM) &&
SupportsTextureFormat(DXGI_FORMAT_BC2_UNORM) &&
@@ -1458,6 +1464,21 @@ void GSDevice12::DrawPrimitive()
GetCommandList().list4->DrawInstanced(m_vertex.count, 1, m_vertex.start, 0);
}
void GSDevice12::DrawIndexedPrimitiveVSExpand(int offset, int count, bool vs_indexing, int vs_indexing_expansion)
{
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
if (vs_indexing)
{
SetVSPushConstants(m_vertex.start, m_index.start + offset);
GetCommandList().list4->DrawInstanced(count * vs_indexing_expansion, 1, 0, 0);
}
else
{
SetVSPushConstants(m_vertex.start);
GetCommandList().list4->DrawIndexedInstanced(count, 1, m_index.start + offset, 0, 0);
}
}
void GSDevice12::DrawIndexedPrimitive()
{
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
@@ -2383,22 +2404,32 @@ void GSDevice12::IASetVertexBuffer(const void* vertex, size_t stride, size_t cou
m_vertex_stream_buffer.CommitMemory(size);
}
void GSDevice12::IASetIndexBuffer(const void* index, size_t count)
void GSDevice12::UploadIndices(D3D12StreamBuffer& buffer, const void* index, size_t count)
{
const u32 size = sizeof(u16) * static_cast<u32>(count);
if (!m_index_stream_buffer.ReserveMemory(size, sizeof(u16)))
if (!buffer.ReserveMemory(size, sizeof(u16)))
{
ExecuteCommandListAndRestartRenderPass(false, "Uploading bytes to index buffer");
if (!m_index_stream_buffer.ReserveMemory(size, sizeof(u16)))
pxFailRel("Failed to reserve space for vertices");
if (!buffer.ReserveMemory(size, sizeof(u16)))
pxFailRel("Failed to reserve space for indices");
}
m_index.start = m_index_stream_buffer.GetCurrentOffset() / sizeof(u16);
m_index.start = buffer.GetCurrentOffset() / sizeof(u16);
m_index.count = count;
SetIndexBuffer(m_index_stream_buffer.GetGPUPointer(), m_index_stream_buffer.GetSize(), DXGI_FORMAT_R16_UINT);
std::memcpy(m_index_stream_buffer.GetCurrentHostPointer(), index, size);
m_index_stream_buffer.CommitMemory(size);
std::memcpy(buffer.GetCurrentHostPointer(), index, size);
buffer.CommitMemory(size);
}
void GSDevice12::IASetIndexBuffer(const void* index, size_t count)
{
UploadIndices(m_index_stream_buffer, index, count);
SetIndexBuffer(m_index_stream_buffer.GetGPUPointer(), m_index_stream_buffer.GetSize(), DXGI_FORMAT_R16_UINT);
}
void GSDevice12::VSSetIndexBuffer(const void* index, size_t count)
{
UploadIndices(m_expand_index_stream_buffer, index, count);
}
void GSDevice12::OMSetRenderTargets(GSTexture* rt, GSTexture* ds_as_rt, GSTexture* ds, const GSVector4i& scissor,
@@ -2591,6 +2622,12 @@ bool GSDevice12::CreateBuffers()
return false;
}
if (!m_expand_index_stream_buffer.Create(m_features.aa1 ? INDEX_BUFFER_SIZE : 4, false))
{
Host::ReportErrorAsync("GS", "Failed to allocate expansion index buffer (VS resource)");
return false;
}
if (!m_vertex_constant_buffer.Create(VERTEX_UNIFORM_BUFFER_SIZE, !m_uma))
{
Host::ReportErrorAsync("GS", "Failed to allocate vertex uniform buffer");
@@ -2641,9 +2678,11 @@ bool GSDevice12::CreateRootSignatures()
rsb.AddCBVParameter(0, D3D12_SHADER_VISIBILITY_ALL);
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_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
rsb.Add32BitConstants(2, sizeof(m_vs_pc_cache) / sizeof(u32), D3D12_SHADER_VISIBILITY_VERTEX);
if (!(m_tfx_root_signature = rsb.Create()))
return false;
D3D12::SetObjectName(m_tfx_root_signature.get(), "TFX root signature");
@@ -3037,6 +3076,7 @@ void GSDevice12::DestroyResources()
m_vertex_constant_buffer.Destroy(false);
m_index_stream_buffer.Destroy(false);
m_vertex_stream_buffer.Destroy(false);
m_expand_index_stream_buffer.Destroy(false);
m_utility_root_signature.reset();
m_tfx_root_signature.reset();
@@ -3157,6 +3197,8 @@ const ID3DBlob* GSDevice12::GetTFXPixelShader(const GSHWDrawConfig::PSSelector&
sm.AddMacro("PS_NO_COLOR", sel.no_color);
sm.AddMacro("PS_NO_COLOR1", sel.no_color1);
sm.AddMacro("PS_ZTST", sel.ztst);
sm.AddMacro("PS_AA1", static_cast<u32>(sel.aa1));
sm.AddMacro("PS_ABE", sel.abe);
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;
@@ -3393,7 +3435,7 @@ void GSDevice12::ExecuteCommandListForReadback()
void GSDevice12::InvalidateCachedState()
{
m_dirty_flags |= DIRTY_BASE_STATE | DIRTY_TFX_STATE | DIRTY_UTILITY_STATE | DIRTY_CONSTANT_BUFFER_STATE;
m_dirty_flags |= DIRTY_BASE_STATE | DIRTY_ROOT_PARAMS | DIRTY_TFX_STATE | DIRTY_UTILITY_STATE | DIRTY_CONSTANT_BUFFER_STATE;
m_current_root_signature = RootSignature::Undefined;
m_utility_texture_cpu.Clear();
m_utility_texture_gpu.Clear();
@@ -3786,7 +3828,7 @@ __ri void GSDevice12::ApplyBaseState(u32 flags, ID3D12GraphicsCommandList* cmdli
if (flags & DIRTY_FLAG_PRIMITIVE_TOPOLOGY)
cmdlist->IASetPrimitiveTopology(m_primitive_topology);
if (flags & DIRTY_FLAG_PIPELINE)
if ((flags & DIRTY_FLAG_PIPELINE) && m_current_pipeline)
cmdlist->SetPipelineState(const_cast<ID3D12PipelineState*>(m_current_pipeline));
if (flags & DIRTY_FLAG_VIEWPORT)
@@ -3918,9 +3960,7 @@ bool GSDevice12::ApplyTFXState(bool already_execed)
if (m_current_root_signature != RootSignature::TFX)
{
m_current_root_signature = RootSignature::TFX;
flags |= DIRTY_FLAG_VS_CONSTANT_BUFFER_BINDING | DIRTY_FLAG_PS_CONSTANT_BUFFER_BINDING |
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE | DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE |
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE_2 | DIRTY_FLAG_PIPELINE;
flags |= DIRTY_ROOT_PARAMS | DIRTY_FLAG_PIPELINE;
cmdlist->SetGraphicsRootSignature(m_tfx_root_signature.get());
}
@@ -3928,10 +3968,17 @@ bool GSDevice12::ApplyTFXState(bool already_execed)
cmdlist->SetGraphicsRootConstantBufferView(TFX_ROOT_SIGNATURE_PARAM_VS_CBV, m_tfx_constant_buffers[0]);
if (flags & DIRTY_FLAG_PS_CONSTANT_BUFFER_BINDING)
cmdlist->SetGraphicsRootConstantBufferView(TFX_ROOT_SIGNATURE_PARAM_PS_CBV, m_tfx_constant_buffers[1]);
if (m_features.vs_expand && (flags & DIRTY_FLAG_VS_PUSH_CONSTANTS))
SetVSPushConstants(m_vs_pc_cache.base_vertex, m_vs_pc_cache.base_index, true);
if (flags & DIRTY_FLAG_VS_VERTEX_BUFFER_BINDING)
{
cmdlist->SetGraphicsRootShaderResourceView(TFX_ROOT_SIGNATURE_PARAM_VS_SRV,
m_vertex_stream_buffer.GetGPUPointer() + m_vertex.start * sizeof(GSVertex));
cmdlist->SetGraphicsRootShaderResourceView(TFX_ROOT_SIGNATURE_PARAM_VS_VB_SRV,
m_vertex_stream_buffer.GetGPUPointer());
}
if (flags & DIRTY_FLAG_VS_INDEX_BUFFER_BINDING)
{
cmdlist->SetGraphicsRootShaderResourceView(TFX_ROOT_SIGNATURE_PARAM_VS_IB_SRV,
m_expand_index_stream_buffer.GetGPUPointer());
}
if (flags & DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE)
cmdlist->SetGraphicsRootDescriptorTable(TFX_ROOT_SIGNATURE_PARAM_PS_TEXTURES, m_tfx_textures_handle_gpu);
@@ -3982,6 +4029,19 @@ void GSDevice12::SetPSConstantBuffer(const GSHWDrawConfig::PSConstantBuffer& cb)
m_dirty_flags |= DIRTY_FLAG_PS_CONSTANT_BUFFER;
}
void GSDevice12::SetVSPushConstants(u32 base_vertex, u32 base_index, bool force_update)
{
GSHWDrawConfig::VSPushConstants pc;
pc.base_vertex = base_vertex;
pc.base_index = base_index;
if (m_vs_pc_cache.Update(pc) || force_update)
{
GetCommandList().list4->SetGraphicsRoot32BitConstants(
TFX_ROOT_SIGNATURE_PARAM_VS_PUSH_CONSTANTS, sizeof(m_vs_pc_cache) / sizeof(u32),
&m_vs_pc_cache, 0);
}
}
void GSDevice12::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSVector4i& bbox)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
@@ -4470,6 +4530,21 @@ void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig&
const int n_barriers = static_cast<int>(feedback_rt) + static_cast<int>(feedback_depth);
const bool vs_expand = config.vs.expand != GSHWDrawConfig::VSExpand::None;
const bool vs_indexing = config.vs.UseVSExpandIndexBuffer();
const u32 vs_indexing_expansion = GetExpansionFactor(config.vs.expand);
auto Draw = [&](int offset, int count) {
if (vs_expand)
{
DrawIndexedPrimitiveVSExpand(offset, count, vs_indexing, vs_indexing_expansion);
}
else
{
DrawIndexedPrimitive(offset, count);
}
};
if (feedback_rt || feedback_depth)
{
#ifdef PCSX2_DEVBUILD
@@ -4502,7 +4577,7 @@ void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig&
FeedbackBarrier(draw_ds);
if (BindDrawPipeline(pipe))
DrawIndexedPrimitive(p, count);
Draw(p, count);
p += count;
}
@@ -4514,14 +4589,14 @@ void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig&
g_perfmon.Put(GSPerfMon::Barriers, n_barriers);
if (feedback_rt)
FeedbackBarrier(draw_rt);
FeedbackBarrier(draw_rt);
if (feedback_depth)
FeedbackBarrier(draw_ds);
}
}
if (BindDrawPipeline(pipe))
DrawIndexedPrimitive();
Draw(0, m_index.count);
}
void GSDevice12::UpdateHWPipelineSelector(GSHWDrawConfig& config)
@@ -4542,17 +4617,17 @@ void GSDevice12::UpdateHWPipelineSelector(GSHWDrawConfig& config)
void GSDevice12::UploadHWDrawVerticesAndIndices(GSHWDrawConfig& config)
{
IASetVertexBuffer(config.verts, sizeof(GSVertex), config.nverts);
// Update SRV in root signature directly, rather than using a uniform for base vertex.
if (config.vs.expand != GSHWDrawConfig::VSExpand::None)
m_dirty_flags |= DIRTY_FLAG_VS_VERTEX_BUFFER_BINDING;
if (config.vs.UseExpandIndexBuffer())
if (config.vs.UseFixedExpandIndexBuffer())
{
m_index.start = 0;
m_index.count = config.nindices;
SetIndexBuffer(m_expand_index_buffer->GetGPUVirtualAddress(), EXPAND_BUFFER_SIZE, DXGI_FORMAT_R16_UINT);
}
else if (config.vs.UseVSExpandIndexBuffer())
{
VSSetIndexBuffer(config.indices, config.nindices);
}
else
{
IASetIndexBuffer(config.indices, config.nindices);
+36 -23
View File
@@ -148,6 +148,7 @@ public:
// Allocates a temporary CPU staging buffer, fires the callback with it to populate, then copies to a GPU buffer.
bool AllocatePreinitializedGPUBuffer(u32 size, ID3D12Resource** gpu_buffer, D3D12MA::Allocation** gpu_allocation,
const std::function<void(void*)>& fill_callback);
void UploadIndices(D3D12StreamBuffer& buffer, const void* index, size_t count);
private:
struct CommandListResources
@@ -298,10 +299,12 @@ public:
TFX_ROOT_SIGNATURE_PARAM_VS_CBV = 0,
TFX_ROOT_SIGNATURE_PARAM_PS_CBV = 1,
TFX_ROOT_SIGNATURE_PARAM_VS_SRV = 2,
TFX_ROOT_SIGNATURE_PARAM_PS_TEXTURES = 3,
TFX_ROOT_SIGNATURE_PARAM_PS_SAMPLERS = 4,
TFX_ROOT_SIGNATURE_PARAM_PS_RT_TEXTURES = 5,
TFX_ROOT_SIGNATURE_PARAM_VS_VB_SRV = 2,
TFX_ROOT_SIGNATURE_PARAM_VS_IB_SRV = 3,
TFX_ROOT_SIGNATURE_PARAM_PS_TEXTURES = 4,
TFX_ROOT_SIGNATURE_PARAM_PS_SAMPLERS = 5,
TFX_ROOT_SIGNATURE_PARAM_PS_RT_TEXTURES = 6,
TFX_ROOT_SIGNATURE_PARAM_VS_PUSH_CONSTANTS = 7,
UTILITY_ROOT_SIGNATURE_PARAM_PUSH_CONSTANTS = 0,
UTILITY_ROOT_SIGNATURE_PARAM_PS_TEXTURES = 1,
@@ -331,6 +334,7 @@ private:
D3D12StreamBuffer m_vertex_stream_buffer;
D3D12StreamBuffer m_index_stream_buffer;
D3D12StreamBuffer m_expand_index_stream_buffer;
D3D12StreamBuffer m_vertex_constant_buffer;
D3D12StreamBuffer m_pixel_constant_buffer;
D3D12StreamBuffer m_texture_stream_buffer;
@@ -365,6 +369,7 @@ private:
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
GSHWDrawConfig::VSPushConstants m_vs_pc_cache;
D3D12ShaderCache m_shader_cache;
ComPtr<ID3DBlob> m_convert_vs;
@@ -461,6 +466,7 @@ public:
void DrawPrimitive();
void DrawIndexedPrimitive();
void DrawIndexedPrimitive(int offset, int count);
void DrawIndexedPrimitiveVSExpand(int offset, int count, bool vs_indexing = false, int vs_indexing_expansion = 1);
std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) override;
@@ -489,6 +495,7 @@ public:
void IASetVertexBuffer(const void* vertex, size_t stride, size_t count);
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 PSSetSampler(GSHWDrawConfig::SamplerSelector sel);
@@ -498,6 +505,7 @@ public:
void SetVSConstantBuffer(const GSHWDrawConfig::VSConstantBuffer& cb);
void SetPSConstantBuffer(const GSHWDrawConfig::PSConstantBuffer& cb);
void SetVSPushConstants(u32 base_vertex, u32 base_index = 0, bool force_update = false);
bool BindDrawPipeline(const PipelineSelector& p);
void RenderHW(GSHWDrawConfig& config) override;
@@ -568,29 +576,34 @@ private:
DIRTY_FLAG_VS_CONSTANT_BUFFER_BINDING = (1 << 5),
DIRTY_FLAG_PS_CONSTANT_BUFFER_BINDING = (1 << 6),
DIRTY_FLAG_VS_VERTEX_BUFFER_BINDING = (1 << 7),
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE = (1 << 8),
DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE = (1 << 9),
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE_2 = (1 << 10),
DIRTY_FLAG_VS_INDEX_BUFFER_BINDING = (1 << 8),
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE = (1 << 9),
DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE = (1 << 10),
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE_2 = (1 << 11),
DIRTY_FLAG_VS_PUSH_CONSTANTS = (1 << 12),
DIRTY_FLAG_VERTEX_BUFFER = (1 << 11),
DIRTY_FLAG_INDEX_BUFFER = (1 << 12),
DIRTY_FLAG_PRIMITIVE_TOPOLOGY = (1 << 13),
DIRTY_FLAG_VIEWPORT = (1 << 14),
DIRTY_FLAG_SCISSOR = (1 << 15),
DIRTY_FLAG_RENDER_TARGET = (1 << 16),
DIRTY_FLAG_PIPELINE = (1 << 17),
DIRTY_FLAG_BLEND_CONSTANTS = (1 << 18),
DIRTY_FLAG_STENCIL_REF = (1 << 19),
DIRTY_FLAG_VERTEX_BUFFER = (1 << 13),
DIRTY_FLAG_INDEX_BUFFER = (1 << 14),
DIRTY_FLAG_PRIMITIVE_TOPOLOGY = (1 << 15),
DIRTY_FLAG_VIEWPORT = (1 << 16),
DIRTY_FLAG_SCISSOR = (1 << 17),
DIRTY_FLAG_RENDER_TARGET = (1 << 18),
DIRTY_FLAG_PIPELINE = (1 << 19),
DIRTY_FLAG_BLEND_CONSTANTS = (1 << 20),
DIRTY_FLAG_STENCIL_REF = (1 << 21),
DIRTY_BASE_STATE = DIRTY_FLAG_VS_CONSTANT_BUFFER_BINDING | DIRTY_FLAG_PS_CONSTANT_BUFFER_BINDING |
DIRTY_FLAG_VS_VERTEX_BUFFER_BINDING | DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE |
DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE | DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE_2 |
DIRTY_FLAG_VERTEX_BUFFER | DIRTY_FLAG_INDEX_BUFFER |
DIRTY_FLAG_PRIMITIVE_TOPOLOGY | DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR | DIRTY_FLAG_RENDER_TARGET |
DIRTY_FLAG_PIPELINE | DIRTY_FLAG_BLEND_CONSTANTS | DIRTY_FLAG_STENCIL_REF,
DIRTY_ROOT_PARAMS = DIRTY_FLAG_VS_CONSTANT_BUFFER_BINDING | DIRTY_FLAG_PS_CONSTANT_BUFFER_BINDING |
DIRTY_FLAG_VS_VERTEX_BUFFER_BINDING | DIRTY_FLAG_VS_INDEX_BUFFER_BINDING |
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE | DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE |
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE_2 | DIRTY_FLAG_VS_PUSH_CONSTANTS,
DIRTY_TFX_STATE = DIRTY_BASE_STATE | DIRTY_FLAG_TFX_TEXTURES | DIRTY_FLAG_TFX_SAMPLERS |
DIRTY_BASE_STATE = DIRTY_FLAG_VERTEX_BUFFER | DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_PRIMITIVE_TOPOLOGY |
DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR | DIRTY_FLAG_RENDER_TARGET | DIRTY_FLAG_PIPELINE |
DIRTY_FLAG_BLEND_CONSTANTS | DIRTY_FLAG_STENCIL_REF,
DIRTY_TFX_STATE = DIRTY_BASE_STATE | DIRTY_ROOT_PARAMS | DIRTY_FLAG_TFX_TEXTURES | DIRTY_FLAG_TFX_SAMPLERS |
DIRTY_FLAG_TFX_RT_TEXTURES,
DIRTY_UTILITY_STATE = DIRTY_BASE_STATE,
DIRTY_CONSTANT_BUFFER_STATE = DIRTY_FLAG_VS_CONSTANT_BUFFER | DIRTY_FLAG_PS_CONSTANT_BUFFER,
};
+104 -13
View File
@@ -2922,7 +2922,7 @@ void GSRendererHW::Draw()
// Need to fix the alpha test, since the alpha will be fixed to 1.0 if ABE is disabled and AA1 is enabled
// So if it doesn't meet the condition, always fail, if it does, always pass (turn off the test).
if (IsCoverageAlpha() && m_cached_ctx.TEST.ATE && m_cached_ctx.TEST.ATST > 1)
if (IsCoverageAlphaFixedOne() && m_cached_ctx.TEST.ATE && m_cached_ctx.TEST.ATST > 1)
{
const float aref = static_cast<float>(m_cached_ctx.TEST.AREF);
const int old_ATST = m_cached_ctx.TEST.ATST;
@@ -5419,7 +5419,20 @@ void GSRendererHW::SetupIA(float target_scale, float sx, float sy, bool req_vert
{
m_conf.topology = GSHWDrawConfig::Topology::Line;
m_conf.indices_per_prim = 2;
if (unscale_pt_ln)
if (PRIM->AA1 && features.aa1)
{
// AA1 expansion uses a similar path as upscale expansion but it is used
// for both upscaling and native resolution drawing.
GL_INS("HW: AA1 line expand.");
m_conf.vs.expand = GSHWDrawConfig::VSExpand::LineAA1;
m_conf.cb_vs.point_size = GSVector2(16.0f * sx, 16.0f * sy);
m_conf.topology = GSHWDrawConfig::Topology::Triangle;
m_conf.indices_per_prim = 6;
m_conf.ps.aa1 = GSHWDrawConfig::PS_AA1::LINE;
m_conf.ps.abe = PRIM->ABE != 0;
ExpandLineIndices();
}
else if (unscale_pt_ln)
{
if (features.line_expand)
{
@@ -5476,8 +5489,22 @@ void GSRendererHW::SetupIA(float target_scale, float sx, float sy, bool req_vert
case GS_TRIANGLE_CLASS:
{
m_conf.topology = GSHWDrawConfig::Topology::Triangle;
m_conf.indices_per_prim = 3;
if (PRIM->AA1 && features.aa1)
{
GL_INS("HW: AA1 triangle expand.");
m_conf.vs.expand = GSHWDrawConfig::VSExpand::TriangleAA1;
m_conf.cb_vs.point_size = GSVector2(16.0f * sx, 16.0f * sy);
m_conf.topology = GSHWDrawConfig::Topology::Triangle;
m_conf.indices_per_prim = 3;
m_conf.ps.aa1 = m_cached_ctx.DepthWrite() ? GSHWDrawConfig::PS_AA1::TRIANGLE_SW_Z :
GSHWDrawConfig::PS_AA1::TRIANGLE;
m_conf.ps.abe = PRIM->ABE != 0;
}
else
{
m_conf.topology = GSHWDrawConfig::Topology::Triangle;
m_conf.indices_per_prim = 3;
}
// See note above in GS_SPRITE_CLASS.
if (m_vt.m_accurate_stq && m_vt.m_eq.stq) [[unlikely]]
@@ -5765,6 +5792,46 @@ void GSRendererHW::DetermineAlphaScaling(GSTextureCache::Target* rt, GSTextureCa
}
}
void GSRendererHW::EmulateZbufferAA1()
{
const GSDevice::FeatureSupport& features = g_gs_device->Features();
pxAssert(!features.aa1 || features.depth_feedback != GSDevice::DepthFeedbackSupport::None);
if (IsCoverageAlphaSupported())
{
if (m_vt.m_primclass == GS_LINE_CLASS)
{
// AA1: Z is not written on lines since coverage is always less than 0x80.
m_conf.depth.zwe = false;
}
else if (m_vt.m_primclass == GS_TRIANGLE_CLASS)
{
// Force SW depth so that Z writes can be prevented for edge pixels.
if (m_cached_ctx.DepthWrite())
{
// Z test must be also done in the shader.
if (m_conf.depth.ztst == ZTST_GEQUAL || m_conf.depth.ztst == ZTST_GREATER)
{
m_conf.ps.ztst = m_conf.depth.ztst; // Enable shader Z test.
m_conf.depth.ztst = ZTST_ALWAYS; // Disable HW Z test.
// We need barriers for the feedback
if (features.texture_barrier || features.multidraw_fb_copy)
{
m_conf.require_one_barrier |= (m_prim_overlap == PRIM_OVERLAP_NO);
m_conf.require_full_barrier |= (m_prim_overlap != PRIM_OVERLAP_NO);
}
}
}
}
else
{
pxFail("Unsupported primclass for AA1"); // Impossible
}
}
}
bool GSRendererHW::EmulateDATEEarlyFail(DATEOptions& date, GSTextureCache::Target* rt)
{
if (!date.enabled)
@@ -5774,7 +5841,7 @@ bool GSRendererHW::EmulateDATEEarlyFail(DATEOptions& date, GSTextureCache::Targe
if (m_cached_ctx.TEST.DATM == 0)
{
// Some pixels are >= 1 so some fail, or some pixels get written but the written alpha matches or exceeds 1 (so overlap doesn't always pass).
date.enabled = rt->m_alpha_max >= 128 || (is_overlap_alpha && rt->m_alpha_min < 128 && (GetAlphaMinMax().max >= 128 || (m_context->FBA.FBA || IsCoverageAlpha())));
date.enabled = rt->m_alpha_max >= 128 || (is_overlap_alpha && rt->m_alpha_min < 128 && (GetAlphaMinMax().max >= 128 || (m_context->FBA.FBA || IsCoverageAlphaFixedOne())));
// All pixels fail.
if (date.enabled && rt->m_alpha_min >= 128)
@@ -5783,7 +5850,7 @@ bool GSRendererHW::EmulateDATEEarlyFail(DATEOptions& date, GSTextureCache::Targe
else
{
// Some pixels are < 1 so some fail, or some pixels get written but the written alpha goes below 1 (so overlap doesn't always pass).
date.enabled = rt->m_alpha_min < 128 || (is_overlap_alpha && rt->m_alpha_max >= 128 && (GetAlphaMinMax().min < 128 && !(m_context->FBA.FBA || IsCoverageAlpha())));
date.enabled = rt->m_alpha_min < 128 || (is_overlap_alpha && rt->m_alpha_max >= 128 && (GetAlphaMinMax().min < 128 && !(m_context->FBA.FBA || IsCoverageAlphaFixedOne())));
// All pixels fail.
if (date.enabled && rt->m_alpha_max < 128)
@@ -5823,6 +5890,13 @@ void GSRendererHW::EmulateDATESelectMethod(DATEOptions& date_options, GSTextureC
date_options.barrier = true;
m_conf.require_full_barrier = true;
}
else if (IsCoverageAlphaSupported())
{
// We're using AA1 for this draw so use only full barrier DATE, to avoid the complications
// with stencil/primid setup with AA1 vertex shaders. AA1 triangles usually require full barriers anyway.
date_options.barrier = true;
m_conf.require_full_barrier = true;
}
else if ((features.texture_barrier && m_prim_overlap == PRIM_OVERLAP_NO) || m_texture_shuffle)
{
GL_PERF("DATE: Accurate with %s", (features.texture_barrier && m_prim_overlap == PRIM_OVERLAP_NO) ? "no overlap" : "texture shuffle");
@@ -5834,7 +5908,7 @@ void GSRendererHW::EmulateDATESelectMethod(DATEOptions& date_options, GSTextureC
}
// When Blending is disabled and Edge Anti Aliasing is enabled,
// the output alpha is Coverage (which we force to 128) so DATE will fail/pass guaranteed on second pass.
else if (m_conf.colormask.wa && (m_context->FBA.FBA || IsCoverageAlpha()) && features.stencil_buffer)
else if (m_conf.colormask.wa && (m_context->FBA.FBA || IsCoverageAlphaFixedOne()) && features.stencil_buffer)
{
GL_PERF("DATE: Fast with FBA, all pixels will be >= 128");
date_options.stencil_one = !m_cached_ctx.TEST.DATM;
@@ -6593,15 +6667,13 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, DATEOptio
{
const GIFRegALPHA& ALPHA = m_context->ALPHA;
{
// AA1: Blending needs to be enabled on draw.
const bool AA1 = PRIM->AA1 && (m_vt.m_primclass == GS_LINE_CLASS || m_vt.m_primclass == GS_TRIANGLE_CLASS);
// PABE: Check condition early as an optimization, no blending when As < 128.
// For Cs*As + Cd*(1 - As) if As is 128 then blending can be disabled as well.
const bool PABE_skip = m_draw_env->PABE.PABE &&
((GetAlphaMinMax().max < 128) || (GetAlphaMinMax().max == 128 && ALPHA.A == 0 && ALPHA.B == 1 && ALPHA.C == 0 && ALPHA.D == 1));
// No blending or coverage anti-aliasing so early exit
if (PABE_skip || !(NeedsBlending() || AA1))
if (PABE_skip || !(NeedsBlending() || IsCoverageAlpha()))
{
m_conf.blend = {};
@@ -8568,11 +8640,21 @@ void GSRendererHW::EmulateAlphaTestSecondPass()
"Alpha second pass has no color/depth write.");
}
// Some housekeeping for the first pass.
if (!m_conf.depth.zwe)
{
m_conf.ps.DisableDepthOutput();
}
// Some housekeeping for the second pass.
if (m_conf.alpha_second_pass.colormask.wrgba == 0)
{
m_conf.alpha_second_pass.ps.DisableColorOutput();
}
if (!m_conf.alpha_second_pass.depth.zwe)
{
m_conf.alpha_second_pass.ps.DisableDepthOutput();
}
if (m_conf.alpha_second_pass.ps.IsFeedbackLoopRT() || m_conf.alpha_second_pass.ps.IsFeedbackLoopDepth())
{
m_conf.alpha_second_pass.require_one_barrier = m_conf.require_one_barrier;
@@ -8660,6 +8742,8 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
m_prim_overlap = PrimitiveOverlap(false);
EmulateZbufferAA1();
if (rt)
{
EmulateTextureShuffleAndFbmask(rt, tex);
@@ -8726,8 +8810,8 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
// Perform alpha test first pass setup here as bending depends on it.
EmulateAlphaTest(date_options);
// AA1: Set alpha source to coverage 128 when there is no alpha blending.
m_conf.ps.fixed_one_a = IsCoverageAlpha();
// AA1: Set alpha source to coverage 128 when AA1 is not supported.
m_conf.ps.fixed_one_a = IsCoverageAlphaFixedOne();
if ((!IsOpaque() || m_context->ALPHA.IsBlack()) && rt && ((m_conf.colormask.wrgba & 0x7) || (m_texture_shuffle && !m_texture_shuffle.real_16_bit_source && !m_texture_shuffle.SameGroupShuffle())))
{
@@ -10388,7 +10472,7 @@ void GSRendererHW::StartDepthAsRTFeedback()
// We should have depth output or feedback doesn't make sense.
// We will output to both the depth buffer and color clone simultaneously in the shader.
pxAssert(m_conf.depth.zwe);
pxAssert(m_conf.depth.zwe || (m_conf.alpha_second_pass.enable && m_conf.alpha_second_pass.depth.zwe));
// Disable HW depth test since we will be using SW depth test (if needed).
m_conf.depth.ztst = ZTST_ALWAYS;
@@ -10417,3 +10501,10 @@ void GSRendererHW::CleanupDepthAsRTFeedback()
m_conf.ds_as_rt = nullptr;
}
}
bool GSRendererHW::IsCoverageAlphaSupported()
{
return IsCoverageAlpha() &&
((m_vt.m_primclass == GS_LINE_CLASS || m_vt.m_primclass == GS_TRIANGLE_CLASS) &&
g_gs_device->Features().aa1);
}
+4
View File
@@ -226,6 +226,7 @@ private:
const TextureMinMaxResult& tmm);
void EmulateZbuffer(const GSTextureCache::Target* ds);
void EmulateZbufferAA1();
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();
@@ -399,6 +400,9 @@ public:
/// Compute the drawlist (if not already present) and bounding boxes for the current draw.
std::size_t ComputeDrawlistGetSize(float scale);
/// Does the current draw allow using AA1 coverage (if AA1 is enabled).
bool IsCoverageAlphaSupported() override;
/// Create a temporary color clone of depth for depth feedback (DX12 and GL only right now)
void StartDepthAsRTFeedback();
void CleanupDepthAsRTFeedback();
+1 -1
View File
@@ -202,7 +202,7 @@ public:
bool iip : 1;
bool fst : 1;
bool point_size : 1;
GSShader::VSExpand expand : 2;
GSShader::VSExpand expand : 3;
};
u8 key;
};
+2 -2
View File
@@ -2131,13 +2131,13 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
EndRenderPass(); // Barrier
size_t vertsize = config.nverts * sizeof(*config.verts);
size_t idxsize = config.vs.UseExpandIndexBuffer() ? 0 : (config.nindices * sizeof(*config.indices));
size_t idxsize = config.vs.UseFixedExpandIndexBuffer() ? 0 : (config.nindices * sizeof(*config.indices));
Map allocation = Allocate(m_vertex_upload_buf, vertsize + idxsize);
memcpy(allocation.cpu_buffer, config.verts, vertsize);
id<MTLBuffer> index_buffer;
size_t index_buffer_offset;
if (!config.vs.UseExpandIndexBuffer())
if (!config.vs.UseFixedExpandIndexBuffer())
{
memcpy(static_cast<u8*>(allocation.cpu_buffer) + vertsize, config.indices, idxsize);
index_buffer = allocation.gpu_buffer;
@@ -95,6 +95,7 @@ struct GSMTLMainVSUniform
vector_float2 texture_offset;
vector_float2 point_size;
uint max_depth;
uint _pad0;
};
struct GSMTLMainPSUniform
+124 -28
View File
@@ -22,6 +22,9 @@
#include <fstream>
#include <sstream>
static constexpr u32 g_vs_pc_index = 4;
static constexpr u32 g_vs_ib_index = 3;
static constexpr u32 g_vs_vb_index = 2;
static constexpr u32 g_vs_cb_index = 1;
static constexpr u32 g_ps_cb_index = 0;
@@ -29,6 +32,7 @@ static constexpr u32 VERTEX_BUFFER_SIZE = 32 * 1024 * 1024;
static constexpr u32 INDEX_BUFFER_SIZE = 16 * 1024 * 1024;
static constexpr u32 VERTEX_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024;
static constexpr u32 FRAGMENT_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024;
static constexpr u32 VERTEX_PUSH_CONSTANT_BUFFER_SIZE = 1024 * 1024;
static constexpr u32 TEXTURE_UPLOAD_BUFFER_SIZE = 128 * 1024 * 1024;
namespace ReplaceGL
@@ -326,10 +330,13 @@ bool GSDeviceOGL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
m_vertex_stream_buffer = GLStreamBuffer::Create(GL_ARRAY_BUFFER, VERTEX_BUFFER_SIZE);
m_index_stream_buffer = GLStreamBuffer::Create(GL_ELEMENT_ARRAY_BUFFER, INDEX_BUFFER_SIZE);
m_expand_index_stream_buffer = GLStreamBuffer::Create(GL_ARRAY_BUFFER, INDEX_BUFFER_SIZE);
m_vertex_uniform_stream_buffer = GLStreamBuffer::Create(GL_UNIFORM_BUFFER, VERTEX_UNIFORM_BUFFER_SIZE);
m_fragment_uniform_stream_buffer = GLStreamBuffer::Create(GL_UNIFORM_BUFFER, FRAGMENT_UNIFORM_BUFFER_SIZE);
m_vertex_push_constants_stream_buffer = GLStreamBuffer::Create(GL_UNIFORM_BUFFER, VERTEX_PUSH_CONSTANT_BUFFER_SIZE);
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &m_uniform_buffer_alignment);
if (!m_vertex_stream_buffer || !m_index_stream_buffer || !m_vertex_uniform_stream_buffer || !m_fragment_uniform_stream_buffer)
if (!m_vertex_stream_buffer || !m_index_stream_buffer || !m_expand_index_stream_buffer ||
!m_vertex_uniform_stream_buffer || !m_fragment_uniform_stream_buffer || !m_vertex_push_constants_stream_buffer)
{
Host::ReportErrorAsync("GS", "Failed to create vertex/index/uniform streaming buffers");
return false;
@@ -369,8 +376,16 @@ bool GSDeviceOGL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
glGenBuffers(1, &m_expand_ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_expand_ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, EXPAND_BUFFER_SIZE, expand_data.get(), GL_STATIC_DRAW);
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 2, m_vertex_stream_buffer->GetGLBufferId(), 0, VERTEX_BUFFER_SIZE);
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, g_vs_vb_index, m_vertex_stream_buffer->GetGLBufferId(), 0, VERTEX_BUFFER_SIZE);
}
if (m_features.aa1)
{
glGenVertexArrays(1, &m_dummy_vao);
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, g_vs_ib_index, m_expand_index_stream_buffer->GetGLBufferId(), 0, INDEX_BUFFER_SIZE);
}
VSSetPushConstants(0, 0, true); // Avoid undefined data.
}
// ****************************************************************
@@ -893,6 +908,8 @@ bool GSDeviceOGL::CheckFeatures()
{
Console.Warning("GLAD_GL_ARB_conservative_depth is not supported. This will reduce performance.");
}
m_features.aa1 = GSConfig.HWAA1 && m_features.vs_expand && (m_features.depth_feedback != GSDevice::DepthFeedbackSupport::None);
return true;
}
@@ -964,14 +981,18 @@ void GSDeviceOGL::DestroyResources()
m_fragment_uniform_stream_buffer.reset();
m_vertex_uniform_stream_buffer.reset();
m_vertex_push_constants_stream_buffer.reset();
glBindVertexArray(0);
if (m_expand_ibo != 0)
glDeleteVertexArrays(1, &m_expand_ibo);
if (m_vao != 0)
glDeleteVertexArrays(1, &m_vao);
if (m_dummy_vao != 0)
glDeleteVertexArrays(1, &m_dummy_vao);
m_index_stream_buffer.reset();
m_expand_index_stream_buffer.reset();
m_vertex_stream_buffer.reset();
m_texture_upload_buffer.reset();
if (m_expand_ibo)
@@ -1194,6 +1215,24 @@ void GSDeviceOGL::DrawIndexedPrimitive(int offset, int count)
static_cast<GLint>(m_vertex.start));
}
void GSDeviceOGL::DrawIndexedPrimitiveVSExpand(int offset, int count, bool vs_indexing, int vs_indexing_expansion)
{
//ASSERT(offset + count <= (int)m_index.count);
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
if (vs_indexing)
{
VSSetPushConstants(m_vertex.start, m_index.start + offset);
glDrawArrays(m_draw_topology, 0, count * vs_indexing_expansion);
}
else
{
VSSetPushConstants(m_vertex.start);
glDrawElementsBaseVertex(m_draw_topology, count, GL_UNSIGNED_SHORT,
reinterpret_cast<void*>((static_cast<u32>(m_index.start) + static_cast<u32>(offset)) * sizeof(u16)), 0);
}
}
void GSDeviceOGL::CommitClear(GSTexture* t, bool use_write_fbo)
{
GSTextureOGL* T = static_cast<GSTextureOGL*>(t);
@@ -1561,6 +1600,8 @@ std::string GSDeviceOGL::GetPSSource(const PSSelector& sel)
+ fmt::format("#define PS_NO_COLOR {}\n", sel.no_color)
+ fmt::format("#define PS_NO_COLOR1 {}\n", sel.no_color1)
+ 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)
;
std::string src = GenGlslHeader("ps_main", GL_FRAGMENT_SHADER, macro);
@@ -2118,6 +2159,40 @@ void GSDeviceOGL::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GS
DrawPrimitive();
}
__fi static void WriteToStreamBuffer(GLStreamBuffer* sb, u32 index, u32 align, const void* data, u32 size)
{
const auto res = sb->Map(align, size);
std::memcpy(res.pointer, data, size);
sb->Unmap(size);
glBindBufferRange(GL_UNIFORM_BUFFER, index, sb->GetGLBufferId(), res.buffer_offset, size);
}
void GSDeviceOGL::VSSetUniformBuffer(GSHWDrawConfig::VSConstantBuffer& cb)
{
WriteToStreamBuffer(m_vertex_uniform_stream_buffer.get(), g_vs_cb_index,
m_uniform_buffer_alignment, &cb, sizeof(cb));
}
void GSDeviceOGL::PSSetUniformBuffer(GSHWDrawConfig::PSConstantBuffer& cb)
{
WriteToStreamBuffer(m_fragment_uniform_stream_buffer.get(), g_ps_cb_index,
m_uniform_buffer_alignment, &cb, sizeof(cb));
}
void GSDeviceOGL::VSSetPushConstants(u32 base_vertex, u32 base_index, bool force_update)
{
GSHWDrawConfig::VSPushConstants vs_pc;
vs_pc.base_vertex = base_vertex;
vs_pc.base_index = base_index;
if (m_vs_pc_cache.Update(vs_pc) || force_update)
{
WriteToStreamBuffer(m_vertex_push_constants_stream_buffer.get(), g_vs_pc_index,
m_uniform_buffer_alignment, &vs_pc, sizeof(vs_pc));
}
}
void GSDeviceOGL::IASetVAO(GLuint vao)
{
if (GLState::vao == vao)
@@ -2137,14 +2212,24 @@ void GSDeviceOGL::IASetVertexBuffer(const void* vertices, size_t count, size_t a
m_vertex_stream_buffer->Unmap(size);
}
void GSDeviceOGL::IASetIndexBuffer(const void* index, size_t count)
void GSDeviceOGL::SetIndexBuffer(std::unique_ptr<GLStreamBuffer>& buffer, const void* index, size_t count)
{
const u32 size = static_cast<u32>(count) * sizeof(u16);
auto res = m_index_stream_buffer->Map(sizeof(u16), size);
auto res = buffer->Map(sizeof(u16), size);
m_index.start = res.index_aligned;
m_index.count = count;
std::memcpy(res.pointer, index, size);
m_index_stream_buffer->Unmap(size);
buffer->Unmap(size);
}
void GSDeviceOGL::IASetIndexBuffer(const void* index, size_t count)
{
SetIndexBuffer(m_index_stream_buffer, index, count);
}
void GSDeviceOGL::VSSetIndexBuffer(const void* index, size_t count)
{
SetIndexBuffer(m_expand_index_stream_buffer, index, count);
}
void GSDeviceOGL::IASetPrimitiveTopology(GLenum topology)
@@ -2565,15 +2650,6 @@ void GSDeviceOGL::SetScissor(const GSVector4i& scissor)
}
}
__fi static void WriteToStreamBuffer(GLStreamBuffer* sb, u32 index, u32 align, const void* data, u32 size)
{
const auto res = sb->Map(align, size);
std::memcpy(res.pointer, data, size);
sb->Unmap(size);
glBindBufferRange(GL_UNIFORM_BUFFER, index, sb->GetGLBufferId(), res.buffer_offset, size);
}
void GSDeviceOGL::SetupPipeline(const ProgramSelector& psel)
{
auto it = m_programs.find(psel);
@@ -2720,14 +2796,18 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
}
IASetVertexBuffer(config.verts, config.nverts, GetVertexAlignment(config.vs.expand));
m_vertex.start *= GetExpansionFactor(config.vs.expand);
if (config.vs.UseExpandIndexBuffer())
if (config.vs.UseFixedExpandIndexBuffer())
{
IASetVAO(m_expand_vao);
m_index.start = 0;
m_index.count = config.nindices;
}
else if (config.vs.UseVSExpandIndexBuffer())
{
IASetVAO(m_dummy_vao); // Unbind vertex buffer from IA to prevent unwanted fetches.
VSSetIndexBuffer(config.indices, config.nindices);
}
else
{
IASetVAO(m_vao);
@@ -2756,15 +2836,9 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
SetupSampler(config.sampler);
if (m_vs_cb_cache.Update(config.cb_vs))
{
WriteToStreamBuffer(m_vertex_uniform_stream_buffer.get(), g_vs_cb_index,
m_uniform_buffer_alignment, &config.cb_vs, sizeof(config.cb_vs));
}
VSSetUniformBuffer(m_vs_cb_cache);
if (m_ps_cb_cache.Update(config.cb_ps))
{
WriteToStreamBuffer(m_fragment_uniform_stream_buffer.get(), g_ps_cb_index,
m_uniform_buffer_alignment, &config.cb_ps, sizeof(config.cb_ps));
}
PSSetUniformBuffer(m_ps_cb_cache);
ProgramSelector psel;
psel.vs = config.vs;
@@ -2942,8 +3016,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
if (config.cb_ps.FogColor_AREF.a != config.alpha_second_pass.ps_aref)
{
config.cb_ps.FogColor_AREF.a = config.alpha_second_pass.ps_aref;
WriteToStreamBuffer(m_fragment_uniform_stream_buffer.get(), g_ps_cb_index,
m_uniform_buffer_alignment, &config.cb_ps, sizeof(config.cb_ps));
PSSetUniformBuffer(config.cb_ps);
}
psel.ps = config.alpha_second_pass.ps;
@@ -2986,6 +3059,27 @@ 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 vs_expand = config.vs.expand != GSHWDrawConfig::VSExpand::None;
const bool vs_indexing = config.vs.UseVSExpandIndexBuffer();
const u32 vs_indexing_expansion = GetExpansionFactor(config.vs.expand);
auto Draw = [&](int offset, int count) {
if (vs_expand)
{
DrawIndexedPrimitiveVSExpand(offset, count, vs_indexing, vs_indexing_expansion);
}
else
{
DrawIndexedPrimitive(offset, count);
}
};
if (!m_features.texture_barrier) [[unlikely]]
{
Draw(0, m_index.count);
return;
}
#ifdef PCSX2_DEVBUILD
if ((one_barrier || full_barrier) && !(config.ps.IsFeedbackLoopRT() || config.ps.IsFeedbackLoopDepth())) [[unlikely]]
Console.Warning("OpenGL: Possible unnecessary barrier detected.");
@@ -3026,14 +3120,16 @@ void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config,
const u32 count = (*config.drawlist)[n] * indices_per_prim;
if (m_features.texture_barrier)
{
glTextureBarrier();
}
else
{
const GSVector4i original_bbox = (*config.drawlist_bbox)[n].rintersect(config.drawarea);
CopyAndBind(ProcessCopyArea(rtsize, original_bbox));
}
DrawIndexedPrimitive(p, count);
Draw(p, count);
p += count;
}
@@ -3054,7 +3150,7 @@ void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config,
}
}
DrawIndexedPrimitive();
Draw(0, m_index.count);
}
// Note: used as a callback of DebugMessageCallback. Don't change the signature
+12
View File
@@ -157,13 +157,16 @@ private:
std::unique_ptr<GLStreamBuffer> m_vertex_stream_buffer;
std::unique_ptr<GLStreamBuffer> m_index_stream_buffer;
std::unique_ptr<GLStreamBuffer> m_expand_index_stream_buffer;
GLuint m_expand_ibo = 0;
GLuint m_vao = 0;
GLuint m_expand_vao = 0;
GLuint m_dummy_vao = 0;
GLenum m_draw_topology = 0;
std::unique_ptr<GLStreamBuffer> m_vertex_uniform_stream_buffer;
std::unique_ptr<GLStreamBuffer> m_fragment_uniform_stream_buffer;
std::unique_ptr<GLStreamBuffer> m_vertex_push_constants_stream_buffer;
GLint m_uniform_buffer_alignment = 0;
struct
@@ -233,6 +236,7 @@ private:
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
GSHWDrawConfig::VSPushConstants m_vs_pc_cache;
std::string m_shader_tfx_vgs;
std::string m_shader_tfx_fs;
@@ -272,6 +276,8 @@ private:
void DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2i& ds);
void SetIndexBuffer(std::unique_ptr<GLStreamBuffer>& buffer, const void* index, size_t count);
protected:
virtual void DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
GSHWDrawConfig::ColorMaskSelector cms, ShaderConvert shader, bool linear) override;
@@ -315,6 +321,7 @@ public:
void DrawPrimitive();
void DrawIndexedPrimitive();
void DrawIndexedPrimitive(int offset, int count);
void DrawIndexedPrimitiveVSExpand(int offset, int count, bool vs_indexing = false, int vs_indexing_expansion = 1);
std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) override;
@@ -345,10 +352,15 @@ public:
const bool one_barrier, const bool full_barrier);
void SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSVector4i& bbox);
void VSSetUniformBuffer(GSHWDrawConfig::VSConstantBuffer& cb);
void PSSetUniformBuffer(GSHWDrawConfig::PSConstantBuffer& cb);
void VSSetPushConstants(u32 base_vertex, u32 base_index = 0, bool force_update = false);
void IASetVAO(GLuint vao);
void IASetPrimitiveTopology(GLenum topology);
void IASetVertexBuffer(const void* vertices, size_t count, size_t align_multiplier = 1);
void IASetIndexBuffer(const void* index, size_t count);
void VSSetIndexBuffer(const void* index, size_t count);
void PSSetShaderResource(int i, GSTexture* sr);
void PSSetSamplerState(GLuint ss);
+1
View File
@@ -82,6 +82,7 @@ protected:
template <u32 primclass>
void RewriteVerticesIfSTOverflow();
bool IsCoverageAlphaSupported() override { return true; }
public:
GSRendererSW(int threads);
~GSRendererSW() override;
+102 -14
View File
@@ -953,7 +953,7 @@ bool GSDeviceVK::CreateGlobalDescriptorPool()
{
static constexpr const VkDescriptorPoolSize pool_sizes[] = {
{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 2},
{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 2},
{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3},
};
VkDescriptorPoolCreateInfo pool_create_info = {VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, nullptr,
@@ -1421,6 +1421,9 @@ void GSDeviceVK::ExecuteCommandBuffer(WaitType wait_for_completion)
}
WaitForCommandBufferCompletion(current_frame);
}
// Push constants need to be refreshed each command buffer.
m_dirty_flags |= DIRTY_FLAG_VS_PUSH_CONSTANTS;
}
void GSDeviceVK::DeferBufferDestruction(VkBuffer object, VmaAllocation allocation)
@@ -2709,6 +2712,9 @@ bool GSDeviceVK::CheckFeatures()
m_features.depth_feedback = GSDevice::DepthFeedbackSupport::None;
}
m_features.aa1 = GSConfig.HWAA1 && m_features.vs_expand && (m_features.depth_feedback != GSDevice::DepthFeedbackSupport::None);
DevCon.WriteLn("Optional features:%s%s%s%s%s", m_features.primitive_id ? " primitive_id" : "",
m_features.texture_barrier ? " texture_barrier" : "", m_features.framebuffer_fetch ? " framebuffer_fetch" : "",
m_features.provoking_vertex_last ? " provoking_vertex_last" : "", m_features.vs_expand ? " vs_expand" : "");
@@ -2772,6 +2778,23 @@ void GSDeviceVK::DrawIndexedPrimitive(int offset, int count)
vkCmdDrawIndexed(GetCurrentCommandBuffer(), count, 1, m_index.start + offset, m_vertex.start, 0);
}
void GSDeviceVK::DrawIndexedPrimitiveVSExpand(int offset, int count, bool vs_indexing, int vs_indexing_expansion)
{
pxAssert(offset + count <= (int)m_index.count);
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
if (vs_indexing)
{
SetVSPushConstants(m_vertex.start, m_index.start + offset);
vkCmdDraw(GetCurrentCommandBuffer(), count * vs_indexing_expansion, 1, 0, 0);
}
else
{
SetVSPushConstants(m_vertex.start);
vkCmdDrawIndexed(GetCurrentCommandBuffer(), count, 1, m_index.start + offset, 0, 0);
}
}
VkFormat GSDeviceVK::LookupNativeFormat(GSTexture::Format format) const
{
static constexpr std::array<VkFormat, static_cast<int>(GSTexture::Format::Last) + 1> s_format_mapping = {{
@@ -3427,25 +3450,35 @@ void GSDeviceVK::IASetVertexBuffer(const void* vertex, size_t stride, size_t cou
m_vertex_stream_buffer.CommitMemory(size);
}
void GSDeviceVK::IASetIndexBuffer(const void* index, size_t count)
void GSDeviceVK::UploadIndices(VKStreamBuffer& buffer, const void* index, size_t count)
{
const u32 size = sizeof(u16) * static_cast<u32>(count);
if (!m_index_stream_buffer.ReserveMemory(size, sizeof(u16)))
if (!buffer.ReserveMemory(size, sizeof(u16)))
{
ExecuteCommandBufferAndRestartRenderPass(false, "Uploading bytes to index buffer");
if (!m_index_stream_buffer.ReserveMemory(size, sizeof(u16)))
if (!buffer.ReserveMemory(size, sizeof(u16)))
pxFailRel("Failed to reserve space for vertices");
}
m_index.start = m_index_stream_buffer.GetCurrentOffset() / sizeof(u16);
m_index.start = buffer.GetCurrentOffset() / sizeof(u16);
m_index.count = count;
std::memcpy(m_index_stream_buffer.GetCurrentHostPointer(), index, size);
m_index_stream_buffer.CommitMemory(size);
std::memcpy(buffer.GetCurrentHostPointer(), index, size);
buffer.CommitMemory(size);
}
void GSDeviceVK::IASetIndexBuffer(const void* index, size_t count)
{
UploadIndices(m_index_stream_buffer, index, count);
SetIndexBuffer(m_index_stream_buffer.GetBuffer());
}
void GSDeviceVK::VSSetIndexBuffer(const void* index, size_t count)
{
UploadIndices(m_expand_index_stream_buffer, index, count);
}
void GSDeviceVK::OMSetRenderTargets(
GSTexture* rt, GSTexture* ds, const GSVector4i& scissor, FeedbackLoopFlag feedback_loop)
{
@@ -3775,6 +3808,12 @@ bool GSDeviceVK::CreateBuffers()
return false;
}
if (!m_expand_index_stream_buffer.Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, m_features.aa1 ? INDEX_BUFFER_SIZE : 4))
{
Host::ReportErrorAsync("GS", "Failed to allocate expansion index buffer (VS resource)");
return false;
}
if (!m_vertex_uniform_stream_buffer.Create(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VERTEX_UNIFORM_BUFFER_SIZE))
{
Host::ReportErrorAsync("GS", "Failed to allocate vertex uniform buffer");
@@ -3833,7 +3872,12 @@ bool GSDeviceVK::CreatePipelineLayouts()
0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT);
dslb.AddBinding(1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
if (m_features.vs_expand)
{
dslb.AddBinding(2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT);
plb.AddPushConstants(VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(GSHWDrawConfig::VSPushConstants));
}
if (m_features.aa1)
dslb.AddBinding(3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT);
if ((m_tfx_ubo_ds_layout = dslb.Create(dev)) == VK_NULL_HANDLE)
return false;
Vulkan::SetObjectName(dev, m_tfx_ubo_ds_layout, "TFX UBO descriptor layout");
@@ -4713,6 +4757,7 @@ void GSDeviceVK::DestroyResources()
m_vertex_uniform_stream_buffer.Destroy(false);
m_index_stream_buffer.Destroy(false);
m_vertex_stream_buffer.Destroy(false);
m_expand_index_stream_buffer.Destroy(false);
if (m_expand_index_buffer != VK_NULL_HANDLE)
vmaDestroyBuffer(m_allocator, m_expand_index_buffer, m_expand_index_buffer_allocation);
@@ -4855,6 +4900,9 @@ VkShaderModule GSDeviceVK::GetTFXFragmentShader(const GSHWDrawConfig::PSSelector
AddMacro(ss, "PS_NO_COLOR", sel.no_color);
AddMacro(ss, "PS_NO_COLOR1", sel.no_color1);
AddMacro(ss, "PS_ZTST", sel.ztst);
AddMacro(ss, "PS_AA1", static_cast<u32>(sel.aa1));
AddMacro(ss, "PS_ABE", sel.abe);
ss << m_tfx_source;
VkShaderModule mod = g_vulkan_shader_cache->GetFragmentShader(ss.str());
@@ -5056,6 +5104,11 @@ bool GSDeviceVK::CreatePersistentDescriptorSets()
dsub.AddBufferDescriptorWrite(m_tfx_ubo_descriptor_set, 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
m_vertex_stream_buffer.GetBuffer(), 0, VERTEX_BUFFER_SIZE);
}
if (m_features.aa1)
{
dsub.AddBufferDescriptorWrite(m_tfx_ubo_descriptor_set, 3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
m_expand_index_stream_buffer.GetBuffer(), 0, INDEX_BUFFER_SIZE);
}
dsub.Update(dev);
Vulkan::SetObjectName(dev, m_tfx_ubo_descriptor_set, "Persistent TFX UBO set");
return true;
@@ -5434,7 +5487,7 @@ bool GSDeviceVK::ApplyTFXState(bool already_execed)
if (flags & DIRTY_FLAG_VS_CONSTANT_BUFFER)
{
if (!m_vertex_uniform_stream_buffer.ReserveMemory(
sizeof(m_vs_cb_cache), static_cast<u32>(m_device_properties.limits.minUniformBufferOffsetAlignment)))
sizeof(m_vs_cb_cache), static_cast<u32>(m_device_properties.limits.minUniformBufferOffsetAlignment)))
{
if (already_execed)
{
@@ -5477,7 +5530,7 @@ bool GSDeviceVK::ApplyTFXState(bool already_execed)
if (m_current_pipeline_layout != PipelineLayout::TFX)
{
m_current_pipeline_layout = PipelineLayout::TFX;
flags |= DIRTY_FLAG_TFX_UBO | DIRTY_FLAG_TFX_TEXTURES;
flags |= DIRTY_FLAG_TFX_UBO | DIRTY_FLAG_TFX_TEXTURES | DIRTY_FLAG_VS_PUSH_CONSTANTS;
// Clear out the RT/DS binding if feedback loop isn't on, because it'll be in the wrong state and make
// the validation layer cranky. Not a big deal since we need to write it anyway.
@@ -5497,6 +5550,9 @@ bool GSDeviceVK::ApplyTFXState(bool already_execed)
&m_tfx_ubo_descriptor_set, NUM_TFX_DYNAMIC_OFFSETS, m_tfx_dynamic_offsets.data());
}
if (m_features.vs_expand && (flags & DIRTY_FLAG_VS_PUSH_CONSTANTS))
SetVSPushConstants(m_vs_pc_cache.base_vertex, m_vs_pc_cache.base_index, true);
if (flags & DIRTY_FLAG_TFX_TEXTURES)
{
if (flags & DIRTY_FLAG_TFX_TEXTURE_TEX)
@@ -5585,6 +5641,19 @@ void GSDeviceVK::SetPSConstantBuffer(const GSHWDrawConfig::PSConstantBuffer& cb)
m_dirty_flags |= DIRTY_FLAG_PS_CONSTANT_BUFFER;
}
void GSDeviceVK::SetVSPushConstants(u32 base_vertex, u32 base_index, bool force_update)
{
GSHWDrawConfig::VSPushConstants pc;
pc.base_vertex = base_vertex;
pc.base_index = base_index;
if (m_vs_pc_cache.Update(pc) || force_update)
{
vkCmdPushConstants(GetCurrentCommandBuffer(), m_tfx_pipeline_layout,
VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(pc), &pc);
}
}
void GSDeviceVK::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSVector4i& bbox)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
@@ -6154,14 +6223,18 @@ void GSDeviceVK::UpdateHWPipelineSelector(GSHWDrawConfig& config, PipelineSelect
void GSDeviceVK::UploadHWDrawVerticesAndIndices(GSHWDrawConfig& config)
{
IASetVertexBuffer(config.verts, sizeof(GSVertex), config.nverts, GetVertexAlignment(config.vs.expand));
m_vertex.start *= GetExpansionFactor(config.vs.expand);
if (config.vs.UseExpandIndexBuffer())
if (config.vs.UseFixedExpandIndexBuffer())
{
m_index.start = 0;
m_index.count = config.nindices;
SetIndexBuffer(m_expand_index_buffer);
}
else if (config.vs.UseVSExpandIndexBuffer())
{
VSSetIndexBuffer(config.indices, config.nindices);
SetVSConstantBuffer(config.cb_vs);
}
else
{
IASetIndexBuffer(config.indices, config.nindices);
@@ -6200,9 +6273,24 @@ VkDependencyFlags GSDeviceVK::GetFeedbackBarrierDependencyFlags() const
void GSDeviceVK::SendHWDraw(const GSHWDrawConfig& config, GSTextureVK* draw_rt, GSTextureVK* draw_ds,
bool one_barrier, bool full_barrier)
{
const bool vs_expand = config.vs.expand != GSHWDrawConfig::VSExpand::None;
const bool vs_indexing = config.vs.UseVSExpandIndexBuffer();
const u32 vs_indexing_expansion = GetExpansionFactor(config.vs.expand);
auto Draw = [&](int offset, int count) {
if (vs_expand)
{
DrawIndexedPrimitiveVSExpand(offset, count, vs_indexing, vs_indexing_expansion);
}
else
{
DrawIndexedPrimitive(offset, count);
}
};
if (!m_features.texture_barrier) [[unlikely]]
{
DrawIndexedPrimitive();
Draw(0, m_index.count);
return;
}
@@ -6258,7 +6346,7 @@ void GSDeviceVK::SendHWDraw(const GSHWDrawConfig& config, GSTextureVK* draw_rt,
IssueBarriers();
const u32 count = (*config.drawlist)[n] * indices_per_prim;
DrawIndexedPrimitive(p, count);
Draw(p, count);
p += count;
}
@@ -6271,5 +6359,5 @@ void GSDeviceVK::SendHWDraw(const GSHWDrawConfig& config, GSTextureVK* draw_rt,
IssueBarriers();
}
DrawIndexedPrimitive();
Draw(0, m_index.count);
}
+10 -1
View File
@@ -158,6 +158,9 @@ private:
// Allocates a temporary CPU staging buffer, fires the callback with it to populate, then copies to a GPU buffer.
bool AllocatePreinitializedGPUBuffer(u32 size, VkBuffer* gpu_buffer, VmaAllocation* gpu_allocation,
VkBufferUsageFlags gpu_usage, const std::function<void(void*)>& fill_callback);
// Helper function for uploading indices.
void UploadIndices(VKStreamBuffer& buffer, const void* index, size_t count);
union RenderPassCacheKey
{
@@ -383,6 +386,7 @@ private:
VKStreamBuffer m_vertex_stream_buffer;
VKStreamBuffer m_index_stream_buffer;
VKStreamBuffer m_expand_index_stream_buffer;
VKStreamBuffer m_vertex_uniform_stream_buffer;
VKStreamBuffer m_fragment_uniform_stream_buffer;
VKStreamBuffer m_texture_stream_buffer;
@@ -429,6 +433,7 @@ private:
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
GSHWDrawConfig::VSPushConstants m_vs_pc_cache;
std::string m_tfx_source;
@@ -531,6 +536,7 @@ public:
void DrawPrimitive();
void DrawIndexedPrimitive();
void DrawIndexedPrimitive(int offset, int count);
void DrawIndexedPrimitiveVSExpand(int offset, int count, bool vs_indexing = false, int vs_indexing_expansion = 1);
std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) override;
@@ -562,6 +568,7 @@ public:
void IASetVertexBuffer(const void* vertex, size_t stride, size_t count, size_t align_multiplier = 1);
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 PSSetSampler(GSHWDrawConfig::SamplerSelector sel);
@@ -571,6 +578,7 @@ public:
void SetVSConstantBuffer(const GSHWDrawConfig::VSConstantBuffer& cb);
void SetPSConstantBuffer(const GSHWDrawConfig::PSConstantBuffer& cb);
void SetVSPushConstants(u32 base_vertex, u32 base_index = 0, bool force_update = false);
bool BindDrawPipeline(const PipelineSelector& p);
void RenderHW(GSHWDrawConfig& config) override;
@@ -641,6 +649,7 @@ private:
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_TEX = (DIRTY_FLAG_TFX_TEXTURE_0 << 0),
DIRTY_FLAG_TFX_TEXTURE_PALETTE = (DIRTY_FLAG_TFX_TEXTURE_0 << 1),
@@ -656,7 +665,7 @@ private:
DIRTY_FLAG_BLEND_CONSTANTS | DIRTY_FLAG_LINE_WIDTH,
DIRTY_TFX_STATE = DIRTY_BASE_STATE | DIRTY_FLAG_TFX_TEXTURES,
DIRTY_UTILITY_STATE = DIRTY_BASE_STATE | DIRTY_FLAG_UTILITY_TEXTURE,
DIRTY_CONSTANT_BUFFER_STATE = DIRTY_FLAG_VS_CONSTANT_BUFFER | DIRTY_FLAG_PS_CONSTANT_BUFFER,
DIRTY_CONSTANT_BUFFER_STATE = DIRTY_FLAG_VS_CONSTANT_BUFFER | DIRTY_FLAG_PS_CONSTANT_BUFFER | DIRTY_FLAG_VS_PUSH_CONSTANTS,
ALL_DIRTY_STATE = DIRTY_BASE_STATE | DIRTY_TFX_STATE | DIRTY_UTILITY_STATE | DIRTY_CONSTANT_BUFFER_STATE,
};
+3
View File
@@ -753,6 +753,7 @@ Pcsx2Config::GSOptions::GSOptions()
Mipmap = true;
HWMipmap = true;
HWAccurateAlphaTest = false;
HWAA1 = false;
ManualUserHacks = false;
UserHacks_AlignSpriteX = false;
@@ -908,6 +909,7 @@ bool Pcsx2Config::GSOptions::RestartOptionsAreEqual(const GSOptions& right) cons
OpEqu(DisableVertexShaderExpand) &&
OpEqu(OverrideTextureBarriers) &&
OpEqu(DepthFeedbackMode) &&
OpEqu(HWAA1) &&
OpEqu(ExclusiveFullscreenControl);
}
@@ -1038,6 +1040,7 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
SettingsWrapBitBoolEx(HWMipmap, "hw_mipmap");
SettingsWrapBitBool(HWAccurateAlphaTest);
SettingsWrapBitBool(HWAA1);
SettingsWrapIntEnumEx(AccurateBlendingUnit, "accurate_blending_unit");
SettingsWrapIntEnumEx(TextureFiltering, "filter");
SettingsWrapIntEnumEx(TexturePreloading, "texture_preloading");
+1 -1
View File
@@ -3,4 +3,4 @@
/// Version number for GS and other shaders. Increment whenever any of the contents of the
/// shaders change, to invalidate the cache.
static constexpr u32 SHADER_CACHE_VERSION = 85; // Last changed in PR 14195
static constexpr u32 SHADER_CACHE_VERSION = 86; // Last changed in PR 13681