From 7ae29a74cd39e67390f712edc0ae47fd7d0ab114 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sun, 28 Jul 2024 18:43:47 +0200 Subject: [PATCH] present --- .../HW/Latte/Renderer/Metal/MetalRenderer.cpp | 106 +++++++++++++----- .../HW/Latte/Renderer/Metal/MetalRenderer.h | 68 ++++++++++- .../Renderer/Metal/RendererShaderMtl.cpp | 4 +- .../Renderer/Metal/ShaderSourcePresent.h | 23 ++++ 4 files changed, 168 insertions(+), 33 deletions(-) create mode 100644 src/Cafe/HW/Latte/Renderer/Metal/ShaderSourcePresent.h diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp index a0f73c02..cb363d61 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp @@ -6,11 +6,12 @@ #include "Cafe/HW/Latte/Renderer/Metal/CachedFBOMtl.h" #include "Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h" +#include "Cafe/HW/Latte/Renderer/Metal/ShaderSourcePresent.h" + #include "Cafe/HW/Latte/Core/FetchShader.h" #include "Cafe/HW/Latte/Core/LatteShader.h" #include "Cafe/HW/Latte/Core/LatteIndices.h" -#include "Metal/MTLSampler.hpp" -#include "Metal/MTLVertexDescriptor.hpp" +#include "Foundation/NSTypes.hpp" #include "gui/guiWrapper.h" extern bool hasValidFramebufferAttached; @@ -43,6 +44,35 @@ void MetalRenderer::InitializeLayer(const Vector2i& size, bool mainWindow) m_metalLayer = (CA::MetalLayer*)CreateMetalLayer(windowInfo.handle); m_metalLayer->setDevice(m_device); + + // Present pipeline + NS::Error* error = nullptr; + MTL::Library* presentLibrary = m_device->newLibrary(NS::String::string(presentLibrarySource, NS::ASCIIStringEncoding), nullptr, &error); + if (error) + { + printf("failed to create present library (error: %s)\n", error->localizedDescription()->utf8String()); + error->release(); + throw; + return; + } + MTL::Function* presentVertexFunction = presentLibrary->newFunction(NS::String::string("presentVertex", NS::ASCIIStringEncoding)); + MTL::Function* presentFragmentFunction = presentLibrary->newFunction(NS::String::string("presentFragment", NS::ASCIIStringEncoding)); + presentLibrary->release(); + + MTL::RenderPipelineDescriptor* renderPipelineDescriptor = MTL::RenderPipelineDescriptor::alloc()->init(); + renderPipelineDescriptor->setVertexFunction(presentVertexFunction); + renderPipelineDescriptor->setFragmentFunction(presentFragmentFunction); + renderPipelineDescriptor->colorAttachments()->object(0)->setPixelFormat(m_metalLayer->pixelFormat()); + m_presentPipeline = m_device->newRenderPipelineState(renderPipelineDescriptor, &error); + presentVertexFunction->release(); + presentFragmentFunction->release(); + if (error) + { + printf("failed to create present pipeline (error: %s)\n", error->localizedDescription()->utf8String()); + error->release(); + throw; + return; + } } void MetalRenderer::Initialize() @@ -83,40 +113,48 @@ void MetalRenderer::DrawEmptyFrame(bool mainWindow) void MetalRenderer::SwapBuffers(bool swapTV, bool swapDRC) { - if (m_renderCommandEncoder) - { - m_renderCommandEncoder->endEncoding(); - m_renderCommandEncoder->release(); - m_renderCommandEncoder = nullptr; - } + EndEncoding(); - CA::MetalDrawable* drawable = m_metalLayer->nextDrawable(); - if (drawable) + if (m_drawable) { - ensureCommandBuffer(); - m_commandBuffer->presentDrawable(drawable); + EnsureCommandBuffer(); + m_commandBuffer->presentDrawable(m_drawable); } else { printf("skipped present!\n"); } + m_drawable = nullptr; - if (m_commandBuffer) - { - m_commandBuffer->commit(); - - m_commandBuffer->release(); - m_commandBuffer = nullptr; - - // Debug - m_commandQueue->insertDebugCaptureBoundary(); - } + CommitCommandBuffer(); } void MetalRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutputShader* shader, bool useLinearTexFilter, sint32 imageX, sint32 imageY, sint32 imageWidth, sint32 imageHeight, bool padView, bool clearBackground) { - printf("MetalRenderer::DrawBackbufferQuad not implemented\n"); + // Acquire drawable + m_drawable = m_metalLayer->nextDrawable(); + if (!m_drawable) + { + return; + } + + MTL::Texture* presentTexture = static_cast(texView)->GetTexture(); + + // Create render pass + MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init(); + renderPassDescriptor->colorAttachments()->object(0)->setTexture(m_drawable->texture()); + + MTL::Texture* colorRenderTargets[8] = {nullptr}; + colorRenderTargets[0] = m_drawable->texture(); + BeginRenderPassIfNeeded(renderPassDescriptor, colorRenderTargets, nullptr); + + // Draw to Metal layer + m_renderCommandEncoder->setRenderPipelineState(m_presentPipeline); + m_renderCommandEncoder->setFragmentTexture(presentTexture, 0); + m_renderCommandEncoder->setFragmentSamplerState(m_nearestSampler, 0); + + m_renderCommandEncoder->drawPrimitives(MTL::PrimitiveTypeTriangle, NS::UInteger(0), NS::UInteger(3)); } bool MetalRenderer::BeginFrame(bool mainWindow) @@ -178,7 +216,6 @@ void MetalRenderer::texture_releaseTextureUploadBuffer(uint8* mem) TextureDecoder* MetalRenderer::texture_chooseDecodedFormat(Latte::E_GX2SURFFMT format, bool isDepth, Latte::E_DIM dim, uint32 width, uint32 height) { - printf("decoding format %u\n", (uint32)format); // TODO: move to LatteToMtl if (isDepth) { @@ -468,7 +505,6 @@ void MetalRenderer::draw_beginSequence() void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32 instanceCount, uint32 count, MPTR indexDataMPTR, Latte::LATTE_VGT_DMA_INDEX_TYPE::E_INDEX_TYPE indexType, bool isFirst) { - ensureCommandBuffer(); // TODO: uncomment //if (m_state.skipDrawSequence) //{ @@ -484,7 +520,22 @@ void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32 } auto renderPassDescriptor = m_state.activeFBO->GetRenderPassDescriptor(); - beginRenderPassIfNeeded(renderPassDescriptor); + MTL::Texture* colorRenderTargets[8] = {nullptr}; + MTL::Texture* depthRenderTarget = nullptr; + for (uint32 i = 0; i < 8; i++) + { + auto colorTexture = static_cast(m_state.activeFBO->colorBuffer[i].texture); + if (colorTexture) + { + colorRenderTargets[i] = colorTexture->GetTexture(); + } + } + auto depthTexture = static_cast(m_state.activeFBO->depthBuffer.texture); + if (depthTexture) + { + depthRenderTarget = depthTexture->GetTexture(); + } + BeginRenderPassIfNeeded(renderPassDescriptor, colorRenderTargets, depthRenderTarget); // Shaders LatteSHRC_UpdateActiveShaders(); @@ -598,7 +649,8 @@ void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32 if (vertexBufferRange.needsRebind) { m_renderCommandEncoder->setVertexBuffer(m_memoryManager->GetBufferCache(), vertexBufferRange.offset, GET_MTL_VERTEX_BUFFER_INDEX(i)); - vertexBufferRange.needsRebind = false; + // TODO: uncomment + //vertexBufferRange.needsRebind = false; } } diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h index ee1d865b..079e5bba 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h @@ -8,6 +8,7 @@ #include "Cafe/HW/Latte/Renderer/Metal/MetalMemoryManager.h" #include "Metal/MTLRenderCommandEncoder.hpp" #include "Metal/MTLRenderPass.hpp" +#include "Metal/MTLRenderPipeline.hpp" #define MAX_MTL_BUFFERS 31 #define GET_MTL_VERTEX_BUFFER_INDEX(index) (MAX_MTL_BUFFERS - index - 2) @@ -28,6 +29,8 @@ struct MetalState class CachedFBOMtl* activeFBO = nullptr; MetalBufferRange vertexBuffers[MAX_MTL_BUFFERS] = {{}}; class LatteTextureViewMtl* textures[MAX_MTL_TEXTURES] = {nullptr}; + MTL::Texture* colorRenderTargets[8] = {nullptr}; + MTL::Texture* depthRenderTarget = nullptr; }; class MetalRenderer : public Renderer @@ -186,18 +189,22 @@ private: MTL::Device* m_device; MTL::CommandQueue* m_commandQueue; + // Pipelines + MTL::RenderPipelineState* m_presentPipeline; + // Basic MTL::SamplerState* m_nearestSampler; // Active objects MTL::CommandBuffer* m_commandBuffer = nullptr; MTL::RenderCommandEncoder* m_renderCommandEncoder = nullptr; + CA::MetalDrawable* m_drawable; // State MetalState m_state; // Helpers - void ensureCommandBuffer() + void EnsureCommandBuffer() { if (!m_commandBuffer) { @@ -208,11 +215,64 @@ private: } } - void beginRenderPassIfNeeded(MTL::RenderPassDescriptor* renderPassDescriptor) + void BeginRenderPassIfNeeded(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* colorRenderTargets[8], MTL::Texture* depthRenderTarget) { - if (!m_renderCommandEncoder) + EnsureCommandBuffer(); + + // Check if we need to begin a new render pass + if (m_renderCommandEncoder) { - m_renderCommandEncoder = m_commandBuffer->renderCommandEncoder(renderPassDescriptor); + bool needsNewRenderPass = false; + for (uint8 i = 0; i < 8; i++) + { + if (colorRenderTargets[i] && (colorRenderTargets[i] != m_state.colorRenderTargets[i])) + { + needsNewRenderPass = true; + break; + } + } + + if (!needsNewRenderPass) + { + if (depthRenderTarget && (depthRenderTarget != m_state.depthRenderTarget)) + { + needsNewRenderPass = true; + } + } + + if (!needsNewRenderPass) + { + return; + } + + EndEncoding(); + } + + m_renderCommandEncoder = m_commandBuffer->renderCommandEncoder(renderPassDescriptor); + } + + void EndEncoding() + { + if (m_renderCommandEncoder) + { + m_renderCommandEncoder->endEncoding(); + m_renderCommandEncoder->release(); + m_renderCommandEncoder = nullptr; + } + } + + void CommitCommandBuffer() + { + EndEncoding(); + + if (m_commandBuffer) + { + m_commandBuffer->commit(); + m_commandBuffer->release(); + m_commandBuffer = nullptr; + + // Debug + m_commandQueue->insertDebugCaptureBoundary(); } } diff --git a/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.cpp b/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.cpp index b870fc68..b4a43a7c 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.cpp @@ -10,7 +10,7 @@ RendererShaderMtl::RendererShaderMtl(MetalRenderer* mtlRenderer, ShaderType type MTL::Library* library = mtlRenderer->GetDevice()->newLibrary(NS::String::string(mslCode.c_str(), NS::ASCIIStringEncoding), nullptr, &error); if (error) { - printf("Failed to create library (error: %s) -> source:\n%s\n", error->localizedDescription()->utf8String(), mslCode.c_str()); + printf("failed to create library (error: %s) -> source:\n%s\n", error->localizedDescription()->utf8String(), mslCode.c_str()); error->release(); return; } @@ -20,7 +20,7 @@ RendererShaderMtl::RendererShaderMtl(MetalRenderer* mtlRenderer, ShaderType type m_function = library->newFunction(desc, &error); if (error) { - printf("Failed to create function (error: %s)\n", error->localizedDescription()->utf8String()); + printf("failed to create function (error: %s)\n", error->localizedDescription()->utf8String()); error->release(); return; } diff --git a/src/Cafe/HW/Latte/Renderer/Metal/ShaderSourcePresent.h b/src/Cafe/HW/Latte/Renderer/Metal/ShaderSourcePresent.h new file mode 100644 index 00000000..0ca7cbbe --- /dev/null +++ b/src/Cafe/HW/Latte/Renderer/Metal/ShaderSourcePresent.h @@ -0,0 +1,23 @@ +#include +const char* presentLibrarySource = \ +"#include \n" \ +"using namespace metal;\n" \ +"\n" \ +"constant float2 positions[] = {float2(-1.0, -3.0), float2(-1.0, 1.0), float2(3.0, 1.0)};\n" +"\n" \ +"struct VertexOut {\n" \ +" float4 position [[position]];\n" \ +" float2 texCoord;\n" \ +"};\n" \ +"\n" \ +"vertex VertexOut presentVertex(ushort vid [[vertex_id]]) {\n" \ +" VertexOut out;\n" \ +" out.position = float4(positions[vid], 0.0, 1.0);\n" \ +" out.texCoord = positions[vid] * 0.5 + 0.5;\n" \ +"\n" \ +" return out;\n" \ +"}\n" \ +"\n" \ +"fragment float4 presentFragment(VertexOut in [[stage_in]], texture2d tex [[texture(0)]], sampler samplr [[sampler(0)]]) {\n" \ +" return tex.sample(samplr, in.texCoord);\n" \ +"}\n";