From 3fececc3baaab17a440a7be4f9d5a624df7abb43 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Tue, 28 Jan 2025 19:23:47 +0100 Subject: [PATCH] support line strip as vertex output with geometry shaders --- .../LatteDecompilerEmitMSL.cpp | 6 +++- .../LatteDecompilerEmitMSLHeader.hpp | 25 +++------------ .../HW/Latte/Renderer/Metal/MetalCommon.h | 31 +++++++++++++++++++ .../HW/Latte/Renderer/Metal/MetalRenderer.cpp | 27 +++++----------- 4 files changed, 48 insertions(+), 41 deletions(-) diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSL.cpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSL.cpp index 1c731127..a2b0bc2d 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSL.cpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSL.cpp @@ -4140,7 +4140,11 @@ void LatteDecompiler_emitMSLShader(LatteDecompilerShaderContext* shaderContext, if (usesGeometryShader) { // Calculate the imaginary vertex id - src->add("uint vid = tig * VERTICES_PER_VERTEX_PRIMITIVE + tid;" _CRLF); + LattePrimitiveMode vsOutPrimType = shaderContext->contextRegistersNew->VGT_PRIMITIVE_TYPE.get_PRIMITIVE_MODE(); + if (PrimitiveRequiresConnection(vsOutPrimType)) + src->add("uint vid = tig + tid;" _CRLF); + else + src->add("uint vid = tig * VERTICES_PER_VERTEX_PRIMITIVE + tid;" _CRLF); src->add("uint iid = vid / supportBuffer.verticesPerInstance;" _CRLF); src->add("vid %= supportBuffer.verticesPerInstance;" _CRLF); diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSLHeader.hpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSLHeader.hpp index ab890671..96797699 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSLHeader.hpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSLHeader.hpp @@ -2,7 +2,7 @@ #include "Common/precompiled.h" #include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h" -#include "HW/Latte/Core/LatteShader.h" +#include "Cafe/HW/Latte/Core/LatteShader.h" namespace LatteDecompiler { @@ -363,27 +363,10 @@ namespace LatteDecompiler if ((decompilerContext->options->usesGeometryShader || isRectVertexShader) && (decompilerContext->shaderType == LatteConst::ShaderType::Vertex || decompilerContext->shaderType == LatteConst::ShaderType::Geometry)) { - LattePrimitiveMode vsOutPrimType = static_cast(decompilerContext->contextRegisters[mmVGT_PRIMITIVE_TYPE]); - uint32 gsOutPrimType = decompilerContext->contextRegisters[mmVGT_GS_OUT_PRIM_TYPE]; + LattePrimitiveMode vsOutPrimType = decompilerContext->contextRegistersNew->VGT_PRIMITIVE_TYPE.get_PRIMITIVE_MODE(); + src->addFmt("#define VERTICES_PER_VERTEX_PRIMITIVE {}" _CRLF, GetVerticesPerPrimitive(vsOutPrimType)); - switch (vsOutPrimType) - { - case LattePrimitiveMode::POINTS: - src->add("#define VERTICES_PER_VERTEX_PRIMITIVE 1" _CRLF); - break; - case LattePrimitiveMode::LINES: - src->add("#define VERTICES_PER_VERTEX_PRIMITIVE 2" _CRLF); - break; - case LattePrimitiveMode::TRIANGLES: - src->add("#define VERTICES_PER_VERTEX_PRIMITIVE 3" _CRLF); - break; - case LattePrimitiveMode::RECTS: - src->add("#define VERTICES_PER_VERTEX_PRIMITIVE 3" _CRLF); - break; - default: - cemuLog_log(LogType::Force, "Unknown vertex out primitive type {}", vsOutPrimType); - break; - } + uint32 gsOutPrimType = decompilerContext->contextRegisters[mmVGT_GS_OUT_PRIM_TYPE]; if (decompilerContext->shaderType == LatteConst::ShaderType::Geometry) { switch (gsOutPrimType) diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalCommon.h b/src/Cafe/HW/Latte/Renderer/Metal/MetalCommon.h index e858baf0..b46978ec 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalCommon.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalCommon.h @@ -117,6 +117,7 @@ inline bool executeCommand(fmt::format_string fmt, T&&... args) { return true; } +/* class MemoryMappedFile { public: @@ -179,3 +180,33 @@ private: void* m_data = nullptr; size_t m_fileSize = 0; }; +*/ + +inline uint32 GetVerticesPerPrimitive(LattePrimitiveMode primitiveMode) +{ + switch (primitiveMode) + { + case LattePrimitiveMode::POINTS: + return 1; + case LattePrimitiveMode::LINES: + return 2; + case LattePrimitiveMode::LINE_STRIP: + // Same as line, but requires connection + return 2; + case LattePrimitiveMode::TRIANGLES: + return 3; + case LattePrimitiveMode::RECTS: + return 3; + default: + cemuLog_log(LogType::Force, "Unimplemented primitive type {}", primitiveMode); + return 0; + } +} + +inline bool PrimitiveRequiresConnection(LattePrimitiveMode primitiveMode) +{ + if (primitiveMode == LattePrimitiveMode::LINE_STRIP) + return true; + else + return false; +} diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp index b9e84db5..02bf91f8 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp @@ -1132,7 +1132,7 @@ void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32 */ // Primitive type - const LattePrimitiveMode primitiveMode = static_cast(LatteGPUState.contextRegister[mmVGT_PRIMITIVE_TYPE]); + const LattePrimitiveMode primitiveMode = LatteGPUState.contextNew.VGT_PRIMITIVE_TYPE.get_PRIMITIVE_MODE(); auto mtlPrimitiveType = GetMtlPrimitiveType(primitiveMode); bool isPrimitiveRect = (primitiveMode == Latte::LATTE_VGT_PRIMITIVE_TYPE::E_PRIMITIVE_TYPE::RECTS); @@ -1394,25 +1394,14 @@ void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32 renderCommandEncoder->setObjectBytes(&hostIndexTypeU8, sizeof(hostIndexTypeU8), vertexShader->resourceMapping.indexTypeBinding); encoderState.m_buffers[METAL_SHADER_TYPE_OBJECT][vertexShader->resourceMapping.indexTypeBinding] = {nullptr}; - uint32 verticesPerPrimitive = 0; - switch (primitiveMode) - { - case LattePrimitiveMode::POINTS: - verticesPerPrimitive = 1; - break; - case LattePrimitiveMode::LINES: - verticesPerPrimitive = 2; - break; - case LattePrimitiveMode::TRIANGLES: - case LattePrimitiveMode::RECTS: - verticesPerPrimitive = 3; - break; - default: - cemuLog_log(LogType::Force, "unimplemented geometry shader primitive mode {}", (uint32)primitiveMode); - break; - } + uint32 verticesPerPrimitive = GetVerticesPerPrimitive(primitiveMode); + uint32 threadgroupCount = count * instanceCount; + if (PrimitiveRequiresConnection(primitiveMode)) + threadgroupCount -= verticesPerPrimitive - 1; + else + threadgroupCount /= verticesPerPrimitive; - renderCommandEncoder->drawMeshThreadgroups(MTL::Size(count * instanceCount / verticesPerPrimitive, 1, 1), MTL::Size(verticesPerPrimitive, 1, 1), MTL::Size(1, 1, 1)); + renderCommandEncoder->drawMeshThreadgroups(MTL::Size(threadgroupCount, 1, 1), MTL::Size(verticesPerPrimitive, 1, 1), MTL::Size(1, 1, 1)); } else {