use proper render targets

This commit is contained in:
Samuliak 2024-07-02 16:54:48 +02:00
parent 53c9611ac2
commit 56262c2c24
4 changed files with 130 additions and 31 deletions

View file

@ -17,7 +17,7 @@ struct RenderTarget {
MTL::Device* device; MTL::Device* device;
u32 location; u32 location;
PICA::TextureFmt format; PICA::ColorFmt format;
OpenGL::uvec2 size; OpenGL::uvec2 size;
bool valid; bool valid;
@ -28,7 +28,7 @@ struct RenderTarget {
RenderTarget() : valid(false) {} RenderTarget() : valid(false) {}
RenderTarget(MTL::Device* dev, u32 loc, PICA::TextureFmt format, u32 x, u32 y, bool valid = true) RenderTarget(MTL::Device* dev, u32 loc, PICA::ColorFmt format, u32 x, u32 y, bool valid = true)
: device(dev), location(loc), format(format), size({x, y}), valid(valid) { : device(dev), location(loc), format(format), size({x, y}), valid(valid) {
u64 endLoc = (u64)loc + sizeInBytes(); u64 endLoc = (u64)loc + sizeInBytes();
@ -45,7 +45,7 @@ struct RenderTarget {
// For 2 textures to "match" we only care about their locations, formats, and dimensions to match // For 2 textures to "match" we only care about their locations, formats, and dimensions to match
// For other things, such as filtering mode, etc, we can just switch the attributes of the cached texture // For other things, such as filtering mode, etc, we can just switch the attributes of the cached texture
bool matches(Texture& other) { bool matches(RenderTarget& other) {
return location == other.location && format == other.format && return location == other.location && format == other.format &&
size.x() == other.size.x() && size.y() == other.size.y(); size.x() == other.size.x() && size.y() == other.size.y();
} }

View file

@ -35,13 +35,10 @@ class RendererMTL final : public Renderer {
MTL::CommandQueue* commandQueue; MTL::CommandQueue* commandQueue;
// Caches // Caches
SurfaceCache<Metal::Texture, 16, true> colorRenderTargetCache; SurfaceCache<Metal::RenderTarget, 16, true> colorRenderTargetCache;
SurfaceCache<Metal::Texture, 16, true> depthStencilRenderTargetCache; SurfaceCache<Metal::RenderTarget, 16, true> depthStencilRenderTargetCache;
SurfaceCache<Metal::Texture, 256, true> textureCache; SurfaceCache<Metal::Texture, 256, true> textureCache;
// HACK
MTL::Texture* topScreenTexture;
// Helpers // Helpers
MTL::SamplerState* basicSampler; MTL::SamplerState* basicSampler;
@ -58,6 +55,7 @@ class RendererMTL final : public Renderer {
} }
} }
std::optional<Metal::RenderTarget> getColorRenderTarget(u32 addr, PICA::ColorFmt format, u32 width, u32 height, bool createIfnotFound = true);
MTL::Texture* getTexture(Metal::Texture& tex); MTL::Texture* getTexture(Metal::Texture& tex);
void setupTextureEnvState(MTL::RenderCommandEncoder* encoder); void setupTextureEnvState(MTL::RenderCommandEncoder* encoder);
void bindTexturesToSlots(MTL::RenderCommandEncoder* encoder); void bindTexturesToSlots(MTL::RenderCommandEncoder* encoder);

View file

@ -515,7 +515,7 @@ void RendererGL::display() {
const u32 bottomActiveFb = externalRegs[Framebuffer1Select] & 1; const u32 bottomActiveFb = externalRegs[Framebuffer1Select] & 1;
const u32 bottomScreenAddr = externalRegs[bottomActiveFb == 0 ? Framebuffer1AFirstAddr : Framebuffer1ASecondAddr]; const u32 bottomScreenAddr = externalRegs[bottomActiveFb == 0 ? Framebuffer1AFirstAddr : Framebuffer1ASecondAddr];
auto bottomScreen = colourBufferCache.findFromAddress(bottomScreenAddr); auto bottomScreen = colourBufferCache.findFromAddress(bottomScreenAddr);
if (bottomScreen) { if (bottomScreen) {
bottomScreen->get().texture.bind(); bottomScreen->get().texture.bind();
OpenGL::setViewport(40, 0, 320, 240); OpenGL::setViewport(40, 0, 320, 240);
@ -812,4 +812,4 @@ void RendererGL::deinitGraphicsContext() {
// All other GL objects should be invalidated automatically and be recreated by the next call to initGraphicsContext // All other GL objects should be invalidated automatically and be recreated by the next call to initGraphicsContext
// TODO: Make it so that depth and colour buffers get written back to 3DS memory // TODO: Make it so that depth and colour buffers get written back to 3DS memory
printf("RendererGL::DeinitGraphicsContext called\n"); printf("RendererGL::DeinitGraphicsContext called\n");
} }

View file

@ -13,6 +13,15 @@ CMRC_DECLARE(RendererMTL);
// Bind the vertex buffer to binding 30 so that it doesn't occupy the lower indices // Bind the vertex buffer to binding 30 so that it doesn't occupy the lower indices
#define VERTEX_BUFFER_BINDING_INDEX 30 #define VERTEX_BUFFER_BINDING_INDEX 30
// HACK: redefinition...
PICA::ColorFmt ToColorFormat(u32 format) {
switch (format) {
case 2: return PICA::ColorFmt::RGB565;
case 3: return PICA::ColorFmt::RGBA5551;
default: return static_cast<PICA::ColorFmt>(format);
}
}
RendererMTL::RendererMTL(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs) RendererMTL::RendererMTL(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs)
: Renderer(gpu, internalRegs, externalRegs) {} : Renderer(gpu, internalRegs, externalRegs) {}
RendererMTL::~RendererMTL() {} RendererMTL::~RendererMTL() {}
@ -32,18 +41,46 @@ void RendererMTL::display() {
CA::MetalDrawable* drawable = metalLayer->nextDrawable(); CA::MetalDrawable* drawable = metalLayer->nextDrawable();
MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init(); MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init();
MTL::RenderPassColorAttachmentDescriptor* colorAttachment = renderPassDescriptor->colorAttachments()->object(0); MTL::RenderPassColorAttachmentDescriptor* colorAttachment = renderPassDescriptor->colorAttachments()->object(0);
colorAttachment->setTexture(drawable->texture()); colorAttachment->setTexture(drawable->texture());
colorAttachment->setLoadAction(MTL::LoadActionDontCare); colorAttachment->setLoadAction(MTL::LoadActionDontCare);
colorAttachment->setStoreAction(MTL::StoreActionStore); colorAttachment->setStoreAction(MTL::StoreActionStore);
MTL::RenderCommandEncoder* renderCommandEncoder = commandBuffer->renderCommandEncoder(renderPassDescriptor); MTL::RenderCommandEncoder* renderCommandEncoder = commandBuffer->renderCommandEncoder(renderPassDescriptor);
renderCommandEncoder->setRenderPipelineState(displayPipeline); renderCommandEncoder->setRenderPipelineState(displayPipeline);
renderCommandEncoder->setFragmentTexture(topScreenTexture, 0); renderCommandEncoder->setFragmentSamplerState(basicSampler, 0);
renderCommandEncoder->setFragmentSamplerState(basicSampler, 0);
renderCommandEncoder->drawPrimitives(MTL::PrimitiveTypeTriangleStrip, NS::UInteger(0), NS::UInteger(4));
renderCommandEncoder->endEncoding(); 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.has_value()) {
renderCommandEncoder->setFragmentTexture(topScreen->get().texture, 0);
renderCommandEncoder->drawPrimitives(MTL::PrimitiveTypeTriangleStrip, NS::UInteger(0), NS::UInteger(4));
} else {
Helpers::warn("Top screen not found");
}
}
// Bottom screen
{
const u32 bottomActiveFb = externalRegs[Framebuffer1Select] & 1;
const u32 bottomScreenAddr = externalRegs[bottomActiveFb == 0 ? Framebuffer1AFirstAddr : Framebuffer1ASecondAddr];
auto bottomScreen = colorRenderTargetCache.findFromAddress(bottomScreenAddr);
if (bottomScreen.has_value()) {
renderCommandEncoder->setFragmentTexture(bottomScreen->get().texture, 0);
renderCommandEncoder->drawPrimitives(MTL::PrimitiveTypeTriangleStrip, NS::UInteger(0), NS::UInteger(4));
} else {
Helpers::warn("Bottom screen not found");
}
}
renderCommandEncoder->endEncoding();
commandBuffer->presentDrawable(drawable); commandBuffer->presentDrawable(drawable);
commandBuffer->commit(); commandBuffer->commit();
@ -58,15 +95,6 @@ void RendererMTL::initGraphicsContext(SDL_Window* window) {
metalLayer->setDevice(device); metalLayer->setDevice(device);
commandQueue = device->newCommandQueue(); commandQueue = device->newCommandQueue();
// HACK
MTL::TextureDescriptor* descriptor = MTL::TextureDescriptor::alloc()->init();
descriptor->setTextureType(MTL::TextureType2D);
descriptor->setPixelFormat(MTL::PixelFormatRGBA8Unorm);
descriptor->setWidth(400);
descriptor->setHeight(240);
descriptor->setUsage(MTL::TextureUsageRenderTarget | MTL::TextureUsageShaderRead);
topScreenTexture = device->newTexture(descriptor);
// Helpers // Helpers
MTL::SamplerDescriptor* samplerDescriptor = MTL::SamplerDescriptor::alloc()->init(); MTL::SamplerDescriptor* samplerDescriptor = MTL::SamplerDescriptor::alloc()->init();
basicSampler = device->newSamplerState(samplerDescriptor); basicSampler = device->newSamplerState(samplerDescriptor);
@ -110,7 +138,7 @@ void RendererMTL::initGraphicsContext(SDL_Window* window) {
drawPipelineDescriptor->setFragmentFunction(fragmentDrawFunction); drawPipelineDescriptor->setFragmentFunction(fragmentDrawFunction);
// HACK // HACK
auto* drawColorAttachment = drawPipelineDescriptor->colorAttachments()->object(0); auto* drawColorAttachment = drawPipelineDescriptor->colorAttachments()->object(0);
drawColorAttachment->setPixelFormat(topScreenTexture->pixelFormat()); drawColorAttachment->setPixelFormat(MTL::PixelFormatRGBA8Unorm);
drawColorAttachment->setBlendingEnabled(true); drawColorAttachment->setBlendingEnabled(true);
drawColorAttachment->setSourceRGBBlendFactor(MTL::BlendFactorSourceAlpha); drawColorAttachment->setSourceRGBBlendFactor(MTL::BlendFactorSourceAlpha);
drawColorAttachment->setDestinationRGBBlendFactor(MTL::BlendFactorOneMinusSourceAlpha); drawColorAttachment->setDestinationRGBBlendFactor(MTL::BlendFactorOneMinusSourceAlpha);
@ -187,6 +215,58 @@ void RendererMTL::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 c
} }
void RendererMTL::displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) { void RendererMTL::displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) {
createCommandBufferIfNeeded();
const u32 inputWidth = inputSize & 0xffff;
const u32 inputHeight = inputSize >> 16;
const auto inputFormat = ToColorFormat(Helpers::getBits<8, 3>(flags));
const auto outputFormat = ToColorFormat(Helpers::getBits<12, 3>(flags));
const bool verticalFlip = flags & 1;
const PICA::Scaling scaling = static_cast<PICA::Scaling>(Helpers::getBits<24, 2>(flags));
u32 outputWidth = outputSize & 0xffff;
u32 outputHeight = outputSize >> 16;
auto srcFramebuffer = getColorRenderTarget(inputAddr, inputFormat, inputWidth, outputHeight);
Math::Rect<u32> srcRect = srcFramebuffer->getSubRect(inputAddr, outputWidth, outputHeight);
if (verticalFlip) {
std::swap(srcRect.bottom, srcRect.top);
}
// Apply scaling for the destination rectangle.
if (scaling == PICA::Scaling::X || scaling == PICA::Scaling::XY) {
outputWidth >>= 1;
}
if (scaling == PICA::Scaling::XY) {
outputHeight >>= 1;
}
auto destFramebuffer = getColorRenderTarget(outputAddr, outputFormat, outputWidth, outputHeight);
Math::Rect<u32> destRect = destFramebuffer->getSubRect(outputAddr, outputWidth, outputHeight);
if (inputWidth != outputWidth) {
// Helpers::warn("Strided display transfer is not handled correctly!\n");
}
// TODO: respect regions
MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init();
MTL::RenderPassColorAttachmentDescriptor* colorAttachment = renderPassDescriptor->colorAttachments()->object(0);
colorAttachment->setTexture(destFramebuffer->texture);
colorAttachment->setLoadAction(MTL::LoadActionClear);
colorAttachment->setClearColor(MTL::ClearColor{0.0, 0.0, 0.0, 1.0});
colorAttachment->setStoreAction(MTL::StoreActionStore);
MTL::RenderCommandEncoder* renderCommandEncoder = commandBuffer->renderCommandEncoder(renderPassDescriptor);
renderCommandEncoder->setRenderPipelineState(displayPipeline);
renderCommandEncoder->setFragmentTexture(srcFramebuffer->texture, 0);
renderCommandEncoder->setFragmentSamplerState(basicSampler, 0);
renderCommandEncoder->drawPrimitives(MTL::PrimitiveTypeTriangleStrip, NS::UInteger(0), NS::UInteger(4));
renderCommandEncoder->endEncoding();
// TODO: implement // TODO: implement
Helpers::warn("RendererMTL::displayTransfer not implemented"); Helpers::warn("RendererMTL::displayTransfer not implemented");
} }
@ -199,10 +279,12 @@ void RendererMTL::textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32
void RendererMTL::drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) { void RendererMTL::drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) {
createCommandBufferIfNeeded(); createCommandBufferIfNeeded();
MTL::Texture* renderTarget = getColorRenderTarget(colourBufferLoc, colourBufferFormat, fbSize[0], fbSize[1])->texture;
// TODO: don't begin a new render pass every time // TODO: don't begin a new render pass every time
MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init(); MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init();
MTL::RenderPassColorAttachmentDescriptor* colorAttachment = renderPassDescriptor->colorAttachments()->object(0); MTL::RenderPassColorAttachmentDescriptor* colorAttachment = renderPassDescriptor->colorAttachments()->object(0);
colorAttachment->setTexture(topScreenTexture); colorAttachment->setTexture(renderTarget);
colorAttachment->setLoadAction(MTL::LoadActionLoad); colorAttachment->setLoadAction(MTL::LoadActionLoad);
colorAttachment->setStoreAction(MTL::StoreActionStore); colorAttachment->setStoreAction(MTL::StoreActionStore);
@ -243,6 +325,25 @@ void RendererMTL::deinitGraphicsContext() {
Helpers::warn("RendererMTL::deinitGraphicsContext not implemented"); Helpers::warn("RendererMTL::deinitGraphicsContext not implemented");
} }
std::optional<Metal::RenderTarget> 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.
auto buffer = colorRenderTargetCache.findFromAddress(addr);
if (buffer.has_value()) {
return buffer.value().get();
}
if (!createIfnotFound) {
return std::nullopt;
}
// Otherwise create and cache a new buffer.
Metal::RenderTarget sampleBuffer(device, addr, format, width, height);
return colorRenderTargetCache.add(sampleBuffer);
}
MTL::Texture* RendererMTL::getTexture(Metal::Texture& tex) { MTL::Texture* RendererMTL::getTexture(Metal::Texture& tex) {
auto buffer = textureCache.find(tex); auto buffer = textureCache.find(tex);