From a50b2c422b76236cf08665a800a548ed8579dcee Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 4 Aug 2026 19:18:03 -0400 Subject: [PATCH] [Fix] (DirectVulkan): submit a generated mip chain before a later upload can overtake it Texture uploads go out on a command buffer of their own the moment they happen, while glGenerateMipmap records its blit chain into the frame's command buffer, which is not submitted until the frame ends. So a glTexSubImage2D into a level that was just generated reached the GPU FIRST and the blits then wrote over it. KHR-GL40.texture_gather.base-level does exactly that - generates the chain, then writes the texels it is going to sample into level 1 and points TEXTURE_BASE_LEVEL at it - and read back the generated content instead of what it had written. The image view, the mip range and the upload itself were all correct; only their order on the GPU was not. This is the same hazard the mip-chain-growth recreate above already flushes for, from the other side: there the recorded work had to reach the GPU before an out-of-band copy read the image, here before an out-of-band copy writes it. Submitting at the end of the generation orders every upload that can follow. --- .../DirectVulkan/Renderer/VulkanRenderer.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index d7303faf..8dea0e78 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -8013,6 +8013,18 @@ void main() { } resource->layout = finalLayout; + + // The chain above is GPU work recorded into this frame's command buffer, which is not + // submitted until the frame ends - but a texture upload goes out on a command buffer of + // its own the moment it happens. A glTexSubImage2D into a level this just generated + // would therefore reach the GPU FIRST and be overwritten by these blits, which is how + // KHR-GL40.texture_gather.base-level lost the texels it wrote into level 1 right after + // generating the chain. Submitting here is what orders the two. + if (HasPendingRecordedWork() && FlushPendingCommands()) { + // Fresh command buffer: the sampled-descriptor-set memo describes bindings that + // only existed in the retired one. + m_lastSampledSetValid = false; + } } Uint32 VulkanRenderer::CurrentXfbCounterSlot() {