diff --git a/include/renderer_mtl/mtl_pipeline_cache.hpp b/include/renderer_mtl/mtl_pipeline_cache.hpp new file mode 100644 index 00000000..785d0d4a --- /dev/null +++ b/include/renderer_mtl/mtl_pipeline_cache.hpp @@ -0,0 +1,83 @@ +#pragma once + +#include "pica_to_mtl.hpp" + +using namespace PICA; + +namespace Metal { + +struct PipelineHash { + ColorFmt colorFmt; + DepthFmt depthFmt; +}; + +// Bind the vertex buffer to binding 30 so that it doesn't occupy the lower indices +#define VERTEX_BUFFER_BINDING_INDEX 30 + +// This pipeline only caches the pipeline with all of its color and depth attachment variations +class PipelineCache { +public: + PipelineCache() = default; + + ~PipelineCache() { + clear(); + vertexDescriptor->release(); + vertexFunction->release(); + fragmentFunction->release(); + } + + void set(MTL::Device* dev, MTL::Function* vert, MTL::Function* frag, MTL::VertexDescriptor* vertDesc) { + device = dev; + vertexFunction = vert; + fragmentFunction = frag; + vertexDescriptor = vertDesc; + } + + MTL::RenderPipelineState* get(PipelineHash hash) { + u8 intHash = (u8)hash.colorFmt << 4 | (u8)hash.depthFmt; + auto& pipeline = pipelineCache[intHash]; + if (!pipeline) { + MTL::RenderPipelineDescriptor* desc = MTL::RenderPipelineDescriptor::alloc()->init(); + desc->setVertexFunction(vertexFunction); + desc->setFragmentFunction(fragmentFunction); + desc->setVertexDescriptor(vertexDescriptor); + + auto colorAttachment = desc->colorAttachments()->object(0); + colorAttachment->setPixelFormat(toMTLPixelFormatColor(hash.colorFmt)); + colorAttachment->setBlendingEnabled(true); + colorAttachment->setSourceRGBBlendFactor(MTL::BlendFactorSourceAlpha); + colorAttachment->setDestinationRGBBlendFactor(MTL::BlendFactorOneMinusSourceAlpha); + colorAttachment->setSourceAlphaBlendFactor(MTL::BlendFactorSourceAlpha); + colorAttachment->setDestinationAlphaBlendFactor(MTL::BlendFactorOneMinusSourceAlpha); + + desc->setDepthAttachmentPixelFormat(toMTLPixelFormatDepth(hash.depthFmt)); + + NS::Error* error = nullptr; + pipeline = device->newRenderPipelineState(desc, &error); + if (error) { + Helpers::panic("Error creating draw pipeline state: %s", error->description()->cString(NS::ASCIIStringEncoding)); + } + + desc->release(); + } + + return pipeline; + } + + void clear() { + for (auto& pair : pipelineCache) { + pair.second->release(); + } + pipelineCache.clear(); + } + +private: + std::unordered_map pipelineCache; + + MTL::Device* device; + MTL::Function* vertexFunction; + MTL::Function* fragmentFunction; + MTL::VertexDescriptor* vertexDescriptor; +}; + +} // namespace Metal diff --git a/include/renderer_mtl/pica_to_mtl.hpp b/include/renderer_mtl/pica_to_mtl.hpp index eefcd293..66b890cc 100644 --- a/include/renderer_mtl/pica_to_mtl.hpp +++ b/include/renderer_mtl/pica_to_mtl.hpp @@ -3,28 +3,25 @@ #include #include "PICA/regs.hpp" -// HACK: both functions return a hardcoded format for now, since render pipeline needs to know the format at creation time namespace PICA { inline MTL::PixelFormat toMTLPixelFormatColor(ColorFmt format) { - return MTL::PixelFormatRGBA8Unorm; - //switch (format) { - //case ColorFmt::RGBA8: return MTL::PixelFormatRGBA8Unorm; - //case ColorFmt::RGB8: return MTL::PixelFormatRGBA8Unorm; // TODO: return the correct format - //case ColorFmt::RGBA5551: return MTL::PixelFormatBGR5A1Unorm; - //case ColorFmt::RGB565: return MTL::PixelFormatB5G6R5Unorm; // TODO: check if this is correct - //case ColorFmt::RGBA4: return MTL::PixelFormatABGR4Unorm; // TODO: check if this is correct - //} + switch (format) { + case ColorFmt::RGBA8: return MTL::PixelFormatRGBA8Unorm; + case ColorFmt::RGB8: return MTL::PixelFormatRGBA8Unorm; // TODO: return the correct format + case ColorFmt::RGBA5551: return MTL::PixelFormatBGR5A1Unorm; + case ColorFmt::RGB565: return MTL::PixelFormatB5G6R5Unorm; // TODO: check if this is correct + case ColorFmt::RGBA4: return MTL::PixelFormatABGR4Unorm; // TODO: check if this is correct + } } inline MTL::PixelFormat toMTLPixelFormatDepth(DepthFmt format) { - return MTL::PixelFormatDepth24Unorm_Stencil8; - //switch (format) { - //case DepthFmt::Depth16: return MTL::PixelFormatDepth16Unorm; - //case DepthFmt::Unknown1: return MTL::PixelFormatInvalid; - //case DepthFmt::Depth24: return MTL::PixelFormatDepth32Float; // TODO: is this okay? - //case DepthFmt::Depth24Stencil8: return MTL::PixelFormatDepth24Unorm_Stencil8; - //} + switch (format) { + case DepthFmt::Depth16: return MTL::PixelFormatDepth16Unorm; + case DepthFmt::Unknown1: return MTL::PixelFormatInvalid; + case DepthFmt::Depth24: return MTL::PixelFormatDepth32Float; // TODO: is this okay? + case DepthFmt::Depth24Stencil8: return MTL::PixelFormatDepth24Unorm_Stencil8; + } } } // namespace PICA diff --git a/include/renderer_mtl/renderer_mtl.hpp b/include/renderer_mtl/renderer_mtl.hpp index 43f71b3a..8b6da390 100644 --- a/include/renderer_mtl/renderer_mtl.hpp +++ b/include/renderer_mtl/renderer_mtl.hpp @@ -4,6 +4,7 @@ #include "renderer.hpp" #include "texture.hpp" #include "render_target.hpp" +#include "mtl_pipeline_cache.hpp" // HACK: use the OpenGL cache #include "../renderer_gl/surface_cache.hpp" @@ -38,14 +39,14 @@ class RendererMTL final : public Renderer { SurfaceCache colorRenderTargetCache; SurfaceCache depthStencilRenderTargetCache; SurfaceCache textureCache; + Metal::PipelineCache blitPipelineCache; + Metal::PipelineCache drawPipelineCache; // Helpers MTL::SamplerState* basicSampler; // Pipelines MTL::RenderPipelineState* displayPipeline; - MTL::RenderPipelineState* blitPipeline; - MTL::RenderPipelineState* drawPipeline; // Active state MTL::CommandBuffer* commandBuffer = nullptr; diff --git a/src/core/renderer_mtl/renderer_mtl.cpp b/src/core/renderer_mtl/renderer_mtl.cpp index 2debf592..dfb105e4 100644 --- a/src/core/renderer_mtl/renderer_mtl.cpp +++ b/src/core/renderer_mtl/renderer_mtl.cpp @@ -10,9 +10,6 @@ using namespace PICA; CMRC_DECLARE(RendererMTL); -// Bind the vertex buffer to binding 30 so that it doesn't occupy the lower indices -#define VERTEX_BUFFER_BINDING_INDEX 30 - // HACK: redefinition... PICA::ColorFmt ToColorFormat(u32 format) { switch (format) { @@ -131,96 +128,69 @@ void RendererMTL::initGraphicsContext(SDL_Window* window) { MTL::Function* vertexBlitFunction = library->newFunction(NS::String::string("vertexBlit", NS::ASCIIStringEncoding)); MTL::Function* fragmentBlitFunction = library->newFunction(NS::String::string("fragmentBlit", NS::ASCIIStringEncoding)); - MTL::RenderPipelineDescriptor* blitPipelineDescriptor = MTL::RenderPipelineDescriptor::alloc()->init(); - blitPipelineDescriptor->setVertexFunction(vertexBlitFunction); - blitPipelineDescriptor->setFragmentFunction(fragmentBlitFunction); - auto* blitColorAttachment = blitPipelineDescriptor->colorAttachments()->object(0); - blitColorAttachment->setPixelFormat(MTL::PixelFormat::PixelFormatBGRA8Unorm); - - error = nullptr; - blitPipeline = device->newRenderPipelineState(blitPipelineDescriptor, &error); - if (error) { - Helpers::panic("Error creating blit pipeline state: %s", error->description()->cString(NS::ASCIIStringEncoding)); - } + blitPipelineCache.set(device, vertexBlitFunction, fragmentBlitFunction, nullptr); // Draw MTL::Function* vertexDrawFunction = library->newFunction(NS::String::string("vertexDraw", NS::ASCIIStringEncoding)); MTL::Function* fragmentDrawFunction = library->newFunction(NS::String::string("fragmentDraw", NS::ASCIIStringEncoding)); - MTL::RenderPipelineDescriptor* drawPipelineDescriptor = MTL::RenderPipelineDescriptor::alloc()->init(); - drawPipelineDescriptor->setVertexFunction(vertexDrawFunction); - drawPipelineDescriptor->setFragmentFunction(fragmentDrawFunction); - - auto* drawColorAttachment = drawPipelineDescriptor->colorAttachments()->object(0); - drawColorAttachment->setPixelFormat(MTL::PixelFormatRGBA8Unorm); - drawColorAttachment->setBlendingEnabled(true); - drawColorAttachment->setSourceRGBBlendFactor(MTL::BlendFactorSourceAlpha); - drawColorAttachment->setDestinationRGBBlendFactor(MTL::BlendFactorOneMinusSourceAlpha); - drawColorAttachment->setSourceAlphaBlendFactor(MTL::BlendFactorSourceAlpha); - drawColorAttachment->setDestinationAlphaBlendFactor(MTL::BlendFactorOneMinusSourceAlpha); - // -------- Vertex descriptor -------- - MTL::VertexDescriptor* vertexDescriptor = MTL::VertexDescriptor::alloc()->init(); + MTL::VertexDescriptor* vertexDescriptor = MTL::VertexDescriptor::alloc()->init(); - // Position - MTL::VertexAttributeDescriptor* positionAttribute = vertexDescriptor->attributes()->object(0); - positionAttribute->setFormat(MTL::VertexFormatFloat4); - positionAttribute->setOffset(offsetof(Vertex, s.positions)); - positionAttribute->setBufferIndex(VERTEX_BUFFER_BINDING_INDEX); + // Position + MTL::VertexAttributeDescriptor* positionAttribute = vertexDescriptor->attributes()->object(0); + positionAttribute->setFormat(MTL::VertexFormatFloat4); + positionAttribute->setOffset(offsetof(Vertex, s.positions)); + positionAttribute->setBufferIndex(VERTEX_BUFFER_BINDING_INDEX); - // Quaternion - MTL::VertexAttributeDescriptor* quaternionAttribute = vertexDescriptor->attributes()->object(1); - quaternionAttribute->setFormat(MTL::VertexFormatFloat4); - quaternionAttribute->setOffset(offsetof(Vertex, s.quaternion)); - quaternionAttribute->setBufferIndex(VERTEX_BUFFER_BINDING_INDEX); + // Quaternion + MTL::VertexAttributeDescriptor* quaternionAttribute = vertexDescriptor->attributes()->object(1); + quaternionAttribute->setFormat(MTL::VertexFormatFloat4); + quaternionAttribute->setOffset(offsetof(Vertex, s.quaternion)); + quaternionAttribute->setBufferIndex(VERTEX_BUFFER_BINDING_INDEX); - // Color - MTL::VertexAttributeDescriptor* colorAttribute = vertexDescriptor->attributes()->object(2); - colorAttribute->setFormat(MTL::VertexFormatFloat4); - colorAttribute->setOffset(offsetof(Vertex, s.colour)); - colorAttribute->setBufferIndex(VERTEX_BUFFER_BINDING_INDEX); + // Color + MTL::VertexAttributeDescriptor* colorAttribute = vertexDescriptor->attributes()->object(2); + colorAttribute->setFormat(MTL::VertexFormatFloat4); + colorAttribute->setOffset(offsetof(Vertex, s.colour)); + colorAttribute->setBufferIndex(VERTEX_BUFFER_BINDING_INDEX); - // Texture coordinate 0 - MTL::VertexAttributeDescriptor* texCoord0Attribute = vertexDescriptor->attributes()->object(3); - texCoord0Attribute->setFormat(MTL::VertexFormatFloat2); - texCoord0Attribute->setOffset(offsetof(Vertex, s.texcoord0)); - texCoord0Attribute->setBufferIndex(VERTEX_BUFFER_BINDING_INDEX); + // Texture coordinate 0 + MTL::VertexAttributeDescriptor* texCoord0Attribute = vertexDescriptor->attributes()->object(3); + texCoord0Attribute->setFormat(MTL::VertexFormatFloat2); + texCoord0Attribute->setOffset(offsetof(Vertex, s.texcoord0)); + texCoord0Attribute->setBufferIndex(VERTEX_BUFFER_BINDING_INDEX); - // Texture coordinate 1 - MTL::VertexAttributeDescriptor* texCoord1Attribute = vertexDescriptor->attributes()->object(4); - texCoord1Attribute->setFormat(MTL::VertexFormatFloat2); - texCoord1Attribute->setOffset(offsetof(Vertex, s.texcoord1)); - texCoord1Attribute->setBufferIndex(VERTEX_BUFFER_BINDING_INDEX); + // Texture coordinate 1 + MTL::VertexAttributeDescriptor* texCoord1Attribute = vertexDescriptor->attributes()->object(4); + texCoord1Attribute->setFormat(MTL::VertexFormatFloat2); + texCoord1Attribute->setOffset(offsetof(Vertex, s.texcoord1)); + texCoord1Attribute->setBufferIndex(VERTEX_BUFFER_BINDING_INDEX); - // Texture coordinate 0 W - MTL::VertexAttributeDescriptor* texCoord0WAttribute = vertexDescriptor->attributes()->object(5); - texCoord0WAttribute->setFormat(MTL::VertexFormatFloat); - texCoord0WAttribute->setOffset(offsetof(Vertex, s.texcoord0_w)); - texCoord0WAttribute->setBufferIndex(VERTEX_BUFFER_BINDING_INDEX); + // Texture coordinate 0 W + MTL::VertexAttributeDescriptor* texCoord0WAttribute = vertexDescriptor->attributes()->object(5); + texCoord0WAttribute->setFormat(MTL::VertexFormatFloat); + texCoord0WAttribute->setOffset(offsetof(Vertex, s.texcoord0_w)); + texCoord0WAttribute->setBufferIndex(VERTEX_BUFFER_BINDING_INDEX); - // View - MTL::VertexAttributeDescriptor* viewAttribute = vertexDescriptor->attributes()->object(6); - viewAttribute->setFormat(MTL::VertexFormatFloat3); - viewAttribute->setOffset(offsetof(Vertex, s.view)); - viewAttribute->setBufferIndex(VERTEX_BUFFER_BINDING_INDEX); + // View + MTL::VertexAttributeDescriptor* viewAttribute = vertexDescriptor->attributes()->object(6); + viewAttribute->setFormat(MTL::VertexFormatFloat3); + viewAttribute->setOffset(offsetof(Vertex, s.view)); + viewAttribute->setBufferIndex(VERTEX_BUFFER_BINDING_INDEX); - // Texture coordinate 2 - MTL::VertexAttributeDescriptor* texCoord2Attribute = vertexDescriptor->attributes()->object(7); - texCoord2Attribute->setFormat(MTL::VertexFormatFloat2); - texCoord2Attribute->setOffset(offsetof(Vertex, s.texcoord2)); - texCoord2Attribute->setBufferIndex(VERTEX_BUFFER_BINDING_INDEX); + // Texture coordinate 2 + MTL::VertexAttributeDescriptor* texCoord2Attribute = vertexDescriptor->attributes()->object(7); + texCoord2Attribute->setFormat(MTL::VertexFormatFloat2); + texCoord2Attribute->setOffset(offsetof(Vertex, s.texcoord2)); + texCoord2Attribute->setBufferIndex(VERTEX_BUFFER_BINDING_INDEX); - MTL::VertexBufferLayoutDescriptor* vertexBufferLayout = vertexDescriptor->layouts()->object(VERTEX_BUFFER_BINDING_INDEX); - vertexBufferLayout->setStride(sizeof(Vertex)); - vertexBufferLayout->setStepFunction(MTL::VertexStepFunctionPerVertex); - vertexBufferLayout->setStepRate(1); - drawPipelineDescriptor->setVertexDescriptor(vertexDescriptor); + MTL::VertexBufferLayoutDescriptor* vertexBufferLayout = vertexDescriptor->layouts()->object(VERTEX_BUFFER_BINDING_INDEX); + vertexBufferLayout->setStride(sizeof(Vertex)); + vertexBufferLayout->setStepFunction(MTL::VertexStepFunctionPerVertex); + vertexBufferLayout->setStepRate(1); - error = nullptr; - drawPipeline = device->newRenderPipelineState(drawPipelineDescriptor, &error); - if (error) { - Helpers::panic("Error creating draw pipeline state: %s", error->description()->cString(NS::ASCIIStringEncoding)); - } + drawPipelineCache.set(device, vertexDrawFunction, fragmentDrawFunction, vertexDescriptor); } void RendererMTL::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) { @@ -295,6 +265,10 @@ void RendererMTL::displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, colorAttachment->setClearColor(MTL::ClearColor{0.0, 0.0, 0.0, 1.0}); colorAttachment->setStoreAction(MTL::StoreActionStore); + // Pipeline + Metal::PipelineHash hash{destFramebuffer->format, DepthFmt::Unknown1}; + auto blitPipeline = blitPipelineCache.get(hash); + MTL::RenderCommandEncoder* renderCommandEncoder = commandBuffer->renderCommandEncoder(renderPassDescriptor); renderCommandEncoder->setRenderPipelineState(blitPipeline); renderCommandEncoder->setFragmentTexture(srcFramebuffer->texture, 0); @@ -313,17 +287,21 @@ void RendererMTL::textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32 void RendererMTL::drawVertices(PICA::PrimType primType, std::span vertices) { createCommandBufferIfNeeded(); - MTL::Texture* renderTarget = getColorRenderTarget(colourBufferLoc, colourBufferFormat, fbSize[0], fbSize[1])->texture; + auto renderTarget = getColorRenderTarget(colourBufferLoc, colourBufferFormat, fbSize[0], fbSize[1]); // TODO: don't begin a new render pass every time MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init(); MTL::RenderPassColorAttachmentDescriptor* colorAttachment = renderPassDescriptor->colorAttachments()->object(0); - colorAttachment->setTexture(renderTarget); + colorAttachment->setTexture(renderTarget->texture); colorAttachment->setLoadAction(MTL::LoadActionLoad); colorAttachment->setStoreAction(MTL::StoreActionStore); + // Pipeline + Metal::PipelineHash hash{renderTarget->format, PICA::DepthFmt::Unknown1}; + MTL::RenderPipelineState* pipeline = drawPipelineCache.get(hash); + MTL::RenderCommandEncoder* renderCommandEncoder = commandBuffer->renderCommandEncoder(renderPassDescriptor); - renderCommandEncoder->setRenderPipelineState(drawPipeline); + renderCommandEncoder->setRenderPipelineState(pipeline); // If size is < 4KB, use inline vertex data, otherwise use a buffer if (vertices.size_bytes() < 4 * 1024) { renderCommandEncoder->setVertexBytes(vertices.data(), vertices.size_bytes(), VERTEX_BUFFER_BINDING_INDEX);