Merge pull request #4 from SamoZ256/metal-load-ops

Metal load ops
This commit is contained in:
SamoZ256 2024-07-07 08:42:42 +02:00 committed by GitHub
commit eb24b2490a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 112 additions and 60 deletions

View file

@ -13,6 +13,10 @@
class GPU;
struct Color4 {
float r, g, b, a;
};
class RendererMTL final : public Renderer {
public:
RendererMTL(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs);
@ -57,6 +61,11 @@ class RendererMTL final : public Renderer {
MTL::RenderPipelineState* displayPipeline;
MTL::RenderPipelineState* copyToLutTexturePipeline;
// Clears
std::map<MTL::Texture*, Color4> colorClearOps;
std::map<MTL::Texture*, float> depthClearOps;
std::map<MTL::Texture*, u8> stencilClearOps;
// Active state
MTL::CommandBuffer* commandBuffer = nullptr;
MTL::RenderCommandEncoder* renderCommandEncoder = nullptr;
@ -76,10 +85,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 && !(lastDepthTexture && !depthTexture))) {
endRenderPass();
renderCommandEncoder = commandBuffer->renderCommandEncoder(renderPassDescriptor);
@ -100,6 +109,64 @@ class RendererMTL final : public Renderer {
}
}
template<typename AttachmentT, typename ClearDataT, typename GetAttachmentT, typename SetClearDataT>
inline void clearAttachment(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* texture, ClearDataT clearData, GetAttachmentT getAttachment, SetClearDataT setClearData) {
bool beginRenderPass = (renderPassDescriptor == nullptr);
if (!renderPassDescriptor) {
renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init();
}
AttachmentT* attachment = getAttachment(renderPassDescriptor);
attachment->setTexture(texture);
setClearData(attachment, clearData);
attachment->setLoadAction(MTL::LoadActionClear);
attachment->setStoreAction(MTL::StoreActionStore);
if (beginRenderPass) {
if (std::is_same<AttachmentT, MTL::RenderPassColorAttachmentDescriptor>::value)
beginRenderPassIfNeeded(renderPassDescriptor, true, texture);
else
beginRenderPassIfNeeded(renderPassDescriptor, true, nullptr, texture);
}
}
template<typename AttachmentT, typename ClearDataT, typename GetAttachmentT, typename SetClearDataT>
inline bool clearAttachment(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* texture, std::map<MTL::Texture*, ClearDataT>& clearOps, GetAttachmentT getAttachment, SetClearDataT setClearData) {
auto it = clearOps.find(texture);
if (it != clearOps.end()) {
clearAttachment<AttachmentT>(renderPassDescriptor, texture, it->second, getAttachment, setClearData);
clearOps.erase(it);
return true;
}
if (renderPassDescriptor) {
AttachmentT* attachment = getAttachment(renderPassDescriptor);
attachment->setTexture(texture);
attachment->setLoadAction(MTL::LoadActionLoad);
attachment->setStoreAction(MTL::StoreActionStore);
}
return false;
}
bool clearColor(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* texture) {
return clearAttachment<MTL::RenderPassColorAttachmentDescriptor, Color4>(renderPassDescriptor, texture, colorClearOps, [](MTL::RenderPassDescriptor* renderPassDescriptor) { return renderPassDescriptor->colorAttachments()->object(0); }, [](auto attachment, auto& color) {
attachment->setClearColor(MTL::ClearColor(color.r, color.g, color.b, color.a));
});
}
bool clearDepth(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* texture) {
return clearAttachment<MTL::RenderPassDepthAttachmentDescriptor, float>(renderPassDescriptor, texture, depthClearOps, [](MTL::RenderPassDescriptor* renderPassDescriptor) { return renderPassDescriptor->depthAttachment(); }, [](auto attachment, auto& depth) {
attachment->setClearDepth(depth);
});
}
bool clearStencil(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* texture) {
return clearAttachment<MTL::RenderPassStencilAttachmentDescriptor, u8>(renderPassDescriptor, texture, stencilClearOps, [](MTL::RenderPassDescriptor* renderPassDescriptor) { return renderPassDescriptor->stencilAttachment(); }, [](auto attachment, auto& stencil) {
attachment->setClearStencil(stencil);
});
}
std::optional<Metal::ColorRenderTarget> getColorRenderTarget(u32 addr, PICA::ColorFmt format, u32 width, u32 height, bool createIfnotFound = true);
Metal::DepthStencilRenderTarget& getDepthRenderTarget();
Metal::Texture& getTexture(Metal::Texture& tex);

View file

@ -53,6 +53,27 @@ void RendererMTL::display() {
return;
}
using namespace PICA::ExternalRegs;
// Top screen
const u32 topActiveFb = externalRegs[Framebuffer0Select] & 1;
const u32 topScreenAddr = externalRegs[topActiveFb == 0 ? Framebuffer0AFirstAddr : Framebuffer0ASecondAddr];
auto topScreen = colorRenderTargetCache.findFromAddress(topScreenAddr);
if (topScreen) {
clearColor(nullptr, topScreen->get().texture);
}
// Bottom screen
const u32 bottomActiveFb = externalRegs[Framebuffer1Select] & 1;
const u32 bottomScreenAddr = externalRegs[bottomActiveFb == 0 ? Framebuffer1AFirstAddr : Framebuffer1ASecondAddr];
auto bottomScreen = colorRenderTargetCache.findFromAddress(bottomScreenAddr);
if (bottomScreen) {
clearColor(nullptr, bottomScreen->get().texture);
}
// -------- Draw --------
MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init();
MTL::RenderPassColorAttachmentDescriptor* colorAttachment = renderPassDescriptor->colorAttachments()->object(0);
colorAttachment->setTexture(drawable->texture());
@ -60,36 +81,22 @@ 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);
using namespace PICA::ExternalRegs;
// Top screen
{
const u32 topActiveFb = externalRegs[Framebuffer0Select] & 1;
const u32 topScreenAddr = externalRegs[topActiveFb == 0 ? Framebuffer0AFirstAddr : Framebuffer0ASecondAddr];
auto topScreen = colorRenderTargetCache.findFromAddress(topScreenAddr);
if (topScreen) {
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));
}
if (topScreen) {
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));
}
// Bottom screen
{
const u32 bottomActiveFb = externalRegs[Framebuffer1Select] & 1;
const u32 bottomScreenAddr = externalRegs[bottomActiveFb == 0 ? Framebuffer1AFirstAddr : Framebuffer1ASecondAddr];
auto bottomScreen = colorRenderTargetCache.findFromAddress(bottomScreenAddr);
if (bottomScreen) {
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));
}
if (bottomScreen) {
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));
}
endRenderPass();
@ -264,14 +271,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[color->get().texture] = {r, g, b, a};
return;
}
@ -286,23 +286,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[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[depth->get().texture] = stencilVal;
}
beginRenderPassIfNeeded(renderPassDescriptor, nullptr, depth->get().texture);
return;
}
@ -321,6 +311,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<u32> srcRect = srcFramebuffer->getSubRect(inputAddr, outputWidth, outputHeight);
if (verticalFlip) {
@ -337,6 +328,7 @@ void RendererMTL::displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize,
}
auto destFramebuffer = getColorRenderTarget(outputAddr, outputFormat, outputWidth, outputHeight);
// TODO: clear if not blitting to the whole framebuffer
Math::Rect<u32> destRect = destFramebuffer->getSubRect(outputAddr, outputWidth, outputHeight);
if (inputWidth != outputWidth) {
@ -355,7 +347,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 +436,17 @@ void RendererMTL::drawVertices(PICA::PrimType primType, std::span<const PICA::Ve
// -------- Render --------
MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init();
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) {