From 35658eb9989c113a399dcc81e9e45e520385d600 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Mon, 23 Mar 2026 16:36:17 +0800 Subject: [PATCH] [Refactor] (MG_Backend/DirectVulkan): move uniform reflection from UniformDescriptorBinder to ProgramFactory --- .../DirectVulkan/Renderer/ProgramFactory.cpp | 348 +++++++++++++- .../DirectVulkan/Renderer/ProgramFactory.h | 36 +- .../Renderer/UniformDescriptorBinder.cpp | 450 ++---------------- .../Renderer/UniformDescriptorBinder.h | 38 +- .../DirectVulkan/Renderer/VulkanRenderer.cpp | 18 +- 5 files changed, 431 insertions(+), 459 deletions(-) diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp index ad1f254b..9cbb002b 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp @@ -8,6 +8,8 @@ #include "ProgramFactory.h" +#include "MG_Util/ShaderTranspiler/Types.h" +#include #include #include #include @@ -321,7 +323,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { } } // namespace - ProgramFactory::~ProgramFactory() = default; + ProgramFactory::~ProgramFactory() { + DestroyLayoutCache(); + } VkShaderStageFlagBits ProgramFactory::ToVkStage(ShaderStage stage) { switch (stage) { @@ -355,6 +359,266 @@ namespace MobileGL::MG_Backend::DirectVulkan { return hash; } + ProgramFactory::HashType ProgramFactory::ComputeLayoutHash(const MG_State::GLState::ProgramObject& program) const { + XXHASH_VERIFY(XXH64_reset(m_hashState, m_config.CacheVersion)); + const auto& spirvs = program.GetGeneratedSpirv(); + for (const auto& spv : spirvs) { + XXHASH_VERIFY(XXH64_update(m_hashState, spv.data(), spv.size() * sizeof(Uint))); + } + + const Uint32 blockCount = static_cast(program.GetActiveUniformBlocksCount()); + XXHASH_VERIFY(XXH64_update(m_hashState, &blockCount, sizeof(blockCount))); + for (Uint32 i = 0; i < blockCount; ++i) { + const Uint32 binding = program.GetUniformBlockBinding(i); + XXHASH_VERIFY(XXH64_update(m_hashState, &binding, sizeof(binding))); + } + return XXH64_digest(m_hashState); + } + + TextureTarget ProgramFactory::UniformTypeToTextureTarget(GLenum glType) { + switch (glType) { + case GL_SAMPLER_1D: + case GL_INT_SAMPLER_1D: + case GL_UNSIGNED_INT_SAMPLER_1D: + return TextureTarget::Texture1D; + case GL_SAMPLER_3D: + case GL_INT_SAMPLER_3D: + case GL_UNSIGNED_INT_SAMPLER_3D: + return TextureTarget::Texture3D; + case GL_SAMPLER_CUBE: + case GL_SAMPLER_CUBE_SHADOW: + case GL_INT_SAMPLER_CUBE: + case GL_UNSIGNED_INT_SAMPLER_CUBE: + return TextureTarget::TextureCubeMap; + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_INT_SAMPLER_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + return TextureTarget::Texture2DMultisample; + case GL_SAMPLER_BUFFER: + case GL_INT_SAMPLER_BUFFER: + case GL_UNSIGNED_INT_SAMPLER_BUFFER: + return TextureTarget::TextureBuffer; + case GL_SAMPLER_1D_ARRAY: + case GL_SAMPLER_1D_ARRAY_SHADOW: + case GL_INT_SAMPLER_1D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: + return TextureTarget::Texture1DArray; + case GL_SAMPLER_2D_ARRAY: + case GL_SAMPLER_2D_ARRAY_SHADOW: + case GL_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + return TextureTarget::Texture2DArray; + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + return TextureTarget::Texture2DMultisampleArray; + case GL_SAMPLER_2D_RECT: + case GL_SAMPLER_2D_RECT_SHADOW: + case GL_INT_SAMPLER_2D_RECT: + case GL_UNSIGNED_INT_SAMPLER_2D_RECT: + return TextureTarget::TextureRectangle; + case GL_SAMPLER_2D: + case GL_SAMPLER_2D_SHADOW: + case GL_INT_SAMPLER_2D: + case GL_UNSIGNED_INT_SAMPLER_2D: + default: + return TextureTarget::Texture2D; + } + } + + Bool ProgramFactory::ReflectBindingKinds(const MG_State::GLState::ProgramObject& program, + Vector& outKinds) const { + outKinds.assign(m_maxBindings, DescriptorBindingKind::None); + + const auto& spirv = program.GetGeneratedSpirv(); + for (const auto& module : spirv) { + if (module.empty()) { + continue; + } + + spvc_context context = nullptr; + spvc_parsed_ir ir = nullptr; + spvc_compiler compiler = nullptr; + spvc_resources resources = nullptr; + + if (spvc_context_create(&context) != SPVC_SUCCESS) { + return false; + } + + const spvc_result parseResult = spvc_context_parse_spirv(context, module.data(), module.size(), &ir); + if (parseResult != SPVC_SUCCESS) { + spvc_context_destroy(context); + continue; + } + + const spvc_result compilerResult = + spvc_context_create_compiler(context, SPVC_BACKEND_GLSL, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler); + if (compilerResult != SPVC_SUCCESS) { + spvc_context_destroy(context); + continue; + } + + if (spvc_compiler_create_shader_resources(compiler, &resources) != SPVC_SUCCESS) { + spvc_context_destroy(context); + continue; + } + + const auto applyBindings = [&](spvc_resource_type resourceType, DescriptorBindingKind kind) { + const spvc_reflected_resource* list = nullptr; + size_t count = 0; + if (spvc_resources_get_resource_list_for_type(resources, resourceType, &list, &count) != SPVC_SUCCESS) { + return; + } + for (size_t i = 0; i < count; ++i) { + const Uint32 binding = + spvc_compiler_get_decoration(compiler, list[i].id, SpvDecorationBinding); + if (binding >= m_maxBindings) { + continue; + } + if (kind == DescriptorBindingKind::CombinedImageSampler) { + outKinds[binding] = DescriptorBindingKind::CombinedImageSampler; + } else if (outKinds[binding] == DescriptorBindingKind::None) { + outKinds[binding] = kind; + } + } + }; + + applyBindings(SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, DescriptorBindingKind::UniformBufferDynamic); + applyBindings(SPVC_RESOURCE_TYPE_SAMPLED_IMAGE, DescriptorBindingKind::CombinedImageSampler); + + spvc_context_destroy(context); + } + + return true; + } + + Bool ProgramFactory::ReflectSamplerBindings(const MG_State::GLState::ProgramObject& program, + VkProgramLayout& layout) const { + layout.samplerUniformLocationByBinding.assign(m_maxBindings, -1); + layout.samplerTextureTargetByBinding.assign(m_maxBindings, TextureTarget::Texture2D); + + const auto& spirv = program.GetGeneratedSpirv(); + for (const auto& module : spirv) { + if (module.empty()) { + continue; + } + + spvc_context context = nullptr; + spvc_parsed_ir ir = nullptr; + spvc_compiler compiler = nullptr; + spvc_resources resources = nullptr; + + if (spvc_context_create(&context) != SPVC_SUCCESS) { + return false; + } + if (spvc_context_parse_spirv(context, module.data(), module.size(), &ir) != SPVC_SUCCESS) { + spvc_context_destroy(context); + continue; + } + if (spvc_context_create_compiler(context, SPVC_BACKEND_GLSL, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, + &compiler) != SPVC_SUCCESS) { + spvc_context_destroy(context); + continue; + } + if (spvc_compiler_create_shader_resources(compiler, &resources) != SPVC_SUCCESS) { + spvc_context_destroy(context); + continue; + } + + const spvc_reflected_resource* list = nullptr; + size_t count = 0; + if (spvc_resources_get_resource_list_for_type(resources, SPVC_RESOURCE_TYPE_SAMPLED_IMAGE, &list, &count) == + SPVC_SUCCESS) { + for (size_t i = 0; i < count; ++i) { + const Uint32 binding = + spvc_compiler_get_decoration(compiler, list[i].id, SpvDecorationBinding); + if (binding >= m_maxBindings) { + continue; + } + + String uniformName = list[i].name ? list[i].name : ""; + Int location = program.GetUniformLocation(uniformName); + if (location < 0) { + const auto arraySuffix = uniformName.find("[0]"); + if (arraySuffix != String::npos) { + uniformName = uniformName.substr(0, arraySuffix); + location = program.GetUniformLocation(uniformName); + } + } + if (location < 0) { + continue; + } + + layout.samplerUniformLocationByBinding[binding] = location; + layout.samplerTextureTargetByBinding[binding] = + UniformTypeToTextureTarget(program.GetUniformType(static_cast(location))); + } + } + + spvc_context_destroy(context); + } + + return true; + } + + Bool ProgramFactory::ReflectGlobalUboBinding(const MG_State::GLState::ProgramObject& program, + VkProgramLayout& layout) const { + layout.globalUboBinding = -1; + + const auto& spirv = program.GetGeneratedSpirv(); + for (const auto& module : spirv) { + if (module.empty()) { + continue; + } + + spvc_context context = nullptr; + spvc_parsed_ir ir = nullptr; + spvc_compiler compiler = nullptr; + spvc_resources resources = nullptr; + + if (spvc_context_create(&context) != SPVC_SUCCESS) { + return false; + } + if (spvc_context_parse_spirv(context, module.data(), module.size(), &ir) != SPVC_SUCCESS) { + spvc_context_destroy(context); + continue; + } + if (spvc_context_create_compiler(context, SPVC_BACKEND_GLSL, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, + &compiler) != SPVC_SUCCESS) { + spvc_context_destroy(context); + continue; + } + if (spvc_compiler_create_shader_resources(compiler, &resources) != SPVC_SUCCESS) { + spvc_context_destroy(context); + continue; + } + + const spvc_reflected_resource* list = nullptr; + size_t count = 0; + if (spvc_resources_get_resource_list_for_type(resources, SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, &list, &count) == + SPVC_SUCCESS) { + for (size_t i = 0; i < count; ++i) { + const char* name = list[i].name ? list[i].name : ""; + if (std::strstr(name, MG_Util::ShaderTranspiler::GLOBAL_UBO_NAME) == nullptr) { + continue; + } + const Uint32 binding = + spvc_compiler_get_decoration(compiler, list[i].id, SpvDecorationBinding); + if (binding < m_maxBindings) { + layout.globalUboBinding = static_cast(binding); + } + break; + } + } + + spvc_context_destroy(context); + if (layout.globalUboBinding >= 0) { + break; + } + } + return true; + } + Vector& ProgramFactory::GetOrCreatePipelineShaderStages( const MG_State::GLState::ProgramObject& program, CompileOptionFlags flags) { auto hash = ComputeHash(program, flags); @@ -402,4 +666,86 @@ namespace MobileGL::MG_Backend::DirectVulkan { return entry.stages; } + + const ProgramFactory::VkProgramLayout* ProgramFactory::GetOrCreateProgramLayout( + const MG_State::GLState::ProgramObject& program) { + const HashType hash = ComputeLayoutHash(program); + auto it = m_layoutCache.find(hash); + if (it != m_layoutCache.end()) { + return &it->second; + } + + VkProgramLayout layout{}; + layout.hash = hash; + if (!ReflectBindingKinds(program, layout.bindingKinds)) { + MGLOG_E("ProgramFactory::GetOrCreateProgramLayout failed: reflection failed"); + return nullptr; + } + if (!ReflectSamplerBindings(program, layout)) { + MGLOG_E("ProgramFactory::GetOrCreateProgramLayout failed: sampler reflection failed"); + return nullptr; + } + if (!ReflectGlobalUboBinding(program, layout)) { + MGLOG_E("ProgramFactory::GetOrCreateProgramLayout failed: global UBO reflection failed"); + return nullptr; + } + + Vector bindings; + bindings.reserve(m_maxBindings); + for (Uint32 binding = 0; binding < m_maxBindings; ++binding) { + const auto kind = layout.bindingKinds[binding]; + if (kind == DescriptorBindingKind::None) { + continue; + } + + VkDescriptorSetLayoutBinding layoutBinding{}; + layoutBinding.binding = binding; + layoutBinding.descriptorCount = 1; + layoutBinding.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS; + layoutBinding.pImmutableSamplers = nullptr; + if (kind == DescriptorBindingKind::UniformBufferDynamic) { + layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + layout.dynamicBindings.push_back(binding); + } else { + layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + } + bindings.push_back(layoutBinding); + } + + VkDescriptorSetLayoutCreateInfo setLayoutInfo{}; + setLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + setLayoutInfo.bindingCount = static_cast(bindings.size()); + setLayoutInfo.pBindings = bindings.data(); + VK_VERIFY(vkCreateDescriptorSetLayout(m_device, &setLayoutInfo, nullptr, &layout.descriptorSetLayout), + "ProgramFactory::GetOrCreateProgramLayout, vkCreateDescriptorSetLayout"); + + VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &layout.descriptorSetLayout; + VK_VERIFY(vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &layout.pipelineLayout), + "ProgramFactory::GetOrCreateProgramLayout, vkCreatePipelineLayout"); + + auto [insertIt, _] = m_layoutCache.emplace(hash, std::move(layout)); + return &insertIt->second; + } + + VkPipelineLayout ProgramFactory::GetOrCreatePipelineLayout(const MG_State::GLState::ProgramObject& program) { + const auto* layout = GetOrCreateProgramLayout(program); + return layout ? layout->pipelineLayout : VK_NULL_HANDLE; + } + + void ProgramFactory::DestroyLayoutCache() { + for (auto& [_, layout] : m_layoutCache) { + if (layout.pipelineLayout != VK_NULL_HANDLE) { + vkDestroyPipelineLayout(m_device, layout.pipelineLayout, nullptr); + layout.pipelineLayout = VK_NULL_HANDLE; + } + if (layout.descriptorSetLayout != VK_NULL_HANDLE) { + vkDestroyDescriptorSetLayout(m_device, layout.descriptorSetLayout, nullptr); + layout.descriptorSetLayout = VK_NULL_HANDLE; + } + } + m_layoutCache.clear(); + } } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.h index b44358cd..cc7d8635 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.h @@ -11,11 +11,19 @@ #include "../VkIncludes.h" #include "MG_State/GLState/ProgramState/ProgramObject.h" #include "MG_State/GLState/ProgramState/ShaderObject.h" +#include "MG_State/GLState/TextureState/TextureEnum.h" + #include namespace MobileGL::MG_Backend::DirectVulkan { class ProgramFactory { public: + enum class DescriptorBindingKind : Uint8 { + None = 0, + UniformBufferDynamic, + CombinedImageSampler + }; + enum class CompileOptionBit : Uint { None = 0, PositionYFlip = 1 << 0, @@ -26,6 +34,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { }; using CompileOptionFlags = Flags; using HashType = Uint64; + struct VkProgramObject { HashType hash = 0; Vector stages; @@ -70,8 +79,19 @@ namespace MobileGL::MG_Backend::DirectVulkan { } }; - explicit ProgramFactory(VkDevice device, const VulkanRendererConfig& config) - : m_device(device), m_config(config) { + struct VkProgramLayout { + HashType hash = 0; + VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE; + VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; + Vector bindingKinds; + Vector dynamicBindings; + Vector samplerUniformLocationByBinding; + Vector samplerTextureTargetByBinding; + Int globalUboBinding = -1; + }; + + explicit ProgramFactory(VkDevice device, const VulkanRendererConfig& config, Uint32 maxBindings = 16) + : m_device(device), m_config(config), m_maxBindings(maxBindings) { VkProgramObject::s_device = device; } ~ProgramFactory(); @@ -80,12 +100,24 @@ namespace MobileGL::MG_Backend::DirectVulkan { HashType ComputeHash(const MG_State::GLState::ProgramObject& program, CompileOptionFlags flags) const; Vector& GetOrCreatePipelineShaderStages( const MG_State::GLState::ProgramObject& program, CompileOptionFlags flags); + const VkProgramLayout* GetOrCreateProgramLayout(const MG_State::GLState::ProgramObject& program); + VkPipelineLayout GetOrCreatePipelineLayout(const MG_State::GLState::ProgramObject& program); static VkShaderStageFlagBits ToVkStage(ShaderStage stage); private: + HashType ComputeLayoutHash(const MG_State::GLState::ProgramObject& program) const; + static TextureTarget UniformTypeToTextureTarget(GLenum glType); + Bool ReflectBindingKinds(const MG_State::GLState::ProgramObject& program, + Vector& outKinds) const; + Bool ReflectSamplerBindings(const MG_State::GLState::ProgramObject& program, VkProgramLayout& layout) const; + Bool ReflectGlobalUboBinding(const MG_State::GLState::ProgramObject& program, VkProgramLayout& layout) const; + void DestroyLayoutCache(); + VkDevice m_device = VK_NULL_HANDLE; + Uint32 m_maxBindings = 0; UnorderedMap m_cache; + UnorderedMap m_layoutCache; const VulkanRendererConfig& m_config; static inline XXH64_state_t* m_hashState = XXH64_createState(); }; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp index b9cafd44..ae3e21a7 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp @@ -11,7 +11,6 @@ #include "MG_State/GLState/Core.h" #include "MG_State/GLState/ProgramState/ProgramObject.h" #include "MG_Util/Converters/MGToStr/FramebufferEnumConverter.h" -#include "MG_Util/ShaderTranspiler/Types.h" #include namespace MobileGL::MG_Backend::DirectVulkan { @@ -54,127 +53,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { } } - VkDeviceSize UniformDescriptorBinder::AlignUp(VkDeviceSize value, VkDeviceSize alignment) { - if (alignment == 0) { - return value; - } - return (value + alignment - 1) / alignment * alignment; - } - - Uint64 UniformDescriptorBinder::ComputeProgramHash(const MG_State::GLState::ProgramObject& program) { - XXH64_state_t* state = XXH64_createState(); - XXHASH_VERIFY(XXH64_reset(state, 0xC0D3A11ULL)); - const auto& spirv = program.GetGeneratedSpirv(); - for (const auto& module : spirv) { - XXHASH_VERIFY(XXH64_update(state, module.data(), module.size() * sizeof(Uint))); - } - const Uint32 blockCount = static_cast(program.GetActiveUniformBlocksCount()); - XXHASH_VERIFY(XXH64_update(state, &blockCount, sizeof(blockCount))); - for (Uint32 i = 0; i < blockCount; ++i) { - const Uint32 binding = program.GetUniformBlockBinding(i); - XXHASH_VERIFY(XXH64_update(state, &binding, sizeof(binding))); - } - const Uint64 hash = XXH64_digest(state); - XXH64_freeState(state); - return hash; - } - - Bool UniformDescriptorBinder::IsSamplerUniformType(GLenum glType) { - switch (glType) { - case GL_SAMPLER_1D: - case GL_SAMPLER_2D: - case GL_SAMPLER_3D: - case GL_SAMPLER_CUBE: - case GL_SAMPLER_1D_SHADOW: - case GL_SAMPLER_2D_SHADOW: - case GL_SAMPLER_1D_ARRAY: - case GL_SAMPLER_2D_ARRAY: - case GL_SAMPLER_1D_ARRAY_SHADOW: - case GL_SAMPLER_2D_ARRAY_SHADOW: - case GL_SAMPLER_2D_MULTISAMPLE: - case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: - case GL_SAMPLER_CUBE_SHADOW: - case GL_SAMPLER_BUFFER: - case GL_SAMPLER_2D_RECT: - case GL_SAMPLER_2D_RECT_SHADOW: - case GL_INT_SAMPLER_1D: - case GL_INT_SAMPLER_2D: - case GL_INT_SAMPLER_3D: - case GL_INT_SAMPLER_CUBE: - case GL_INT_SAMPLER_1D_ARRAY: - case GL_INT_SAMPLER_2D_ARRAY: - case GL_INT_SAMPLER_2D_MULTISAMPLE: - case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: - case GL_INT_SAMPLER_BUFFER: - case GL_INT_SAMPLER_2D_RECT: - case GL_UNSIGNED_INT_SAMPLER_1D: - case GL_UNSIGNED_INT_SAMPLER_2D: - case GL_UNSIGNED_INT_SAMPLER_3D: - case GL_UNSIGNED_INT_SAMPLER_CUBE: - case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_BUFFER: - case GL_UNSIGNED_INT_SAMPLER_2D_RECT: - return true; - default: - return false; - } - } - - TextureTarget UniformDescriptorBinder::UniformTypeToTextureTarget(GLenum glType) { - switch (glType) { - case GL_SAMPLER_1D: - case GL_INT_SAMPLER_1D: - case GL_UNSIGNED_INT_SAMPLER_1D: - return TextureTarget::Texture1D; - case GL_SAMPLER_3D: - case GL_INT_SAMPLER_3D: - case GL_UNSIGNED_INT_SAMPLER_3D: - return TextureTarget::Texture3D; - case GL_SAMPLER_CUBE: - case GL_SAMPLER_CUBE_SHADOW: - case GL_INT_SAMPLER_CUBE: - case GL_UNSIGNED_INT_SAMPLER_CUBE: - return TextureTarget::TextureCubeMap; - case GL_SAMPLER_2D_MULTISAMPLE: - case GL_INT_SAMPLER_2D_MULTISAMPLE: - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: - return TextureTarget::Texture2DMultisample; - case GL_SAMPLER_BUFFER: - case GL_INT_SAMPLER_BUFFER: - case GL_UNSIGNED_INT_SAMPLER_BUFFER: - return TextureTarget::TextureBuffer; - case GL_SAMPLER_1D_ARRAY: - case GL_SAMPLER_1D_ARRAY_SHADOW: - case GL_INT_SAMPLER_1D_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: - return TextureTarget::Texture1DArray; - case GL_SAMPLER_2D_ARRAY: - case GL_SAMPLER_2D_ARRAY_SHADOW: - case GL_INT_SAMPLER_2D_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: - return TextureTarget::Texture2DArray; - case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: - case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: - return TextureTarget::Texture2DMultisampleArray; - case GL_SAMPLER_2D_RECT: - case GL_SAMPLER_2D_RECT_SHADOW: - case GL_INT_SAMPLER_2D_RECT: - case GL_UNSIGNED_INT_SAMPLER_2D_RECT: - return TextureTarget::TextureRectangle; - case GL_SAMPLER_2D: - case GL_SAMPLER_2D_SHADOW: - case GL_INT_SAMPLER_2D: - case GL_UNSIGNED_INT_SAMPLER_2D: - default: - return TextureTarget::Texture2D; - } - } - Bool UniformDescriptorBinder::Initialize(VkDevice device, VkBufferManager* bufferManager, + ProgramFactory* programFactory, VkDeviceSize minUniformBufferOffsetAlignment, Uint32 frameCount, Uint32 maxBindings, Uint32 setsPerFrame, VkTextureManager* textureManager, VkSamplerManager* samplerManager) { @@ -182,6 +62,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { MOBILEGL_ASSERT(device != VK_NULL_HANDLE, "UniformDescriptorBinder::Initialize requires valid VkDevice"); MOBILEGL_ASSERT(bufferManager != nullptr, "UniformDescriptorBinder::Initialize requires valid buffer manager"); + MOBILEGL_ASSERT(programFactory != nullptr, + "UniformDescriptorBinder::Initialize requires valid program factory"); MOBILEGL_ASSERT(frameCount > 0, "UniformDescriptorBinder::Initialize requires frameCount > 0"); MOBILEGL_ASSERT(maxBindings > 0, "UniformDescriptorBinder::Initialize requires maxBindings > 0"); MOBILEGL_ASSERT(setsPerFrame > 0, "UniformDescriptorBinder::Initialize requires setsPerFrame > 0"); @@ -192,6 +74,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_device = device; m_bufferManager = bufferManager; + m_programFactory = programFactory; m_minDynamicOffsetAlignment = std::max(1, minUniformBufferOffsetAlignment); m_frameCount = frameCount; m_maxBindings = maxBindings; @@ -216,7 +99,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { return false; } frame.descriptorPools.push_back({initialPool, m_setsPerFrame, 0}); - MGLOG_D("UniformDescriptorBinder: frame %u descriptor pool created (maxSets=%u)", frameIndex, m_setsPerFrame); + MGLOG_D("UniformDescriptorBinder: frame %u descriptor pool created (maxSets=%u)", frameIndex, + m_setsPerFrame); } return true; @@ -238,9 +122,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { frame.peakAllocatedSetsThisFrame = 0; } m_frames.clear(); - DestroyProgramLayouts(); m_bufferManager = nullptr; + m_programFactory = nullptr; m_device = VK_NULL_HANDLE; m_minDynamicOffsetAlignment = 1; m_frameCount = 0; @@ -273,203 +157,10 @@ namespace MobileGL::MG_Backend::DirectVulkan { } } - Bool UniformDescriptorBinder::ReflectBindingKinds(const MG_State::GLState::ProgramObject& program, - Vector& outKinds) const { - outKinds.assign(m_maxBindings, BindingKind::None); - - const auto& spirv = program.GetGeneratedSpirv(); - for (const auto& module : spirv) { - if (module.empty()) { - continue; - } - - spvc_context context = nullptr; - spvc_parsed_ir ir = nullptr; - spvc_compiler compiler = nullptr; - spvc_resources resources = nullptr; - - if (spvc_context_create(&context) != SPVC_SUCCESS) { - return false; - } - - const spvc_result parseResult = spvc_context_parse_spirv(context, module.data(), module.size(), &ir); - if (parseResult != SPVC_SUCCESS) { - spvc_context_destroy(context); - continue; - } - - const spvc_result compilerResult = - spvc_context_create_compiler(context, SPVC_BACKEND_GLSL, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler); - if (compilerResult != SPVC_SUCCESS) { - spvc_context_destroy(context); - continue; - } - - if (spvc_compiler_create_shader_resources(compiler, &resources) != SPVC_SUCCESS) { - spvc_context_destroy(context); - continue; - } - - const auto applyBindings = [&](spvc_resource_type resourceType, BindingKind kind) { - const spvc_reflected_resource* list = nullptr; - size_t count = 0; - if (spvc_resources_get_resource_list_for_type(resources, resourceType, &list, &count) != SPVC_SUCCESS) { - return; - } - for (size_t i = 0; i < count; ++i) { - const Uint32 binding = - spvc_compiler_get_decoration(compiler, list[i].id, SpvDecorationBinding); - if (binding >= m_maxBindings) { - continue; - } - if (kind == BindingKind::CombinedImageSampler) { - outKinds[binding] = BindingKind::CombinedImageSampler; - } else if (outKinds[binding] == BindingKind::None) { - outKinds[binding] = kind; - } - } - }; - - applyBindings(SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, BindingKind::UniformBufferDynamic); - applyBindings(SPVC_RESOURCE_TYPE_SAMPLED_IMAGE, BindingKind::CombinedImageSampler); - - spvc_context_destroy(context); - } - - return true; - } - - Bool UniformDescriptorBinder::ReflectSamplerBindings(const MG_State::GLState::ProgramObject& program, - ProgramLayout& layout) const { - layout.samplerUniformLocationByBinding.assign(m_maxBindings, -1); - layout.samplerTextureTargetByBinding.assign(m_maxBindings, TextureTarget::Texture2D); - - const auto& spirv = program.GetGeneratedSpirv(); - for (const auto& module : spirv) { - if (module.empty()) { - continue; - } - - spvc_context context = nullptr; - spvc_parsed_ir ir = nullptr; - spvc_compiler compiler = nullptr; - spvc_resources resources = nullptr; - - if (spvc_context_create(&context) != SPVC_SUCCESS) { - return false; - } - if (spvc_context_parse_spirv(context, module.data(), module.size(), &ir) != SPVC_SUCCESS) { - spvc_context_destroy(context); - continue; - } - if (spvc_context_create_compiler(context, SPVC_BACKEND_GLSL, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, - &compiler) != SPVC_SUCCESS) { - spvc_context_destroy(context); - continue; - } - if (spvc_compiler_create_shader_resources(compiler, &resources) != SPVC_SUCCESS) { - spvc_context_destroy(context); - continue; - } - - const spvc_reflected_resource* list = nullptr; - size_t count = 0; - if (spvc_resources_get_resource_list_for_type(resources, SPVC_RESOURCE_TYPE_SAMPLED_IMAGE, &list, &count) == - SPVC_SUCCESS) { - for (size_t i = 0; i < count; ++i) { - const Uint32 binding = - spvc_compiler_get_decoration(compiler, list[i].id, SpvDecorationBinding); - if (binding >= m_maxBindings) { - continue; - } - - String uniformName = list[i].name ? list[i].name : ""; - Int location = program.GetUniformLocation(uniformName); - if (location < 0) { - const auto arraySuffix = uniformName.find("[0]"); - if (arraySuffix != String::npos) { - uniformName = uniformName.substr(0, arraySuffix); - location = program.GetUniformLocation(uniformName); - } - } - if (location < 0) { - continue; - } - - layout.samplerUniformLocationByBinding[binding] = location; - layout.samplerTextureTargetByBinding[binding] = - UniformTypeToTextureTarget(program.GetUniformType(static_cast(location))); - } - } - - spvc_context_destroy(context); - } - - return true; - } - - Bool UniformDescriptorBinder::ReflectGlobalUboBinding(const MG_State::GLState::ProgramObject& program, - ProgramLayout& layout) const { - layout.globalUboBinding = -1; - - const auto& spirv = program.GetGeneratedSpirv(); - for (const auto& module : spirv) { - if (module.empty()) { - continue; - } - - spvc_context context = nullptr; - spvc_parsed_ir ir = nullptr; - spvc_compiler compiler = nullptr; - spvc_resources resources = nullptr; - - if (spvc_context_create(&context) != SPVC_SUCCESS) { - return false; - } - if (spvc_context_parse_spirv(context, module.data(), module.size(), &ir) != SPVC_SUCCESS) { - spvc_context_destroy(context); - continue; - } - if (spvc_context_create_compiler(context, SPVC_BACKEND_GLSL, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, - &compiler) != SPVC_SUCCESS) { - spvc_context_destroy(context); - continue; - } - if (spvc_compiler_create_shader_resources(compiler, &resources) != SPVC_SUCCESS) { - spvc_context_destroy(context); - continue; - } - - const spvc_reflected_resource* list = nullptr; - size_t count = 0; - if (spvc_resources_get_resource_list_for_type(resources, SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, &list, &count) == - SPVC_SUCCESS) { - for (size_t i = 0; i < count; ++i) { - const char* name = list[i].name ? list[i].name : ""; - if (std::strstr(name, MG_Util::ShaderTranspiler::GLOBAL_UBO_NAME) == nullptr) { - continue; - } - const Uint32 binding = - spvc_compiler_get_decoration(compiler, list[i].id, SpvDecorationBinding); - if (binding < m_maxBindings) { - layout.globalUboBinding = static_cast(binding); - } - break; - } - } - - spvc_context_destroy(context); - if (layout.globalUboBinding >= 0) { - break; - } - } - return true; - } - Bool UniformDescriptorBinder::ResolveSamplerDescriptor(VkCommandBuffer commandBuffer, const MG_State::GLState::ProgramObject& program, - const ProgramLayout& layout, Uint32 binding, - VkDescriptorImageInfo& outImageInfo) const { + const ProgramFactory::VkProgramLayout& layout, + Uint32 binding, VkDescriptorImageInfo& outImageInfo) const { (void)commandBuffer; MOBILEGL_ASSERT(m_textureManager != nullptr, "ResolveSamplerDescriptor: texture manager is null"); MOBILEGL_ASSERT(m_samplerManager != nullptr, "ResolveSamplerDescriptor: sampler manager is null"); @@ -486,12 +177,12 @@ namespace MobileGL::MG_Backend::DirectVulkan { return false; } - const MG_State::GLState::SamplerObject* samplerToUse = samplerOverride ? samplerOverride.get() : texture->GetSamplerObject().get(); + const MG_State::GLState::SamplerObject* samplerToUse = + samplerOverride ? samplerOverride.get() : texture->GetSamplerObject().get(); if (!samplerToUse) { return false; } - VkTextureManager::TextureResource* resource = - m_textureManager->SyncTextureAndGetDescriptor(*texture); + VkTextureManager::TextureResource* resource = m_textureManager->SyncTextureAndGetDescriptor(*texture); if (resource == nullptr) { return false; } @@ -524,8 +215,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { } Bool UniformDescriptorBinder::ResolveSamplerDescriptorOverride( - const SamplerBindingOverride& samplerBindingOverride, - VkDescriptorImageInfo& outImageInfo) const { + const SamplerBindingOverride& samplerBindingOverride, VkDescriptorImageInfo& outImageInfo) const { MOBILEGL_ASSERT(m_textureManager != nullptr, "ResolveSamplerDescriptorOverride: texture manager is null"); MOBILEGL_ASSERT(m_samplerManager != nullptr, "ResolveSamplerDescriptorOverride: sampler manager is null"); if (samplerBindingOverride.texture == nullptr || samplerBindingOverride.sampler == nullptr) { @@ -546,7 +236,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { } Bool UniformDescriptorBinder::ResolveSamplerTexture(const MG_State::GLState::ProgramObject& program, - const ProgramLayout& layout, Uint32 binding, + const ProgramFactory::VkProgramLayout& layout, Uint32 binding, SharedPtr& outTexture) const { outTexture.reset(); if (!MG_State::pGLContext || binding >= layout.samplerUniformLocationByBinding.size()) { @@ -572,13 +262,16 @@ namespace MobileGL::MG_Backend::DirectVulkan { Bool UniformDescriptorBinder::CollectSampledTextures(const MG_State::GLState::ProgramObject& program, Vector& outTextures) { outTextures.clear(); - ProgramLayout* layout = GetOrCreateProgramLayout(program); + MOBILEGL_ASSERT(m_programFactory != nullptr, "CollectSampledTextures: program factory is null"); + const auto* layout = m_programFactory->GetOrCreateProgramLayout(program); if (layout == nullptr) { return false; } - for (Uint32 binding = 0; binding < m_maxBindings; ++binding) { - if (layout->bindingKinds[binding] != BindingKind::CombinedImageSampler) { + const Uint32 bindingCount = + std::min(m_maxBindings, static_cast(layout->bindingKinds.size())); + for (Uint32 binding = 0; binding < bindingCount; ++binding) { + if (layout->bindingKinds[binding] != ProgramFactory::DescriptorBindingKind::CombinedImageSampler) { continue; } @@ -595,74 +288,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { return true; } - UniformDescriptorBinder::ProgramLayout* UniformDescriptorBinder::GetOrCreateProgramLayout( - const MG_State::GLState::ProgramObject& program) { - const Uint64 hash = ComputeProgramHash(program); - auto it = m_programLayouts.find(hash); - if (it != m_programLayouts.end()) { - return &it->second; - } - - ProgramLayout layout{}; - layout.hash = hash; - if (!ReflectBindingKinds(program, layout.bindingKinds)) { - MGLOG_E("UniformDescriptorBinder::GetOrCreateProgramLayout failed: reflection failed"); - return nullptr; - } - if (!ReflectSamplerBindings(program, layout)) { - MGLOG_E("UniformDescriptorBinder::GetOrCreateProgramLayout failed: sampler reflection failed"); - return nullptr; - } - if (!ReflectGlobalUboBinding(program, layout)) { - MGLOG_E("UniformDescriptorBinder::GetOrCreateProgramLayout failed: global UBO reflection failed"); - return nullptr; - } - - Vector bindings; - bindings.reserve(m_maxBindings); - for (Uint32 binding = 0; binding < m_maxBindings; ++binding) { - const auto kind = layout.bindingKinds[binding]; - if (kind == BindingKind::None) { - continue; - } - - VkDescriptorSetLayoutBinding layoutBinding{}; - layoutBinding.binding = binding; - layoutBinding.descriptorCount = 1; - layoutBinding.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS; - layoutBinding.pImmutableSamplers = nullptr; - if (kind == BindingKind::UniformBufferDynamic) { - layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - layout.dynamicBindings.push_back(binding); - } else { - layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - } - bindings.push_back(layoutBinding); - } - - VkDescriptorSetLayoutCreateInfo setLayoutInfo{}; - setLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - setLayoutInfo.bindingCount = static_cast(bindings.size()); - setLayoutInfo.pBindings = bindings.data(); - VK_VERIFY(vkCreateDescriptorSetLayout(m_device, &setLayoutInfo, nullptr, &layout.descriptorSetLayout), - "UniformDescriptorBinder::GetOrCreateProgramLayout, vkCreateDescriptorSetLayout"); - - VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; - pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutInfo.setLayoutCount = 1; - pipelineLayoutInfo.pSetLayouts = &layout.descriptorSetLayout; - VK_VERIFY(vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &layout.pipelineLayout), - "UniformDescriptorBinder::GetOrCreateProgramLayout, vkCreatePipelineLayout"); - - auto [insertIt, _] = m_programLayouts.emplace(hash, std::move(layout)); - return &insertIt->second; - } - - VkPipelineLayout UniformDescriptorBinder::GetOrCreatePipelineLayout(const MG_State::GLState::ProgramObject& program) { - auto* layout = GetOrCreateProgramLayout(program); - return layout ? layout->pipelineLayout : VK_NULL_HANDLE; - } - Bool UniformDescriptorBinder::GatherBindingPayloads(const MG_State::GLState::ProgramObject& program, Vector& outData, Vector& outSizes) const { @@ -749,7 +374,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { const VkResult result = vkCreateDescriptorPool(m_device, &poolInfo, nullptr, &outPool); if (result != VK_SUCCESS) { - MGLOG_E("UniformDescriptorBinder::CreateDescriptorPool failed: vkCreateDescriptorPool returned %d", result); + MGLOG_E("UniformDescriptorBinder::CreateDescriptorPool failed: vkCreateDescriptorPool returned %d", + result); return false; } return true; @@ -790,7 +416,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { const MG_State::GLState::ProgramObject& program, Uint32 frameIndex, const SamplerBindingOverride* samplerBindingOverride) { - ProgramLayout* layout = GetOrCreateProgramLayout(program); + MOBILEGL_ASSERT(m_programFactory != nullptr, "BindProgramUniformBuffers: program factory is null"); + const auto* layout = m_programFactory->GetOrCreateProgramLayout(program); MOBILEGL_ASSERT(layout != nullptr, "UniformDescriptorBinder::BindProgramUniformBuffers: program layout is null"); @@ -816,7 +443,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (outResult == VK_SUCCESS) { ++bucket.allocatedSets; ++frame.allocatedSetsThisFrame; - frame.peakAllocatedSetsThisFrame = std::max(frame.peakAllocatedSetsThisFrame, frame.allocatedSetsThisFrame); + frame.peakAllocatedSetsThisFrame = + std::max(frame.peakAllocatedSetsThisFrame, frame.allocatedSetsThisFrame); } }; @@ -856,9 +484,11 @@ namespace MobileGL::MG_Backend::DirectVulkan { imageInfos.reserve(m_maxBindings); dynamicOffsets.reserve(layout->dynamicBindings.size()); - for (Uint32 binding = 0; binding < m_maxBindings; ++binding) { + const Uint32 bindingCount = + std::min(m_maxBindings, static_cast(layout->bindingKinds.size())); + for (Uint32 binding = 0; binding < bindingCount; ++binding) { const auto kind = layout->bindingKinds[binding]; - if (kind == BindingKind::None) { + if (kind == ProgramFactory::DescriptorBindingKind::None) { continue; } @@ -869,7 +499,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { write.dstArrayElement = 0; write.descriptorCount = 1; - if (kind == BindingKind::UniformBufferDynamic) { + if (kind == ProgramFactory::DescriptorBindingKind::UniformBufferDynamic) { const void* payload = bindingData[binding]; VkDeviceSize payloadSize = bindingSizes[binding]; if (payload == nullptr || payloadSize == 0) { @@ -937,22 +567,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { vkUpdateDescriptorSets(m_device, static_cast(writes.size()), writes.data(), 0, nullptr); } - vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, layout->pipelineLayout, 0, 1, &descriptorSet, - static_cast(dynamicOffsets.size()), dynamicOffsets.data()); + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, layout->pipelineLayout, 0, 1, + &descriptorSet, static_cast(dynamicOffsets.size()), dynamicOffsets.data()); return true; } - - void UniformDescriptorBinder::DestroyProgramLayouts() { - for (auto& [_, layout] : m_programLayouts) { - if (layout.pipelineLayout != VK_NULL_HANDLE) { - vkDestroyPipelineLayout(m_device, layout.pipelineLayout, nullptr); - layout.pipelineLayout = VK_NULL_HANDLE; - } - if (layout.descriptorSetLayout != VK_NULL_HANDLE) { - vkDestroyDescriptorSetLayout(m_device, layout.descriptorSetLayout, nullptr); - layout.descriptorSetLayout = VK_NULL_HANDLE; - } - } - m_programLayouts.clear(); - } } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h index d023988b..5697f32d 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h @@ -8,6 +8,7 @@ #pragma once +#include "ProgramFactory.h" #include "VkBufferManager.h" #include "VkSamplerManager.h" #include "VkTextureManager.h" @@ -23,12 +24,6 @@ namespace MobileGL::MG_State::GLState { namespace MobileGL::MG_Backend::DirectVulkan { class UniformDescriptorBinder { public: - enum class BindingKind : Uint8 { - None = 0, - UniformBufferDynamic, - CombinedImageSampler - }; - struct SamplerBindingOverride { Uint32 binding = 0; MG_State::GLState::ITextureObject* texture = nullptr; @@ -36,13 +31,13 @@ namespace MobileGL::MG_Backend::DirectVulkan { }; Bool Initialize(VkDevice device, VkBufferManager* bufferManager, + ProgramFactory* programFactory, VkDeviceSize minUniformBufferOffsetAlignment, Uint32 frameCount, Uint32 maxBindings = 16, Uint32 setsPerFrame = 64, VkTextureManager* textureManager = nullptr, VkSamplerManager* samplerManager = nullptr); void Shutdown(); 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, @@ -65,42 +60,23 @@ namespace MobileGL::MG_Backend::DirectVulkan { Uint32 peakAllocatedSetsThisFrame = 0; }; - struct ProgramLayout { - Uint64 hash = 0; - VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE; - VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; - Vector bindingKinds; - Vector dynamicBindings; - Vector samplerUniformLocationByBinding; - Vector samplerTextureTargetByBinding; - Int globalUboBinding = -1; - }; - - static VkDeviceSize AlignUp(VkDeviceSize value, VkDeviceSize alignment); - static Uint64 ComputeProgramHash(const MG_State::GLState::ProgramObject& program); - static Bool IsSamplerUniformType(GLenum glType); - 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 ResolveSamplerTexture(const MG_State::GLState::ProgramObject& program, + const ProgramFactory::VkProgramLayout& layout, Uint32 binding, + SharedPtr& outTexture) const; Bool ResolveSamplerDescriptor(VkCommandBuffer commandBuffer, const MG_State::GLState::ProgramObject& program, - const ProgramLayout& layout, Uint32 binding, + const ProgramFactory::VkProgramLayout& layout, Uint32 binding, VkDescriptorImageInfo& outImageInfo) const; Bool ResolveSamplerDescriptorOverride(const SamplerBindingOverride& samplerBindingOverride, VkDescriptorImageInfo& outImageInfo) const; - Bool ReflectBindingKinds(const MG_State::GLState::ProgramObject& program, Vector& outKinds) const; - ProgramLayout* GetOrCreateProgramLayout(const MG_State::GLState::ProgramObject& program); Bool GatherBindingPayloads(const MG_State::GLState::ProgramObject& program, Vector& outData, Vector& outSizes) const; Bool CreateDescriptorPool(Uint32 maxSets, VkDescriptorPool& outPool) const; Bool GrowFrameDescriptorPool(FrameResources& frame, Uint32 frameIndex); - void DestroyProgramLayouts(); VkDevice m_device = VK_NULL_HANDLE; VkBufferManager* m_bufferManager = nullptr; + ProgramFactory* m_programFactory = nullptr; Vector m_frames; - UnorderedMap m_programLayouts; VkDeviceSize m_minDynamicOffsetAlignment = 1; Uint32 m_frameCount = 0; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index dd549cf5..eb01deb3 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -98,6 +98,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { } namespace { + static constexpr Uint32 kMaxProgramBindings = 16; + static constexpr Uint32 kDescriptorSetsPerFrame = 64; static constexpr Uint kHiddenBlitProgramId = 0xFFFFFFF0u; static constexpr Uint kHiddenBlitVertexShaderId = 0xFFFFFFF1u; static constexpr Uint kHiddenBlitFragmentShaderId = 0xFFFFFFF2u; @@ -339,7 +341,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_pipelineFactory = MakeUnique(m_device, m_config); MOBILEGL_ASSERT(m_pipelineFactory != nullptr, "PipelineFactory creation failed."); - m_programFactory = MakeUnique(m_device, m_config); + m_programFactory = MakeUnique(m_device, m_config, kMaxProgramBindings); MOBILEGL_ASSERT(m_programFactory != nullptr, "ProgramFactory creation failed."); m_samplerManager = MakeUnique(); @@ -351,10 +353,10 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_uniformDescriptorBinder = MakeUnique(); MOBILEGL_ASSERT(m_uniformDescriptorBinder != nullptr, "UniformDescriptorBinder creation failed."); - succeeded = m_uniformDescriptorBinder->Initialize(m_device, &m_bufferManager, - m_physicalDevice.properties.limits.minUniformBufferOffsetAlignment, - m_config.MaxFramesInFlight, 16, 64, - m_textureManager.get(), m_samplerManager.get()); + succeeded = m_uniformDescriptorBinder->Initialize( + m_device, &m_bufferManager, m_programFactory.get(), + m_physicalDevice.properties.limits.minUniformBufferOffsetAlignment, m_config.MaxFramesInFlight, + kMaxProgramBindings, kDescriptorSetsPerFrame, m_textureManager.get(), m_samplerManager.get()); MOBILEGL_ASSERT(succeeded, "UniformDescriptorBinder initialization failed."); m_vertexInputStateFactory = MakeUnique(m_config); MOBILEGL_ASSERT(m_vertexInputStateFactory != nullptr, "VertexInputStateFactory creation failed."); @@ -372,7 +374,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { VK_VERIFY(vkDeviceWaitIdle(m_device)); m_pipelineFactory.reset(); - m_programFactory.reset(); ShutdownBlitResources(); if (m_samplerManager) { m_samplerManager->Shutdown(); @@ -392,6 +393,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_uniformDescriptorBinder->Shutdown(); m_uniformDescriptorBinder.reset(); } + m_programFactory.reset(); ShutdownSwapchain(); m_renderPassManager.reset(); @@ -669,7 +671,7 @@ void main() { PipelineFactory::PipelineCreatePayload payload{ .programHash = m_programFactory->ComputeHash(*m_blitResources.program, transformFlags), .vertexInputHash = 0, - .pipelineLayout = m_uniformDescriptorBinder->GetOrCreatePipelineLayout(*m_blitResources.program), + .pipelineLayout = m_programFactory->GetOrCreatePipelineLayout(*m_blitResources.program), .renderPass = renderPassEntry.renderPass, .subpass = 0, .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, @@ -707,7 +709,7 @@ void main() { auto vertexInputHash = m_vertexInputStateFactory->ComputeHash(vao); auto& vis = m_vertexInputStateFactory->GetOrCreateVertexInputState(vao); - auto pipelineLayout = m_uniformDescriptorBinder->GetOrCreatePipelineLayout(program); + auto pipelineLayout = m_programFactory->GetOrCreatePipelineLayout(program); auto cullFaceEnabled = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::CullFace); auto depthTestEnabled = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::DepthTest); BlendFactor srcRGB = BlendFactor::One;