mirror of
https://github.com/cemu-project/Cemu.git
synced 2026-07-10 01:24:41 -06:00
Metal: zero-initialize render target textures on first use
Private storage Metal textures have undefined initial content. Using LoadActionLoad on first render pass reads this garbage, causing corrupt/brownish backgrounds on scene transitions. Track initialization state per texture and use LoadActionClear (black/zero) for the first render pass on uninitialized textures, matching OpenGL's zero-initialization behavior and what MoltenVK does implicitly for the Vulkan backend.
This commit is contained in:
parent
cba8ef79a7
commit
a098946d15
@ -19,6 +19,8 @@ public:
|
||||
|
||||
void AllocateOnHost() override;
|
||||
|
||||
bool m_isInitialized = false;
|
||||
|
||||
protected:
|
||||
LatteTextureView* CreateView(Latte::E_DIM dim, Latte::E_GX2SURFFMT format, sint32 firstMip, sint32 mipCount, sint32 firstSlice, sint32 sliceCount) override;
|
||||
|
||||
|
||||
@ -778,6 +778,8 @@ void MetalRenderer::texture_loadSlice(LatteTexture* hostTexture, sint32 width, s
|
||||
// Copy the data from the temporary buffer to the texture
|
||||
blitCommandEncoder->copyFromBuffer(allocation.mtlBuffer, allocation.bufferOffset, bytesPerRow, 0, MTL::Size(width, height, 1), textureMtl->GetTexture(), sliceIndex, mipIndex, MTL::Origin(0, 0, offsetZ));
|
||||
//}
|
||||
|
||||
textureMtl->m_isInitialized = true;
|
||||
}
|
||||
|
||||
void MetalRenderer::texture_clearColorSlice(LatteTexture* hostTexture, sint32 sliceIndex, sint32 mipIndex, float r, float g, float b, float a)
|
||||
@ -788,6 +790,8 @@ void MetalRenderer::texture_clearColorSlice(LatteTexture* hostTexture, sint32 sl
|
||||
return;
|
||||
}
|
||||
|
||||
static_cast<LatteTextureMtl*>(hostTexture)->m_isInitialized = true;
|
||||
|
||||
auto mtlTexture = static_cast<LatteTextureMtl*>(hostTexture)->GetTexture();
|
||||
|
||||
ClearColorTextureInternal(mtlTexture, sliceIndex, mipIndex, r, g, b, a);
|
||||
@ -826,6 +830,8 @@ void MetalRenderer::texture_clearDepthSlice(LatteTexture* hostTexture, uint32 sl
|
||||
stencilAttachment->setLevel(mipIndex);
|
||||
}
|
||||
|
||||
static_cast<LatteTextureMtl*>(hostTexture)->m_isInitialized = true;
|
||||
|
||||
GetTemporaryRenderCommandEncoder(renderPassDescriptor);
|
||||
EndEncoding();
|
||||
|
||||
@ -1784,9 +1790,54 @@ MTL::RenderCommandEncoder* MetalRenderer::GetRenderCommandEncoder(bool forceRecr
|
||||
|
||||
auto commandBuffer = GetCommandBuffer();
|
||||
|
||||
// Use LoadActionClear for any uninitialized render target textures (Private storage textures have undefined initial content)
|
||||
auto fbo = static_cast<CachedFBOMtl*>(m_state.m_activeFBO.m_fbo);
|
||||
auto renderPassDescriptor = fbo->GetRenderPassDescriptor();
|
||||
for (uint8 i = 0; i < 8; i++)
|
||||
{
|
||||
if (fbo->colorBuffer[i].texture)
|
||||
{
|
||||
auto texMtl = static_cast<LatteTextureMtl*>(fbo->colorBuffer[i].texture->baseTexture);
|
||||
if (!texMtl->m_isInitialized)
|
||||
{
|
||||
renderPassDescriptor->colorAttachments()->object(i)->setLoadAction(MTL::LoadActionClear);
|
||||
renderPassDescriptor->colorAttachments()->object(i)->setClearColor(MTL::ClearColor(0, 0, 0, 0));
|
||||
texMtl->m_isInitialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fbo->depthBuffer.texture)
|
||||
{
|
||||
auto texMtl = static_cast<LatteTextureMtl*>(fbo->depthBuffer.texture->baseTexture);
|
||||
if (!texMtl->m_isInitialized)
|
||||
{
|
||||
renderPassDescriptor->depthAttachment()->setLoadAction(MTL::LoadActionClear);
|
||||
renderPassDescriptor->depthAttachment()->setClearDepth(1.0);
|
||||
if (fbo->depthBuffer.hasStencil && GetMtlPixelFormatInfo(fbo->depthBuffer.texture->format, true).hasStencil)
|
||||
{
|
||||
renderPassDescriptor->stencilAttachment()->setLoadAction(MTL::LoadActionClear);
|
||||
renderPassDescriptor->stencilAttachment()->setClearStencil(0);
|
||||
}
|
||||
texMtl->m_isInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
auto pool = NS::AutoreleasePool::alloc()->init();
|
||||
auto renderCommandEncoder = commandBuffer->renderCommandEncoder(m_state.m_activeFBO.m_fbo->GetRenderPassDescriptor())->retain();
|
||||
auto renderCommandEncoder = commandBuffer->renderCommandEncoder(renderPassDescriptor)->retain();
|
||||
pool->release();
|
||||
|
||||
// Reset any temporarily modified load actions back to LoadActionLoad
|
||||
for (uint8 i = 0; i < 8; i++)
|
||||
{
|
||||
if (fbo->colorBuffer[i].texture)
|
||||
renderPassDescriptor->colorAttachments()->object(i)->setLoadAction(MTL::LoadActionLoad);
|
||||
}
|
||||
if (fbo->depthBuffer.texture)
|
||||
{
|
||||
renderPassDescriptor->depthAttachment()->setLoadAction(MTL::LoadActionLoad);
|
||||
if (fbo->depthBuffer.hasStencil && GetMtlPixelFormatInfo(fbo->depthBuffer.texture->format, true).hasStencil)
|
||||
renderPassDescriptor->stencilAttachment()->setLoadAction(MTL::LoadActionLoad);
|
||||
}
|
||||
#ifdef CEMU_DEBUG_ASSERT
|
||||
renderCommandEncoder->setLabel(GetLabel("Render command encoder", renderCommandEncoder));
|
||||
#endif
|
||||
|
||||
Loading…
Reference in New Issue
Block a user