diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp index 9786233d..f2650365 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp @@ -174,14 +174,14 @@ namespace MobileGL::MG_Backend::DirectVulkan { } } - Bool UniformDescriptorBinder::Initialize(VkDevice device, VmaAllocator allocator, + Bool UniformDescriptorBinder::Initialize(VkDevice device, VkBufferManager* bufferManager, VkDeviceSize minUniformBufferOffsetAlignment, Uint32 frameCount, - Uint32 maxBindings, Uint32 setsPerFrame, VkDeviceSize perFrameUploadBytes, + Uint32 maxBindings, Uint32 setsPerFrame, VkTextureManager* textureManager, VkSamplerManager* samplerManager) { Shutdown(); MOBILEGL_ASSERT(device != VK_NULL_HANDLE, "UniformDescriptorBinder::Initialize requires valid VkDevice"); - MOBILEGL_ASSERT(allocator != nullptr, "UniformDescriptorBinder::Initialize requires valid VMA allocator"); + MOBILEGL_ASSERT(bufferManager != nullptr, "UniformDescriptorBinder::Initialize requires valid buffer manager"); 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"); @@ -191,9 +191,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { "UniformDescriptorBinder::Initialize requires valid sampler manager"); m_device = device; - m_allocator = allocator; + m_bufferManager = bufferManager; m_minDynamicOffsetAlignment = std::max(1, minUniformBufferOffsetAlignment); - m_perFrameUploadBytes = perFrameUploadBytes; m_frameCount = frameCount; m_maxBindings = maxBindings; m_setsPerFrame = setsPerFrame; @@ -204,7 +203,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_frames.resize(m_frameCount); for (Uint32 frameIndex = 0; frameIndex < m_frameCount; ++frameIndex) { auto& frame = m_frames[frameIndex]; - frame.writeCursor = 0; frame.activeDescriptorPoolIndex = 0; frame.allocatedSetsThisFrame = 0; frame.peakAllocatedSetsThisFrame = 0; @@ -219,15 +217,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { } frame.descriptorPools.push_back({initialPool, m_setsPerFrame, 0}); MGLOG_D("UniformDescriptorBinder: frame %u descriptor pool created (maxSets=%u)", frameIndex, m_setsPerFrame); - - 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); - if (!created) { - MGLOG_E("UniformDescriptorBinder::Initialize failed: cannot create frame upload buffer %u", frameIndex); - Shutdown(); - return false; - } } return true; @@ -235,7 +224,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { void UniformDescriptorBinder::Shutdown() { for (auto& frame : m_frames) { - frame.uploadBuffer.Destroy(); if (m_device != VK_NULL_HANDLE) { for (auto& bucket : frame.descriptorPools) { if (bucket.handle != VK_NULL_HANDLE) { @@ -248,15 +236,13 @@ namespace MobileGL::MG_Backend::DirectVulkan { frame.activeDescriptorPoolIndex = 0; frame.allocatedSetsThisFrame = 0; frame.peakAllocatedSetsThisFrame = 0; - frame.writeCursor = 0; } m_frames.clear(); DestroyProgramLayouts(); - m_allocator = nullptr; + m_bufferManager = nullptr; m_device = VK_NULL_HANDLE; m_minDynamicOffsetAlignment = 1; - m_perFrameUploadBytes = 0; m_frameCount = 0; m_maxBindings = 0; m_setsPerFrame = 0; @@ -274,7 +260,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { "UniformDescriptorBinder: new descriptor set peak observed=%u (base setsPerFrame=%u, frame=%u, pools=%zu)", m_peakDescriptorSetsObserved, m_setsPerFrame, frameIndex, frame.descriptorPools.size()); } - frame.writeCursor = 0; frame.activeDescriptorPoolIndex = 0; frame.allocatedSetsThisFrame = 0; frame.peakAllocatedSetsThisFrame = 0; @@ -678,16 +663,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { return layout ? layout->pipelineLayout : VK_NULL_HANDLE; } - Bool UniformDescriptorBinder::AllocateUploadRegion(FrameResources& frame, VkDeviceSize size, VkDeviceSize& outOffset) { - const VkDeviceSize alignedOffset = AlignUp(frame.writeCursor, m_minDynamicOffsetAlignment); - if (alignedOffset + size > m_perFrameUploadBytes) { - return false; - } - outOffset = alignedOffset; - frame.writeCursor = alignedOffset + size; - return true; - } - Bool UniformDescriptorBinder::GatherBindingPayloads(const MG_State::GLState::ProgramObject& program, Vector& outData, Vector& outSizes) const { @@ -870,6 +845,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { static const Uint8 kFallbackData[16] = {}; MOBILEGL_ASSERT(m_textureManager != nullptr, "BindProgramUniformBuffers: texture manager is null"); MOBILEGL_ASSERT(m_samplerManager != nullptr, "BindProgramUniformBuffers: sampler manager is null"); + MOBILEGL_ASSERT(m_bufferManager != nullptr, "BindProgramUniformBuffers: buffer manager is null"); Vector writes; writes.reserve(m_maxBindings); @@ -911,19 +887,16 @@ namespace MobileGL::MG_Backend::DirectVulkan { } } - 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)) { + BufferSlice slice{}; + if (!m_bufferManager->UploadTransient(TransientBufferKind::Uniform, frameIndex, payload, payloadSize, + m_minDynamicOffsetAlignment, slice)) { MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: UBO upload failed on binding %u", binding); return false; } VkDescriptorBufferInfo bufferInfo{}; - bufferInfo.buffer = frame.uploadBuffer.GetHandle(); + bufferInfo.buffer = slice.buffer; bufferInfo.offset = 0; bufferInfo.range = payloadSize; bufferInfos.push_back(bufferInfo); @@ -931,7 +904,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; write.pBufferInfo = &bufferInfos.back(); writes.push_back(write); - dynamicOffsets.push_back(static_cast(payloadOffset)); + dynamicOffsets.push_back(static_cast(slice.offset)); } else { VkDescriptorImageInfo imageInfo{}; Bool hasImage = false; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h index 64ff2310..d023988b 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h @@ -8,12 +8,11 @@ #pragma once -#include "VkBufferObject.h" +#include "VkBufferManager.h" #include "VkSamplerManager.h" #include "VkTextureManager.h" #include "../VkIncludes.h" #include -#include namespace MobileGL::MG_State::GLState { class ITextureObject; @@ -36,9 +35,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { const MG_State::GLState::SamplerObject* sampler = nullptr; }; - Bool Initialize(VkDevice device, VmaAllocator allocator, VkDeviceSize minUniformBufferOffsetAlignment, - Uint32 frameCount, Uint32 maxBindings = 16, Uint32 setsPerFrame = 64, - VkDeviceSize perFrameUploadBytes = 4 * 1024 * 1024, + Bool Initialize(VkDevice device, VkBufferManager* bufferManager, + VkDeviceSize minUniformBufferOffsetAlignment, Uint32 frameCount, + Uint32 maxBindings = 16, Uint32 setsPerFrame = 64, VkTextureManager* textureManager = nullptr, VkSamplerManager* samplerManager = nullptr); void Shutdown(); @@ -60,12 +59,10 @@ namespace MobileGL::MG_Backend::DirectVulkan { }; struct FrameResources { - VkBufferObject uploadBuffer; Vector descriptorPools; Uint32 activeDescriptorPoolIndex = 0; Uint32 allocatedSetsThisFrame = 0; Uint32 peakAllocatedSetsThisFrame = 0; - VkDeviceSize writeCursor = 0; }; struct ProgramLayout { @@ -94,7 +91,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkDescriptorImageInfo& outImageInfo) const; 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; Bool CreateDescriptorPool(Uint32 maxSets, VkDescriptorPool& outPool) const; @@ -102,12 +98,11 @@ namespace MobileGL::MG_Backend::DirectVulkan { void DestroyProgramLayouts(); VkDevice m_device = VK_NULL_HANDLE; - VmaAllocator m_allocator = nullptr; + VkBufferManager* m_bufferManager = nullptr; 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; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp index b7ab8aa8..f5c6d85c 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp @@ -22,6 +22,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void VkBufferManager::Shutdown() { m_vertexUploadArena.Shutdown(); m_indexUploadArena.Shutdown(); + m_uniformUploadArena.Shutdown(); m_initInfo = {}; } @@ -31,6 +32,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_vertexUploadArena.Shutdown(); m_indexUploadArena.Shutdown(); + m_uniformUploadArena.Shutdown(); m_initInfo.frameCount = frameCount; return InitializeTransientArenas(); } @@ -38,6 +40,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void VkBufferManager::BeginFrame(Uint32 frameIndex) { m_vertexUploadArena.BeginFrame(frameIndex); m_indexUploadArena.BeginFrame(frameIndex); + m_uniformUploadArena.BeginFrame(frameIndex); } Bool VkBufferManager::UploadTransient(TransientBufferKind kind, Uint32 frameIndex, const void* data, @@ -47,6 +50,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { return m_vertexUploadArena.Upload(frameIndex, data, size, alignment, outSlice); case TransientBufferKind::Index: return m_indexUploadArena.Upload(frameIndex, data, size, alignment, outSlice); + case TransientBufferKind::Uniform: + return m_uniformUploadArena.Upload(frameIndex, data, size, alignment, outSlice); default: return false; } @@ -80,6 +85,21 @@ namespace MobileGL::MG_Backend::DirectVulkan { return false; } + ok = m_uniformUploadArena.Initialize({ + .allocator = m_initInfo.allocator, + .frameCount = m_initInfo.frameCount, + .usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + .memoryUsage = m_initInfo.transientMemoryUsage, + .allocationFlags = m_initInfo.transientAllocationFlags, + .minBufferSize = m_initInfo.minUniformUploadBytes, + .persistentlyMapped = m_initInfo.transientPersistentMapping, + }); + if (!ok) { + m_vertexUploadArena.Shutdown(); + m_indexUploadArena.Shutdown(); + return false; + } + return true; } } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.h index 20469123..e0eda8ab 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.h @@ -17,6 +17,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { enum class TransientBufferKind : Uint8 { Vertex, Index, + Uniform, }; struct VkBufferManagerInitInfo { @@ -24,6 +25,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { Uint32 frameCount = 0; VkDeviceSize minVertexUploadBytes = 4 * 1024 * 1024; VkDeviceSize minIndexUploadBytes = 1 * 1024 * 1024; + VkDeviceSize minUniformUploadBytes = 4 * 1024 * 1024; VmaMemoryUsage transientMemoryUsage = VMA_MEMORY_USAGE_AUTO; VmaAllocationCreateFlags transientAllocationFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; Bool transientPersistentMapping = false; @@ -47,5 +49,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkBufferManagerInitInfo m_initInfo{}; BufferArena m_vertexUploadArena; BufferArena m_indexUploadArena; + BufferArena m_uniformUploadArena; }; } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index 9505f98a..c55db12d 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -293,6 +293,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { .frameCount = m_frameContext.GetFrameCount(), .minVertexUploadBytes = 4 * 1024 * 1024, .minIndexUploadBytes = 1 * 1024 * 1024, + .minUniformUploadBytes = 4 * 1024 * 1024, .transientMemoryUsage = VMA_MEMORY_USAGE_AUTO, .transientAllocationFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, .transientPersistentMapping = false, @@ -329,9 +330,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_uniformDescriptorBinder = MakeUnique(); MOBILEGL_ASSERT(m_uniformDescriptorBinder != nullptr, "UniformDescriptorBinder creation failed."); - succeeded = m_uniformDescriptorBinder->Initialize(m_device, m_allocator, + succeeded = m_uniformDescriptorBinder->Initialize(m_device, &m_bufferManager, m_physicalDevice.properties.limits.minUniformBufferOffsetAlignment, - m_config.MaxFramesInFlight, 16, 64, 4 * 1024 * 1024, + m_config.MaxFramesInFlight, 16, 64, m_textureManager.get(), m_samplerManager.get()); MOBILEGL_ASSERT(succeeded, "UniformDescriptorBinder initialization failed."); m_vertexInputStateFactory = MakeUnique(m_config);