mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-07-11 01:34:17 +02:00
GS/HW: Support HW AA1 for lines/triangles.
Credits: concept by TellowKrinkle.
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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> "
|
||||
|
||||
@@ -792,6 +792,7 @@ struct Pcsx2Config
|
||||
Mipmap : 1,
|
||||
HWMipmap : 1,
|
||||
HWAccurateAlphaTest: 1,
|
||||
HWAA1 : 1,
|
||||
ManualUserHacks : 1,
|
||||
UserHacks_AlignSpriteX : 1,
|
||||
UserHacks_CPUFBConversion : 1,
|
||||
|
||||
+33
-12
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -82,6 +82,7 @@ protected:
|
||||
template <u32 primclass>
|
||||
void RewriteVerticesIfSTOverflow();
|
||||
|
||||
bool IsCoverageAlphaSupported() override { return true; }
|
||||
public:
|
||||
GSRendererSW(int threads);
|
||||
~GSRendererSW() override;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user