diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp index 9cbb002b..efdbffb9 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp @@ -8,6 +8,7 @@ #include "ProgramFactory.h" +#include "MG_Util/ShaderTranspiler/SpvcSession.h" #include "MG_Util/ShaderTranspiler/Types.h" #include #include @@ -23,6 +24,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { namespace { using ShaderObject = MG_State::GLState::ShaderObject; + using SpvcSession = MG_Util::ShaderTranspiler::SpvcSession; + using SessionUsageBit = MG_Util::ShaderTranspiler::SessionUsageBit; struct PositionTargetInfo { Uint32 variableId = 0; @@ -323,10 +326,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { } } // namespace - ProgramFactory::~ProgramFactory() { - DestroyLayoutCache(); - } - VkShaderStageFlagBits ProgramFactory::ToVkStage(ShaderStage stage) { switch (stage) { case ShaderStage::Vertex: @@ -355,24 +354,17 @@ namespace MobileGL::MG_Backend::DirectVulkan { XXHASH_VERIFY(XXH64_update(m_hashState, spv.data(), spv.size() * sizeof(Uint))); } XXHASH_VERIFY(XXH64_update(m_hashState, &flags, sizeof(CompileOptionFlags))); - HashType hash = XXH64_digest(m_hashState); - 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))); - } + // Include UBO block bindings in hash so different binding configurations produce different entries 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); + + HashType hash = XXH64_digest(m_hashState); + return hash; } TextureTarget ProgramFactory::UniformTypeToTextureTarget(GLenum glType) { @@ -426,205 +418,117 @@ namespace MobileGL::MG_Backend::DirectVulkan { } } - Bool ProgramFactory::ReflectBindingKinds(const MG_State::GLState::ProgramObject& program, - Vector& outKinds) const { - outKinds.assign(m_maxBindings, DescriptorBindingKind::None); + void ProgramFactory::ReflectLayout(const MG_State::GLState::ProgramObject& program, + VkProgramObject& entry) const { + // Initialize layout vectors + entry.bindingKinds.assign(m_maxBindings, DescriptorBindingKind::None); + entry.samplerUniformLocationByBinding.assign(m_maxBindings, -1); + entry.samplerTextureTargetByBinding.assign(m_maxBindings, TextureTarget::Texture2D); + entry.globalUboBinding = -1; + entry.dynamicBindings.clear(); + // Use SpvcSession (Reflection mode) to reflect all SPIR-V modules in a single pass per module 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; + SpvcSession session(module, SessionUsageBit::Reflection); - 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; + // Reflect uniform buffers + auto ubos = session.GetShaderInterface(SPVC_RESOURCE_TYPE_UNIFORM_BUFFER); + for (const auto& ubo : ubos) { + const Uint32 binding = ubo.location; // GetShaderInterface stores binding in location field + if (binding >= m_maxBindings) { + continue; } - 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; + if (entry.bindingKinds[binding] == DescriptorBindingKind::None) { + entry.bindingKinds[binding] = DescriptorBindingKind::UniformBufferDynamic; + } + + // Check for global UBO + if (entry.globalUboBinding < 0 && + std::strstr(ubo.name.c_str(), MG_Util::ShaderTranspiler::GLOBAL_UBO_NAME) != nullptr) { + entry.globalUboBinding = static_cast(binding); + } + } + + // Reflect sampled images + auto samplers = session.GetShaderInterface(SPVC_RESOURCE_TYPE_SAMPLED_IMAGE); + for (const auto& sampler : samplers) { + const Uint32 binding = sampler.location; // GetShaderInterface stores binding in location field + if (binding >= m_maxBindings) { + continue; + } + + // Sampler always wins over UBO for a binding slot + entry.bindingKinds[binding] = DescriptorBindingKind::CombinedImageSampler; + + // Resolve uniform location for this sampler + String uniformName = sampler.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; + } - applyBindings(SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, DescriptorBindingKind::UniformBufferDynamic); - applyBindings(SPVC_RESOURCE_TYPE_SAMPLED_IMAGE, DescriptorBindingKind::CombinedImageSampler); - - spvc_context_destroy(context); + entry.samplerUniformLocationByBinding[binding] = location; + entry.samplerTextureTargetByBinding[binding] = + UniformTypeToTextureTarget(program.GetUniformType(static_cast(location))); + } } - 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()) { + // Build Vulkan descriptor set layout and pipeline layout from reflected binding kinds + Vector bindings; + bindings.reserve(m_maxBindings); + for (Uint32 binding = 0; binding < m_maxBindings; ++binding) { + const auto kind = entry.bindingKinds[binding]; + if (kind == DescriptorBindingKind::None) { 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; + 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; + entry.dynamicBindings.push_back(binding); + } else { + layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; } - 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); + bindings.push_back(layoutBinding); } - return true; + 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, &entry.descriptorSetLayout), + "ProgramFactory::ReflectLayout, vkCreateDescriptorSetLayout"); + + VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &entry.descriptorSetLayout; + VK_VERIFY(vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &entry.pipelineLayout), + "ProgramFactory::ReflectLayout, vkCreatePipelineLayout"); } - 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 ProgramFactory::VkProgramObject& ProgramFactory::GetOrCreateProgram( const MG_State::GLState::ProgramObject& program, CompileOptionFlags flags) { auto hash = ComputeHash(program, flags); auto it = m_cache.find(hash); if (it != m_cache.end()) { - return it->second.stages; + return it->second; } auto& entry = m_cache[hash]; @@ -664,88 +568,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { entry.stages.push_back(stage); } - return entry.stages; - } + // Reflect and create layout as part of the program object + ReflectLayout(program, entry); - 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(); + return entry; } } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.h index cc7d8635..06fe897c 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.h @@ -39,6 +39,16 @@ namespace MobileGL::MG_Backend::DirectVulkan { HashType hash = 0; Vector stages; Vector modules; + + // Layout data (previously in separate VkProgramLayout) + VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE; + VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; + Vector bindingKinds; + Vector dynamicBindings; + Vector samplerUniformLocationByBinding; + Vector samplerTextureTargetByBinding; + Int globalUboBinding = -1; + static inline VkDevice s_device = VK_NULL_HANDLE; VkProgramObject() = default; @@ -48,76 +58,86 @@ namespace MobileGL::MG_Backend::DirectVulkan { hash = other.hash; stages = std::move(other.stages); modules = std::move(other.modules); + descriptorSetLayout = other.descriptorSetLayout; + pipelineLayout = other.pipelineLayout; + bindingKinds = std::move(other.bindingKinds); + dynamicBindings = std::move(other.dynamicBindings); + samplerUniformLocationByBinding = std::move(other.samplerUniformLocationByBinding); + samplerTextureTargetByBinding = std::move(other.samplerTextureTargetByBinding); + globalUboBinding = other.globalUboBinding; other.hash = 0; + other.descriptorSetLayout = VK_NULL_HANDLE; + other.pipelineLayout = VK_NULL_HANDLE; + other.globalUboBinding = -1; } VkProgramObject& operator=(VkProgramObject&& other) noexcept { if (this == &other) { return *this; } - DestroyModules(); - stages.clear(); + Destroy(); hash = other.hash; stages = std::move(other.stages); modules = std::move(other.modules); + descriptorSetLayout = other.descriptorSetLayout; + pipelineLayout = other.pipelineLayout; + bindingKinds = std::move(other.bindingKinds); + dynamicBindings = std::move(other.dynamicBindings); + samplerUniformLocationByBinding = std::move(other.samplerUniformLocationByBinding); + samplerTextureTargetByBinding = std::move(other.samplerTextureTargetByBinding); + globalUboBinding = other.globalUboBinding; other.hash = 0; + other.descriptorSetLayout = VK_NULL_HANDLE; + other.pipelineLayout = VK_NULL_HANDLE; + other.globalUboBinding = -1; return *this; } ~VkProgramObject() { - DestroyModules(); - stages.clear(); + Destroy(); } private: - void DestroyModules() { - for (auto module : modules) { - if (module != VK_NULL_HANDLE && s_device != VK_NULL_HANDLE) { - vkDestroyShaderModule(s_device, module, nullptr); + void Destroy() { + if (s_device != VK_NULL_HANDLE) { + if (pipelineLayout != VK_NULL_HANDLE) { + vkDestroyPipelineLayout(s_device, pipelineLayout, nullptr); + pipelineLayout = VK_NULL_HANDLE; + } + if (descriptorSetLayout != VK_NULL_HANDLE) { + vkDestroyDescriptorSetLayout(s_device, descriptorSetLayout, nullptr); + descriptorSetLayout = VK_NULL_HANDLE; + } + for (auto module : modules) { + if (module != VK_NULL_HANDLE) { + vkDestroyShaderModule(s_device, module, nullptr); + } } } modules.clear(); + stages.clear(); } }; - 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(); + ~ProgramFactory() = default; ProgramFactory(const ProgramFactory&) = delete; HashType ComputeHash(const MG_State::GLState::ProgramObject& program, CompileOptionFlags flags) const; - Vector& GetOrCreatePipelineShaderStages( + const VkProgramObject& GetOrCreateProgram( 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(); + void ReflectLayout(const MG_State::GLState::ProgramObject& program, VkProgramObject& entry) const; 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/UniformManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp index 85414a41..7f16d652 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp @@ -158,18 +158,18 @@ namespace MobileGL::MG_Backend::DirectVulkan { } Bool UniformManager::ResolveSamplerDescriptor(VkCommandBuffer commandBuffer, - const MG_State::GLState::ProgramObject& program, - const ProgramFactory::VkProgramLayout& layout, - Uint32 binding, VkDescriptorImageInfo& outImageInfo) const { + const MG_State::GLState::ProgramObject& program, + const ProgramFactory::VkProgramObject& programObj, + 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"); SharedPtr texture; - if (!ResolveSamplerTexture(program, layout, binding, texture)) { + if (!ResolveSamplerTexture(program, programObj, binding, texture)) { return false; } - const Int location = layout.samplerUniformLocationByBinding[binding]; + const Int location = programObj.samplerUniformLocationByBinding[binding]; const Int unit = program.GetUniformSamplerOrImageUnitIndex(static_cast(location)); auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit); const auto samplerOverride = textureUnit.GetSamplerObject(); @@ -236,14 +236,14 @@ namespace MobileGL::MG_Backend::DirectVulkan { } Bool UniformManager::ResolveSamplerTexture(const MG_State::GLState::ProgramObject& program, - const ProgramFactory::VkProgramLayout& layout, Uint32 binding, - SharedPtr& outTexture) const { + const ProgramFactory::VkProgramObject& programObj, Uint32 binding, + SharedPtr& outTexture) const { outTexture.reset(); - if (!MG_State::pGLContext || binding >= layout.samplerUniformLocationByBinding.size()) { + if (!MG_State::pGLContext || binding >= programObj.samplerUniformLocationByBinding.size()) { return false; } - const Int location = layout.samplerUniformLocationByBinding[binding]; + const Int location = programObj.samplerUniformLocationByBinding[binding]; if (location < 0) { return false; } @@ -254,29 +254,25 @@ namespace MobileGL::MG_Backend::DirectVulkan { } auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit); - const TextureTarget preferredTarget = layout.samplerTextureTargetByBinding[binding]; + const TextureTarget preferredTarget = programObj.samplerTextureTargetByBinding[binding]; outTexture = textureUnit.GetBindingSlot(preferredTarget).GetBoundObject(); return outTexture != nullptr; } Bool UniformManager::CollectSampledTextures(const MG_State::GLState::ProgramObject& program, - Vector& outTextures) { + const ProgramFactory::VkProgramObject& programObj, + Vector& outTextures) { outTextures.clear(); - MOBILEGL_ASSERT(m_programFactory != nullptr, "CollectSampledTextures: program factory is null"); - const auto* layout = m_programFactory->GetOrCreateProgramLayout(program); - if (layout == nullptr) { - return false; - } const Uint32 bindingCount = - std::min(m_maxBindings, static_cast(layout->bindingKinds.size())); + std::min(m_maxBindings, static_cast(programObj.bindingKinds.size())); for (Uint32 binding = 0; binding < bindingCount; ++binding) { - if (layout->bindingKinds[binding] != ProgramFactory::DescriptorBindingKind::CombinedImageSampler) { + if (programObj.bindingKinds[binding] != ProgramFactory::DescriptorBindingKind::CombinedImageSampler) { continue; } SharedPtr texture; - if (!ResolveSamplerTexture(program, *layout, binding, texture) || !texture) { + if (!ResolveSamplerTexture(program, programObj, binding, texture) || !texture) { continue; } @@ -406,13 +402,13 @@ namespace MobileGL::MG_Backend::DirectVulkan { return true; } - VkResult UniformManager::AllocateDescriptorSetsFromActivePool(Uint32 frameIndex, const ProgramFactory::VkProgramLayout& layout, VkDescriptorSet& outDescriptorSet) { + VkResult UniformManager::AllocateDescriptorSetsFromActivePool(Uint32 frameIndex, const ProgramFactory::VkProgramObject& programObj, VkDescriptorSet& outDescriptorSet) { auto& frame = m_frames[frameIndex]; auto& bucket = frame.descriptorPools[frame.activeDescriptorPoolIndex]; VkDescriptorSetAllocateInfo allocInfo{}; allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; allocInfo.descriptorSetCount = 1; - allocInfo.pSetLayouts = &layout.descriptorSetLayout; + allocInfo.pSetLayouts = &programObj.descriptorSetLayout; allocInfo.descriptorPool = bucket.handle; VkResult result = vkAllocateDescriptorSets(m_device, &allocInfo, &outDescriptorSet); @@ -426,14 +422,10 @@ namespace MobileGL::MG_Backend::DirectVulkan { } Bool UniformManager::BindProgramUniformBuffers(VkCommandBuffer commandBuffer, - const MG_State::GLState::ProgramObject& program, - Uint32 frameIndex, - const SamplerBindingOverride* samplerBindingOverride) { - 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"); - + const MG_State::GLState::ProgramObject& program, + const ProgramFactory::VkProgramObject& programObj, + Uint32 frameIndex, + const SamplerBindingOverride* samplerBindingOverride) { auto& frame = m_frames[frameIndex]; if (frame.descriptorPools.empty()) { MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: frame descriptor pools are invalid"); @@ -444,13 +436,13 @@ namespace MobileGL::MG_Backend::DirectVulkan { } VkDescriptorSet descriptorSet = VK_NULL_HANDLE; - VkResult allocResult = AllocateDescriptorSetsFromActivePool(frameIndex, *layout, descriptorSet); + 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, *layout, descriptorSet); + allocResult = AllocateDescriptorSetsFromActivePool(frameIndex, programObj, descriptorSet); } if (allocResult != VK_SUCCESS || descriptorSet == VK_NULL_HANDLE) { MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: vkAllocateDescriptorSets returned %d", @@ -476,12 +468,12 @@ namespace MobileGL::MG_Backend::DirectVulkan { Vector dynamicOffsets; bufferInfos.reserve(m_maxBindings); imageInfos.reserve(m_maxBindings); - dynamicOffsets.reserve(layout->dynamicBindings.size()); + dynamicOffsets.reserve(programObj.dynamicBindings.size()); const Uint32 bindingCount = - std::min(m_maxBindings, static_cast(layout->bindingKinds.size())); + std::min(m_maxBindings, static_cast(programObj.bindingKinds.size())); for (Uint32 binding = 0; binding < bindingCount; ++binding) { - const auto kind = layout->bindingKinds[binding]; + const auto kind = programObj.bindingKinds[binding]; if (kind == ProgramFactory::DescriptorBindingKind::None) { continue; } @@ -497,7 +489,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { const void* payload = bindingData[binding]; VkDeviceSize payloadSize = bindingSizes[binding]; if (payload == nullptr || payloadSize == 0) { - if (layout->globalUboBinding == static_cast(binding)) { + if (programObj.globalUboBinding == static_cast(binding)) { const void* globalUboData = program.GetUBOData(); const VkDeviceSize globalUboSize = static_cast(program.GetUBOSize()); if (globalUboData != nullptr && globalUboSize > 0) { @@ -510,7 +502,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { BufferSlice slice{}; if (!m_bufferManager->UploadTransient(BufferKind::Uniform, frameIndex, payload, payloadSize, m_minDynamicOffsetAlignment, slice)) { - MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: UBO upload failed on binding %u", + MOBILEGL_ASSERT(false, "UniformDescriptorBinder::BindProgramUniformBuffers failed: UBO upload failed on binding %u", binding); return false; } @@ -534,7 +526,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { samplerBindingOverride->sampler != nullptr) { hasImage = ResolveSamplerDescriptorOverride(*samplerBindingOverride, imageInfo); } else { - hasImage = ResolveSamplerDescriptor(commandBuffer, program, *layout, binding, imageInfo); + hasImage = ResolveSamplerDescriptor(commandBuffer, program, programObj, binding, imageInfo); } if (!hasImage) { MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: sampler binding %u has no valid texture descriptor", @@ -557,7 +549,7 @@ 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, + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, programObj.pipelineLayout, 0, 1, &descriptorSet, static_cast(dynamicOffsets.size()), dynamicOffsets.data()); return true; } diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h index 16c9148e..36d43997 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h @@ -39,9 +39,12 @@ namespace MobileGL::MG_Backend::DirectVulkan { void BeginFrame(Uint32 frameIndex); Bool CollectSampledTextures(const MG_State::GLState::ProgramObject& program, + const ProgramFactory::VkProgramObject& programObj, Vector& outTextures); Bool BindProgramUniformBuffers(VkCommandBuffer commandBuffer, - const MG_State::GLState::ProgramObject& program, Uint32 frameIndex, + const MG_State::GLState::ProgramObject& program, + const ProgramFactory::VkProgramObject& programObj, + Uint32 frameIndex, const SamplerBindingOverride* samplerBindingOverride = nullptr); private: @@ -59,10 +62,10 @@ namespace MobileGL::MG_Backend::DirectVulkan { }; Bool ResolveSamplerTexture(const MG_State::GLState::ProgramObject& program, - const ProgramFactory::VkProgramLayout& layout, Uint32 binding, + const ProgramFactory::VkProgramObject& programObj, Uint32 binding, SharedPtr& outTexture) const; Bool ResolveSamplerDescriptor(VkCommandBuffer commandBuffer, const MG_State::GLState::ProgramObject& program, - const ProgramFactory::VkProgramLayout& layout, Uint32 binding, + const ProgramFactory::VkProgramObject& programObj, Uint32 binding, VkDescriptorImageInfo& outImageInfo) const; Bool ResolveSamplerDescriptorOverride(const SamplerBindingOverride& samplerBindingOverride, VkDescriptorImageInfo& outImageInfo) const; @@ -71,7 +74,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { Bool CreateDescriptorPool(Uint32 maxSets, VkDescriptorPool& outPool) const; Bool GrowFrameDescriptorPool(FrameResources& frame, Uint32 frameIndex); VkResult AllocateDescriptorSetsFromActivePool( - Uint32 frameIndex, const ProgramFactory::VkProgramLayout& layout, VkDescriptorSet& outDescriptorSet); + 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/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index dc50c518..d4cd3101 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -472,7 +472,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (!m_bufferManager.UploadTransient(BufferKind::Vertex, m_frameContext.GetCurrentFrameIndex(), sourceData->data(), static_cast(sourceSize), 16, slice)) { - MGLOG_E("UploadAndBindVertexStreams skipped: failed to upload transient binding %zu", binding); + MOBILEGL_ASSERT(false, "UploadAndBindVertexStreams skipped: failed to upload transient binding %zu", binding); return false; } if (!transientThisFrame) { @@ -534,7 +534,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { if (!m_bufferManager.UploadTransient(BufferKind::Index, m_frameContext.GetCurrentFrameIndex(), indexData->data() + pIndexBufferView->indexByteOffset, static_cast(indexDataSizeBytes), indexSize, slice)) { - MGLOG_E("DrawElements skipped: failed to prepare transient index buffer"); + MOBILEGL_ASSERT(false, "DrawElements skipped: failed to prepare transient index buffer"); return false; } if (!transientThisFrame) { @@ -667,11 +667,11 @@ void main() { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; ProgramFactory::CompileOptionFlags transformFlags = 0; - auto& stages = m_programFactory->GetOrCreatePipelineShaderStages(*m_blitResources.program, transformFlags); + const auto& programObj = m_programFactory->GetOrCreateProgram(*m_blitResources.program, transformFlags); PipelineFactory::PipelineCreatePayload payload{ - .programHash = m_programFactory->ComputeHash(*m_blitResources.program, transformFlags), + .programHash = programObj.hash, .vertexInputHash = 0, - .pipelineLayout = m_programFactory->GetOrCreatePipelineLayout(*m_blitResources.program), + .pipelineLayout = programObj.pipelineLayout, .renderPass = renderPassEntry.renderPass, .subpass = 0, .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, @@ -687,7 +687,7 @@ void main() { .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, - .stages = &stages, + .stages = &programObj.stages, .vertexInputState = &kEmptyVertexInputState }; return m_pipelineFactory->GetOrCreatePipeline(payload); @@ -700,16 +700,14 @@ void main() { const RenderPassEntry& renderPassEntry) { ProgramFactory::CompileOptionFlags transformFlags = GetShaderTransformFlags(m_swapchainObject.GetPreTransform()); Bool invertClockwise = transformFlags & ProgramFactory::CompileOptionBit::PositionYFlip; - auto& stages = m_programFactory->GetOrCreatePipelineShaderStages(program, transformFlags); - if (stages.empty()) { + 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; } - const Uint64 programHash = m_programFactory->ComputeHash(program, transformFlags); auto vertexInputHash = m_vertexInputStateFactory->ComputeHash(vao); auto& vis = m_vertexInputStateFactory->GetOrCreateVertexInputState(vao); - 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; @@ -720,9 +718,9 @@ void main() { auto mask = MG_State::pGLContext->GetColorMask(); PipelineFactory::PipelineCreatePayload payload { - .programHash = programHash, + .programHash = programObj.hash, .vertexInputHash = vertexInputHash, - .pipelineLayout = pipelineLayout, + .pipelineLayout = programObj.pipelineLayout, .renderPass = renderPassEntry.renderPass, .subpass = 0, .topology = MG_Util::ConvertPrimitiveModeToVkEnum(mode), @@ -743,7 +741,7 @@ void main() { (mask.g() ? VK_COLOR_COMPONENT_G_BIT : 0u) | (mask.b() ? VK_COLOR_COMPONENT_B_BIT : 0u) | (mask.a() ? VK_COLOR_COMPONENT_A_BIT : 0u) ), - .stages = &stages, + .stages = &programObj.stages, .vertexInputState = &vis.state }; return m_pipelineFactory->GetOrCreatePipeline(payload); @@ -756,6 +754,8 @@ void main() { MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); const auto& vao = *MG_State::pGLContext->GetBoundVertexArray(); const auto& program = *MG_State::pGLContext->GetCurrentProgram(); + ProgramFactory::CompileOptionFlags transformFlags = GetShaderTransformFlags(m_swapchainObject.GetPreTransform()); + const auto& programObj = m_programFactory->GetOrCreateProgram(program, transformFlags); // Begin command recording if not yet if (!frame.isCommandRecording) { @@ -770,7 +770,7 @@ void main() { // without draws in between to give it a chance to materialize such clear. // Deal with this situation here. Vector sampledTextures; - Bool hasSampledTextures = m_uniformManager->CollectSampledTextures(program, sampledTextures); + Bool hasSampledTextures = m_uniformManager->CollectSampledTextures(program, programObj, sampledTextures); MOBILEGL_ASSERT(hasSampledTextures, "%s: CollectSampledTextures failed", __func__); MGLOG_D("SetupDraw: program=%u drawFbo=%u sampledTextureCount=%zu activeRenderPass=%s", program.GetExternalIndex(), drawFbo ? drawFbo->GetExternalIndex() : 0u, sampledTextures.size(), @@ -856,7 +856,7 @@ void main() { vkCmdBindPipeline(frame.commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - m_uniformManager->BindProgramUniformBuffers(frame.commandBuffer, program, + m_uniformManager->BindProgramUniformBuffers(frame.commandBuffer, program, programObj, m_frameContext.GetCurrentFrameIndex()); auto vtxUploadOk = UploadAndBindVertexBuffers(frame.commandBuffer, vao); @@ -1086,8 +1086,10 @@ void main() { .sampler = (filter == GL_LINEAR ? m_blitResources.linearSampler.get() : m_blitResources.nearestSampler.get()), }; + ProgramFactory::CompileOptionFlags blitTransformFlags = 0; + const auto& blitProgramObj = m_programFactory->GetOrCreateProgram(*m_blitResources.program, blitTransformFlags); const Bool bound = m_uniformManager->BindProgramUniformBuffers( - frame.commandBuffer, *m_blitResources.program, m_frameContext.GetCurrentFrameIndex(), + frame.commandBuffer, *m_blitResources.program, blitProgramObj, m_frameContext.GetCurrentFrameIndex(), &samplerBindingOverride); MOBILEGL_ASSERT(bound, "TryBlitToDefaultFramebufferWithShader: BindProgramUniformBuffers failed"); vkCmdDraw(frame.commandBuffer, 3, 1, 0, 0); diff --git a/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.cpp b/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.cpp index 81ebb237..7c31ecb0 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.cpp +++ b/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.cpp @@ -179,7 +179,14 @@ namespace MobileGL { for (uint32_t i = 0; i < count; ++i) { if (bindings[i]->descriptor_type == SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER) { InterfaceVariable var; - var.name = bindings[i]->name; + // Use the block/type name (e.g. "MGL_GLOBAL_UBO") rather than + // the variable name, which may be empty or meaningless for UBOs. + // This is consistent with ParseMetaData() which uses type_description->type_name. + if (bindings[i]->type_description && bindings[i]->type_description->type_name) { + var.name = bindings[i]->type_description->type_name; + } else { + var.name = bindings[i]->name; + } var.location = bindings[i]->binding; variables.push_back(var); }