From 9e0e03d4c08050393a68ecb450a6d8d0e2918925 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Mon, 11 May 2026 16:04:42 +0800 Subject: [PATCH] [Optimaization] (MG_Backend/DirectVulkan): optimize perf - Properly reuse descriptor sets rather than always allocating - Add program lookup cache --- .../DirectVulkan/Renderer/ProgramFactory.cpp | 10 ++- .../DirectVulkan/Renderer/ProgramFactory.h | 8 ++ .../DirectVulkan/Renderer/UniformManager.cpp | 75 ++++++++++++++----- .../DirectVulkan/Renderer/UniformManager.h | 9 +++ .../Renderer/VertexInputStateFactory.cpp | 5 ++ .../Renderer/VertexInputStateFactory.h | 2 + .../DirectVulkan/Renderer/VulkanRenderer.cpp | 21 +++--- .../DirectVulkan/Renderer/VulkanRenderer.h | 2 + .../DirectVulkan/VulkanRendererConfig.h | 4 + .../GLState/ProgramState/ProgramObject.cpp | 1 + .../GLState/ProgramState/ProgramObject.h | 10 ++- 11 files changed, 117 insertions(+), 30 deletions(-) diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp index f9c8ce5f..055e3f42 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp @@ -1530,7 +1530,15 @@ namespace MobileGL::MG_Backend::DirectVulkan { const ProgramFactory::VkProgramObject& ProgramFactory::GetOrCreateProgram( const MG_State::GLState::ProgramObject& program, CompileOptionFlags flags) { - auto hash = ComputeHash(program, flags); + const Uint32 backendStateVersion = program.GetBackendStateVersion(); + HashType hash = 0; + if (m_lastLookup.program == &program && m_lastLookup.backendStateVersion == backendStateVersion && + m_lastLookup.flags == flags) { + hash = m_lastLookup.hash; + } else { + hash = ComputeHash(program, flags); + m_lastLookup = {.program = &program, .backendStateVersion = backendStateVersion, .flags = flags, .hash = hash}; + } auto it = m_cache.find(hash); if (it != m_cache.end()) { return it->second; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.h index 0b53724e..18ed3288 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.h @@ -172,6 +172,13 @@ namespace MobileGL::MG_Backend::DirectVulkan { static VkShaderStageFlagBits ToVkStage(ShaderStage stage); private: + struct ProgramLookupCache { + const MG_State::GLState::ProgramObject* program = nullptr; + Uint32 backendStateVersion = 0; + CompileOptionFlags flags{}; + HashType hash = 0; + }; + static TextureTarget UniformTypeToTextureTarget(GLenum glType); void ReflectVertexInputs(const Vector>& shaders, const Vector>& spirv, @@ -186,6 +193,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { Uint32 m_maxBindings = 0; UnorderedMap m_cache; const VulkanRendererConfig& m_config; + mutable ProgramLookupCache m_lastLookup; static inline XXH64_state_t* m_hashState = XXH64_createState(); }; } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp index 3ce46fe0..41bdb05e 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp @@ -135,6 +135,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { } } frame.texelBufferViews.clear(); + frame.descriptorSetCacheByLayout.clear(); for (auto& bucket : frame.descriptorPools) { if (bucket.handle != VK_NULL_HANDLE) { vkDestroyDescriptorPool(m_device, bucket.handle, nullptr); @@ -180,13 +181,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { frame.activeDescriptorPoolIndex = 0; frame.allocatedSetsThisFrame = 0; frame.peakAllocatedSetsThisFrame = 0; - for (auto& bucket : frame.descriptorPools) { - bucket.allocatedSets = 0; - if (bucket.handle == VK_NULL_HANDLE) { - continue; - } - VK_VERIFY(vkResetDescriptorPool(m_device, bucket.handle, 0), - "UniformDescriptorBinder::BeginFrame, vkResetDescriptorPool"); + for (auto& cacheEntryPair : frame.descriptorSetCacheByLayout) { + cacheEntryPair.second.cursor = 0; } } @@ -584,7 +580,26 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkResult UniformManager::AllocateDescriptorSetsFromActivePool(Uint32 frameIndex, const ProgramFactory::VkProgramObject& programObj, VkDescriptorSet& outDescriptorSet) { auto& frame = m_frames[frameIndex]; + if (frame.activeDescriptorPoolIndex >= frame.descriptorPools.size()) { + frame.activeDescriptorPoolIndex = 0; + } + if (frame.descriptorPools[frame.activeDescriptorPoolIndex].allocatedSets >= + frame.descriptorPools[frame.activeDescriptorPoolIndex].maxSets) { + const auto availableBucket = std::find_if( + frame.descriptorPools.begin(), frame.descriptorPools.end(), + [](const DescriptorPoolBucket& candidate) { return candidate.allocatedSets < candidate.maxSets; }); + if (availableBucket == frame.descriptorPools.end()) { + outDescriptorSet = VK_NULL_HANDLE; + return VK_ERROR_OUT_OF_POOL_MEMORY; + } + frame.activeDescriptorPoolIndex = + static_cast(std::distance(frame.descriptorPools.begin(), availableBucket)); + } auto& bucket = frame.descriptorPools[frame.activeDescriptorPoolIndex]; + if (bucket.allocatedSets >= bucket.maxSets) { + outDescriptorSet = VK_NULL_HANDLE; + return VK_ERROR_OUT_OF_POOL_MEMORY; + } VkDescriptorSetAllocateInfo allocInfo{}; allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; allocInfo.descriptorSetCount = 1; @@ -594,13 +609,42 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkResult result = vkAllocateDescriptorSets(m_device, &allocInfo, &outDescriptorSet); if (result == VK_SUCCESS) { ++bucket.allocatedSets; - ++frame.allocatedSetsThisFrame; - frame.peakAllocatedSetsThisFrame = - std::max(frame.peakAllocatedSetsThisFrame, frame.allocatedSetsThisFrame); } return result; } + VkResult UniformManager::AcquireDescriptorSet(Uint32 frameIndex, + const ProgramFactory::VkProgramObject& programObj, + VkDescriptorSet& outDescriptorSet) { + auto& frame = m_frames[frameIndex]; + auto& cache = frame.descriptorSetCacheByLayout[programObj.descriptorSetLayout]; + if (cache.cursor < cache.sets.size()) { + outDescriptorSet = cache.sets[cache.cursor++]; + } else { + VkResult allocResult = AllocateDescriptorSetsFromActivePool(frameIndex, programObj, outDescriptorSet); + if (allocResult == VK_ERROR_OUT_OF_POOL_MEMORY || allocResult == VK_ERROR_FRAGMENTED_POOL) { + if (!GrowFrameDescriptorPool(frame, frameIndex)) { + MGLOG_E("UniformDescriptorBinder::AcquireDescriptorSet failed: descriptor pool growth failed"); + return allocResult; + } + allocResult = AllocateDescriptorSetsFromActivePool(frameIndex, programObj, outDescriptorSet); + } + if (allocResult != VK_SUCCESS || outDescriptorSet == VK_NULL_HANDLE) { + return allocResult; + } + + cache.sets.push_back(outDescriptorSet); + ++cache.cursor; + MGLOG_D("UniformDescriptorBinder: cached descriptor set count for frame=%u grew to %zu", frameIndex, + cache.sets.size()); + } + + ++frame.allocatedSetsThisFrame; + frame.peakAllocatedSetsThisFrame = + std::max(frame.peakAllocatedSetsThisFrame, frame.allocatedSetsThisFrame); + return VK_SUCCESS; + } + Bool UniformManager::BindProgramUniformBuffers(VkCommandBuffer commandBuffer, const MG_State::GLState::ProgramObject& program, const ProgramFactory::VkProgramObject& programObj, @@ -616,16 +660,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { } VkDescriptorSet descriptorSet = VK_NULL_HANDLE; - VkResult allocResult = AllocateDescriptorSetsFromActivePool(frameIndex, programObj, descriptorSet); - if (allocResult == VK_ERROR_OUT_OF_POOL_MEMORY || allocResult == VK_ERROR_FRAGMENTED_POOL) { - if (!GrowFrameDescriptorPool(frame, frameIndex)) { - MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: descriptor pool growth failed"); - return false; - } - allocResult = AllocateDescriptorSetsFromActivePool(frameIndex, programObj, descriptorSet); - } + VkResult allocResult = AcquireDescriptorSet(frameIndex, programObj, descriptorSet); if (allocResult != VK_SUCCESS || descriptorSet == VK_NULL_HANDLE) { - MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: vkAllocateDescriptorSets returned %d", + MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: descriptor set acquire returned %d", allocResult); return false; } diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h index 640c169c..f2067cad 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h @@ -55,8 +55,14 @@ namespace MobileGL::MG_Backend::DirectVulkan { Uint32 allocatedSets = 0; }; + struct DescriptorSetCacheEntry { + Vector sets; + Uint32 cursor = 0; + }; + struct FrameResources { Vector descriptorPools; + UnorderedMap descriptorSetCacheByLayout; Vector texelBufferViews; Uint32 activeDescriptorPoolIndex = 0; Uint32 allocatedSetsThisFrame = 0; @@ -82,6 +88,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { Bool GrowFrameDescriptorPool(FrameResources& frame, Uint32 frameIndex); VkResult AllocateDescriptorSetsFromActivePool( Uint32 frameIndex, const ProgramFactory::VkProgramObject& programObj, VkDescriptorSet& outDescriptorSet); + VkResult AcquireDescriptorSet(Uint32 frameIndex, + const ProgramFactory::VkProgramObject& programObj, + VkDescriptorSet& outDescriptorSet); VkDevice m_device = VK_NULL_HANDLE; VkBufferManager* m_bufferManager = nullptr; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.cpp index c759487e..68da880d 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.cpp @@ -41,6 +41,11 @@ namespace MobileGL::MG_Backend::DirectVulkan { const VertexInputStateFactory::BackendVertexInputState& VertexInputStateFactory::GetOrCreateVertexInputState( const MG_State::GLState::VertexArrayObject& vao) { const HashType hash = ComputeHash(vao); + return GetOrCreateVertexInputState(vao, hash); + } + + const VertexInputStateFactory::BackendVertexInputState& VertexInputStateFactory::GetOrCreateVertexInputState( + const MG_State::GLState::VertexArrayObject& vao, HashType hash) { auto it = m_cache.find(hash); if (it != m_cache.end()) { return it->second; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.h index e55cdee4..2b79a4f8 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.h @@ -35,6 +35,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { VertexInputStateFactory(const VertexInputStateFactory&) = delete; HashType ComputeHash(const MG_State::GLState::VertexArrayObject& vao) const; + const BackendVertexInputState& GetOrCreateVertexInputState( + const MG_State::GLState::VertexArrayObject& vao, HashType hash); const BackendVertexInputState& GetOrCreateVertexInputState(const MG_State::GLState::VertexArrayObject& vao); private: diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index 7033c708..d07dd3be 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -18,6 +18,7 @@ #include "MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h" #include "MG_Util/Converters/GLToMG/TextureEnumConverter.h" #include "MG_Util/Converters/MGToVk/RenderStateEnumConverter.h" +#include "MG_Util/Converters/MGToVk/TextureEnumConverter.h" #include "MG_Util/Metrics/TextureMetrics.h" #include @@ -2027,11 +2028,11 @@ void main() { VkPipeline VulkanRenderer::GetOrCreatePipeline( GLenum mode, const MG_State::GLState::ProgramObject& program, + const ProgramFactory::VkProgramObject& programObj, + ProgramFactory::CompileOptionFlags transformFlags, const MG_State::GLState::VertexArrayObject& vao, const RenderPassEntry& renderPassEntry) { - ProgramFactory::CompileOptionFlags transformFlags = GetShaderTransformFlags(m_swapchainObject.GetPreTransform()); Bool invertClockwise = transformFlags & ProgramFactory::CompileOptionBit::PositionYFlip; - const auto& programObj = m_programFactory->GetOrCreateProgram(program, transformFlags); if (programObj.stages.empty()) { MGLOG_D("GetOrCreatePipeline skipped: program has no shader stages"); return VK_NULL_HANDLE; @@ -2076,7 +2077,7 @@ void main() { #endif auto vertexInputHash = m_vertexInputStateFactory->ComputeHash(vao); - auto& vis = m_vertexInputStateFactory->GetOrCreateVertexInputState(vao); + auto& vis = m_vertexInputStateFactory->GetOrCreateVertexInputState(vao, vertexInputHash); const Uint32 vertexInputAttribMask = BuildVertexInputAttributeMask(vis.attributes); const Uint32 activeAttribMask = programObj.activeVertexInputLocationMask; const Uint32 missingAttribMask = activeAttribMask & ~vertexInputAttribMask; @@ -2219,6 +2220,7 @@ void main() { MOBILEGL_ASSERT(texture != nullptr, "GetOrCreatePipeline: color attachment %u texture is null", i); +#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG const auto* textureResource = m_textureManager->SyncTextureAndGetDescriptor(*texture); MOBILEGL_ASSERT(textureResource != nullptr, "GetOrCreatePipeline: failed to sync color attachment textureId=%d", @@ -2235,7 +2237,9 @@ void main() { static_cast(textureResource->format), texture->GetExternalIndex(), program.GetExternalIndex()); +#endif const SizeT componentCount = MG_Util::GetBaseInternalFormatComponentCount(texture->GetFormat()); +#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG const NumericDomain attachmentNumericDomain = GetNumericDomainForTextureInternalFormat(texture->GetFormat()); for (Uint32 outputLocation = 0; @@ -2263,6 +2267,7 @@ void main() { texture->GetExternalIndex(), program.GetExternalIndex()); } +#endif const VkColorComponentFlags supportedColorWriteMask = GetSupportedColorWriteMaskForComponentCount(componentCount); if ((attachmentColorWriteMask & ~supportedColorWriteMask) != 0) { @@ -2303,13 +2308,10 @@ void main() { "GetOrCreatePipeline: color attachment %u texture is null", i); textureExternalIndex = texture->GetExternalIndex(); - const auto* textureResource = m_textureManager->SyncTextureAndGetDescriptor(*texture); - MOBILEGL_ASSERT(textureResource != nullptr, - "GetOrCreatePipeline: failed to sync color attachment textureId=%d", - textureExternalIndex); - colorAttachmentFormat = textureResource->format; + colorAttachmentFormat = MG_Util::ConvertTextureInternalFormatToVkEnum(texture->GetFormat()); } +#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG VkFormatProperties formatProperties{}; vkGetPhysicalDeviceFormatProperties(m_physicalDevice.handle, colorAttachmentFormat, &formatProperties); MOBILEGL_ASSERT( @@ -2319,6 +2321,7 @@ void main() { static_cast(colorAttachmentFormat), textureExternalIndex, program.GetExternalIndex()); +#endif } payload.colorBlendAttachments[i] = MakeColorBlendAttachmentState( effectiveBlendEnabled, @@ -2429,7 +2432,7 @@ void main() { renderPassEntry = &m_renderPassManager->GetOrCreateRenderPass(*drawFbo, m_imageIndexAcquired); } - auto pipeline = GetOrCreatePipeline(mode, program, vao, *renderPassEntry); + auto pipeline = GetOrCreatePipeline(mode, program, programObj, transformFlags, vao, *renderPassEntry); activeRenderPass = VkRenderPassManager::GetActiveRenderPass(); // Begin render pass, and handle clear diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h index 32dfd5e3..062cbee2 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h @@ -237,6 +237,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkPipeline GetOrCreatePipeline( GLenum mode, const MG_State::GLState::ProgramObject& program, + const ProgramFactory::VkProgramObject& programObj, + ProgramFactory::CompileOptionFlags transformFlags, const MG_State::GLState::VertexArrayObject& vao, const RenderPassEntry& renderPassEntry); diff --git a/MobileGL/MG_Backend/DirectVulkan/VulkanRendererConfig.h b/MobileGL/MG_Backend/DirectVulkan/VulkanRendererConfig.h index 3c3efb34..5e58bd3c 100644 --- a/MobileGL/MG_Backend/DirectVulkan/VulkanRendererConfig.h +++ b/MobileGL/MG_Backend/DirectVulkan/VulkanRendererConfig.h @@ -15,6 +15,10 @@ namespace MobileGL::MG_Backend::DirectVulkan { String AppName = "MobileGL-VulkanRenderer"; MobileGL::Version Version = MG_Config::CoreVersion; Uint64 CacheVersion = MG_Config::CacheVersion; +#if defined(__ANDROID__) + Bool EnableValidationLayers = false; +#else Bool EnableValidationLayers = true; +#endif }; } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp index 5005a568..4fb7d242 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp +++ b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp @@ -133,6 +133,7 @@ namespace MobileGL::MG_State::GLState { void ProgramObject::Link(Bool addDefaultFSIfMissingForRenderingPipelineProgram) { MGLOG_D("ProgramObject %u: Link start, shaders to link: %zu", m_externalIndex, m_shaders.size()); + ++m_backendStateVersion; // Remove detached shaders first for (const auto& detachedShader : m_detachedShaders) { RemoveShader(detachedShader); diff --git a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h index 81acc38f..2ef2765a 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h +++ b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h @@ -110,6 +110,7 @@ namespace MobileGL::MG_State::GLState { void* MapUBO() { return m_globalUboScratch.data(); } const void* GetUBOData() const { return m_globalUboScratch.data(); } Uint GetUBOSize() const { return static_cast(m_globalUboScratch.size()); } + Uint32 GetBackendStateVersion() const { return m_backendStateVersion; } void SetUniformSamplerOrImageUnitIndex(Uint location, Int unit) { m_uniformSamplerOrImageUnitIndex[location] = unit; @@ -147,7 +148,13 @@ namespace MobileGL::MG_State::GLState { } // Set by glUniformBlockBinding - void SetUniformBlockBinding(Uint index, Uint binding) { m_uniformBlockBinding[index] = (Int)binding; } + void SetUniformBlockBinding(Uint index, Uint binding) { + if (index >= m_uniformBlockBinding.size() || m_uniformBlockBinding[index] == static_cast(binding)) { + return; + } + m_uniformBlockBinding[index] = static_cast(binding); + ++m_backendStateVersion; + } Uint GetUniformBlockBinding(Uint index) const { return m_uniformBlockBinding[index]; } @@ -219,5 +226,6 @@ namespace MobileGL::MG_State::GLState { Bool m_deleteStatus = false; Bool m_linkStatus = false; Bool m_validateStatus = true; + Uint32 m_backendStateVersion = 0; }; } // namespace MobileGL::MG_State::GLState