From 450215d12ca46f75eb9a28177a665f90bcd1dfe7 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Sun, 26 Jul 2026 11:30:54 -0400 Subject: [PATCH] [Fix] (DirectVulkan): implement color renderbuffer attachments - render pass/pipeline/blit/copy/readback/clear paths treated color renderbuffers as absent (writes masked to VK_ATTACHMENT_UNUSED, glClear dropped, readback zeros) --- .../Renderer/VkRenderPassManager.cpp | 103 ++++++++- .../Renderer/VkRenderPassManager.h | 13 +- .../DirectVulkan/Renderer/VulkanRenderer.cpp | 217 ++++++++++++++++-- .../DirectVulkan/Renderer/VulkanRenderer.h | 3 + 4 files changed, 309 insertions(+), 27 deletions(-) diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp index ac11c599..99b276cb 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp @@ -251,11 +251,13 @@ namespace MobileGL::MG_Backend::DirectVulkan { const auto internalFormat = renderbuffer->GetInternalFormat(); const VkFormat format = MG_Util::ConvertTextureInternalFormatToVkEnum(internalFormat); const VkImageAspectFlags aspect = ResolveImageAspectMaskForFormat(format); - if ((aspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0) { - MGLOG_E("GetOrCreateRenderbufferResource: color renderbuffer %u is not supported by DirectVulkan render passes yet", - renderbuffer->GetExternalIndex()); - return nullptr; - } + // Renderbuffers are never sampled (GL has no way to bind one to a sampler), so the + // usage set is attachment + transfer: transfer covers readback (vkCmdCopyImageToBuffer), + // BlitFramebuffer, CopyTexImage sources, and out-of-render-pass clear materialization. + const VkImageUsageFlags imageUsage = + ((aspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0 ? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT + : VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; auto& resource = m_renderbufferResources[renderbuffer.get()]; const Bool needsCreate = @@ -285,7 +287,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { imageInfo.format = format; imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + imageInfo.usage = imageUsage; imageInfo.samples = sampleCount; imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; @@ -386,6 +388,18 @@ namespace MobileGL::MG_Backend::DirectVulkan { void VkRenderPassManager::QueueRenderbufferClear( GLbitfield mask, const ClearFramebufferPayload& clearPayload, const MG_State::GLState::FramebufferObject& drawFbo) { + if ((mask & GL_COLOR_BUFFER_BIT) != 0) { + // Color renderbuffer draw buffers take the framebuffer-level clear too; texture + // attachments are skipped by the per-attachment overload's IsRenderbuffer guard. + for (const auto attachmentType : drawFbo.GetDrawBuffers()) { + if (attachmentType == FramebufferAttachmentType::None) { + continue; + } + QueueRenderbufferClear( + ClearAttachmentPayload{.mask = GL_COLOR_BUFFER_BIT, .color = clearPayload.color}, + drawFbo.GetAttachment(attachmentType)); + } + } if ((mask & GL_DEPTH_BUFFER_BIT) != 0) { QueueRenderbufferClear( ClearAttachmentPayload{.mask = GL_DEPTH_BUFFER_BIT, .depth = clearPayload.depth}, @@ -682,6 +696,83 @@ namespace MobileGL::MG_Backend::DirectVulkan { // assuming default FBO has the right param for (Uint32 i = 0; i < colorAttachmentSlotCount; ++i) { auto drawbuf = drawbufs[i]; + + // Renderbuffer color attachments mirror the texture path below, with the + // resource (image/view/format/layout) coming from the render-pass manager's + // renderbuffer store instead of the texture manager. + if (drawbuf != FramebufferAttachmentType::None && !isDefaultFbo) { + const auto& rbAtt = fbo.GetAttachment(drawbuf); + if (rbAtt.IsRenderbuffer() && rbAtt.IsComplete()) { + const auto& renderbuffer = rbAtt.GetRenderbuffer(); + auto* rbResource = GetOrCreateRenderbufferResource(renderbuffer); + if (rbResource == nullptr || (rbResource->aspect & VK_IMAGE_ASPECT_COLOR_BIT) == 0) { + MGLOG_E("GetOrCreateRenderPass: draw buffer slot %u on FBO %u has an unsupported color " + "renderbuffer %u; using VK_ATTACHMENT_UNUSED", + i, fbo.GetExternalIndex(), renderbuffer->GetExternalIndex()); + continue; + } + + const Uint32 rbAttachmentIndex = static_cast(attachmentDescriptions.size()); + attachmentDescriptions.emplace_back(); + VkAttachmentDescription& rbDesc = attachmentDescriptions.back(); + + ClearAttachmentPayload rbClearPayload{}; + Bool rbHasClear = GetPendingRenderbufferClear(renderbuffer.get(), rbClearPayload) && + (rbClearPayload.mask & GL_COLOR_BUFFER_BIT) != 0; + if (rbHasClear && + MG_Util::GetBaseInternalFormatComponentCount(renderbuffer->GetInternalFormat()) == 3) { + // RGB renderbuffers are backed by an RGBA image; the missing alpha reads as 1. + rbClearPayload.color = + FloatVec4(rbClearPayload.color.x(), rbClearPayload.color.y(), + rbClearPayload.color.z(), 1.0f); + } + + const VkImageLayout trackedRbLayout = rbResource->layout; + rbDesc.flags = 0; + rbDesc.format = rbResource->format; + rbDesc.samples = rbResource->sampleCount; + rbDesc.loadOp = rbHasClear ? VK_ATTACHMENT_LOAD_OP_CLEAR : + (trackedRbLayout == VK_IMAGE_LAYOUT_UNDEFINED ? VK_ATTACHMENT_LOAD_OP_DONT_CARE + : VK_ATTACHMENT_LOAD_OP_LOAD); + rbDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + rbDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + rbDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + rbDesc.initialLayout = (rbHasClear || trackedRbLayout == VK_IMAGE_LAYOUT_UNDEFINED) ? + VK_IMAGE_LAYOUT_UNDEFINED : trackedRbLayout; + rbDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + adoptRenderPassSampleCount(rbResource->sampleCount, "color", + static_cast(renderbuffer->GetExternalIndex())); + + if (rbHasClear) { + pendingClearAttachments.emplace_back(PendingClearAttachmentInfo { + .attachmentIndex = rbAttachmentIndex, + .colorAttachmentSlot = i, + .renderbuffer = renderbuffer.get(), + .hasInlinePayload = true, + .inlinePayload = rbClearPayload, + }); + } + + if (width == 0) + width = static_cast(rbResource->extent.width); + if (height == 0) + height = static_cast(rbResource->extent.height); + + trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo { + .target = TrackedAttachmentTarget::Renderbuffer, + .renderbuffer = renderbuffer, + .finalLayout = rbDesc.finalLayout, + }); + textureResources.emplace_back(nullptr); + attachmentViews.emplace_back(rbResource->view); + MOBILEGL_ASSERT(attachmentViews.back() != VK_NULL_HANDLE, + "GetOrCreateRenderPass: renderbuffer view missing at color attachment %d", i); + + colorAttachmentRefs[i].attachment = rbAttachmentIndex; + continue; + } + } + auto* texture = ResolveCompleteColorAttachmentTexture(fbo, drawbuf, i); if (texture == nullptr) continue; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h index 889065ca..71731435 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h @@ -211,6 +211,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { Uint64 m_rpFastRbEpoch = 0; Uint64 m_rpFastRenderPassHash = 0; + public: struct RenderbufferResource { WeakPtr renderbuffer; VkImage image = VK_NULL_HANDLE; @@ -227,6 +228,14 @@ namespace MobileGL::MG_Backend::DirectVulkan { void Destroy(VkDevice device, VmaAllocator allocator); }; + // Public so the renderer's blit/copy/readback bindings can source renderbuffer + // attachments the same way texture attachments go through the texture manager. + RenderbufferResource* GetOrCreateRenderbufferResource( + const SharedPtr& renderbuffer); + Bool GetPendingRenderbufferClear(MG_State::GLState::RenderbufferObject* renderbuffer, + ClearAttachmentPayload& outPayload) const; + + private: struct PendingRenderbufferClear { WeakPtr renderbuffer; ClearAttachmentPayload payload{}; @@ -235,10 +244,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { UnorderedMap m_renderbufferResources; UnorderedMap m_pendingRenderbufferClears; - RenderbufferResource* GetOrCreateRenderbufferResource( - const SharedPtr& renderbuffer); - Bool GetPendingRenderbufferClear(MG_State::GLState::RenderbufferObject* renderbuffer, - ClearAttachmentPayload& outPayload) const; Bool HasPendingRenderbufferClear( const MG_State::GLState::FramebufferAttachmentObject& attachment) const; void CollectRenderbufferGarbage(); diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index a0ec851d..e5606d0a 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -1274,7 +1274,8 @@ void main() { static Bool ResolveColorBlitBinding(MG_State::GLState::FramebufferObject& fbo, Bool isReadFramebuffer, Uint32 swapchainImageIndex, SwapchainObject& swapchainObject, - VkTextureManager& textureManager, BlitImageBinding& outBinding) { + VkTextureManager& textureManager, + VkRenderPassManager& renderPassManager, BlitImageBinding& outBinding) { const Bool isDefaultFbo = fbo.IsDefaultFramebuffer(); const FramebufferAttachmentType attachmentType = isReadFramebuffer ? fbo.GetReadBuffer() : fbo.GetDrawBuffers()[0]; @@ -1316,8 +1317,24 @@ void main() { return false; } if (attachment.IsRenderbuffer()) { - MGLOG_E("BlitFramebuffer skipped: renderbuffer attachments are not supported yet"); - return false; + const auto& renderbuffer = attachment.GetRenderbuffer(); + auto* rbResource = renderPassManager.GetOrCreateRenderbufferResource(renderbuffer); + if (rbResource == nullptr || (rbResource->aspect & VK_IMAGE_ASPECT_COLOR_BIT) == 0) { + MGLOG_E("BlitFramebuffer skipped: %s framebuffer color renderbuffer %u is unsupported", + outBinding.label, renderbuffer->GetExternalIndex()); + return false; + } + outBinding.image = rbResource->image; + outBinding.trackedLayout = &rbResource->layout; + outBinding.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + outBinding.format = rbResource->format; + outBinding.extent = {static_cast(rbResource->extent.width), + static_cast(rbResource->extent.height)}; + outBinding.mipLevel = 0; + outBinding.mipLevelCount = 1; + outBinding.baseArrayLayer = 0; + outBinding.layerCount = 1; + return true; } if (!attachment.IsTexture()) { MGLOG_E("BlitFramebuffer skipped: unsupported framebuffer attachment type"); @@ -1355,6 +1372,7 @@ void main() { static Bool ResolveFramebufferBlitBinding(MG_State::GLState::FramebufferObject& fbo, Bool isReadFramebuffer, Uint32 swapchainImageIndex, SwapchainObject& swapchainObject, VkTextureManager& textureManager, + VkRenderPassManager& renderPassManager, VkImageAspectFlags requiredAspectMask, BlitImageBinding& outBinding) { const Bool isDefaultFbo = fbo.IsDefaultFramebuffer(); @@ -1398,8 +1416,29 @@ void main() { return false; } if (attachment.IsRenderbuffer()) { - MGLOG_E("BlitFramebuffer skipped: renderbuffer attachments are not supported yet"); - return false; + const auto& renderbuffer = attachment.GetRenderbuffer(); + auto* rbResource = renderPassManager.GetOrCreateRenderbufferResource(renderbuffer); + if (rbResource == nullptr) { + MGLOG_E("BlitFramebuffer skipped: %s framebuffer renderbuffer %u is unsupported", + outBinding.label, renderbuffer->GetExternalIndex()); + return false; + } + if ((rbResource->aspect & requiredAspectMask) != requiredAspectMask) { + MGLOG_E("BlitFramebuffer skipped: %s framebuffer renderbuffer %u is missing aspect mask=0x%x", + outBinding.label, renderbuffer->GetExternalIndex(), + static_cast(requiredAspectMask)); + return false; + } + outBinding.image = rbResource->image; + outBinding.trackedLayout = &rbResource->layout; + outBinding.aspectMask = requiredAspectMask; + outBinding.extent = {static_cast(rbResource->extent.width), + static_cast(rbResource->extent.height)}; + outBinding.mipLevel = 0; + outBinding.mipLevelCount = 1; + outBinding.baseArrayLayer = 0; + outBinding.layerCount = 1; + return true; } if (!attachment.IsTexture()) { MGLOG_E("BlitFramebuffer skipped: unsupported framebuffer attachment type"); @@ -1470,6 +1509,7 @@ void main() { static Bool ResolveTextureCopySourceBinding(MG_State::GLState::FramebufferObject& fbo, Uint32 swapchainImageIndex, SwapchainObject& swapchainObject, VkTextureManager& textureManager, + VkRenderPassManager& renderPassManager, VkImageAspectFlags requiredAspectMask, BlitImageBinding& outBinding) { const Bool isDefaultFbo = fbo.IsDefaultFramebuffer(); @@ -1514,8 +1554,30 @@ void main() { return false; } if (attachment.IsRenderbuffer()) { - MGLOG_E("CopyTexSubImage2D skipped: renderbuffer read attachments are not supported yet"); - return false; + const auto& renderbuffer = attachment.GetRenderbuffer(); + auto* rbResource = renderPassManager.GetOrCreateRenderbufferResource(renderbuffer); + if (rbResource == nullptr) { + MGLOG_E("CopyTexSubImage2D skipped: read framebuffer renderbuffer %u is unsupported", + renderbuffer->GetExternalIndex()); + return false; + } + if ((rbResource->aspect & requiredAspectMask) != requiredAspectMask) { + MGLOG_E("CopyTexSubImage2D skipped: read framebuffer renderbuffer %u aspect mask=0x%x " + "does not satisfy requested mask=0x%x", + renderbuffer->GetExternalIndex(), static_cast(rbResource->aspect), + static_cast(requiredAspectMask)); + return false; + } + outBinding.image = rbResource->image; + outBinding.trackedLayout = &rbResource->layout; + outBinding.aspectMask = requiredAspectMask; + outBinding.extent = {static_cast(rbResource->extent.width), + static_cast(rbResource->extent.height)}; + outBinding.mipLevel = 0; + outBinding.mipLevelCount = 1; + outBinding.baseArrayLayer = 0; + outBinding.layerCount = 1; + return true; } if (!attachment.IsTexture()) { MGLOG_E("CopyTexSubImage2D skipped: unsupported read framebuffer attachment type"); @@ -3560,6 +3622,7 @@ void main() { (bufferMask.a() ? VK_COLOR_COMPONENT_A_BIT : 0u)); Bool effectiveBlendEnabled = blendEnabled; MG_State::GLState::ITextureObject* colorAttachmentTexture = nullptr; + MG_State::GLState::RenderbufferObject* colorAttachmentRenderbuffer = nullptr; if (isDefaultDrawFbo && i < drawBuffers.size() && drawBuffers[i] == FramebufferAttachmentType::None) { // The default framebuffer spans the same MAX_DRAW_BUFFERS slots as an FBO @@ -3574,12 +3637,24 @@ void main() { if (!isDefaultDrawFbo && i < drawBuffers.size()) { const auto drawBuffer = drawBuffers[i]; colorAttachmentTexture = resolveCompleteColorAttachmentTexture(i); - if (drawBuffer == FramebufferAttachmentType::None || colorAttachmentTexture == nullptr) { + if (colorAttachmentTexture == nullptr && drawBuffer != FramebufferAttachmentType::None) { + const auto& attachment = drawFboBinding->GetAttachment(drawBuffer); + if (attachment.IsRenderbuffer() && attachment.IsComplete()) { + colorAttachmentRenderbuffer = attachment.GetRenderbuffer().get(); + } + } + if (drawBuffer == FramebufferAttachmentType::None || + (colorAttachmentTexture == nullptr && colorAttachmentRenderbuffer == nullptr)) { // GL ignores writes and per-target blend state for GL_NONE draw buffer slots. // Depth-only or otherwise unattached draw buffers should also discard color writes. attachmentColorWriteMask = 0; effectiveBlendEnabled = false; } + if (colorAttachmentRenderbuffer != nullptr) { + const SizeT componentCount = MG_Util::GetBaseInternalFormatComponentCount( + colorAttachmentRenderbuffer->GetInternalFormat()); + attachmentColorWriteMask &= GetSupportedColorWriteMaskForComponentCount(componentCount); + } if (colorAttachmentTexture != nullptr) { auto* texture = colorAttachmentTexture; #if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG @@ -3656,10 +3731,14 @@ void main() { Int textureExternalIndex = -1; if (isDefaultDrawFbo) { colorAttachmentFormat = m_swapchainObject.GetSurfaceFormat().format; + } else if (colorAttachmentRenderbuffer != nullptr) { + textureExternalIndex = static_cast(colorAttachmentRenderbuffer->GetExternalIndex()); + colorAttachmentFormat = MG_Util::ConvertTextureInternalFormatToVkEnum( + colorAttachmentRenderbuffer->GetInternalFormat()); } else { auto* texture = colorAttachmentTexture; MOBILEGL_ASSERT(texture != nullptr, - "GetOrCreatePipeline: blend is enabled on draw buffer %u but no complete texture attachment is bound", + "GetOrCreatePipeline: blend is enabled on draw buffer %u but no complete color attachment is bound", i); textureExternalIndex = texture->GetExternalIndex(); auto* textureResource = m_textureManager->SyncTextureAndGetDescriptor(*texture); @@ -4810,6 +4889,98 @@ void main() { return true; } + Bool VulkanRenderer::MaterializePendingClearForRenderbuffer( + VkCommandBuffer commandBuffer, const SharedPtr& renderbuffer) { + if (renderbuffer == nullptr) { + return true; + } + ClearAttachmentPayload clearPayload{}; + if (!m_renderPassManager->GetPendingRenderbufferClear(renderbuffer.get(), clearPayload)) { + return true; + } + MOBILEGL_ASSERT(VkRenderPassManager::GetActiveRenderPass() == nullptr, + "MaterializePendingClearForRenderbuffer requires no active render pass"); + + auto* resource = m_renderPassManager->GetOrCreateRenderbufferResource(renderbuffer); + if (resource == nullptr) { + MGLOG_E("MaterializePendingClearForRenderbuffer: no resource for renderbuffer %u", + renderbuffer->GetExternalIndex()); + return false; + } + + VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + VkAccessFlags srcAccessMask = 0; + GetImageTransitionSourceState(resource->layout, srcStageMask, srcAccessMask); + + Bool ok = VkTextureManager::TransitionImageLayout( + commandBuffer, resource->image, resource->layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + srcStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT, srcAccessMask, VK_ACCESS_TRANSFER_WRITE_BIT, + resource->aspect, 0, 1, 1); + MOBILEGL_ASSERT(ok, + "MaterializePendingClearForRenderbuffer: failed to transition renderbuffer %u to TRANSFER_DST", + renderbuffer->GetExternalIndex()); + + VkImageSubresourceRange subresourceRange{}; + subresourceRange.baseMipLevel = 0; + subresourceRange.levelCount = 1; + subresourceRange.baseArrayLayer = 0; + subresourceRange.layerCount = 1; + + VkImageLayout steadyLayout; + if ((resource->aspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0) { + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + VkClearColorValue clearValue{}; + clearValue.float32[0] = clearPayload.color.x(); + clearValue.float32[1] = clearPayload.color.y(); + clearValue.float32[2] = clearPayload.color.z(); + // RGB renderbuffers are backed by an RGBA image; the missing alpha reads as 1. + clearValue.float32[3] = + MG_Util::GetBaseInternalFormatComponentCount(renderbuffer->GetInternalFormat()) == 3 ? + 1.0f : clearPayload.color.w(); + vkCmdClearColorImage(commandBuffer, resource->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + &clearValue, 1, &subresourceRange); + steadyLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } else { + VkImageAspectFlags clearAspectMask = 0; + if ((resource->aspect & VK_IMAGE_ASPECT_DEPTH_BIT) != 0 && + (clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0) { + clearAspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + } + if ((resource->aspect & VK_IMAGE_ASPECT_STENCIL_BIT) != 0 && + (clearPayload.mask & GL_STENCIL_BUFFER_BIT) != 0) { + clearAspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + if (clearAspectMask != 0) { + subresourceRange.aspectMask = clearAspectMask; + VkClearDepthStencilValue clearValue{}; + clearValue.depth = clearPayload.depth; + clearValue.stencil = clearPayload.stencil; + vkCmdClearDepthStencilImage(commandBuffer, resource->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + &clearValue, 1, &subresourceRange); + } + steadyLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } + + VkImageLayout clearLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + ok = VkTextureManager::TransitionImageLayout( + commandBuffer, resource->image, clearLayout, steadyLayout, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | + VK_ACCESS_TRANSFER_READ_BIT, + resource->aspect, 0, 1, 1); + MOBILEGL_ASSERT(ok, + "MaterializePendingClearForRenderbuffer: failed to transition renderbuffer %u to steady layout", + renderbuffer->GetExternalIndex()); + resource->layout = steadyLayout; + + m_renderPassManager->PopPendingRenderbufferClear(renderbuffer.get()); + MGLOG_D("MaterializePendingClearForRenderbuffer: renderbuffer %u pending clear materialized", + renderbuffer->GetExternalIndex()); + return true; + } + Bool VulkanRenderer::TryBlitToDefaultFramebufferWithShader(FrameContext::FrameData& frame, MG_State::GLState::FramebufferObject& readFbo, MG_State::GLState::FramebufferObject& drawFbo, @@ -4823,8 +4994,10 @@ void main() { BlitImageBinding srcBinding{}; BlitImageBinding dstBinding{}; - if (!ResolveColorBlitBinding(readFbo, true, m_imageIndexAcquired, m_swapchainObject, *m_textureManager, srcBinding) || - !ResolveColorBlitBinding(drawFbo, false, m_imageIndexAcquired, m_swapchainObject, *m_textureManager, dstBinding)) { + if (!ResolveColorBlitBinding(readFbo, true, m_imageIndexAcquired, m_swapchainObject, *m_textureManager, + *m_renderPassManager, srcBinding) || + !ResolveColorBlitBinding(drawFbo, false, m_imageIndexAcquired, m_swapchainObject, *m_textureManager, + *m_renderPassManager, dstBinding)) { return false; } if (srcBinding.trackedLayout == nullptr) { @@ -5010,9 +5183,11 @@ void main() { BlitImageBinding srcBinding{}; BlitImageBinding dstBinding{}; if (!ResolveFramebufferBlitBinding(*readFbo, true, m_imageIndexAcquired, m_swapchainObject, - *m_textureManager, VK_IMAGE_ASPECT_DEPTH_BIT, srcBinding) || + *m_textureManager, *m_renderPassManager, + VK_IMAGE_ASPECT_DEPTH_BIT, srcBinding) || !ResolveFramebufferBlitBinding(*drawFbo, false, m_imageIndexAcquired, m_swapchainObject, - *m_textureManager, VK_IMAGE_ASPECT_DEPTH_BIT, dstBinding)) { + *m_textureManager, *m_renderPassManager, + VK_IMAGE_ASPECT_DEPTH_BIT, dstBinding)) { return; } @@ -5177,8 +5352,10 @@ void main() { BlitImageBinding srcBinding{}; BlitImageBinding dstBinding{}; - if (!ResolveColorBlitBinding(*readFbo, true, m_imageIndexAcquired, m_swapchainObject, *m_textureManager, srcBinding) || - !ResolveColorBlitBinding(*drawFbo, false, m_imageIndexAcquired, m_swapchainObject, *m_textureManager, dstBinding)) { + if (!ResolveColorBlitBinding(*readFbo, true, m_imageIndexAcquired, m_swapchainObject, *m_textureManager, + *m_renderPassManager, srcBinding) || + !ResolveColorBlitBinding(*drawFbo, false, m_imageIndexAcquired, m_swapchainObject, *m_textureManager, + *m_renderPassManager, dstBinding)) { return; } @@ -5382,7 +5559,7 @@ void main() { BlitImageBinding srcBinding{}; if (!ResolveTextureCopySourceBinding(*readFbo, m_imageIndexAcquired, m_swapchainObject, *m_textureManager, - dstBinding.aspectMask, srcBinding)) { + *m_renderPassManager, dstBinding.aspectMask, srcBinding)) { RecordTextureCopyError(__func__, ErrorCode::InvalidOperation, "CopyTexSubImage2D requires a complete read attachment compatible with the destination texture."); return; @@ -5723,7 +5900,7 @@ void main() { const Bool readIsDefaultFbo = readFbo->IsDefaultFramebuffer(); BlitImageBinding srcBinding{}; if (!ResolveColorBlitBinding(*readFbo, true, m_imageIndexAcquired, m_swapchainObject, *m_textureManager, - srcBinding)) { + *m_renderPassManager, srcBinding)) { return; } if (!readIsDefaultFbo) { @@ -5734,6 +5911,12 @@ void main() { MOBILEGL_ASSERT(clearReady, "ReadPixels: failed to materialize pending clear for source textureId=%d", sourceTexture->GetExternalIndex()); + } else if (sourceAttachment.IsRenderbuffer()) { + const Bool clearReady = + MaterializePendingClearForRenderbuffer(frame.commandBuffer, sourceAttachment.GetRenderbuffer()); + MOBILEGL_ASSERT(clearReady, + "ReadPixels: failed to materialize pending clear for source renderbuffer %u", + sourceAttachment.GetRenderbuffer()->GetExternalIndex()); } } diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h index d691aff8..6a92abe0 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h @@ -561,6 +561,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { GLenum filter); Bool MaterializePendingClearForTexture(VkCommandBuffer commandBuffer, MG_State::GLState::ITextureObject& texture); + Bool MaterializePendingClearForRenderbuffer( + VkCommandBuffer commandBuffer, + const SharedPtr& renderbuffer); VkPipeline GetOrCreateBlitPipeline(const RenderPassEntry& renderPassEntry); Bool GenerateDepthMipmapWithShader(FrameContext::FrameData& frame, MG_State::GLState::ITextureObject& texture,