Metal: Fixed blit encoder released without endEncoding on shutdown. (#4005)

Resource updates (texture/buffer updates, texture zero-init) open a blit
encoder that is ended in submit. When updates are pending at shutdown,
RendererShutdownBegin clears m_rendererInitialized, submit is skipped, and
renderFrame's autorelease pool frees the still-open encoder, which Metal
API validation reports as 'Command encoder released without endEncoding'.

Retain the blit encoder while it's open, and end encoding before the final
command buffer kick in shutdown.
This commit is contained in:
Thomas Clegg
2026-09-30 12:04:53 -05:00
committed by GitHub
parent b743ef8f7e
commit 9304c09edc

View File

@@ -1256,6 +1256,7 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
{
NS::AutoreleasePool* pool = NS::AutoreleasePool::alloc()->init();
endEncoding();
m_gpuTimer.shutdown();
m_cmd.kick(false, true);
@@ -1828,8 +1829,7 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
#if BX_PLATFORM_OSX
m_blitCommandEncoder->synchronizeResource(swapChain->m_screenshotTarget);
#endif // BX_PLATFORM_OSX
m_blitCommandEncoder->endEncoding();
m_blitCommandEncoder = NULL;
endBlitEncoding();
m_cmd.kick(false, true);
m_commandBuffer = 0;
@@ -3531,7 +3531,10 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
m_commandBuffer = m_cmd.alloc();
}
// Retained, since it can stay open past the end of renderFrame's autorelease pool
// when submit is skipped (e.g. resource updates pending at shutdown).
m_blitCommandEncoder = m_commandBuffer->blitCommandEncoder();
retain(m_blitCommandEncoder);
}
return m_blitCommandEncoder;
@@ -3618,10 +3621,15 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
m_computeCommandEncoder = NULL;
}
endBlitEncoding();
}
void endBlitEncoding()
{
if (NULL != m_blitCommandEncoder)
{
m_blitCommandEncoder->endEncoding();
m_blitCommandEncoder = NULL;
MTL_RELEASE_I(m_blitCommandEncoder);
}
}
@@ -5965,11 +5973,7 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
}
}
if (0 != m_blitCommandEncoder)
{
m_blitCommandEncoder->endEncoding();
m_blitCommandEncoder = 0;
}
endBlitEncoding();
}
void RendererContextMtl::submitUniformCache(UniformCacheState& _ucs, uint16_t _view)
@@ -6014,11 +6018,7 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
int64_t timeBegin = bx::getHPCounter();
int64_t captureElapsed = 0;
if (m_blitCommandEncoder)
{
m_blitCommandEncoder->endEncoding();
m_blitCommandEncoder = NULL;
}
endBlitEncoding();
updateResolution(_render->m_mainSwapChain, _render->m_reset);