GS:MTL: AA1 Support

This commit is contained in:
TellowKrinkle
2026-04-26 13:06:57 +02:00
committed by lightningterror
parent 226b4d25dc
commit 83da1b6479
4 changed files with 179 additions and 47 deletions
+4 -2
View File
@@ -265,7 +265,7 @@ public:
MRCOwned<id<MTLRenderPipelineState>> m_shadeboost_pipeline;
MRCOwned<id<MTLRenderPipelineState>> m_imgui_pipeline;
MRCOwned<id<MTLFunction>> m_hw_vs[1 << 5];
MRCOwned<id<MTLFunction>> m_hw_vs[6 << 3];
std::unordered_map<PSSelector, MRCOwned<id<MTLFunction>>> m_hw_ps;
std::unordered_map<PipelineSelectorMTL, MRCOwned<id<MTLRenderPipelineState>>> m_hw_pipeline;
@@ -290,7 +290,8 @@ public:
GSTexture* depth_target = nullptr;
GSTexture* stencil_target = nullptr;
GSTexture* tex[GSMTLTextureIndexCount] = {};
void* vertex_buffer = nullptr;
id<MTLBuffer> vertex_buffer = nullptr;
id<MTLBuffer> vs_index_buffer = nullptr;
void* name = nullptr;
struct Has
{
@@ -430,6 +431,7 @@ public:
void MRESetSampler(SamplerSelector sel);
void MRESetTexture(GSTexture* tex, int pos);
void MRESetVertices(id<MTLBuffer> buffer, size_t offset);
void MRESetVSIndices(id<MTLBuffer> buffer, size_t offset);
void MRESetScissor(const GSVector4i& scissor);
void MREClearScissor();
void MRESetCB(const GSHWDrawConfig::VSConstantBuffer& cb_vs);
+58 -33
View File
@@ -971,6 +971,7 @@ bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
m_features.cas_sharpening = true;
m_features.test_and_sample_depth = true;
m_features.depth_feedback = getDepthFeedback(m_dev, m_features.framebuffer_fetch);
m_features.aa1 = GSConfig.HWAA1 && m_features.vs_expand;
m_max_texture_size = m_dev.features.max_texsize;
// Init metal stuff
@@ -1115,13 +1116,8 @@ bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
setFnConstantB(m_fn_constants, sel.fst, GSMTLConstantIndex_FST);
setFnConstantB(m_fn_constants, sel.iip, GSMTLConstantIndex_IIP);
setFnConstantB(m_fn_constants, sel.point_size, GSMTLConstantIndex_VS_POINT_SIZE);
NSString* shader = @"vs_main";
if (sel.expand != GSShader::VSExpand::None)
{
setFnConstantI(m_fn_constants, sel.expand, GSMTLConstantIndex_VS_EXPAND_TYPE);
shader = @"vs_main_expand";
}
m_hw_vs[i] = LoadShader(shader);
setFnConstantI(m_fn_constants, sel.expand, GSMTLConstantIndex_VS_EXPAND_TYPE);
m_hw_vs[i] = LoadShader(sel.expand == GSShader::VSExpand::None ? @"vs_main" : @"vs_main_expand");
}
// Init pipelines
@@ -1993,6 +1989,8 @@ void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDr
setFnConstantB(m_fn_constants, pssel.manual_lod, GSMTLConstantIndex_PS_MANUAL_LOD);
setFnConstantB(m_fn_constants, pssel.region_rect, GSMTLConstantIndex_PS_REGION_RECT);
setFnConstantI(m_fn_constants, pssel.scanmsk, GSMTLConstantIndex_PS_SCANMSK);
setFnConstantI(m_fn_constants, pssel.aa1, GSMTLConstantIndex_PS_AA1);
setFnConstantB(m_fn_constants, pssel.abe, GSMTLConstantIndex_PS_ABE);
setFnConstantI(m_fn_constants, pssel.sw_aniso, GSMTLConstantIndex_PS_SW_ANISO);
auto newps = LoadShader(@"ps_main");
ps = newps;
@@ -2086,9 +2084,9 @@ void GSDeviceMTL::MRESetTexture(GSTexture* tex, int pos)
void GSDeviceMTL::MRESetVertices(id<MTLBuffer> buffer, size_t offset)
{
if (m_current_render.vertex_buffer != (__bridge void*)buffer)
if (m_current_render.vertex_buffer != buffer)
{
m_current_render.vertex_buffer = (__bridge void*)buffer;
m_current_render.vertex_buffer = buffer;
[m_current_render.encoder setVertexBuffer:buffer offset:offset atIndex:GSMTLBufferIndexHWVertices];
}
else
@@ -2097,6 +2095,19 @@ void GSDeviceMTL::MRESetVertices(id<MTLBuffer> buffer, size_t offset)
}
}
void GSDeviceMTL::MRESetVSIndices(id<MTLBuffer> buffer, size_t offset)
{
if (m_current_render.vs_index_buffer != buffer)
{
m_current_render.vs_index_buffer = buffer;
[m_current_render.encoder setVertexBuffer:buffer offset:offset atIndex:GSMTLBufferIndexHWIndices];
}
else
{
[m_current_render.encoder setVertexBufferOffset:offset atIndex:GSMTLBufferIndexHWIndices];
}
}
void GSDeviceMTL::MRESetScissor(const GSVector4i& scissor)
{
if (m_current_render.has.scissor && (m_current_render.scissor == scissor).alltrue())
@@ -2236,6 +2247,8 @@ void GSDeviceMTL::MREInitHWDraw(GSHWDrawConfig& config, const Map& verts)
MRESetCB(config.cb_vs);
MRESetCB(config.cb_ps);
MRESetVertices(verts.gpu_buffer, verts.gpu_offset);
if (config.vs.UseVSExpandIndexBuffer())
MRESetVSIndices(verts.gpu_buffer, verts.gpu_offset + config.nverts * sizeof(*config.verts));
}
void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
@@ -2248,18 +2261,27 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
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;
id<MTLBuffer> index_buffer = nil;
size_t index_buffer_offset = 0;
if (!config.vs.UseFixedExpandIndexBuffer())
{
memcpy(static_cast<u8*>(allocation.cpu_buffer) + vertsize, config.indices, idxsize);
index_buffer = allocation.gpu_buffer;
index_buffer_offset = allocation.gpu_offset + vertsize;
if (config.vs.UseVSExpandIndexBuffer())
{
// VS expand index buffer is bound to the VS instead of the input assembler
u32 expand = GetExpansionFactor(config.vs.expand);
config.nindices *= expand;
config.indices_per_prim *= expand;
}
else
{
index_buffer = allocation.gpu_buffer;
index_buffer_offset = allocation.gpu_offset + vertsize;
}
}
else
{
index_buffer = m_expand_index_buffer;
index_buffer_offset = 0;
}
FlushClears(config.tex);
@@ -2434,6 +2456,24 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
Recycle(primid_tex);
}}
static void EncodeDraw(id<MTLRenderCommandEncoder> enc, MTLPrimitiveType topology, size_t count, id<MTLBuffer> indices, size_t off, size_t base_vertex)
{
if (indices)
{
[enc drawIndexedPrimitives:topology
indexCount:count
indexType:MTLIndexTypeUInt16
indexBuffer:indices
indexBufferOffset:off + base_vertex * sizeof(uint16_t)];
}
else
{
[enc drawPrimitives:topology
vertexStart:base_vertex
vertexCount:count];
}
}
void GSDeviceMTL::SendHWDraw(GSHWDrawConfig& config, id<MTLRenderCommandEncoder> enc, id<MTLBuffer> buffer, size_t off, bool one_barrier, bool full_barrier)
{
MTLPrimitiveType topology;
@@ -2446,14 +2486,8 @@ void GSDeviceMTL::SendHWDraw(GSHWDrawConfig& config, id<MTLRenderCommandEncoder>
if (!m_features.texture_barrier) [[unlikely]]
{
[enc drawIndexedPrimitives:topology
indexCount:config.nindices
indexType:MTLIndexTypeUInt16
indexBuffer:buffer
indexBufferOffset:off];
EncodeDraw(enc, topology, config.nindices, buffer, off, 0);
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
return;
}
@@ -2486,13 +2520,9 @@ void GSDeviceMTL::SendHWDraw(GSHWDrawConfig& config, id<MTLRenderCommandEncoder>
for (u32 n = 0, p = 0; n < draw_list_size; n++)
{
const u32 count = (*config.drawlist)[n] * indices_per_prim;
const size_t count = (*config.drawlist)[n] * indices_per_prim;
textureBarrier(enc);
[enc drawIndexedPrimitives:topology
indexCount:count
indexType:MTLIndexTypeUInt16
indexBuffer:buffer
indexBufferOffset:off + p * sizeof(*config.indices)];
EncodeDraw(enc, topology, count, buffer, off, p);
p += count;
}
@@ -2506,12 +2536,7 @@ void GSDeviceMTL::SendHWDraw(GSHWDrawConfig& config, id<MTLRenderCommandEncoder>
g_perfmon.Put(GSPerfMon::Barriers, 1);
}
[enc drawIndexedPrimitives:topology
indexCount:config.nindices
indexType:MTLIndexTypeUInt16
indexBuffer:buffer
indexBufferOffset:off];
EncodeDraw(enc, topology, config.nindices, buffer, off, 0);
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
}
@@ -11,6 +11,7 @@ enum GSMTLBufferIndices
GSMTLBufferIndexUniforms,
GSMTLBufferIndexHWVertices,
GSMTLBufferIndexHWUniforms,
GSMTLBufferIndexHWIndices,
};
enum GSMTLTextureIndex
@@ -216,5 +217,7 @@ enum GSMTLFnConstants
GSMTLConstantIndex_PS_MANUAL_LOD,
GSMTLConstantIndex_PS_REGION_RECT,
GSMTLConstantIndex_PS_SCANMSK,
GSMTLConstantIndex_PS_AA1,
GSMTLConstantIndex_PS_ABE,
GSMTLConstantIndex_PS_SW_ANISO,
};
+114 -12
View File
@@ -72,16 +72,20 @@ constant bool PS_AUTOMATIC_LOD [[function_constant(GSMTLConstantIndex_PS_AU
constant bool PS_MANUAL_LOD [[function_constant(GSMTLConstantIndex_PS_MANUAL_LOD)]];
constant bool PS_REGION_RECT [[function_constant(GSMTLConstantIndex_PS_REGION_RECT)]];
constant uint PS_SCANMSK [[function_constant(GSMTLConstantIndex_PS_SCANMSK)]];
constant uint PS_AA1_RAW [[function_constant(GSMTLConstantIndex_PS_AA1)]];
constant bool PS_ABE [[function_constant(GSMTLConstantIndex_PS_ABE)]];
constant uint PS_SW_ANISO [[function_constant(GSMTLConstantIndex_PS_SW_ANISO)]];
using GSShader::VSExpand;
using AFAIL = GSShader::PS_AFAIL;
using ATST = GSShader::PS_ATST;
using GSShader::ZTST;
using AA1 = GSShader::PS_AA1;
constant VSExpand VS_EXPAND_TYPE = static_cast<VSExpand>(VS_EXPAND_TYPE_RAW);
constant AFAIL PS_AFAIL = static_cast<AFAIL>(PS_AFAIL_RAW);
constant ATST PS_ATST = static_cast<ATST>(PS_ATST_RAW);
constant ZTST PS_ZTST = static_cast<ZTST>(PS_ZTST_RAW);
constant AA1 PS_AA1 = static_cast<AA1>(PS_AA1_RAW);
#if defined(__METAL_MACOS__) && __METAL_VERSION__ >= 220
#define PRIMID_SUPPORT 1
@@ -112,7 +116,8 @@ constant bool NEEDS_RT_FOR_AFAIL = PS_AFAIL == AFAIL::ZB_ONLY || PS_AFAIL == AFA
constant bool NEEDS_RT = NEEDS_RT_FOR_AFAIL || NEEDS_RT_EARLY || (!PS_PRIM_CHECKING_INIT && (PS_FBMASK || NEEDS_RT_FOR_BLEND));
constant bool NEEDS_DEPTH_FOR_AFAIL = PS_AFAIL == AFAIL::FB_ONLY || PS_AFAIL == AFAIL::RGB_ONLY_SW_Z;
constant bool NEEDS_DEPTH_FOR_ZTST = PS_ZTST == ZTST::GEQUAL || PS_ZTST == ZTST::GREATER;
constant bool SW_DEPTH = NEEDS_DEPTH_FOR_AFAIL || NEEDS_DEPTH_FOR_ZTST;
constant bool NEEDS_DEPTH_FOR_AA1 = PS_AA1 == AA1::TRIANGLE_SW_Z;
constant bool SW_DEPTH = NEEDS_DEPTH_FOR_AFAIL || NEEDS_DEPTH_FOR_ZTST || NEEDS_DEPTH_FOR_AA1;
constant bool PS_COLOR0 = !PS_NO_COLOR;
constant bool PS_COLOR1 = !PS_NO_COLOR1;
@@ -120,6 +125,11 @@ constant bool PS_ZOUTPUT = PS_ZCLAMP || PS_ZFLOOR || SW_DEPTH;
constant bool PS_ZOUTPUT_LESS = PS_ZOUTPUT && !SW_DEPTH;
constant bool PS_ZOUTPUT_ANY = PS_ZOUTPUT && SW_DEPTH;
constant bool PS_ZOUTPUT_COLOR = PS_ZOUTPUT_ANY && !DEPTH_FEEDBACK;
constant bool VS_NEEDS_INDEX_BUFFER = VS_EXPAND_TYPE == VSExpand::TriangleAA1;
constant bool VS_COVERAGE = VS_EXPAND_TYPE == VSExpand::LineAA1 || VS_EXPAND_TYPE == VSExpand::TriangleAA1;
constant bool VS_INTERIOR = VS_EXPAND_TYPE == VSExpand::TriangleAA1;
constant bool PS_COVERAGE = PS_AA1 != AA1::NONE;
constant bool PS_INTERIOR = PS_AA1 == AA1::TRIANGLE_SW_Z;
struct MainVSIn
{
@@ -139,6 +149,8 @@ struct MainVSOut
float4 ti;
float4 c [[function_constant(IIP)]];
float4 fc [[flat, function_constant(NOT_IIP)]];
float inv_cov [[function_constant(VS_COVERAGE)]];
uint interior [[function_constant(VS_INTERIOR)]];
float point_size [[point_size, function_constant(VS_POINT_SIZE)]];
};
@@ -149,6 +161,8 @@ struct MainPSIn
float4 ti;
float4 c [[function_constant(IIP)]];
float4 fc [[flat, function_constant(NOT_IIP)]];
float inv_cov [[function_constant(PS_COVERAGE)]];
uint interior [[function_constant(PS_INTERIOR)]];
};
struct MainPSOut
@@ -242,7 +256,8 @@ static MainVSIn load_vertex(GSMTLMainVertex base)
vertex MainVSOut vs_main_expand(
uint vid [[vertex_id]],
device const GSMTLMainVertex* vertices [[buffer(GSMTLBufferIndexHWVertices)]],
constant GSMTLMainVSUniform& cb [[buffer(GSMTLBufferIndexHWUniforms)]])
constant GSMTLMainVSUniform& cb [[buffer(GSMTLBufferIndexHWUniforms)]],
device const ushort* indices [[buffer(GSMTLBufferIndexHWIndices), function_constant(VS_NEEDS_INDEX_BUFFER)]])
{
switch (VS_EXPAND_TYPE)
{
@@ -258,6 +273,7 @@ vertex MainVSOut vs_main_expand(
return point;
}
case VSExpand::Line:
case VSExpand::LineAA1:
{
uint vid_base = vid >> 2;
bool is_bottom = vid & 2;
@@ -266,13 +282,27 @@ vertex MainVSOut vs_main_expand(
MainVSOut point = vs_main_run(load_vertex(vertices[vid_base]), cb);
MainVSOut other = vs_main_run(load_vertex(vertices[vid_other]), cb);
float2 line_vector = normalize(point.p.xy - other.p.xy);
float2 line_normal = float2(line_vector.y, -line_vector.x);
float2 line_width = (line_normal * cb.point_size) / 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 ? point.p.xy - other.p.xy : other.p.xy - point.p.xy;
float2 line_expand;
if (VS_EXPAND_TYPE == VSExpand::Line)
{
float2 line_vector = normalize(line_delta);
line_expand = float2(line_vector.y, -line_vector.x);
}
else
{
// Expand in y direction for shallow lines and x direction for steep lines.
line_delta /= cb.vertex_scale;
line_expand = abs(line_delta.x) >= abs(line_delta.y) ? float2(0, 2) : float2(2, 0);
}
float2 line_width = (line_expand * cb.point_size) / 2;
float2 offset = is_right ? line_width : -line_width;
point.p.xy += offset;
if (VS_EXPAND_TYPE == VSExpand::LineAA1)
point.inv_cov = is_right ? 1.f : -1.f;
// 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
@@ -306,6 +336,68 @@ vertex MainVSOut vs_main_expand(
out.ti.yw = lt.ti.yw;
}
return out;
}
case VSExpand::TriangleAA1:
{
// 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;
uint i0 = interior ? prim_offset : (prim_offset - 3) / 6;
uint i1 = (i0 >= 2) ? i0 - 2 : i0 + 1;
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
MainVSOut out = vs_main_run(load_vertex(vertices[indices[3 * prim_id + i0]]), cb);
MainVSOut other = vs_main_run(load_vertex(vertices[indices[3 * prim_id + i1]]), cb);
MainVSOut opposite = vs_main_run(load_vertex(vertices[indices[3 * prim_id + i2]]), cb);
if (interior)
{
out.inv_cov = 0.f;
out.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 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;
// 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 = out.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, 2) : float2(2, 0);
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(opposite.p.xy - out.p.xy, line_normal) >= 0.0f))
{
// Expand direction point towards the interior so flip it.
line_expand = -line_expand;
}
float2 line_width = (line_expand * cb.point_size) / 2;
if (is_bottom)
out = other;
if (is_outside)
{
out.p.xy += line_width;
out.inv_cov = 1.0f; // No coverage
}
else
{
out.inv_cov = 0.0f; // Full coverage
}
out.interior = 0;
}
return out;
}
}
@@ -1249,18 +1341,25 @@ struct PSMain
}
float4 C = ps_color();
bool atst_pass = atst(C);
if (PS_AFAIL == GSShader::PS_AFAIL::KEEP && !atst_pass)
discard_fragment();
// Must be done before alpha correction
// AA (Fixed one) will output a coverage of 1.0 as alpha
if (PS_FIXED_ONE_A)
if (PS_AA1 != AA1::NONE)
{
float cov = saturate(1.f - abs(in.inv_cov));
if (!PS_ABE || floor(C.a) == 128.f) // The coverage is only used if the fragment alpha is 128.
C.a = 128.f * cov;
}
else if (PS_FIXED_ONE_A)
{
// AA (Fixed one) will output a coverage of 1.0 as alpha
C.a = 128.0f;
}
bool atst_pass = atst(C);
if (PS_AFAIL == AFAIL::KEEP && !atst_pass)
discard_fragment();
float4 alpha_blend = float4(0.f);
if (SW_AD_TO_HW)
{
@@ -1379,6 +1478,9 @@ struct PSMain
if (PS_ZCLAMP)
input_z = min(input_z, cb.max_depth);
if (PS_AA1 == AA1::TRIANGLE_SW_Z && !in.interior)
input_z = current_depth; // No depth update for triangle edges.
if (!atst_pass)
{
if (PS_AFAIL == AFAIL::RGB_ONLY_SW_Z || PS_AFAIL == AFAIL::RGB_ONLY)