use depth stencil render target

This commit is contained in:
Samuliak 2024-07-03 10:21:49 +02:00
parent 9241306d4d
commit d977f7ef85
4 changed files with 224 additions and 94 deletions

View file

@ -0,0 +1,55 @@
#pragma once
#include "pica_to_mtl.hpp"
using namespace PICA;
namespace Metal {
struct DepthStencilHash {
bool depthWrite;
u8 depthFunc;
};
class DepthStencilCache {
public:
DepthStencilCache() = default;
~DepthStencilCache() {
clear();
}
void set(MTL::Device* dev) {
device = dev;
}
MTL::DepthStencilState* get(DepthStencilHash hash) {
u8 intHash = hash.depthWrite | (hash.depthFunc << 1);
auto& depthStencilState = depthStencilCache[intHash];
if (!depthStencilState) {
MTL::DepthStencilDescriptor* desc = MTL::DepthStencilDescriptor::alloc()->init();
desc->setDepthWriteEnabled(hash.depthWrite);
desc->setDepthCompareFunction(toMTLCompareFunc(hash.depthFunc));
depthStencilState = device->newDepthStencilState(desc);
desc->release();
}
return depthStencilState;
}
void clear() {
for (auto& pair : depthStencilCache) {
pair.second->release();
}
depthStencilCache.clear();
}
private:
std::unordered_map<u8, MTL::DepthStencilState*> depthStencilCache;
MTL::Device* device;
};
} // namespace Metal

View file

@ -20,8 +20,25 @@ inline MTL::PixelFormat toMTLPixelFormatDepth(DepthFmt 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;
// Apple sillicon doesn't support 24-bit depth buffers, so we use 32-bit instead
case DepthFmt::Depth24Stencil8: return MTL::PixelFormatDepth32Float_Stencil8;
}
}
inline MTL::CompareFunction toMTLCompareFunc(u8 func) {
switch (func) {
case 0: return MTL::CompareFunctionNever;
case 1: return MTL::CompareFunctionAlways;
case 2: return MTL::CompareFunctionEqual;
case 3: return MTL::CompareFunctionNotEqual;
case 4: return MTL::CompareFunctionLess;
case 5: return MTL::CompareFunctionLessEqual;
case 6: return MTL::CompareFunctionGreater;
case 7: return MTL::CompareFunctionGreaterEqual;
default: panic("Unknown compare function %u", func);
}
return MTL::CompareFunctionAlways;
}
} // namespace PICA

View file

@ -5,6 +5,7 @@
#include "texture.hpp"
#include "render_target.hpp"
#include "mtl_pipeline_cache.hpp"
#include "mtl_depth_stencil_cache.hpp"
// HACK: use the OpenGL cache
#include "../renderer_gl/surface_cache.hpp"
@ -41,6 +42,7 @@ class RendererMTL final : public Renderer {
SurfaceCache<Metal::Texture, 256, true> textureCache;
Metal::PipelineCache blitPipelineCache;
Metal::PipelineCache drawPipelineCache;
Metal::DepthStencilCache depthStencilCache;
// Helpers
MTL::SamplerState* basicSampler;
@ -58,6 +60,7 @@ class RendererMTL final : public Renderer {
}
std::optional<Metal::ColorRenderTarget> getColorRenderTarget(u32 addr, PICA::ColorFmt format, u32 width, u32 height, bool createIfnotFound = true);
Metal::DepthStencilRenderTarget& getDepthRenderTarget();
MTL::Texture* getTexture(Metal::Texture& tex);
void setupTextureEnvState(MTL::RenderCommandEncoder* encoder);
void bindTexturesToSlots(MTL::RenderCommandEncoder* encoder);

View file

@ -191,6 +191,9 @@ void RendererMTL::initGraphicsContext(SDL_Window* window) {
vertexBufferLayout->setStepRate(1);
drawPipelineCache.set(device, vertexDrawFunction, fragmentDrawFunction, vertexDescriptor);
// Depth stencil cache
depthStencilCache.set(device);
}
void RendererMTL::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) {
@ -287,17 +290,56 @@ void RendererMTL::textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32
void RendererMTL::drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) {
createCommandBufferIfNeeded();
auto renderTarget = getColorRenderTarget(colourBufferLoc, colourBufferFormat, fbSize[0], fbSize[1]);
// Color
auto colorRenderTarget = getColorRenderTarget(colourBufferLoc, colourBufferFormat, fbSize[0], fbSize[1]);
// Depth stencil
const u32 depthControl = regs[PICA::InternalRegs::DepthAndColorMask];
const bool depthWrite = regs[PICA::InternalRegs::DepthBufferWrite];
const bool depthTestEnable = depthControl & 0x1;
const bool depthWriteEnable = Helpers::getBit<12>(depthControl);
const u8 depthFunc = Helpers::getBits<4, 3>(depthControl);
const u8 colorMask = Helpers::getBits<8, 4>(depthControl);
// gl.setColourMask(colorMask & 0x1, colorMask & 0x2, colorMask & 0x4, colorMask & 0x8);
const u32 stencilConfig = regs[PICA::InternalRegs::StencilTest];
const bool stencilEnable = Helpers::getBit<0>(stencilConfig);
std::optional<Metal::DepthStencilRenderTarget> depthStencilRenderTarget = std::nullopt;
Metal::DepthStencilHash depthStencilHash{false, 1};
if (depthTestEnable) {
depthStencilHash.depthWrite = depthWriteEnable && depthWrite;
depthStencilHash.depthFunc = depthFunc;
depthStencilRenderTarget = getDepthRenderTarget();
} else {
if (depthWriteEnable) {
depthStencilHash.depthWrite = true;
depthStencilRenderTarget = getDepthRenderTarget();
} else if (stencilEnable) {
depthStencilRenderTarget = getDepthRenderTarget();
}
}
// TODO: stencil tests
// 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->texture);
colorAttachment->setTexture(colorRenderTarget->texture);
colorAttachment->setLoadAction(MTL::LoadActionLoad);
colorAttachment->setStoreAction(MTL::StoreActionStore);
if (depthStencilRenderTarget) {
MTL::RenderPassDepthAttachmentDescriptor* depthAttachment = renderPassDescriptor->depthAttachment();
depthAttachment->setTexture(depthStencilRenderTarget->texture);
depthAttachment->setLoadAction(MTL::LoadActionLoad);
depthAttachment->setStoreAction(MTL::StoreActionStore);
}
// Pipeline
Metal::PipelineHash hash{renderTarget->format, PICA::DepthFmt::Unknown1};
Metal::PipelineHash hash{colorRenderTarget->format, DepthFmt::Unknown1};
if (depthStencilRenderTarget) {
hash.depthFmt = depthStencilRenderTarget->format;
}
MTL::RenderPipelineState* pipeline = drawPipelineCache.get(hash);
MTL::RenderCommandEncoder* renderCommandEncoder = commandBuffer->renderCommandEncoder(renderPassDescriptor);
@ -337,7 +379,9 @@ void RendererMTL::deinitGraphicsContext() {
Helpers::warn("RendererMTL::deinitGraphicsContext not implemented");
}
std::optional<Metal::ColorRenderTarget> RendererMTL::getColorRenderTarget(u32 addr, PICA::ColorFmt format, u32 width, u32 height, bool createIfnotFound) {
std::optional<Metal::ColorRenderTarget> RendererMTL::getColorRenderTarget(
u32 addr, PICA::ColorFmt format, u32 width, u32 height, bool createIfnotFound
) {
// Try to find an already existing buffer that contains the provided address
// This is a more relaxed check compared to getColourFBO as display transfer/texcopy may refer to
// subrect of a surface and in case of texcopy we don't know the format of the surface.
@ -356,6 +400,17 @@ std::optional<Metal::ColorRenderTarget> RendererMTL::getColorRenderTarget(u32 ad
return colorRenderTargetCache.add(sampleBuffer);
}
Metal::DepthStencilRenderTarget& RendererMTL::getDepthRenderTarget() {
Metal::DepthStencilRenderTarget sampleBuffer(device, depthBufferLoc, depthBufferFormat, fbSize[0], fbSize[1]);
auto buffer = depthStencilRenderTargetCache.find(sampleBuffer);
if (buffer.has_value()) {
return buffer.value().get();
} else {
return depthStencilRenderTargetCache.add(sampleBuffer);
}
}
MTL::Texture* RendererMTL::getTexture(Metal::Texture& tex) {
auto buffer = textureCache.find(tex);