diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp index 6e91d62f..56402ede 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp @@ -1681,14 +1681,22 @@ namespace MobileGL::MG_Backend::DirectVulkan { Bool VkRenderPassManager::EndRenderPass(VkCommandBuffer commandBuffer) { BumpWireImageWriteEpoch(); auto* activeRenderPass = GetActiveRenderPass(); + Vector trackedAttachmentLayouts; + if (activeRenderPass != nullptr) { + trackedAttachmentLayouts = Move(activeRenderPass->trackedAttachmentLayouts); + } vkCmdEndRenderPass(commandBuffer); + // Layout reconciliation below may record barriers, whose guard closes an active + // pass. Detach the ended pass first so that guard cannot recursively end it again. + s_activeRenderPass.Get() = {}; + s_hasActiveRenderPass.Get() = false; // The fast-path memo reuses the ACTIVE render pass; once the pass ends it must not carry // over (the next span may be a different FBO resolved before its render pass is begun). if (s_renderPassManager.Get() != nullptr) { s_renderPassManager.Get()->m_rpFastValid = false; } - if (activeRenderPass != nullptr && !activeRenderPass->trackedAttachmentLayouts.empty()) { - for (const auto& trackedAttachment : activeRenderPass->trackedAttachmentLayouts) { + if (!trackedAttachmentLayouts.empty()) { + for (const auto& trackedAttachment : trackedAttachmentLayouts) { switch (trackedAttachment.target) { case TrackedAttachmentTarget::Texture: MOBILEGL_ASSERT(s_textureManager.Get() != nullptr, "EndRenderPass: texture manager is null"); @@ -1730,8 +1738,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { } } } - s_activeRenderPass.Get() = {}; - s_hasActiveRenderPass.Get() = false; return true; } diff --git a/MobileGL/MG_IntegrationTest/Scenarios/LayeredAttachmentBarrierScenario.cpp b/MobileGL/MG_IntegrationTest/Scenarios/LayeredAttachmentBarrierScenario.cpp index 29b6a736..9a147160 100644 --- a/MobileGL/MG_IntegrationTest/Scenarios/LayeredAttachmentBarrierScenario.cpp +++ b/MobileGL/MG_IntegrationTest/Scenarios/LayeredAttachmentBarrierScenario.cpp @@ -374,5 +374,47 @@ void main() { o_color = vec4(0.1, 0.2, 0.8, 1.0); } << (std::fabs(blitted - 0.125f) < 1e-3f ? " - the destination kept its own clear" : ""); } + TEST_F(LayeredAttachmentBarrierScenario, EndingAMiddleMipDrawPreservesOtherLevels) { + if (!Ready()) return; + GLuint texture = 0; + glGenTextures(1, &texture); + m_textures.push_back(texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexStorage2D(GL_TEXTURE_2D, 3, GL_RGBA8, 16, 16); + for (int level = 0; level < 3; ++level) { + const int size = 16 >> level; + const std::vector initial(size * size, LayerFill(level)); + glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, size, size, + GL_RGBA, GL_UNSIGNED_BYTE, initial.data()); + } + GLuint fbo = 0; + glGenFramebuffers(1, &fbo); + m_fbos.push_back(fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 1); + ASSERT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GL_FRAMEBUFFER_COMPLETE); + glViewport(0, 0, 8, 8); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + glDrawBuffer(GL_COLOR_ATTACHMENT0); + DrawFullViewportQuad(m_program); + // Ending this pass reconciles both lower and upper mip layouts. Their barriers + // must not see the pass as active and recursively end it a second time. + glFinish(); + for (int level = 0; level < 3; ++level) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, level); + glReadBuffer(GL_COLOR_ATTACHMENT0); + Rgba8 actual{}; + glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &actual); + const Rgba8 expected = level == 1 ? kPaintedColor : LayerFill(level); + EXPECT_NEAR(actual.r, expected.r, 2) << "level " << level; + EXPECT_NEAR(actual.g, expected.g, 2); + EXPECT_NEAR(actual.b, expected.b, 2); + EXPECT_EQ(actual.a, expected.a); + } + EXPECT_EQ(FirstGLError(), 0u); + } + } // namespace } // namespace MGITest