diff --git a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.h b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.h index 56dcf40212..bd163a641d 100644 --- a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.h +++ b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.h @@ -265,7 +265,7 @@ public: MRCOwned> m_shadeboost_pipeline; MRCOwned> m_imgui_pipeline; - MRCOwned> m_hw_vs[1 << 5]; + MRCOwned> m_hw_vs[6 << 3]; std::unordered_map>> m_hw_ps; std::unordered_map>> m_hw_pipeline; @@ -290,7 +290,8 @@ public: GSTexture* depth_target = nullptr; GSTexture* stencil_target = nullptr; GSTexture* tex[GSMTLTextureIndexCount] = {}; - void* vertex_buffer = nullptr; + id vertex_buffer = nullptr; + id 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 buffer, size_t offset); + void MRESetVSIndices(id buffer, size_t offset); void MRESetScissor(const GSVector4i& scissor); void MREClearScissor(); void MRESetCB(const GSHWDrawConfig::VSConstantBuffer& cb_vs); diff --git a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm index 2e098173ca..69d20ec258 100644 --- a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm +++ b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm @@ -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 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 buffer, size_t offset) } } +void GSDeviceMTL::MRESetVSIndices(id 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 index_buffer; - size_t index_buffer_offset; + id index_buffer = nil; + size_t index_buffer_offset = 0; if (!config.vs.UseFixedExpandIndexBuffer()) { memcpy(static_cast(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 enc, MTLPrimitiveType topology, size_t count, id 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 enc, id buffer, size_t off, bool one_barrier, bool full_barrier) { MTLPrimitiveType topology; @@ -2446,14 +2486,8 @@ void GSDeviceMTL::SendHWDraw(GSHWDrawConfig& config, id 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 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 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); } diff --git a/pcsx2/GS/Renderers/Metal/GSMTLSharedHeader.h b/pcsx2/GS/Renderers/Metal/GSMTLSharedHeader.h index 2f4c43510a..5b23952513 100644 --- a/pcsx2/GS/Renderers/Metal/GSMTLSharedHeader.h +++ b/pcsx2/GS/Renderers/Metal/GSMTLSharedHeader.h @@ -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, }; diff --git a/pcsx2/GS/Renderers/Metal/tfx.metal b/pcsx2/GS/Renderers/Metal/tfx.metal index 32a48f98fd..9851a15e27 100644 --- a/pcsx2/GS/Renderers/Metal/tfx.metal +++ b/pcsx2/GS/Renderers/Metal/tfx.metal @@ -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(VS_EXPAND_TYPE_RAW); constant AFAIL PS_AFAIL = static_cast(PS_AFAIL_RAW); constant ATST PS_ATST = static_cast(PS_ATST_RAW); constant ZTST PS_ZTST = static_cast(PS_ZTST_RAW); +constant AA1 PS_AA1 = static_cast(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)