From 498c4daed829f0ceb25f02af89ebbcc62df41401 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 6 Jul 2024 17:28:35 +0200 Subject: [PATCH] do clears as load ops if possible --- include/renderer_mtl/renderer_mtl.hpp | 133 ++++++++++++++++++++++++- src/core/renderer_mtl/renderer_mtl.cpp | 51 +++------- 2 files changed, 146 insertions(+), 38 deletions(-) diff --git a/include/renderer_mtl/renderer_mtl.hpp b/include/renderer_mtl/renderer_mtl.hpp index 3ec77ace..6de5c7d9 100644 --- a/include/renderer_mtl/renderer_mtl.hpp +++ b/include/renderer_mtl/renderer_mtl.hpp @@ -13,6 +13,25 @@ class GPU; +namespace Metal { + +struct ColorClearOp { + MTL::Texture* texture; + float r, g, b, a; +}; + +struct DepthClearOp { + MTL::Texture* texture; + float depth; +}; + +struct StencilClearOp { + MTL::Texture* texture; + u8 stencil; +}; + +} // namespace Metal + class RendererMTL final : public Renderer { public: RendererMTL(GPU& gpu, const std::array& internalRegs, const std::array& externalRegs); @@ -57,6 +76,11 @@ class RendererMTL final : public Renderer { MTL::RenderPipelineState* displayPipeline; MTL::RenderPipelineState* copyToLutTexturePipeline; + // Clears + std::vector colorClearOps; + std::vector depthClearOps; + std::vector stencilClearOps; + // Active state MTL::CommandBuffer* commandBuffer = nullptr; MTL::RenderCommandEncoder* renderCommandEncoder = nullptr; @@ -76,10 +100,10 @@ class RendererMTL final : public Renderer { } } - void beginRenderPassIfNeeded(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* colorTexture, MTL::Texture* depthTexture = nullptr) { + void beginRenderPassIfNeeded(MTL::RenderPassDescriptor* renderPassDescriptor, bool doesClears, MTL::Texture* colorTexture, MTL::Texture* depthTexture = nullptr) { createCommandBufferIfNeeded(); - if (!renderCommandEncoder || colorTexture != lastColorTexture || depthTexture != lastDepthTexture) { + if (doesClears || !renderCommandEncoder || colorTexture != lastColorTexture || (depthTexture != lastDepthTexture || depthTexture == nullptr)) { endRenderPass(); renderCommandEncoder = commandBuffer->renderCommandEncoder(renderPassDescriptor); @@ -100,6 +124,111 @@ class RendererMTL final : public Renderer { } } + void clearColor(MTL::RenderPassDescriptor* renderPassDescriptor, Metal::ColorClearOp clearOp) { + bool beginRenderPass = (renderPassDescriptor == nullptr); + if (!renderPassDescriptor) { + renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init(); + } + MTL::RenderPassColorAttachmentDescriptor* colorAttachment = renderPassDescriptor->colorAttachments()->object(0); + colorAttachment->setTexture(clearOp.texture); + colorAttachment->setClearColor(MTL::ClearColor(clearOp.r, clearOp.g, clearOp.b, clearOp.a)); + colorAttachment->setLoadAction(MTL::LoadActionClear); + colorAttachment->setStoreAction(MTL::StoreActionStore); + + if (beginRenderPass) { + beginRenderPassIfNeeded(renderPassDescriptor, true, clearOp.texture); + } + } + + bool clearColor(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* texture) { + for (int32_t i = colorClearOps.size() - 1; i >= 0; i--) { + if (colorClearOps[i].texture == texture) { + clearColor(renderPassDescriptor, colorClearOps[i]); + colorClearOps.erase(colorClearOps.begin() + i); + return true; + } + } + + if (renderPassDescriptor) { + MTL::RenderPassColorAttachmentDescriptor* colorAttachment = renderPassDescriptor->colorAttachments()->object(0); + colorAttachment->setTexture(texture); + colorAttachment->setLoadAction(MTL::LoadActionLoad); + colorAttachment->setStoreAction(MTL::StoreActionStore); + } + + return false; + } + + void clearDepth(MTL::RenderPassDescriptor* renderPassDescriptor, Metal::DepthClearOp clearOp) { + bool beginRenderPass = (renderPassDescriptor == nullptr); + if (!renderPassDescriptor) { + renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init(); + } + MTL::RenderPassDepthAttachmentDescriptor* depthAttachment = renderPassDescriptor->depthAttachment(); + depthAttachment->setTexture(clearOp.texture); + depthAttachment->setClearDepth(clearOp.depth); + depthAttachment->setLoadAction(MTL::LoadActionClear); + depthAttachment->setStoreAction(MTL::StoreActionStore); + + if (beginRenderPass) { + beginRenderPassIfNeeded(renderPassDescriptor, true, nullptr, clearOp.texture); + } + } + + bool clearDepth(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* texture) { + for (int32_t i = depthClearOps.size() - 1; i >= 0; i--) { + if (depthClearOps[i].texture == texture) { + clearDepth(renderPassDescriptor, depthClearOps[i]); + depthClearOps.erase(depthClearOps.begin() + i); + return true; + } + } + + if (renderPassDescriptor) { + MTL::RenderPassDepthAttachmentDescriptor* depthAttachment = renderPassDescriptor->depthAttachment(); + depthAttachment->setTexture(texture); + depthAttachment->setLoadAction(MTL::LoadActionLoad); + depthAttachment->setStoreAction(MTL::StoreActionStore); + } + + return false; + } + + void clearStencil(MTL::RenderPassDescriptor* renderPassDescriptor, Metal::StencilClearOp clearOp) { + bool beginRenderPass = (renderPassDescriptor == nullptr); + if (!renderPassDescriptor) { + renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init(); + } + MTL::RenderPassStencilAttachmentDescriptor* stencilAttachment = renderPassDescriptor->stencilAttachment(); + stencilAttachment->setTexture(clearOp.texture); + stencilAttachment->setClearStencil(clearOp.stencil); + stencilAttachment->setLoadAction(MTL::LoadActionClear); + stencilAttachment->setStoreAction(MTL::StoreActionStore); + + if (beginRenderPass) { + beginRenderPassIfNeeded(renderPassDescriptor, true, nullptr, clearOp.texture); + } + } + + bool clearStencil(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* texture) { + for (int32_t i = stencilClearOps.size() - 1; i >= 0; i--) { + if (stencilClearOps[i].texture == texture) { + clearStencil(renderPassDescriptor, stencilClearOps[i]); + stencilClearOps.erase(stencilClearOps.begin() + i); + return true; + } + } + + if (renderPassDescriptor) { + MTL::RenderPassStencilAttachmentDescriptor* stencilAttachment = renderPassDescriptor->stencilAttachment(); + stencilAttachment->setTexture(texture); + stencilAttachment->setLoadAction(MTL::LoadActionLoad); + stencilAttachment->setStoreAction(MTL::StoreActionStore); + } + + return false; + } + std::optional getColorRenderTarget(u32 addr, PICA::ColorFmt format, u32 width, u32 height, bool createIfnotFound = true); Metal::DepthStencilRenderTarget& getDepthRenderTarget(); Metal::Texture& getTexture(Metal::Texture& tex); diff --git a/src/core/renderer_mtl/renderer_mtl.cpp b/src/core/renderer_mtl/renderer_mtl.cpp index 6b9e5fcb..0fb38d7c 100644 --- a/src/core/renderer_mtl/renderer_mtl.cpp +++ b/src/core/renderer_mtl/renderer_mtl.cpp @@ -60,7 +60,7 @@ void RendererMTL::display() { colorAttachment->setClearColor(MTL::ClearColor{0.0f, 0.0f, 0.0f, 1.0f}); colorAttachment->setStoreAction(MTL::StoreActionStore); - beginRenderPassIfNeeded(renderPassDescriptor, drawable->texture()); + beginRenderPassIfNeeded(renderPassDescriptor, false, drawable->texture()); renderCommandEncoder->setRenderPipelineState(displayPipeline); renderCommandEncoder->setFragmentSamplerState(nearestSampler, 0); @@ -73,6 +73,7 @@ void RendererMTL::display() { auto topScreen = colorRenderTargetCache.findFromAddress(topScreenAddr); if (topScreen) { + clearColor(nullptr, topScreen->get().texture); renderCommandEncoder->setViewport(MTL::Viewport{0, 0, 400, 240, 0.0f, 1.0f}); renderCommandEncoder->setFragmentTexture(topScreen->get().texture, 0); renderCommandEncoder->drawPrimitives(MTL::PrimitiveTypeTriangleStrip, NS::UInteger(0), NS::UInteger(4)); @@ -86,6 +87,7 @@ void RendererMTL::display() { auto bottomScreen = colorRenderTargetCache.findFromAddress(bottomScreenAddr); if (bottomScreen) { + clearColor(nullptr, bottomScreen->get().texture); renderCommandEncoder->setViewport(MTL::Viewport{40, 240, 320, 240, 0.0f, 1.0f}); renderCommandEncoder->setFragmentTexture(bottomScreen->get().texture, 0); renderCommandEncoder->drawPrimitives(MTL::PrimitiveTypeTriangleStrip, NS::UInteger(0), NS::UInteger(4)); @@ -264,14 +266,7 @@ void RendererMTL::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 c const float b = Helpers::getBits<8, 8>(value) / 255.0f; const float a = (value & 0xff) / 255.0f; - MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init(); - MTL::RenderPassColorAttachmentDescriptor* colorAttachment = renderPassDescriptor->colorAttachments()->object(0); - colorAttachment->setTexture(color->get().texture); - colorAttachment->setClearColor(MTL::ClearColor(r, g, b, a)); - colorAttachment->setLoadAction(MTL::LoadActionClear); - colorAttachment->setStoreAction(MTL::StoreActionStore); - - beginRenderPassIfNeeded(renderPassDescriptor, color->get().texture); + colorClearOps.push_back({color->get().texture, r, g, b, a}); return; } @@ -286,23 +281,13 @@ void RendererMTL::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 c depthVal = (value & 0xffffff) / 16777215.0f; } - MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init(); - MTL::RenderPassDepthAttachmentDescriptor* depthAttachment = renderPassDescriptor->depthAttachment(); - depthAttachment->setTexture(depth->get().texture); - depthAttachment->setClearDepth(depthVal); - depthAttachment->setLoadAction(MTL::LoadActionClear); - depthAttachment->setStoreAction(MTL::StoreActionStore); + depthClearOps.push_back({depth->get().texture, depthVal}); if (format == DepthFmt::Depth24Stencil8) { - MTL::RenderPassStencilAttachmentDescriptor* stencilAttachment = renderPassDescriptor->stencilAttachment(); - stencilAttachment->setTexture(depth->get().texture); - stencilAttachment->setClearStencil((value >> 24) & 0xff); - stencilAttachment->setLoadAction(MTL::LoadActionClear); - stencilAttachment->setStoreAction(MTL::StoreActionStore); + const u8 stencilVal = value >> 24; + stencilClearOps.push_back({depth->get().texture, stencilVal}); } - beginRenderPassIfNeeded(renderPassDescriptor, nullptr, depth->get().texture); - return; } @@ -321,6 +306,7 @@ void RendererMTL::displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputHeight = outputSize >> 16; auto srcFramebuffer = getColorRenderTarget(inputAddr, inputFormat, inputWidth, outputHeight); + clearColor(nullptr, srcFramebuffer->texture); Math::Rect srcRect = srcFramebuffer->getSubRect(inputAddr, outputWidth, outputHeight); if (verticalFlip) { @@ -355,7 +341,7 @@ void RendererMTL::displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, Metal::BlitPipelineHash hash{destFramebuffer->format, DepthFmt::Unknown1}; auto blitPipeline = blitPipelineCache.get(hash); - beginRenderPassIfNeeded(renderPassDescriptor, destFramebuffer->texture); + beginRenderPassIfNeeded(renderPassDescriptor, false, destFramebuffer->texture); renderCommandEncoder->setRenderPipelineState(blitPipeline); renderCommandEncoder->setFragmentTexture(srcFramebuffer->texture, 0); renderCommandEncoder->setFragmentSamplerState(nearestSampler, 0); @@ -444,24 +430,17 @@ void RendererMTL::drawVertices(PICA::PrimType primType, std::spaninit(); - MTL::RenderPassColorAttachmentDescriptor* colorAttachment = renderPassDescriptor->colorAttachments()->object(0); - colorAttachment->setTexture(colorRenderTarget->texture); - colorAttachment->setLoadAction(MTL::LoadActionLoad); - colorAttachment->setStoreAction(MTL::StoreActionStore); + bool doesClear = clearColor(renderPassDescriptor, colorRenderTarget->texture); if (depthStencilRenderTarget) { - MTL::RenderPassDepthAttachmentDescriptor* depthAttachment = renderPassDescriptor->depthAttachment(); - depthAttachment->setTexture(depthStencilRenderTarget->texture); - depthAttachment->setLoadAction(MTL::LoadActionLoad); - depthAttachment->setStoreAction(MTL::StoreActionStore); + if (clearDepth(renderPassDescriptor, depthStencilRenderTarget->texture)) + doesClear = true; if (depthStencilRenderTarget->format == DepthFmt::Depth24Stencil8) { - MTL::RenderPassStencilAttachmentDescriptor* stencilAttachment = renderPassDescriptor->stencilAttachment(); - stencilAttachment->setTexture(depthStencilRenderTarget->texture); - stencilAttachment->setLoadAction(MTL::LoadActionLoad); - stencilAttachment->setStoreAction(MTL::StoreActionStore); + if (clearStencil(renderPassDescriptor, depthStencilRenderTarget->texture)) + doesClear = true; } } - beginRenderPassIfNeeded(renderPassDescriptor, colorRenderTarget->texture, (depthStencilRenderTarget ? depthStencilRenderTarget->texture : nullptr)); + beginRenderPassIfNeeded(renderPassDescriptor, doesClear, colorRenderTarget->texture, (depthStencilRenderTarget ? depthStencilRenderTarget->texture : nullptr)); // Update the LUT texture if necessary if (gpu.lightingLUTDirty) {