From 97f441ecf1ffa93951287d0f2b7eaf31c9e23ea0 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Tue, 20 Aug 2024 20:14:02 +0200 Subject: [PATCH] draw with geometry shaders --- .../LatteDecompilerEmitMSL.cpp | 14 +- .../LatteDecompilerEmitMSLHeader.hpp | 8 +- .../Renderer/Metal/MetalPipelineCache.cpp | 230 +++++++------ .../Latte/Renderer/Metal/MetalPipelineCache.h | 11 +- .../HW/Latte/Renderer/Metal/MetalRenderer.cpp | 303 +++++++----------- .../HW/Latte/Renderer/Metal/MetalRenderer.h | 43 ++- .../Renderer/Metal/RendererShaderMtl.cpp | 27 +- 7 files changed, 332 insertions(+), 304 deletions(-) diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSL.cpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSL.cpp index 9945725a..02581b03 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSL.cpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSL.cpp @@ -2822,7 +2822,7 @@ static void _emitGSReadInputVFetchCode(LatteDecompilerShaderContext* shaderConte src->add(" = "); _emitTypeConversionPrefixMSL(shaderContext, LATTE_DECOMPILER_DTYPE_SIGNED_INT, shaderContext->typeTracker.defaultDataType); - src->add("(in["); + src->add("(objectPayload.vertexOut["); if (texInstruction->textureFetch.srcSel[0] >= 4) cemu_assert_unimplemented(); if (texInstruction->textureFetch.srcSel[1] >= 4) @@ -3871,7 +3871,7 @@ void LatteDecompiler_emitMSLShader(LatteDecompilerShaderContext* shaderContext, src->addFmt("{} {} main0(", functionType, outputTypeName); LatteDecompiler::emitInputs(shaderContext); src->add(") {" _CRLF); - if (shaderContext->options->usesGeometryShader) + if (shaderContext->options->usesGeometryShader && (shader->shaderType == LatteConst::ShaderType::Vertex || shader->shaderType == LatteConst::ShaderType::Geometry)) { if (shader->shaderType == LatteConst::ShaderType::Vertex) { @@ -3884,10 +3884,8 @@ void LatteDecompiler_emitMSLShader(LatteDecompilerShaderContext* shaderContext, // Output is defined as object payload src->add("object_data VertexOut& out = objectPayload.vertexOut[tid];" _CRLF); } - else + else if (shader->shaderType == LatteConst::ShaderType::Geometry) { - // Input is defined as object payload - src->add("object_data VertexOut* in = objectPayload.vertexOut;" _CRLF); src->add("GeometryOut out;" _CRLF); // The index of the current vertex that is being emitted src->add("uint vertexIndex = 0;" _CRLF); @@ -4093,9 +4091,9 @@ void LatteDecompiler_emitMSLShader(LatteDecompilerShaderContext* shaderContext, { // import from geometry shader if (shaderContext->typeTracker.defaultDataType == LATTE_DECOMPILER_DTYPE_SIGNED_INT) - src->addFmt("{} = asy_type(passParameterSem{});" _CRLF, _getRegisterVarName(shaderContext, gprIndex), psInputSemanticId & 0x7F); + src->addFmt("{} = as_type(in.passParameterSem{});" _CRLF, _getRegisterVarName(shaderContext, gprIndex), psInputSemanticId & 0x7F); else if (shaderContext->typeTracker.defaultDataType == LATTE_DECOMPILER_DTYPE_FLOAT) - src->addFmt("{} = passParameterSem{};" _CRLF, _getRegisterVarName(shaderContext, gprIndex), psInputSemanticId & 0x7F); + src->addFmt("{} = in.passParameterSem{};" _CRLF, _getRegisterVarName(shaderContext, gprIndex), psInputSemanticId & 0x7F); else cemu_assert_unimplemented(); } @@ -4137,7 +4135,7 @@ void LatteDecompiler_emitMSLShader(LatteDecompilerShaderContext* shaderContext, src->add("out.pointSize = supportBuffer.pointSize;" _CRLF); } - if (shaderContext->options->usesGeometryShader) + if (shaderContext->options->usesGeometryShader && (shader->shaderType == LatteConst::ShaderType::Vertex || shader->shaderType == LatteConst::ShaderType::Geometry)) { if (shader->shaderType == LatteConst::ShaderType::Vertex) { diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSLHeader.hpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSLHeader.hpp index 51385c2e..b5e16ec7 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSLHeader.hpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSLHeader.hpp @@ -325,7 +325,7 @@ namespace LatteDecompiler { if (decompilerContext->parsedGSCopyShader->paramMapping[p].exportType != 2) continue; - src->addFmt("float4 passParameterSem{} [[user(locn)]];" _CRLF, (sint32)decompilerContext->parsedGSCopyShader->paramMapping[p].exportParam, decompilerContext->parsedGSCopyShader->paramMapping[p].exportParam & 0x7F); + src->addFmt("float4 passParameterSem{} [[user(locn{})]];" _CRLF, (sint32)decompilerContext->parsedGSCopyShader->paramMapping[p].exportParam, decompilerContext->parsedGSCopyShader->paramMapping[p].exportParam & 0x7F); } src->add("};" _CRLF _CRLF); @@ -345,9 +345,9 @@ namespace LatteDecompiler { src->add("#if PRIMITIVE_TYPE == point" _CRLF); src->add("#define VERTICES_PER_PRIMITIVE 1" _CRLF); - src->add("#if PRIMITIVE_TYPE == line" _CRLF); + src->add("#elif PRIMITIVE_TYPE == line" _CRLF); src->add("#define VERTICES_PER_PRIMITIVE 2" _CRLF); - src->add("#if PRIMITIVE_TYPE == triangle" _CRLF); + src->add("#elif PRIMITIVE_TYPE == triangle" _CRLF); src->add("#define VERTICES_PER_PRIMITIVE 3" _CRLF); src->add("#else" _CRLF); src->add("#error unsupported primitive type" _CRLF); @@ -462,7 +462,7 @@ namespace LatteDecompiler src->add(", mesh_grid_properties meshGridProperties"); src->add(", uint tig [[threadgroup_position_in_grid]]"); src->add(", uint tid [[thread_index_in_threadgroup]]"); - src->add(", VERTEX_BUFFER_DEFINITIONS"); + src->add(" VERTEX_BUFFER_DEFINITIONS"); } else { diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.cpp index d68ddf5e..d8d39b79 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.cpp @@ -9,6 +9,8 @@ #include "HW/Latte/Core/FetchShader.h" #include "HW/Latte/ISA/RegDefines.h" +#include "Metal/MTLDevice.hpp" +#include "Metal/MTLRenderPipeline.hpp" #include "config/ActiveSettings.h" #define INVALID_TITLE_ID 0xFFFFFFFFFFFFFFFF @@ -18,6 +20,68 @@ uint64 s_cacheTitleId = INVALID_TITLE_ID; extern std::atomic_int g_compiled_shaders_total; extern std::atomic_int g_compiled_shaders_async; +template +void SetFragmentState(T* desc, class CachedFBOMtl* activeFBO, const LatteContextRegister& lcr) +{ + // Color attachments + const Latte::LATTE_CB_COLOR_CONTROL& colorControlReg = lcr.CB_COLOR_CONTROL; + uint32 blendEnableMask = colorControlReg.get_BLEND_MASK(); + uint32 renderTargetMask = lcr.CB_TARGET_MASK.get_MASK(); + for (uint8 i = 0; i < 8; i++) + { + const auto& colorBuffer = activeFBO->colorBuffer[i]; + auto texture = static_cast(colorBuffer.texture); + if (!texture) + { + continue; + } + auto colorAttachment = desc->colorAttachments()->object(i); + colorAttachment->setPixelFormat(texture->GetRGBAView()->pixelFormat()); + colorAttachment->setWriteMask(GetMtlColorWriteMask((renderTargetMask >> (i * 4)) & 0xF)); + + // Blending + bool blendEnabled = ((blendEnableMask & (1 << i))) != 0; + // Only float data type is blendable + if (blendEnabled && GetMtlPixelFormatInfo(texture->format, false).dataType == MetalDataType::FLOAT) + { + colorAttachment->setBlendingEnabled(true); + + const auto& blendControlReg = lcr.CB_BLENDN_CONTROL[i]; + + auto rgbBlendOp = GetMtlBlendOp(blendControlReg.get_COLOR_COMB_FCN()); + auto srcRgbBlendFactor = GetMtlBlendFactor(blendControlReg.get_COLOR_SRCBLEND()); + auto dstRgbBlendFactor = GetMtlBlendFactor(blendControlReg.get_COLOR_DSTBLEND()); + + colorAttachment->setRgbBlendOperation(rgbBlendOp); + colorAttachment->setSourceRGBBlendFactor(srcRgbBlendFactor); + colorAttachment->setDestinationRGBBlendFactor(dstRgbBlendFactor); + if (blendControlReg.get_SEPARATE_ALPHA_BLEND()) + { + colorAttachment->setAlphaBlendOperation(GetMtlBlendOp(blendControlReg.get_ALPHA_COMB_FCN())); + colorAttachment->setSourceAlphaBlendFactor(GetMtlBlendFactor(blendControlReg.get_ALPHA_SRCBLEND())); + colorAttachment->setDestinationAlphaBlendFactor(GetMtlBlendFactor(blendControlReg.get_ALPHA_DSTBLEND())); + } + else + { + colorAttachment->setAlphaBlendOperation(rgbBlendOp); + colorAttachment->setSourceAlphaBlendFactor(srcRgbBlendFactor); + colorAttachment->setDestinationAlphaBlendFactor(dstRgbBlendFactor); + } + } + } + + // Depth stencil attachment + if (activeFBO->depthBuffer.texture) + { + auto texture = static_cast(activeFBO->depthBuffer.texture); + desc->setDepthAttachmentPixelFormat(texture->GetRGBAView()->pixelFormat()); + if (activeFBO->depthBuffer.hasStencil) + { + desc->setStencilAttachmentPixelFormat(texture->GetRGBAView()->pixelFormat()); + } + } +} + void MetalPipelineCache::ShaderCacheLoading_begin(uint64 cacheTitleId) { s_cacheTitleId = cacheTitleId; @@ -53,9 +117,9 @@ MetalPipelineCache::~MetalPipelineCache() m_binaryArchiveURL->release(); } -MTL::RenderPipelineState* MetalPipelineCache::GetPipelineState(const LatteFetchShader* fetchShader, const LatteDecompilerShader* vertexShader, const LatteDecompilerShader* pixelShader, CachedFBOMtl* activeFBO, const LatteContextRegister& lcr) +MTL::RenderPipelineState* MetalPipelineCache::GetRenderPipelineState(const LatteFetchShader* fetchShader, const LatteDecompilerShader* vertexShader, const LatteDecompilerShader* pixelShader, CachedFBOMtl* activeFBO, const LatteContextRegister& lcr) { - uint64 stateHash = CalculatePipelineHash(fetchShader, vertexShader, pixelShader, activeFBO, lcr); + uint64 stateHash = CalculateRenderPipelineHash(fetchShader, vertexShader, pixelShader, activeFBO, lcr); auto& pipeline = m_pipelineCache[stateHash]; if (pipeline) return pipeline; @@ -127,65 +191,18 @@ MTL::RenderPipelineState* MetalPipelineCache::GetPipelineState(const LatteFetchS // TODO: don't always set the vertex descriptor? desc->setVertexDescriptor(vertexDescriptor); - // Color attachments - const Latte::LATTE_CB_COLOR_CONTROL& colorControlReg = lcr.CB_COLOR_CONTROL; - uint32 blendEnableMask = colorControlReg.get_BLEND_MASK(); - uint32 renderTargetMask = lcr.CB_TARGET_MASK.get_MASK(); - for (uint8 i = 0; i < 8; i++) - { - const auto& colorBuffer = activeFBO->colorBuffer[i]; - auto texture = static_cast(colorBuffer.texture); - if (!texture) - { - continue; - } - auto colorAttachment = desc->colorAttachments()->object(i); - colorAttachment->setPixelFormat(texture->GetRGBAView()->pixelFormat()); - colorAttachment->setWriteMask(GetMtlColorWriteMask((renderTargetMask >> (i * 4)) & 0xF)); + SetFragmentState(desc, activeFBO, lcr); - // Blending - bool blendEnabled = ((blendEnableMask & (1 << i))) != 0; - // Only float data type is blendable - if (blendEnabled && GetMtlPixelFormatInfo(texture->format, false).dataType == MetalDataType::FLOAT) - { - colorAttachment->setBlendingEnabled(true); + TryLoadBinaryArchive(); - const auto& blendControlReg = lcr.CB_BLENDN_CONTROL[i]; - - auto rgbBlendOp = GetMtlBlendOp(blendControlReg.get_COLOR_COMB_FCN()); - auto srcRgbBlendFactor = GetMtlBlendFactor(blendControlReg.get_COLOR_SRCBLEND()); - auto dstRgbBlendFactor = GetMtlBlendFactor(blendControlReg.get_COLOR_DSTBLEND()); - - colorAttachment->setRgbBlendOperation(rgbBlendOp); - colorAttachment->setSourceRGBBlendFactor(srcRgbBlendFactor); - colorAttachment->setDestinationRGBBlendFactor(dstRgbBlendFactor); - if (blendControlReg.get_SEPARATE_ALPHA_BLEND()) - { - colorAttachment->setAlphaBlendOperation(GetMtlBlendOp(blendControlReg.get_ALPHA_COMB_FCN())); - colorAttachment->setSourceAlphaBlendFactor(GetMtlBlendFactor(blendControlReg.get_ALPHA_SRCBLEND())); - colorAttachment->setDestinationAlphaBlendFactor(GetMtlBlendFactor(blendControlReg.get_ALPHA_DSTBLEND())); - } - else - { - colorAttachment->setAlphaBlendOperation(rgbBlendOp); - colorAttachment->setSourceAlphaBlendFactor(srcRgbBlendFactor); - colorAttachment->setDestinationAlphaBlendFactor(dstRgbBlendFactor); - } - } - } - - // Depth stencil attachment - if (activeFBO->depthBuffer.texture) - { - auto texture = static_cast(activeFBO->depthBuffer.texture); - desc->setDepthAttachmentPixelFormat(texture->GetRGBAView()->pixelFormat()); - if (activeFBO->depthBuffer.hasStencil) - { - desc->setStencilAttachmentPixelFormat(texture->GetRGBAView()->pixelFormat()); - } - } - - LoadBinary(desc); + // Load binary + if (m_binaryArchive) + { + NS::Object* binArchives[] = {m_binaryArchive}; + auto binaryArchives = NS::Array::alloc()->init(binArchives, 1); + desc->setBinaryArchives(binaryArchives); + binaryArchives->release(); + } NS::Error* error = nullptr; #ifdef CEMU_DEBUG_ASSERT @@ -211,10 +228,21 @@ MTL::RenderPipelineState* MetalPipelineCache::GetPipelineState(const LatteFetchS { debug_printf("error creating render pipeline state: %s\n", error->localizedDescription()->utf8String()); error->release(); + return nullptr; } else { - SaveBinary(desc); + // Save binary + if (m_binaryArchive) + { + NS::Error* error = nullptr; + m_binaryArchive->addRenderPipelineFunctions(desc, &error); + if (error) + { + debug_printf("error saving render pipeline functions: %s\n", error->localizedDescription()->utf8String()); + error->release(); + } + } } //newPipelineCount++; @@ -230,7 +258,57 @@ MTL::RenderPipelineState* MetalPipelineCache::GetPipelineState(const LatteFetchS return pipeline; } -uint64 MetalPipelineCache::CalculatePipelineHash(const LatteFetchShader* fetchShader, const LatteDecompilerShader* vertexShader, const LatteDecompilerShader* pixelShader, class CachedFBOMtl* activeFBO, const LatteContextRegister& lcr) +MTL::RenderPipelineState* MetalPipelineCache::GetMeshPipelineState(const LatteFetchShader* fetchShader, const LatteDecompilerShader* vertexShader, const LatteDecompilerShader* geometryShader, const LatteDecompilerShader* pixelShader, CachedFBOMtl* activeFBO, const LatteContextRegister& lcr, Renderer::INDEX_TYPE hostIndexType) +{ + uint64 stateHash = CalculateRenderPipelineHash(fetchShader, vertexShader, pixelShader, activeFBO, lcr); + + stateHash += lcr.GetRawView()[mmVGT_PRIMITIVE_TYPE]; + stateHash = std::rotl(stateHash, 7); + + stateHash += (uint8)hostIndexType; + stateHash = std::rotl(stateHash, 7); // TODO: 7?s + + auto& pipeline = m_pipelineCache[stateHash]; + if (pipeline) + return pipeline; + + auto mtlObjectShader = static_cast(vertexShader->shader); + auto mtlMeshShader = static_cast(geometryShader->shader); + auto mtlPixelShader = static_cast(pixelShader->shader); + mtlObjectShader->CompileObjectFunction(lcr, fetchShader, vertexShader, hostIndexType); + mtlMeshShader->CompileMeshFunction(lcr, fetchShader); + mtlPixelShader->CompileFragmentFunction(activeFBO); + + // Render pipeline state + MTL::MeshRenderPipelineDescriptor* desc = MTL::MeshRenderPipelineDescriptor::alloc()->init(); + desc->setObjectFunction(mtlObjectShader->GetFunction()); + desc->setMeshFunction(mtlMeshShader->GetFunction()); + desc->setFragmentFunction(mtlPixelShader->GetFunction()); + + SetFragmentState(desc, activeFBO, lcr); + + TryLoadBinaryArchive(); + + // Load binary + // TODO: no binary archives? :( + + NS::Error* error = nullptr; +#ifdef CEMU_DEBUG_ASSERT + desc->setLabel(GetLabel("Mesh pipeline state", desc)); +#endif + pipeline = m_mtlr->GetDevice()->newRenderPipelineState(desc, MTL::PipelineOptionNone, nullptr, &error); + if (error) + { + debug_printf("error creating render pipeline state: %s\n", error->localizedDescription()->utf8String()); + error->release(); + return nullptr; + } + desc->release(); + + return pipeline; +} + +uint64 MetalPipelineCache::CalculateRenderPipelineHash(const LatteFetchShader* fetchShader, const LatteDecompilerShader* vertexShader, const LatteDecompilerShader* pixelShader, class CachedFBOMtl* activeFBO, const LatteContextRegister& lcr) { // Hash uint64 stateHash = 0; @@ -261,9 +339,6 @@ uint64 MetalPipelineCache::CalculatePipelineHash(const LatteFetchShader* fetchSh stateHash += fetchShader->getVkPipelineHashFragment(); stateHash = std::rotl(stateHash, 7); - stateHash += lcr.GetRawView()[mmVGT_PRIMITIVE_TYPE]; - stateHash = std::rotl(stateHash, 7); - stateHash += lcr.GetRawView()[mmVGT_STRMOUT_EN]; stateHash = std::rotl(stateHash, 7); @@ -340,30 +415,3 @@ void MetalPipelineCache::TryLoadBinaryArchive() } desc->release(); } - -void MetalPipelineCache::LoadBinary(MTL::RenderPipelineDescriptor* desc) -{ - TryLoadBinaryArchive(); - - if (!m_binaryArchive) - return; - - NS::Object* binArchives[] = {m_binaryArchive}; - auto binaryArchives = NS::Array::alloc()->init(binArchives, 1); - desc->setBinaryArchives(binaryArchives); - binaryArchives->release(); -} - -void MetalPipelineCache::SaveBinary(MTL::RenderPipelineDescriptor* desc) -{ - if (!m_binaryArchive) - return; - - NS::Error* error = nullptr; - m_binaryArchive->addRenderPipelineFunctions(desc, &error); - if (error) - { - debug_printf("error saving render pipeline functions: %s\n", error->localizedDescription()->utf8String()); - error->release(); - } -} diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h b/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h index 1fa1f87c..30f40208 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h @@ -4,6 +4,7 @@ #include "HW/Latte/ISA/LatteReg.h" #include "HW/Latte/LegacyShaderDecompiler/LatteDecompiler.h" +#include "Cafe/HW/Latte/Renderer/Renderer.h" class MetalPipelineCache { @@ -15,7 +16,9 @@ public: MetalPipelineCache(class MetalRenderer* metalRenderer) : m_mtlr{metalRenderer} {} ~MetalPipelineCache(); - MTL::RenderPipelineState* GetPipelineState(const LatteFetchShader* fetchShader, const LatteDecompilerShader* vertexShader, const LatteDecompilerShader* pixelShader, class CachedFBOMtl* activeFBO, const LatteContextRegister& lcr); + MTL::RenderPipelineState* GetRenderPipelineState(const LatteFetchShader* fetchShader, const LatteDecompilerShader* vertexShader, const LatteDecompilerShader* pixelShader, class CachedFBOMtl* activeFBO, const LatteContextRegister& lcr); + + MTL::RenderPipelineState* GetMeshPipelineState(const LatteFetchShader* fetchShader, const LatteDecompilerShader* vertexShader, const LatteDecompilerShader* geometryShader, const LatteDecompilerShader* pixelShader, class CachedFBOMtl* activeFBO, const LatteContextRegister& lcr, Renderer::INDEX_TYPE hostIndexType); private: class MetalRenderer* m_mtlr; @@ -25,11 +28,7 @@ private: NS::URL* m_binaryArchiveURL; MTL::BinaryArchive* m_binaryArchive; - uint64 CalculatePipelineHash(const LatteFetchShader* fetchShader, const LatteDecompilerShader* vertexShader, const LatteDecompilerShader* pixelShader, class CachedFBOMtl* activeFBO, const LatteContextRegister& lcr); + uint64 CalculateRenderPipelineHash(const LatteFetchShader* fetchShader, const LatteDecompilerShader* vertexShader, const LatteDecompilerShader* pixelShader, class CachedFBOMtl* activeFBO, const LatteContextRegister& lcr); void TryLoadBinaryArchive(); - - void LoadBinary(MTL::RenderPipelineDescriptor* desc); - - void SaveBinary(MTL::RenderPipelineDescriptor* desc); }; diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp index 0fa60eea..323c67dd 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp @@ -20,6 +20,7 @@ #include "Common/precompiled.h" #include "HW/Latte/Renderer/Metal/MetalCommon.h" #include "Metal/MTLDevice.hpp" +#include "Metal/MTLRenderCommandEncoder.hpp" #include "gui/guiWrapper.h" #define COMMIT_TRESHOLD 256 @@ -28,6 +29,63 @@ extern bool hasValidFramebufferAttached; float supportBufferData[512 * 4]; +void SetBuffer(MTL::RenderCommandEncoder* renderCommandEncoder, MetalShaderType shaderType, MTL::Buffer* buffer, size_t offset, uint32 index) +{ + switch (shaderType) + { + case METAL_SHADER_TYPE_VERTEX: + renderCommandEncoder->setVertexBuffer(buffer, offset, index); + break; + case METAL_SHADER_TYPE_OBJECT: + renderCommandEncoder->setObjectBuffer(buffer, offset, index); + break; + case METAL_SHADER_TYPE_MESH: + renderCommandEncoder->setMeshBuffer(buffer, offset, index); + break; + case METAL_SHADER_TYPE_FRAGMENT: + renderCommandEncoder->setFragmentBuffer(buffer, offset, index); + break; + } +} + +void SetTexture(MTL::RenderCommandEncoder* renderCommandEncoder, MetalShaderType shaderType, MTL::Texture* texture, uint32 index) +{ + switch (shaderType) + { + case METAL_SHADER_TYPE_VERTEX: + renderCommandEncoder->setVertexTexture(texture, index); + break; + case METAL_SHADER_TYPE_OBJECT: + renderCommandEncoder->setObjectTexture(texture, index); + break; + case METAL_SHADER_TYPE_MESH: + renderCommandEncoder->setMeshTexture(texture, index); + break; + case METAL_SHADER_TYPE_FRAGMENT: + renderCommandEncoder->setFragmentTexture(texture, index); + break; + } +} + +void SetSamplerState(MTL::RenderCommandEncoder* renderCommandEncoder, MetalShaderType shaderType, MTL::SamplerState* samplerState, uint32 index) +{ + switch (shaderType) + { + case METAL_SHADER_TYPE_VERTEX: + renderCommandEncoder->setVertexSamplerState(samplerState, index); + break; + case METAL_SHADER_TYPE_OBJECT: + renderCommandEncoder->setObjectSamplerState(samplerState, index); + break; + case METAL_SHADER_TYPE_MESH: + renderCommandEncoder->setMeshSamplerState(samplerState, index); + break; + case METAL_SHADER_TYPE_FRAGMENT: + renderCommandEncoder->setFragmentSamplerState(samplerState, index); + break; + } +} + MetalRenderer::MetalRenderer() { m_device = MTL::CreateSystemDefaultDevice(); @@ -654,7 +712,6 @@ void MetalRenderer::buffer_bindVertexBuffer(uint32 bufferIndex, uint32 offset, u m_memoryManager->UntrackVertexBuffer(bufferIndex); } - buffer.needsRebind = true; buffer.offset = offset; buffer.size = size; buffer.restrideInfo = {}; @@ -664,7 +721,7 @@ void MetalRenderer::buffer_bindVertexBuffer(uint32 bufferIndex, uint32 offset, u void MetalRenderer::buffer_bindUniformBuffer(LatteConst::ShaderType shaderType, uint32 bufferIndex, uint32 offset, uint32 size) { - m_state.m_uniformBufferOffsets[GetMtlShaderType(shaderType)][bufferIndex] = offset; + m_state.m_uniformBufferOffsets[GetMtlGeneralShaderType(shaderType)][bufferIndex] = offset; } RendererShader* MetalRenderer::shader_create(RendererShader::ShaderType type, uint64 baseHash, uint64 auxHash, const std::string& source, bool isGameShader, bool isGfxPackShader) @@ -957,16 +1014,19 @@ void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32 auto restridedBuffer = m_memoryManager->RestrideBufferIfNeeded(i, bufferStride); // Bind - if (vertexBufferRange.needsRebind) + if (true) { - renderCommandEncoder->setVertexBuffer(restridedBuffer.buffer, restridedBuffer.offset, GET_MTL_VERTEX_BUFFER_INDEX(i)); - vertexBufferRange.needsRebind = false; + SetBuffer(renderCommandEncoder, GetMtlShaderType(vertexShader->shaderType, (geometryShader != nullptr)),restridedBuffer.buffer, restridedBuffer.offset, GET_MTL_VERTEX_BUFFER_INDEX(i)); } } } // Render pipeline state - MTL::RenderPipelineState* renderPipelineState = m_pipelineCache->GetPipelineState(fetchShader, vertexShader, pixelShader, m_state.m_lastUsedFBO, LatteGPUState.contextNew); + MTL::RenderPipelineState* renderPipelineState; + if (geometryShader) + renderPipelineState = m_pipelineCache->GetMeshPipelineState(fetchShader, vertexShader, geometryShader, pixelShader, m_state.m_lastUsedFBO, LatteGPUState.contextNew, hostIndexType); + else + renderPipelineState = m_pipelineCache->GetRenderPipelineState(fetchShader, vertexShader, pixelShader, m_state.m_lastUsedFBO, LatteGPUState.contextNew); if (renderPipelineState != encoderState.m_renderPipelineState) { renderCommandEncoder->setRenderPipelineState(renderPipelineState); @@ -978,18 +1038,51 @@ void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32 LatteStreamout_PrepareDrawcall(count, instanceCount); // Uniform buffers, textures and samplers - BindStageResources(renderCommandEncoder, vertexShader); - BindStageResources(renderCommandEncoder, pixelShader); + BindStageResources(renderCommandEncoder, vertexShader, (geometryShader != nullptr)); + if (geometryShader) + BindStageResources(renderCommandEncoder, geometryShader, (geometryShader != nullptr)); + BindStageResources(renderCommandEncoder, pixelShader, (geometryShader != nullptr)); // Draw + MTL::Buffer* indexBuffer = nullptr; if (hostIndexType != INDEX_TYPE::NONE) + indexBuffer = m_memoryManager->GetTemporaryBufferAllocator().GetBuffer(indexBufferIndex); + if (geometryShader) { - auto mtlIndexType = GetMtlIndexType(hostIndexType); - MTL::Buffer* indexBuffer = m_memoryManager->GetTemporaryBufferAllocator().GetBuffer(indexBufferIndex); - renderCommandEncoder->drawIndexedPrimitives(mtlPrimitiveType, hostIndexCount, mtlIndexType, indexBuffer, indexBufferOffset, instanceCount, baseVertex, baseInstance); - } else + // TODO: don't hardcode the index + if (indexBuffer) + renderCommandEncoder->setObjectBuffer(indexBuffer, indexBufferOffset, 20); + + uint32 verticesPerPrimitive = 0; + switch (primitiveMode) + { + case LattePrimitiveMode::POINTS: + verticesPerPrimitive = 1; + break; + case LattePrimitiveMode::LINES: + verticesPerPrimitive = 2; + break; + case LattePrimitiveMode::TRIANGLES: + verticesPerPrimitive = 3; + break; + default: + throw std::runtime_error("Invalid primitive mode"); + break; + } + + renderCommandEncoder->drawMeshThreadgroups(MTL::Size(count / verticesPerPrimitive, 1, 1), MTL::Size(verticesPerPrimitive, 1, 1), MTL::Size(1, 1, 1)); + } + else { - renderCommandEncoder->drawPrimitives(mtlPrimitiveType, baseVertex, count, instanceCount, baseInstance); + if (indexBuffer) + { + auto mtlIndexType = GetMtlIndexType(hostIndexType); + renderCommandEncoder->drawIndexedPrimitives(mtlPrimitiveType, hostIndexCount, mtlIndexType, indexBuffer, indexBufferOffset, instanceCount, baseVertex, baseInstance); + } + else + { + renderCommandEncoder->drawPrimitives(mtlPrimitiveType, baseVertex, count, instanceCount, baseInstance); + } } LatteStreamout_FinishDrawcall(false); @@ -1080,7 +1173,7 @@ MTL::RenderCommandEncoder* MetalRenderer::GetTemporaryRenderCommandEncoder(MTL:: } // Some render passes clear the attachments, forceRecreate is supposed to be used in those cases -MTL::RenderCommandEncoder* MetalRenderer::GetRenderCommandEncoder(bool forceRecreate, bool rebindStateIfNewEncoder) +MTL::RenderCommandEncoder* MetalRenderer::GetRenderCommandEncoder(bool forceRecreate) { // Check if we need to begin a new render pass if (m_commandEncoder) @@ -1134,12 +1227,6 @@ MTL::RenderCommandEncoder* MetalRenderer::GetRenderCommandEncoder(bool forceRecr ResetEncoderState(); - if (rebindStateIfNewEncoder) - { - // Rebind all the render state - RebindRenderState(renderCommandEncoder); - } - return renderCommandEncoder; } @@ -1253,9 +1340,9 @@ bool MetalRenderer::AcquireNextDrawable(bool mainWindow) return true; } -void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandEncoder, LatteDecompilerShader* shader) +void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandEncoder, LatteDecompilerShader* shader, bool usesGeometryShader) { - auto mtlShaderType = GetMtlShaderType(shader->shaderType); + auto mtlShaderType = GetMtlShaderType(shader->shaderType, usesGeometryShader); sint32 textureCount = shader->resourceMapping.getTextureCount(); for (int i = 0; i < textureCount; ++i) @@ -1295,88 +1382,21 @@ void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandE { // TODO: don't bind if already bound if (textureDim == Latte::E_DIM::DIM_1D) - { - switch (shader->shaderType) - { - case LatteConst::ShaderType::Vertex: - { - renderCommandEncoder->setVertexTexture(m_nullTexture1D, binding); - renderCommandEncoder->setVertexSamplerState(m_nearestSampler, binding); - break; - } - case LatteConst::ShaderType::Pixel: - { - renderCommandEncoder->setFragmentTexture(m_nullTexture1D, binding); - renderCommandEncoder->setVertexSamplerState(m_nearestSampler, binding); - break; - } - default: - UNREACHABLE; - } - } + SetTexture(renderCommandEncoder, mtlShaderType, m_nullTexture1D, binding); else - { - switch (shader->shaderType) - { - case LatteConst::ShaderType::Vertex: - { - renderCommandEncoder->setVertexTexture(m_nullTexture2D, binding); - renderCommandEncoder->setVertexSamplerState(m_nearestSampler, binding); - break; - } - case LatteConst::ShaderType::Pixel: - { - renderCommandEncoder->setFragmentTexture(m_nullTexture2D, binding); - renderCommandEncoder->setVertexSamplerState(m_nearestSampler, binding); - break; - } - default: - UNREACHABLE; - } - } + SetTexture(renderCommandEncoder, mtlShaderType, m_nullTexture2D, binding); + SetSamplerState(renderCommandEncoder, mtlShaderType, m_nearestSampler, binding); continue; } if (textureDim == Latte::E_DIM::DIM_1D && (textureView->dim != Latte::E_DIM::DIM_1D)) { - switch (shader->shaderType) - { - case LatteConst::ShaderType::Vertex: - { - renderCommandEncoder->setVertexTexture(m_nullTexture1D, binding); - renderCommandEncoder->setVertexSamplerState(m_nearestSampler, binding); - break; - } - case LatteConst::ShaderType::Pixel: - { - renderCommandEncoder->setFragmentTexture(m_nullTexture1D, binding); - renderCommandEncoder->setVertexSamplerState(m_nearestSampler, binding); - break; - } - default: - UNREACHABLE; - } + SetTexture(renderCommandEncoder, mtlShaderType, m_nullTexture1D, binding); continue; } else if (textureDim == Latte::E_DIM::DIM_2D && (textureView->dim != Latte::E_DIM::DIM_2D && textureView->dim != Latte::E_DIM::DIM_2D_MSAA)) { - switch (shader->shaderType) - { - case LatteConst::ShaderType::Vertex: - { - renderCommandEncoder->setVertexTexture(m_nullTexture2D, binding); - renderCommandEncoder->setVertexSamplerState(m_nearestSampler, binding); - break; - } - case LatteConst::ShaderType::Pixel: - { - renderCommandEncoder->setFragmentTexture(m_nullTexture2D, binding); - renderCommandEncoder->setVertexSamplerState(m_nearestSampler, binding); - break; - } - default: - UNREACHABLE; - } + SetTexture(renderCommandEncoder, mtlShaderType, m_nullTexture2D, binding); continue; } @@ -1399,21 +1419,7 @@ void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandE { boundSampler = sampler; - switch (shader->shaderType) - { - case LatteConst::ShaderType::Vertex: - { - renderCommandEncoder->setVertexSamplerState(sampler, binding); - break; - } - case LatteConst::ShaderType::Pixel: - { - renderCommandEncoder->setFragmentSamplerState(sampler, binding); - break; - } - default: - UNREACHABLE; - } + SetSamplerState(renderCommandEncoder, mtlShaderType, sampler, binding); } // get texture register word 0 @@ -1425,21 +1431,7 @@ void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandE boundTexture = {textureView, word4}; MTL::Texture* mtlTexture = textureView->GetSwizzledView(word4); - switch (shader->shaderType) - { - case LatteConst::ShaderType::Vertex: - { - renderCommandEncoder->setVertexTexture(mtlTexture, binding); - break; - } - case LatteConst::ShaderType::Pixel: - { - renderCommandEncoder->setFragmentTexture(mtlTexture, binding); - break; - } - default: - UNREACHABLE; - } + SetTexture(renderCommandEncoder, mtlShaderType, mtlTexture, binding); } // Support buffer @@ -1531,23 +1523,7 @@ void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandE if (!HasUnifiedMemory()) buffer->didModifyRange(NS::Range(supportBuffer.offset, size)); - switch (shader->shaderType) - { - case LatteConst::ShaderType::Vertex: - { - renderCommandEncoder->setVertexBuffer(buffer, supportBuffer.offset, MTL_SUPPORT_BUFFER_BINDING); - //renderCommandEncoder->setVertexBytes(supportBufferData, sizeof(supportBufferData), MTL_SUPPORT_BUFFER_BINDING); - break; - } - case LatteConst::ShaderType::Pixel: - { - renderCommandEncoder->setFragmentBuffer(buffer, supportBuffer.offset, MTL_SUPPORT_BUFFER_BINDING); - //renderCommandEncoder->setFragmentBytes(supportBufferData, sizeof(supportBufferData), MTL_SUPPORT_BUFFER_BINDING); - break; - } - default: - UNREACHABLE; - } + SetBuffer(renderCommandEncoder, mtlShaderType, buffer, supportBuffer.offset, MTL_SUPPORT_BUFFER_BINDING); } // Uniform buffers @@ -1562,7 +1538,7 @@ void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandE continue; } - size_t offset = m_state.m_uniformBufferOffsets[mtlShaderType][i]; + size_t offset = m_state.m_uniformBufferOffsets[GetMtlGeneralShaderType(shader->shaderType)][i]; if (offset == INVALID_OFFSET) continue; @@ -1573,57 +1549,18 @@ void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandE boundOffset = offset; // TODO: only set the offset if already bound - switch (shader->shaderType) - { - case LatteConst::ShaderType::Vertex: - { - renderCommandEncoder->setVertexBuffer(m_memoryManager->GetBufferCache(), offset, binding); - break; - } - case LatteConst::ShaderType::Pixel: - { - renderCommandEncoder->setFragmentBuffer(m_memoryManager->GetBufferCache(), offset, binding); - break; - } - default: - UNREACHABLE; - } + SetBuffer(renderCommandEncoder, mtlShaderType, m_memoryManager->GetBufferCache(), offset, binding); } } // Storage buffer if (shader->resourceMapping.tfStorageBindingPoint >= 0) { - switch (shader->shaderType) - { - case LatteConst::ShaderType::Vertex: - { - renderCommandEncoder->setVertexBuffer(m_xfbRingBuffer, 0, shader->resourceMapping.tfStorageBindingPoint); - break; - } - case LatteConst::ShaderType::Pixel: - { - renderCommandEncoder->setFragmentBuffer(m_xfbRingBuffer, 0, shader->resourceMapping.tfStorageBindingPoint); - break; - } - default: - UNREACHABLE; - } + SetBuffer(renderCommandEncoder, mtlShaderType, m_xfbRingBuffer, 0, shader->resourceMapping.tfStorageBindingPoint); m_state.m_encoderState.m_uniformBufferOffsets[mtlShaderType][shader->resourceMapping.tfStorageBindingPoint] = INVALID_OFFSET; } } -void MetalRenderer::RebindRenderState(MTL::RenderCommandEncoder* renderCommandEncoder) -{ - // Vertex buffers - for (uint8 i = 0; i < MAX_MTL_BUFFERS; i++) - { - auto& vertexBufferRange = m_state.m_vertexBuffers[i]; - if (vertexBufferRange.offset != INVALID_OFFSET) - vertexBufferRange.needsRebind = true; - } -} - void MetalRenderer::ClearColorTextureInternal(MTL::Texture* mtlTexture, sint32 sliceIndex, sint32 mipIndex, float r, float g, float b, float a) { MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init(); diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h index 4ea13fb2..68268534 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h @@ -31,27 +31,57 @@ struct MetalRestrideInfo struct MetalBoundBuffer { - bool needsRebind = false; size_t offset = INVALID_OFFSET; size_t size = 0; // Memory manager will write restride info to this variable MetalRestrideInfo restrideInfo; }; +enum MetalGeneralShaderType +{ + METAL_GENERAL_SHADER_TYPE_VERTEX, + METAL_GENERAL_SHADER_TYPE_GEOMETRY, + METAL_GENERAL_SHADER_TYPE_FRAGMENT, + + METAL_GENERAL_SHADER_TYPE_TOTAL +}; + +inline MetalGeneralShaderType GetMtlGeneralShaderType(LatteConst::ShaderType shaderType) +{ + switch (shaderType) + { + case LatteConst::ShaderType::Vertex: + return METAL_GENERAL_SHADER_TYPE_VERTEX; + case LatteConst::ShaderType::Geometry: + return METAL_GENERAL_SHADER_TYPE_GEOMETRY; + case LatteConst::ShaderType::Pixel: + return METAL_GENERAL_SHADER_TYPE_FRAGMENT; + default: + return METAL_GENERAL_SHADER_TYPE_TOTAL; + } +} + enum MetalShaderType { METAL_SHADER_TYPE_VERTEX, + METAL_SHADER_TYPE_OBJECT, + METAL_SHADER_TYPE_MESH, METAL_SHADER_TYPE_FRAGMENT, METAL_SHADER_TYPE_TOTAL }; -inline MetalShaderType GetMtlShaderType(LatteConst::ShaderType shaderType) +inline MetalShaderType GetMtlShaderType(LatteConst::ShaderType shaderType, bool usesGeometryShader) { switch (shaderType) { case LatteConst::ShaderType::Vertex: - return METAL_SHADER_TYPE_VERTEX; + if (usesGeometryShader) + return METAL_SHADER_TYPE_OBJECT; + else + return METAL_SHADER_TYPE_VERTEX; + case LatteConst::ShaderType::Geometry: + return METAL_SHADER_TYPE_MESH; case LatteConst::ShaderType::Pixel: return METAL_SHADER_TYPE_FRAGMENT; default: @@ -105,7 +135,7 @@ struct MetalState MetalBoundBuffer m_vertexBuffers[MAX_MTL_BUFFERS] = {{}}; // TODO: find out what is the max number of bound textures on the Wii U class LatteTextureViewMtl* m_textures[64] = {nullptr}; - size_t m_uniformBufferOffsets[METAL_SHADER_TYPE_TOTAL][MAX_MTL_BUFFERS]; + size_t m_uniformBufferOffsets[METAL_GENERAL_SHADER_TYPE_TOTAL][MAX_MTL_BUFFERS]; MTL::Viewport m_viewport; MTL::ScissorRect m_scissor; @@ -347,7 +377,7 @@ public: bool CommandBufferCompleted(MTL::CommandBuffer* commandBuffer); void WaitForCommandBufferCompletion(MTL::CommandBuffer* commandBuffer); MTL::RenderCommandEncoder* GetTemporaryRenderCommandEncoder(MTL::RenderPassDescriptor* renderPassDescriptor); - MTL::RenderCommandEncoder* GetRenderCommandEncoder(bool forceRecreate = false, bool rebindStateIfNewEncoder = true); + MTL::RenderCommandEncoder* GetRenderCommandEncoder(bool forceRecreate = false); MTL::ComputeCommandEncoder* GetComputeCommandEncoder(); MTL::BlitCommandEncoder* GetBlitCommandEncoder(); void EndEncoding(); @@ -355,8 +385,7 @@ public: bool AcquireNextDrawable(bool mainWindow); - void BindStageResources(MTL::RenderCommandEncoder* renderCommandEncoder, LatteDecompilerShader* shader); - void RebindRenderState(MTL::RenderCommandEncoder* renderCommandEncoder); + void BindStageResources(MTL::RenderCommandEncoder* renderCommandEncoder, LatteDecompilerShader* shader, bool usesGeometryShader); void ClearColorTextureInternal(MTL::Texture* mtlTexture, sint32 sliceIndex, sint32 mipIndex, float r, float g, float b, float a); diff --git a/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.cpp b/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.cpp index 17322b19..883a85c6 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.cpp @@ -58,7 +58,6 @@ void RendererShaderMtl::CompileObjectFunction(const LatteContextRegister& lcr, c std::string vertexBufferDefinitions = "#define VERTEX_BUFFER_DEFINITIONS "; std::string vertexBuffers = "#define VERTEX_BUFFERS "; std::string inputFetchDefinition = "VertexIn fetchInput(thread uint& vid VERTEX_BUFFER_DEFINITIONS) {\n"; - inputFetchDefinition += "VertexIn in;\n"; if (hostIndexType != Renderer::INDEX_TYPE::NONE) { vertexBufferDefinitions += ", device "; @@ -77,8 +76,9 @@ void RendererShaderMtl::CompileObjectFunction(const LatteContextRegister& lcr, c // TODO: don't hardcode the index vertexBufferDefinitions += "* indexBuffer [[buffer(20)]]"; vertexBuffers += ", indexBuffer"; - inputFetchDefinition += "vid = indexBuffer[vid]\n"; + inputFetchDefinition += "vid = indexBuffer[vid];\n"; } + inputFetchDefinition += "VertexIn in;\n"; for (auto& bufferGroup : fetchShader->bufferGroups) { std::optional fetchType; @@ -92,51 +92,67 @@ void RendererShaderMtl::CompileObjectFunction(const LatteContextRegister& lcr, c continue; // attribute not used? std::string formatName; + uint8 componentCount = 0; switch (GetMtlVertexFormat(attr.format)) { case MTL::VertexFormatUChar: formatName = "uchar"; + componentCount = 1; break; case MTL::VertexFormatUChar2: formatName = "uchar2"; + componentCount = 2; break; case MTL::VertexFormatUChar3: formatName = "uchar3"; + componentCount = 3; break; case MTL::VertexFormatUChar4: formatName = "uchar4"; + componentCount = 4; break; case MTL::VertexFormatUShort: formatName = "ushort"; + componentCount = 1; break; case MTL::VertexFormatUShort2: formatName = "ushort2"; + componentCount = 2; break; case MTL::VertexFormatUShort3: formatName = "ushort3"; + componentCount = 3; break; case MTL::VertexFormatUShort4: formatName = "ushort4"; + componentCount = 4; break; case MTL::VertexFormatUInt: formatName = "uint"; + componentCount = 1; break; case MTL::VertexFormatUInt2: formatName = "uint2"; + componentCount = 2; break; case MTL::VertexFormatUInt3: formatName = "uint3"; + componentCount = 3; break; case MTL::VertexFormatUInt4: formatName = "uint4"; + componentCount = 4; break; } // Fetch the attribute inputFetchDefinition += "in.ATTRIBUTE_NAME" + std::to_string(semanticId) + " = "; - inputFetchDefinition += "*(device " + formatName + "*)"; + inputFetchDefinition += "uint4(*(device " + formatName + "*)"; inputFetchDefinition += "(vertexBuffer" + std::to_string(attr.attributeBufferIndex); - inputFetchDefinition += " + vid + " + std::to_string(attr.offset) + ");\n"; + inputFetchDefinition += " + vid + " + std::to_string(attr.offset) + ")"; + for (uint8 i = 0; i < (4 - componentCount); i++) + inputFetchDefinition += ", 0"; + inputFetchDefinition += ");\n"; if (fetchType.has_value()) cemu_assert_debug(fetchType == attr.fetchType); @@ -153,7 +169,8 @@ void RendererShaderMtl::CompileObjectFunction(const LatteContextRegister& lcr, c uint32 bufferBaseRegisterIndex = mmSQ_VTX_ATTRIBUTE_BLOCK_START + bufferIndex * 7; uint32 bufferStride = (lcr.GetRawView()[bufferBaseRegisterIndex + 2] >> 11) & 0xFFFF; - fullCode += ", device uchar* vertexBuffer" + std::to_string(bufferIndex) + " [[buffer(" + std::to_string(GET_MTL_VERTEX_BUFFER_INDEX(bufferIndex)) + ")]]"; + vertexBufferDefinitions += ", device uchar* vertexBuffer" + std::to_string(bufferIndex) + " [[buffer(" + std::to_string(GET_MTL_VERTEX_BUFFER_INDEX(bufferIndex)) + ")]]"; + vertexBuffers += ", vertexBuffer" + std::to_string(bufferIndex); } inputFetchDefinition += "return in;\n"; inputFetchDefinition += "}\n";