From c810b27a838e99c68cd8adb1ca212a93118f6b35 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 17 Feb 2026 22:31:56 +0800 Subject: [PATCH] [Feat] (MG_Backend/DirectVulkan): initial implementation of VkTextureSamplerManager, only fallback path available now --- CMakeLists.txt | 1 + .../Renderer/UniformDescriptorBinder.cpp | 394 ++++++++++++++---- .../Renderer/UniformDescriptorBinder.h | 34 +- .../Renderer/VkTextureSamplerManager.cpp | 222 ++++++++++ .../Renderer/VkTextureSamplerManager.h | 44 ++ .../DirectVulkan/Renderer/VulkanRenderer.cpp | 52 ++- .../DirectVulkan/Renderer/VulkanRenderer.h | 5 +- 7 files changed, 638 insertions(+), 114 deletions(-) create mode 100644 MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.cpp create mode 100644 MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7692c6b2..7d91edf8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -220,6 +220,7 @@ set(SOURCE_FILES MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.cpp MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.cpp MobileGL/MG_Backend/DirectVulkan/Renderer/VkFramebufferManager.cpp + MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.cpp MobileGL/MG_State/GLState/Core.cpp MobileGL/MG_State/GLState/ErrorState/Error.cpp diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp index e9228e73..5da047ff 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp @@ -20,9 +20,72 @@ namespace MobileGL::MG_Backend::DirectVulkan { 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; + } + } + Bool UniformDescriptorBinder::Initialize(VkDevice device, VmaAllocator allocator, VkDeviceSize minUniformBufferOffsetAlignment, Uint32 frameCount, - Uint32 maxBindings, Uint32 setsPerFrame, VkDeviceSize perFrameUploadBytes) { + Uint32 maxBindings, Uint32 setsPerFrame, VkDeviceSize perFrameUploadBytes, + VkTextureSamplerManager* textureSamplerManager) { Shutdown(); MOBILEGL_ASSERT(device != VK_NULL_HANDLE, "UniformDescriptorBinder::Initialize requires valid VkDevice"); @@ -38,53 +101,28 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_frameCount = frameCount; m_maxBindings = maxBindings; m_setsPerFrame = setsPerFrame; - - Vector bindings; - bindings.reserve(m_maxBindings); - for (Uint32 bindingIndex = 0; bindingIndex < m_maxBindings; ++bindingIndex) { - VkDescriptorSetLayoutBinding binding{}; - binding.binding = bindingIndex; - binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - binding.descriptorCount = 1; - binding.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS; - binding.pImmutableSamplers = nullptr; - bindings.push_back(binding); - } - - 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, &m_descriptorSetLayout), - "UniformDescriptorBinder::Initialize, vkCreateDescriptorSetLayout"); - - VkDescriptorPoolSize poolSize{}; - poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - poolSize.descriptorCount = m_frameCount * m_setsPerFrame * m_maxBindings; - - VkDescriptorPoolCreateInfo poolInfo{}; - poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - poolInfo.maxSets = m_frameCount * m_setsPerFrame; - poolInfo.poolSizeCount = 1; - poolInfo.pPoolSizes = &poolSize; - VK_VERIFY(vkCreateDescriptorPool(m_device, &poolInfo, nullptr, &m_descriptorPool), - "UniformDescriptorBinder::Initialize, vkCreateDescriptorPool"); - - m_descriptorSets.resize(m_frameCount * m_setsPerFrame, VK_NULL_HANDLE); - Vector layouts(m_descriptorSets.size(), m_descriptorSetLayout); - VkDescriptorSetAllocateInfo allocInfo{}; - allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - allocInfo.descriptorPool = m_descriptorPool; - allocInfo.descriptorSetCount = static_cast(layouts.size()); - allocInfo.pSetLayouts = layouts.data(); - VK_VERIFY(vkAllocateDescriptorSets(m_device, &allocInfo, m_descriptorSets.data()), - "UniformDescriptorBinder::Initialize, vkAllocateDescriptorSets"); + m_textureSamplerManager = textureSamplerManager; m_frames.resize(m_frameCount); for (Uint32 frameIndex = 0; frameIndex < m_frameCount; ++frameIndex) { auto& frame = m_frames[frameIndex]; frame.writeCursor = 0; - frame.descriptorCursor = 0; + frame.descriptorPool = VK_NULL_HANDLE; + + VkDescriptorPoolSize poolSizes[2]{}; + poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + poolSizes[0].descriptorCount = m_setsPerFrame * m_maxBindings; + poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + poolSizes[1].descriptorCount = m_setsPerFrame * m_maxBindings; + + VkDescriptorPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolInfo.maxSets = m_setsPerFrame; + poolInfo.poolSizeCount = static_cast(std::size(poolSizes)); + poolInfo.pPoolSizes = poolSizes; + VK_VERIFY(vkCreateDescriptorPool(m_device, &poolInfo, nullptr, &frame.descriptorPool), + "UniformDescriptorBinder::Initialize, vkCreateDescriptorPool(frame)"); + const Bool created = frame.uploadBuffer.Create( m_allocator, m_perFrameUploadBytes, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO, VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT); @@ -101,21 +139,14 @@ namespace MobileGL::MG_Backend::DirectVulkan { void UniformDescriptorBinder::Shutdown() { for (auto& frame : m_frames) { frame.uploadBuffer.Destroy(); + if (m_device != VK_NULL_HANDLE && frame.descriptorPool != VK_NULL_HANDLE) { + vkDestroyDescriptorPool(m_device, frame.descriptorPool, nullptr); + } + frame.descriptorPool = VK_NULL_HANDLE; frame.writeCursor = 0; - frame.descriptorCursor = 0; } m_frames.clear(); - m_descriptorSets.clear(); - - if (m_device != VK_NULL_HANDLE && m_descriptorPool != VK_NULL_HANDLE) { - vkDestroyDescriptorPool(m_device, m_descriptorPool, nullptr); - } - m_descriptorPool = VK_NULL_HANDLE; - - if (m_device != VK_NULL_HANDLE && m_descriptorSetLayout != VK_NULL_HANDLE) { - vkDestroyDescriptorSetLayout(m_device, m_descriptorSetLayout, nullptr); - } - m_descriptorSetLayout = VK_NULL_HANDLE; + DestroyProgramLayouts(); m_allocator = nullptr; m_device = VK_NULL_HANDLE; @@ -124,13 +155,141 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_frameCount = 0; m_maxBindings = 0; m_setsPerFrame = 0; + m_textureSamplerManager = nullptr; } void UniformDescriptorBinder::BeginFrame(Uint32 frameIndex) { MOBILEGL_ASSERT(frameIndex < m_frames.size(), "UniformDescriptorBinder::BeginFrame invalid frame index"); auto& frame = m_frames[frameIndex]; frame.writeCursor = 0; - frame.descriptorCursor = 0; + VK_VERIFY(vkResetDescriptorPool(m_device, frame.descriptorPool, 0), + "UniformDescriptorBinder::BeginFrame, vkResetDescriptorPool"); + } + + 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; + } + + 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; + } + + 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::AllocateUploadRegion(FrameResources& frame, VkDeviceSize size, VkDeviceSize& outOffset) { @@ -221,7 +380,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { Bool UniformDescriptorBinder::BindProgramUniformBuffers(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, const MG_State::GLState::ProgramObject& program, Uint32 frameIndex) { - if (m_descriptorSetLayout == VK_NULL_HANDLE || m_descriptorPool == VK_NULL_HANDLE) { + if (m_frames.empty()) { MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: binder is not initialized"); return false; } @@ -230,16 +389,30 @@ namespace MobileGL::MG_Backend::DirectVulkan { return false; } - auto& frame = m_frames[frameIndex]; - if (frame.descriptorCursor >= m_setsPerFrame) { - MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: per-frame descriptor set budget exceeded"); + ProgramLayout* layout = GetOrCreateProgramLayout(program); + if (!layout || layout->pipelineLayout == VK_NULL_HANDLE || layout->descriptorSetLayout == VK_NULL_HANDLE) { + MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: cannot get program layout"); + return false; + } + if (layout->pipelineLayout != pipelineLayout) { + MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: pipelineLayout mismatch"); return false; } - const Uint32 descriptorSetIndex = frameIndex * m_setsPerFrame + frame.descriptorCursor; - const VkDescriptorSet descriptorSet = m_descriptorSets[descriptorSetIndex]; - ++frame.descriptorCursor; + auto& frame = m_frames[frameIndex]; + if (frame.descriptorPool == VK_NULL_HANDLE) { + MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: frame descriptor pool is invalid"); + return false; + } + VkDescriptorSetAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = frame.descriptorPool; + allocInfo.descriptorSetCount = 1; + allocInfo.pSetLayouts = &layout->descriptorSetLayout; + VkDescriptorSet descriptorSet = VK_NULL_HANDLE; + VK_VERIFY(vkAllocateDescriptorSets(m_device, &allocInfo, &descriptorSet), + "UniformDescriptorBinder::BindProgramUniformBuffers, vkAllocateDescriptorSets"); Vector bindingData; Vector bindingSizes; if (!GatherBindingPayloads(program, bindingData, bindingSizes)) { @@ -248,51 +421,92 @@ namespace MobileGL::MG_Backend::DirectVulkan { } static const Uint8 kFallbackData[16] = {}; + VkDescriptorImageInfo fallbackImageInfo{}; + const Bool hasFallbackImage = m_textureSamplerManager && m_textureSamplerManager->GetFallbackDescriptor(fallbackImageInfo); - Vector bufferInfos(m_maxBindings); Vector writes; writes.reserve(m_maxBindings); - Vector dynamicOffsets(m_maxBindings, 0); + Vector bufferInfos; + Vector imageInfos; + Vector dynamicOffsets; + bufferInfos.reserve(m_maxBindings); + imageInfos.reserve(m_maxBindings); + dynamicOffsets.reserve(layout->dynamicBindings.size()); for (Uint32 binding = 0; binding < m_maxBindings; ++binding) { - const void* payload = bindingData[binding]; - VkDeviceSize payloadSize = bindingSizes[binding]; - if (payload == nullptr || payloadSize == 0) { - payload = kFallbackData; - payloadSize = sizeof(kFallbackData); + const auto kind = layout->bindingKinds[binding]; + if (kind == BindingKind::None) { + continue; } - VkDeviceSize payloadOffset = 0; - if (!AllocateUploadRegion(frame, payloadSize, payloadOffset)) { - MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: frame upload buffer exhausted"); - return false; - } - if (!frame.uploadBuffer.Upload(payload, payloadSize, payloadOffset)) { - MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: upload failed on binding %u", binding); - return false; - } - - bufferInfos[binding].buffer = frame.uploadBuffer.GetHandle(); - bufferInfos[binding].offset = 0; - bufferInfos[binding].range = payloadSize; - dynamicOffsets[binding] = static_cast(payloadOffset); - VkWriteDescriptorSet write{}; write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; write.dstSet = descriptorSet; write.dstBinding = binding; write.dstArrayElement = 0; write.descriptorCount = 1; - write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - write.pBufferInfo = &bufferInfos[binding]; - writes.push_back(write); + + if (kind == BindingKind::UniformBufferDynamic) { + const void* payload = bindingData[binding]; + VkDeviceSize payloadSize = bindingSizes[binding]; + if (payload == nullptr || payloadSize == 0) { + payload = kFallbackData; + payloadSize = sizeof(kFallbackData); + } + + VkDeviceSize payloadOffset = 0; + if (!AllocateUploadRegion(frame, payloadSize, payloadOffset)) { + MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: frame upload buffer exhausted"); + return false; + } + if (!frame.uploadBuffer.Upload(payload, payloadSize, payloadOffset)) { + MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: UBO upload failed on binding %u", + binding); + return false; + } + + VkDescriptorBufferInfo bufferInfo{}; + bufferInfo.buffer = frame.uploadBuffer.GetHandle(); + bufferInfo.offset = 0; + bufferInfo.range = payloadSize; + bufferInfos.push_back(bufferInfo); + + write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + write.pBufferInfo = &bufferInfos.back(); + writes.push_back(write); + dynamicOffsets.push_back(static_cast(payloadOffset)); + } else { + if (!hasFallbackImage) { + MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: fallback sampler/texture is unavailable"); + return false; + } + imageInfos.push_back(fallbackImageInfo); + write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write.pImageInfo = &imageInfos.back(); + writes.push_back(write); + } } - vkUpdateDescriptorSets(m_device, static_cast(writes.size()), writes.data(), 0, nullptr); + if (!writes.empty()) { + vkUpdateDescriptorSets(m_device, static_cast(writes.size()), writes.data(), 0, nullptr); + } vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, static_cast(dynamicOffsets.size()), dynamicOffsets.data()); return true; } -} // namespace MobileGL::MG_Backend::DirectVulkan + 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 47ad0b9f..06ce550d 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h @@ -9,6 +9,7 @@ #pragma once #include "VkBufferObject.h" +#include "VkTextureSamplerManager.h" #include "../VkIncludes.h" #include #include @@ -20,42 +21,59 @@ namespace MobileGL::MG_State::GLState { namespace MobileGL::MG_Backend::DirectVulkan { class UniformDescriptorBinder { public: + enum class BindingKind : Uint8 { + None = 0, + UniformBufferDynamic, + CombinedImageSampler + }; + Bool Initialize(VkDevice device, VmaAllocator allocator, VkDeviceSize minUniformBufferOffsetAlignment, Uint32 frameCount, Uint32 maxBindings = 16, Uint32 setsPerFrame = 64, - VkDeviceSize perFrameUploadBytes = 4 * 1024 * 1024); + VkDeviceSize perFrameUploadBytes = 4 * 1024 * 1024, + VkTextureSamplerManager* textureSamplerManager = nullptr); void Shutdown(); void BeginFrame(Uint32 frameIndex); + VkPipelineLayout GetOrCreatePipelineLayout(const MG_State::GLState::ProgramObject& program); Bool BindProgramUniformBuffers(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, const MG_State::GLState::ProgramObject& program, Uint32 frameIndex); - VkDescriptorSetLayout GetDescriptorSetLayout() const { return m_descriptorSetLayout; } - private: struct FrameResources { VkBufferObject uploadBuffer; + VkDescriptorPool descriptorPool = VK_NULL_HANDLE; VkDeviceSize writeCursor = 0; - Uint32 descriptorCursor = 0; + }; + + struct ProgramLayout { + Uint64 hash = 0; + VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE; + VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; + Vector bindingKinds; + Vector dynamicBindings; }; static VkDeviceSize AlignUp(VkDeviceSize value, VkDeviceSize alignment); + static Uint64 ComputeProgramHash(const MG_State::GLState::ProgramObject& program); + static Bool IsSamplerUniformType(GLenum glType); + Bool ReflectBindingKinds(const MG_State::GLState::ProgramObject& program, Vector& outKinds) const; + ProgramLayout* GetOrCreateProgramLayout(const MG_State::GLState::ProgramObject& program); Bool AllocateUploadRegion(FrameResources& frame, VkDeviceSize size, VkDeviceSize& outOffset); Bool GatherBindingPayloads(const MG_State::GLState::ProgramObject& program, Vector& outData, Vector& outSizes) const; + void DestroyProgramLayouts(); VkDevice m_device = VK_NULL_HANDLE; VmaAllocator m_allocator = nullptr; - VkDescriptorSetLayout m_descriptorSetLayout = VK_NULL_HANDLE; - VkDescriptorPool m_descriptorPool = VK_NULL_HANDLE; - - Vector m_descriptorSets; Vector m_frames; + UnorderedMap m_programLayouts; VkDeviceSize m_minDynamicOffsetAlignment = 1; VkDeviceSize m_perFrameUploadBytes = 0; Uint32 m_frameCount = 0; Uint32 m_maxBindings = 0; Uint32 m_setsPerFrame = 0; + VkTextureSamplerManager* m_textureSamplerManager = nullptr; }; } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.cpp new file mode 100644 index 00000000..46e7325e --- /dev/null +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.cpp @@ -0,0 +1,222 @@ +// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.cpp +// Copyright (c) 2025-2026 MobileGL-Dev +// Licensed under the GNU Lesser General Public License v3.0: +// https://www.gnu.org/licenses/gpl-3.0.txt +// https://www.gnu.org/licenses/lgpl-3.0.txt +// SPDX-License-Identifier: LGPL-3.0-only +// End of Source File Header + +#include "VkTextureSamplerManager.h" + +namespace MobileGL::MG_Backend::DirectVulkan { + Bool VkTextureSamplerManager::Initialize(const InitInfo& initInfo) { + Shutdown(); + m_device = initInfo.device; + m_physicalDevice = initInfo.physicalDevice; + m_commandPool = initInfo.commandPool; + m_graphicsQueue = initInfo.graphicsQueue; + + if (m_device == VK_NULL_HANDLE || m_physicalDevice == VK_NULL_HANDLE || m_commandPool == VK_NULL_HANDLE || + m_graphicsQueue == VK_NULL_HANDLE) { + MGLOG_E("VkTextureSamplerManager::Initialize failed: invalid init handles"); + Shutdown(); + return false; + } + + VkImageCreateInfo imageInfo{}; + imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.extent.width = 1; + imageInfo.extent.height = 1; + imageInfo.extent.depth = 1; + imageInfo.mipLevels = 1; + imageInfo.arrayLayers = 1; + imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM; + imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + VK_VERIFY(vkCreateImage(m_device, &imageInfo, nullptr, &m_fallbackImage), + "VkTextureSamplerManager::Initialize, vkCreateImage"); + + VkMemoryRequirements memoryRequirements{}; + vkGetImageMemoryRequirements(m_device, m_fallbackImage, &memoryRequirements); + + VkMemoryAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memoryRequirements.size; + allocInfo.memoryTypeIndex = FindMemoryType(memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_VERIFY(vkAllocateMemory(m_device, &allocInfo, nullptr, &m_fallbackImageMemory), + "VkTextureSamplerManager::Initialize, vkAllocateMemory"); + VK_VERIFY(vkBindImageMemory(m_device, m_fallbackImage, m_fallbackImageMemory, 0), + "VkTextureSamplerManager::Initialize, vkBindImageMemory"); + + VkImageViewCreateInfo viewInfo{}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = m_fallbackImage; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = VK_FORMAT_R8G8B8A8_UNORM; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = 1; + VK_VERIFY(vkCreateImageView(m_device, &viewInfo, nullptr, &m_fallbackImageView), + "VkTextureSamplerManager::Initialize, vkCreateImageView"); + + VkSamplerCreateInfo samplerInfo{}; + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.mipLodBias = 0.0f; + samplerInfo.anisotropyEnable = VK_FALSE; + samplerInfo.maxAnisotropy = 1.0f; + samplerInfo.compareEnable = VK_FALSE; + samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; + samplerInfo.minLod = 0.0f; + samplerInfo.maxLod = 0.0f; + samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; + samplerInfo.unnormalizedCoordinates = VK_FALSE; + VK_VERIFY(vkCreateSampler(m_device, &samplerInfo, nullptr, &m_fallbackSampler), + "VkTextureSamplerManager::Initialize, vkCreateSampler"); + + if (!UploadFallbackTexture()) { + Shutdown(); + return false; + } + return true; + } + + void VkTextureSamplerManager::Shutdown() { + if (m_device != VK_NULL_HANDLE && m_fallbackSampler != VK_NULL_HANDLE) { + vkDestroySampler(m_device, m_fallbackSampler, nullptr); + } + m_fallbackSampler = VK_NULL_HANDLE; + + if (m_device != VK_NULL_HANDLE && m_fallbackImageView != VK_NULL_HANDLE) { + vkDestroyImageView(m_device, m_fallbackImageView, nullptr); + } + m_fallbackImageView = VK_NULL_HANDLE; + + if (m_device != VK_NULL_HANDLE && m_fallbackImage != VK_NULL_HANDLE) { + vkDestroyImage(m_device, m_fallbackImage, nullptr); + } + m_fallbackImage = VK_NULL_HANDLE; + + if (m_device != VK_NULL_HANDLE && m_fallbackImageMemory != VK_NULL_HANDLE) { + vkFreeMemory(m_device, m_fallbackImageMemory, nullptr); + } + m_fallbackImageMemory = VK_NULL_HANDLE; + + m_device = VK_NULL_HANDLE; + m_physicalDevice = VK_NULL_HANDLE; + m_commandPool = VK_NULL_HANDLE; + m_graphicsQueue = VK_NULL_HANDLE; + } + + Bool VkTextureSamplerManager::GetFallbackDescriptor(VkDescriptorImageInfo& outImageInfo) const { + if (m_fallbackSampler == VK_NULL_HANDLE || m_fallbackImageView == VK_NULL_HANDLE) { + return false; + } + outImageInfo.sampler = m_fallbackSampler; + outImageInfo.imageView = m_fallbackImageView; + outImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + return true; + } + + Uint32 VkTextureSamplerManager::FindMemoryType(Uint32 typeFilter, VkMemoryPropertyFlags properties) const { + VkPhysicalDeviceMemoryProperties memoryProperties{}; + vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memoryProperties); + for (Uint32 i = 0; i < memoryProperties.memoryTypeCount; ++i) { + if ((typeFilter & (1U << i)) && + (memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) { + return i; + } + } + MOBILEGL_ASSERT(false, "VkTextureSamplerManager::FindMemoryType failed"); + return 0; + } + + Bool VkTextureSamplerManager::UploadFallbackTexture() { + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = m_commandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandBufferCount = 1; + + VkCommandBuffer commandBuffer = VK_NULL_HANDLE; + VK_VERIFY(vkAllocateCommandBuffers(m_device, &allocInfo, &commandBuffer), + "VkTextureSamplerManager::UploadFallbackTexture, vkAllocateCommandBuffers"); + + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + VK_VERIFY(vkBeginCommandBuffer(commandBuffer, &beginInfo), + "VkTextureSamplerManager::UploadFallbackTexture, vkBeginCommandBuffer"); + + VkImageMemoryBarrier toTransfer{}; + toTransfer.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + toTransfer.srcAccessMask = 0; + toTransfer.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + toTransfer.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + toTransfer.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + toTransfer.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + toTransfer.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + toTransfer.image = m_fallbackImage; + toTransfer.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + toTransfer.subresourceRange.baseMipLevel = 0; + toTransfer.subresourceRange.levelCount = 1; + toTransfer.subresourceRange.baseArrayLayer = 0; + toTransfer.subresourceRange.layerCount = 1; + vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, + nullptr, 0, nullptr, 1, &toTransfer); + + VkClearColorValue clearColor{}; + clearColor.float32[0] = 1.0f; + clearColor.float32[1] = 1.0f; + clearColor.float32[2] = 1.0f; + clearColor.float32[3] = 1.0f; + VkImageSubresourceRange clearRange{}; + clearRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + clearRange.baseMipLevel = 0; + clearRange.levelCount = 1; + clearRange.baseArrayLayer = 0; + clearRange.layerCount = 1; + vkCmdClearColorImage(commandBuffer, m_fallbackImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearColor, 1, + &clearRange); + + VkImageMemoryBarrier toShaderRead{}; + toShaderRead.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + toShaderRead.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + toShaderRead.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + toShaderRead.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + toShaderRead.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + toShaderRead.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + toShaderRead.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + toShaderRead.image = m_fallbackImage; + toShaderRead.subresourceRange = clearRange; + vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, + 0, nullptr, 0, nullptr, 1, &toShaderRead); + + VK_VERIFY(vkEndCommandBuffer(commandBuffer), + "VkTextureSamplerManager::UploadFallbackTexture, vkEndCommandBuffer"); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + VK_VERIFY(vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE), + "VkTextureSamplerManager::UploadFallbackTexture, vkQueueSubmit"); + VK_VERIFY(vkQueueWaitIdle(m_graphicsQueue), + "VkTextureSamplerManager::UploadFallbackTexture, vkQueueWaitIdle"); + + vkFreeCommandBuffers(m_device, m_commandPool, 1, &commandBuffer); + return true; + } +} // namespace MobileGL::MG_Backend::DirectVulkan + diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.h new file mode 100644 index 00000000..f44fff11 --- /dev/null +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.h @@ -0,0 +1,44 @@ +// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.h +// Copyright (c) 2025-2026 MobileGL-Dev +// Licensed under the GNU Lesser General Public License v3.0: +// https://www.gnu.org/licenses/gpl-3.0.txt +// https://www.gnu.org/licenses/lgpl-3.0.txt +// SPDX-License-Identifier: LGPL-3.0-only +// End of Source File Header + +#pragma once + +#include "../VkIncludes.h" +#include + +namespace MobileGL::MG_Backend::DirectVulkan { + class VkTextureSamplerManager { + public: + struct InitInfo { + VkDevice device = VK_NULL_HANDLE; + VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; + VkCommandPool commandPool = VK_NULL_HANDLE; + VkQueue graphicsQueue = VK_NULL_HANDLE; + }; + + Bool Initialize(const InitInfo& initInfo); + void Shutdown(); + + Bool GetFallbackDescriptor(VkDescriptorImageInfo& outImageInfo) const; + + private: + Uint32 FindMemoryType(Uint32 typeFilter, VkMemoryPropertyFlags properties) const; + Bool UploadFallbackTexture(); + + VkDevice m_device = VK_NULL_HANDLE; + VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE; + VkCommandPool m_commandPool = VK_NULL_HANDLE; + VkQueue m_graphicsQueue = VK_NULL_HANDLE; + + VkImage m_fallbackImage = VK_NULL_HANDLE; + VkDeviceMemory m_fallbackImageMemory = VK_NULL_HANDLE; + VkImageView m_fallbackImageView = VK_NULL_HANDLE; + VkSampler m_fallbackSampler = VK_NULL_HANDLE; + }; +} // namespace MobileGL::MG_Backend::DirectVulkan + diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index 2fdfebb8..2223e027 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -62,7 +62,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { } VkPipeline VulkanRenderer::GetOrCreatePipeline( - const MG_State::GLState::ProgramObject& program, Uint64 vertexInputHash, + const MG_State::GLState::ProgramObject& program, VkPipelineLayout pipelineLayout, Uint64 vertexInputHash, const VkPipelineVertexInputStateCreateInfo& vertexInputState) { MOBILEGL_ASSERT(m_pipelineFactory != nullptr, "PipelineFactory is not initialized"); MOBILEGL_ASSERT(m_programFactory != nullptr, "ProgramFactory is not initialized"); @@ -76,7 +76,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { PipelineFactory::PipelineCreatePayload payload{}; payload.programHash = programHash; payload.vertexInputHash = vertexInputHash; - payload.pipelineLayout = m_pipelineLayout; + payload.pipelineLayout = pipelineLayout; payload.renderPass = (m_activeRenderPass != VK_NULL_HANDLE) ? m_activeRenderPass : m_renderPassLoad; payload.subpass = 0; payload.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; @@ -89,14 +89,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { MGLOG_D("PrepareDemoPipeline called"); VkPipelineLayoutCreateInfo plci{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO}; - VkDescriptorSetLayout descriptorSetLayouts[1] = {VK_NULL_HANDLE}; - if (m_uniformDescriptorBinder) { - descriptorSetLayouts[0] = m_uniformDescriptorBinder->GetDescriptorSetLayout(); - } - if (descriptorSetLayouts[0] != VK_NULL_HANDLE) { - plci.setLayoutCount = 1; - plci.pSetLayouts = descriptorSetLayouts; - } VK_VERIFY(vkCreatePipelineLayout(m_device, &plci, nullptr, &m_pipelineLayout), "vkCreatePipelineLayout"); MGLOG_I("PrepareDemoPipeline completed"); @@ -124,10 +116,16 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_pipelineFactory = MakeUnique(m_device, m_config); m_programFactory = MakeUnique(m_device, m_config); + m_textureSamplerManager = MakeUnique(); + if (!m_textureSamplerManager->Initialize({m_device, m_physicalDevice.handle, m_commandPool, m_graphicsQueue})) { + MGLOG_E("VkTextureSamplerManager initialization failed. Sampler/texture descriptors will fallback."); + m_textureSamplerManager.reset(); + } m_uniformDescriptorBinder = MakeUnique(); if (!m_uniformDescriptorBinder->Initialize(m_device, m_allocator, m_physicalDevice.properties.limits.minUniformBufferOffsetAlignment, - m_config.MaxFramesInFlight)) { + m_config.MaxFramesInFlight, 16, 64, 4 * 1024 * 1024, + m_textureSamplerManager.get())) { MGLOG_E("UniformDescriptorBinder initialization failed. UBO sync on Vulkan backend is disabled."); m_uniformDescriptorBinder.reset(); } @@ -155,6 +153,10 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_pipelineFactory.reset(); m_programFactory.reset(); + if (m_textureSamplerManager) { + m_textureSamplerManager->Shutdown(); + m_textureSamplerManager.reset(); + } m_vertexInputStateFactory.reset(); if (m_framebufferManager) { m_framebufferManager->Shutdown(); @@ -677,7 +679,17 @@ namespace MobileGL::MG_Backend::DirectVulkan { vertexInputInfo = &emptyVertexInputBuilder.Build(); } - VkPipeline pipelineToBind = GetOrCreatePipeline(*payload.program, vertexInputHash, *vertexInputInfo); + VkPipelineLayout pipelineLayoutToUse = m_pipelineLayout; + if (m_uniformDescriptorBinder) { + pipelineLayoutToUse = m_uniformDescriptorBinder->GetOrCreatePipelineLayout(*payload.program); + if (pipelineLayoutToUse == VK_NULL_HANDLE) { + MGLOG_W("DrawArrays skipped: failed to get pipeline layout for program"); + return; + } + } + + VkPipeline pipelineToBind = GetOrCreatePipeline(*payload.program, pipelineLayoutToUse, vertexInputHash, + *vertexInputInfo); if (pipelineToBind == VK_NULL_HANDLE) { MGLOG_W("DrawArrays skipped: failed to create/get pipeline"); return; @@ -685,7 +697,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineToBind); if (m_uniformDescriptorBinder && - !m_uniformDescriptorBinder->BindProgramUniformBuffers(commandBuffer, m_pipelineLayout, *payload.program, + !m_uniformDescriptorBinder->BindProgramUniformBuffers(commandBuffer, pipelineLayoutToUse, *payload.program, m_frameContext.GetCurrentFrameIndex())) { MGLOG_W("DrawArrays skipped: failed to bind uniform descriptors"); return; @@ -765,7 +777,17 @@ namespace MobileGL::MG_Backend::DirectVulkan { vertexInputInfo = &emptyVertexInputBuilder.Build(); } - VkPipeline pipelineToBind = GetOrCreatePipeline(*payload.drawArray.program, vertexInputHash, *vertexInputInfo); + VkPipelineLayout pipelineLayoutToUse = m_pipelineLayout; + if (m_uniformDescriptorBinder) { + pipelineLayoutToUse = m_uniformDescriptorBinder->GetOrCreatePipelineLayout(*payload.drawArray.program); + if (pipelineLayoutToUse == VK_NULL_HANDLE) { + MGLOG_W("DrawElements skipped: failed to get pipeline layout for program"); + return; + } + } + + VkPipeline pipelineToBind = GetOrCreatePipeline(*payload.drawArray.program, pipelineLayoutToUse, vertexInputHash, + *vertexInputInfo); if (pipelineToBind == VK_NULL_HANDLE) { MGLOG_W("DrawElements skipped: failed to create/get pipeline"); return; @@ -794,7 +816,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineToBind); if (m_uniformDescriptorBinder && - !m_uniformDescriptorBinder->BindProgramUniformBuffers(commandBuffer, m_pipelineLayout, + !m_uniformDescriptorBinder->BindProgramUniformBuffers(commandBuffer, pipelineLayoutToUse, *payload.drawArray.program, m_frameContext.GetCurrentFrameIndex())) { MGLOG_W("DrawElements skipped: failed to bind uniform descriptors"); diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h index 42b11b5b..ec93bce9 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h @@ -16,6 +16,7 @@ #include "VertexInputStateFactory.h" #include "VkBufferObject.h" #include "VkFramebufferManager.h" +#include "VkTextureSamplerManager.h" #include "MG_Util/Math/VectorTypes.h" #include #include @@ -152,6 +153,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { UniquePtr m_uniformDescriptorBinder; UniquePtr m_vertexInputStateFactory; UniquePtr m_framebufferManager; + UniquePtr m_textureSamplerManager; void CreateInstance(); VkResult SetupDebugMessenger(); @@ -171,7 +173,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkRenderPass CreateDefaultRenderPass(VkAttachmentLoadOp loadOp); void CreateDefaultFramebuffers(); void PrepareDemoPipeline(); - VkPipeline GetOrCreatePipeline(const MG_State::GLState::ProgramObject& program, Uint64 vertexInputHash, + VkPipeline GetOrCreatePipeline(const MG_State::GLState::ProgramObject& program, VkPipelineLayout pipelineLayout, + Uint64 vertexInputHash, const VkPipelineVertexInputStateCreateInfo& vertexInputState); void TransitionSwapchainImageToColorAttachment(VkCommandBuffer commandBuffer, Uint32 imageIndex); void TransitionDepthStencilImageToAttachment(VkCommandBuffer commandBuffer, Uint32 imageIndex);