diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp index 71c52f1e..db03700e 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp @@ -461,25 +461,15 @@ namespace MobileGL::MG_Backend::DirectVulkan { (void)commandBuffer; MOBILEGL_ASSERT(m_textureManager != nullptr, "ResolveSamplerDescriptor: texture manager is null"); MOBILEGL_ASSERT(m_samplerManager != nullptr, "ResolveSamplerDescriptor: sampler manager is null"); - if (!MG_State::pGLContext || binding >= layout.samplerUniformLocationByBinding.size()) { + SharedPtr texture; + if (!ResolveSamplerTexture(program, layout, binding, texture)) { return false; } const Int location = layout.samplerUniformLocationByBinding[binding]; - if (location < 0) { - return false; - } - const Int unit = program.GetUniformSamplerOrImageUnitIndex(static_cast(location)); - if (unit < 0) { - return false; - } - auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit); const auto samplerOverride = textureUnit.GetSamplerObject(); - - const TextureTarget preferredTarget = layout.samplerTextureTargetByBinding[binding]; - auto texture = textureUnit.GetBindingSlot(preferredTarget).GetBoundObject(); if (!texture) { return false; } @@ -504,6 +494,56 @@ namespace MobileGL::MG_Backend::DirectVulkan { return outImageInfo.sampler != VK_NULL_HANDLE; } + Bool UniformDescriptorBinder::ResolveSamplerTexture(const MG_State::GLState::ProgramObject& program, + const ProgramLayout& layout, Uint32 binding, + SharedPtr& outTexture) const { + outTexture.reset(); + if (!MG_State::pGLContext || binding >= layout.samplerUniformLocationByBinding.size()) { + return false; + } + + const Int location = layout.samplerUniformLocationByBinding[binding]; + if (location < 0) { + return false; + } + + const Int unit = program.GetUniformSamplerOrImageUnitIndex(static_cast(location)); + if (unit < 0) { + return false; + } + + auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit); + const TextureTarget preferredTarget = layout.samplerTextureTargetByBinding[binding]; + outTexture = textureUnit.GetBindingSlot(preferredTarget).GetBoundObject(); + return outTexture != nullptr; + } + + Bool UniformDescriptorBinder::CollectSampledTextures(const MG_State::GLState::ProgramObject& program, + Vector& outTextures) { + outTextures.clear(); + ProgramLayout* layout = GetOrCreateProgramLayout(program); + if (layout == nullptr) { + return false; + } + + for (Uint32 binding = 0; binding < m_maxBindings; ++binding) { + if (layout->bindingKinds[binding] != BindingKind::CombinedImageSampler) { + continue; + } + + SharedPtr texture; + if (!ResolveSamplerTexture(program, *layout, binding, texture) || !texture) { + continue; + } + + auto found = std::find(outTextures.begin(), outTextures.end(), texture.get()); + if (found == outTextures.end()) { + outTextures.push_back(texture.get()); + } + } + return true; + } + UniformDescriptorBinder::ProgramLayout* UniformDescriptorBinder::GetOrCreateProgramLayout( const MG_State::GLState::ProgramObject& program) { const Uint64 hash = ComputeProgramHash(program); diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h index 2fbf297c..0b8d2377 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h @@ -36,6 +36,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { void BeginFrame(Uint32 frameIndex); VkPipelineLayout GetOrCreatePipelineLayout(const MG_State::GLState::ProgramObject& program); + Bool CollectSampledTextures(const MG_State::GLState::ProgramObject& program, + Vector& outTextures); Bool BindProgramUniformBuffers(VkCommandBuffer commandBuffer, const MG_State::GLState::ProgramObject& program, Uint32 frameIndex); @@ -72,6 +74,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { static TextureTarget UniformTypeToTextureTarget(GLenum glType); Bool ReflectSamplerBindings(const MG_State::GLState::ProgramObject& program, ProgramLayout& layout) const; Bool ReflectGlobalUboBinding(const MG_State::GLState::ProgramObject& program, ProgramLayout& layout) const; + Bool ResolveSamplerTexture(const MG_State::GLState::ProgramObject& program, const ProgramLayout& layout, + Uint32 binding, SharedPtr& outTexture) const; Bool ResolveSamplerDescriptor(VkCommandBuffer commandBuffer, const MG_State::GLState::ProgramObject& program, const ProgramLayout& layout, Uint32 binding, VkDescriptorImageInfo& outImageInfo) const; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp index 43afa601..538ba3a7 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp @@ -12,6 +12,19 @@ #include "MG_Util/Converters/MGToVk/TextureEnumConverter.h" namespace MobileGL::MG_Backend::DirectVulkan { + static Bool IsValidSampledImageLayout(VkImageLayout layout) { + switch (layout) { + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + case VK_IMAGE_LAYOUT_GENERAL: + case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL: + case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL: + case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL: + return true; + default: + return false; + } + } + Bool VkTextureManager::Initialize(const InitInfo& initInfo) { Shutdown(); @@ -69,6 +82,44 @@ namespace MobileGL::MG_Backend::DirectVulkan { it->second.layout = newLayout; } + Bool VkTextureManager::TransitionTextureForSampling(VkCommandBuffer commandBuffer, MG_State::GLState::ITextureObject& texture) { + TextureResource* resource = SyncTextureAndGetDescriptor(texture); + if (resource == nullptr) { + return false; + } + if (IsValidSampledImageLayout(resource->layout)) { + return true; + } + + VkImageLayout targetLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + VkAccessFlags srcAccessMask = 0; + if ((resource->aspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0) { + MOBILEGL_ASSERT(resource->layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + "TransitionTextureForSampling: unsupported color layout=%d for textureId=%d", + static_cast(resource->layout), texture.GetExternalIndex()); + srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + targetLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } else if ((resource->aspect & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { + MOBILEGL_ASSERT(resource->layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + "TransitionTextureForSampling: unsupported depth/stencil layout=%d for textureId=%d", + static_cast(resource->layout), texture.GetExternalIndex()); + srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + targetLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; + } else { + MOBILEGL_ASSERT(false, "TransitionTextureForSampling: unsupported aspect mask=0x%x for textureId=%d", + static_cast(resource->aspect), texture.GetExternalIndex()); + } + + const Bool ok = TransitionImageLayout(commandBuffer, resource->image, resource->layout, targetLayout, srcStageMask, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, srcAccessMask, + VK_ACCESS_SHADER_READ_BIT, resource->aspect); + MOBILEGL_ASSERT(ok, "TransitionTextureForSampling: transition failed for textureId=%d", texture.GetExternalIndex()); + return ok; + } + Bool VkTextureManager::TransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout& trackedLayout, VkImageLayout newLayout, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h index 58b4b299..d36f1da8 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h @@ -76,6 +76,7 @@ public: TextureResource* SyncTextureAndGetDescriptor( MG_State::GLState::ITextureObject& texture); void UpdateTrackedImageLayout(MG_State::GLState::ITextureObject* texture, VkImageLayout newLayout); + Bool TransitionTextureForSampling(VkCommandBuffer commandBuffer, MG_State::GLState::ITextureObject& texture); static Bool TransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout& trackedLayout, VkImageLayout newLayout, VkPipelineStageFlags srcStageMask, diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index 6d7b1db6..7e77759b 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -405,8 +405,25 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_uniformDescriptorBinder->BeginFrame(m_frameContext.GetCurrentFrameIndex()); } - // Begin render pass, and handle clear auto* activeRenderPass = VkRenderPassManager::GetActiveRenderPass(); + Vector sampledTextures; + Bool hasSampledTextures = m_uniformDescriptorBinder->CollectSampledTextures(program, sampledTextures); + MOBILEGL_ASSERT(hasSampledTextures, "%s: CollectSampledTextures failed", __func__); + if (activeRenderPass && !sampledTextures.empty()) { + VkRenderPassManager::EndRenderPass(frame.commandBuffer); + activeRenderPass = nullptr; + } + + for (auto* sampledTexture : sampledTextures) { + if (!sampledTexture) { + continue; + } + const Bool ready = m_textureManager->TransitionTextureForSampling(frame.commandBuffer, *sampledTexture); + MOBILEGL_ASSERT(ready, "%s: TransitionTextureForSampling failed for textureId=%d", + __func__, sampledTexture->GetExternalIndex()); + } + + // Begin render pass, and handle clear if (activeRenderPass && (activeRenderPass == &renderPassEntry || activeRenderPass->CompatibleWith(renderPassEntry))) { ClearAttachmentsOnActiveRenderPass(frame.commandBuffer, renderPassEntry);