[Feat] (MG_Backend/DirectVulkan): VkBufferManager transient upload now includes uniform

This commit is contained in:
2026-03-20 13:31:51 +08:00
parent bfa4049ac1
commit 9d1e8bd7cd
5 changed files with 42 additions and 50 deletions
@@ -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, VkDeviceSize minUniformBufferOffsetAlignment, Uint32 frameCount,
Uint32 maxBindings, Uint32 setsPerFrame, VkDeviceSize perFrameUploadBytes, Uint32 maxBindings, Uint32 setsPerFrame,
VkTextureManager* textureManager, VkSamplerManager* samplerManager) { VkTextureManager* textureManager, VkSamplerManager* samplerManager) {
Shutdown(); Shutdown();
MOBILEGL_ASSERT(device != VK_NULL_HANDLE, "UniformDescriptorBinder::Initialize requires valid VkDevice"); 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(frameCount > 0, "UniformDescriptorBinder::Initialize requires frameCount > 0");
MOBILEGL_ASSERT(maxBindings > 0, "UniformDescriptorBinder::Initialize requires maxBindings > 0"); MOBILEGL_ASSERT(maxBindings > 0, "UniformDescriptorBinder::Initialize requires maxBindings > 0");
MOBILEGL_ASSERT(setsPerFrame > 0, "UniformDescriptorBinder::Initialize requires setsPerFrame > 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"); "UniformDescriptorBinder::Initialize requires valid sampler manager");
m_device = device; m_device = device;
m_allocator = allocator; m_bufferManager = bufferManager;
m_minDynamicOffsetAlignment = std::max<VkDeviceSize>(1, minUniformBufferOffsetAlignment); m_minDynamicOffsetAlignment = std::max<VkDeviceSize>(1, minUniformBufferOffsetAlignment);
m_perFrameUploadBytes = perFrameUploadBytes;
m_frameCount = frameCount; m_frameCount = frameCount;
m_maxBindings = maxBindings; m_maxBindings = maxBindings;
m_setsPerFrame = setsPerFrame; m_setsPerFrame = setsPerFrame;
@@ -204,7 +203,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
m_frames.resize(m_frameCount); m_frames.resize(m_frameCount);
for (Uint32 frameIndex = 0; frameIndex < m_frameCount; ++frameIndex) { for (Uint32 frameIndex = 0; frameIndex < m_frameCount; ++frameIndex) {
auto& frame = m_frames[frameIndex]; auto& frame = m_frames[frameIndex];
frame.writeCursor = 0;
frame.activeDescriptorPoolIndex = 0; frame.activeDescriptorPoolIndex = 0;
frame.allocatedSetsThisFrame = 0; frame.allocatedSetsThisFrame = 0;
frame.peakAllocatedSetsThisFrame = 0; frame.peakAllocatedSetsThisFrame = 0;
@@ -219,15 +217,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
frame.descriptorPools.push_back({initialPool, m_setsPerFrame, 0}); frame.descriptorPools.push_back({initialPool, m_setsPerFrame, 0});
MGLOG_D("UniformDescriptorBinder: frame %u descriptor pool created (maxSets=%u)", frameIndex, m_setsPerFrame); MGLOG_D("UniformDescriptorBinder: frame %u descriptor pool created (maxSets=%u)", frameIndex, m_setsPerFrame);
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; return true;
@@ -235,7 +224,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void UniformDescriptorBinder::Shutdown() { void UniformDescriptorBinder::Shutdown() {
for (auto& frame : m_frames) { for (auto& frame : m_frames) {
frame.uploadBuffer.Destroy();
if (m_device != VK_NULL_HANDLE) { if (m_device != VK_NULL_HANDLE) {
for (auto& bucket : frame.descriptorPools) { for (auto& bucket : frame.descriptorPools) {
if (bucket.handle != VK_NULL_HANDLE) { if (bucket.handle != VK_NULL_HANDLE) {
@@ -248,15 +236,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
frame.activeDescriptorPoolIndex = 0; frame.activeDescriptorPoolIndex = 0;
frame.allocatedSetsThisFrame = 0; frame.allocatedSetsThisFrame = 0;
frame.peakAllocatedSetsThisFrame = 0; frame.peakAllocatedSetsThisFrame = 0;
frame.writeCursor = 0;
} }
m_frames.clear(); m_frames.clear();
DestroyProgramLayouts(); DestroyProgramLayouts();
m_allocator = nullptr; m_bufferManager = nullptr;
m_device = VK_NULL_HANDLE; m_device = VK_NULL_HANDLE;
m_minDynamicOffsetAlignment = 1; m_minDynamicOffsetAlignment = 1;
m_perFrameUploadBytes = 0;
m_frameCount = 0; m_frameCount = 0;
m_maxBindings = 0; m_maxBindings = 0;
m_setsPerFrame = 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)", "UniformDescriptorBinder: new descriptor set peak observed=%u (base setsPerFrame=%u, frame=%u, pools=%zu)",
m_peakDescriptorSetsObserved, m_setsPerFrame, frameIndex, frame.descriptorPools.size()); m_peakDescriptorSetsObserved, m_setsPerFrame, frameIndex, frame.descriptorPools.size());
} }
frame.writeCursor = 0;
frame.activeDescriptorPoolIndex = 0; frame.activeDescriptorPoolIndex = 0;
frame.allocatedSetsThisFrame = 0; frame.allocatedSetsThisFrame = 0;
frame.peakAllocatedSetsThisFrame = 0; frame.peakAllocatedSetsThisFrame = 0;
@@ -678,16 +663,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return layout ? layout->pipelineLayout : VK_NULL_HANDLE; 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, Bool UniformDescriptorBinder::GatherBindingPayloads(const MG_State::GLState::ProgramObject& program,
Vector<const void*>& outData, Vector<const void*>& outData,
Vector<VkDeviceSize>& outSizes) const { Vector<VkDeviceSize>& outSizes) const {
@@ -870,6 +845,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
static const Uint8 kFallbackData[16] = {}; static const Uint8 kFallbackData[16] = {};
MOBILEGL_ASSERT(m_textureManager != nullptr, "BindProgramUniformBuffers: texture manager is null"); MOBILEGL_ASSERT(m_textureManager != nullptr, "BindProgramUniformBuffers: texture manager is null");
MOBILEGL_ASSERT(m_samplerManager != nullptr, "BindProgramUniformBuffers: sampler manager is null"); MOBILEGL_ASSERT(m_samplerManager != nullptr, "BindProgramUniformBuffers: sampler manager is null");
MOBILEGL_ASSERT(m_bufferManager != nullptr, "BindProgramUniformBuffers: buffer manager is null");
Vector<VkWriteDescriptorSet> writes; Vector<VkWriteDescriptorSet> writes;
writes.reserve(m_maxBindings); writes.reserve(m_maxBindings);
@@ -911,19 +887,16 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
} }
VkDeviceSize payloadOffset = 0; BufferSlice slice{};
if (!AllocateUploadRegion(frame, payloadSize, payloadOffset)) { if (!m_bufferManager->UploadTransient(TransientBufferKind::Uniform, frameIndex, payload, payloadSize,
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: frame upload buffer exhausted"); m_minDynamicOffsetAlignment, slice)) {
return false;
}
if (!frame.uploadBuffer.Upload(payload, payloadSize, payloadOffset)) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: UBO upload failed on binding %u", MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: UBO upload failed on binding %u",
binding); binding);
return false; return false;
} }
VkDescriptorBufferInfo bufferInfo{}; VkDescriptorBufferInfo bufferInfo{};
bufferInfo.buffer = frame.uploadBuffer.GetHandle(); bufferInfo.buffer = slice.buffer;
bufferInfo.offset = 0; bufferInfo.offset = 0;
bufferInfo.range = payloadSize; bufferInfo.range = payloadSize;
bufferInfos.push_back(bufferInfo); bufferInfos.push_back(bufferInfo);
@@ -931,7 +904,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
write.pBufferInfo = &bufferInfos.back(); write.pBufferInfo = &bufferInfos.back();
writes.push_back(write); writes.push_back(write);
dynamicOffsets.push_back(static_cast<Uint32>(payloadOffset)); dynamicOffsets.push_back(static_cast<Uint32>(slice.offset));
} else { } else {
VkDescriptorImageInfo imageInfo{}; VkDescriptorImageInfo imageInfo{};
Bool hasImage = false; Bool hasImage = false;
@@ -8,12 +8,11 @@
#pragma once #pragma once
#include "VkBufferObject.h" #include "VkBufferManager.h"
#include "VkSamplerManager.h" #include "VkSamplerManager.h"
#include "VkTextureManager.h" #include "VkTextureManager.h"
#include "../VkIncludes.h" #include "../VkIncludes.h"
#include <Includes.h> #include <Includes.h>
#include <vk_mem_alloc.h>
namespace MobileGL::MG_State::GLState { namespace MobileGL::MG_State::GLState {
class ITextureObject; class ITextureObject;
@@ -36,9 +35,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const MG_State::GLState::SamplerObject* sampler = nullptr; const MG_State::GLState::SamplerObject* sampler = nullptr;
}; };
Bool Initialize(VkDevice device, VmaAllocator allocator, VkDeviceSize minUniformBufferOffsetAlignment, Bool Initialize(VkDevice device, VkBufferManager* bufferManager,
Uint32 frameCount, Uint32 maxBindings = 16, Uint32 setsPerFrame = 64, VkDeviceSize minUniformBufferOffsetAlignment, Uint32 frameCount,
VkDeviceSize perFrameUploadBytes = 4 * 1024 * 1024, Uint32 maxBindings = 16, Uint32 setsPerFrame = 64,
VkTextureManager* textureManager = nullptr, VkSamplerManager* samplerManager = nullptr); VkTextureManager* textureManager = nullptr, VkSamplerManager* samplerManager = nullptr);
void Shutdown(); void Shutdown();
@@ -60,12 +59,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}; };
struct FrameResources { struct FrameResources {
VkBufferObject uploadBuffer;
Vector<DescriptorPoolBucket> descriptorPools; Vector<DescriptorPoolBucket> descriptorPools;
Uint32 activeDescriptorPoolIndex = 0; Uint32 activeDescriptorPoolIndex = 0;
Uint32 allocatedSetsThisFrame = 0; Uint32 allocatedSetsThisFrame = 0;
Uint32 peakAllocatedSetsThisFrame = 0; Uint32 peakAllocatedSetsThisFrame = 0;
VkDeviceSize writeCursor = 0;
}; };
struct ProgramLayout { struct ProgramLayout {
@@ -94,7 +91,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkDescriptorImageInfo& outImageInfo) const; VkDescriptorImageInfo& outImageInfo) const;
Bool ReflectBindingKinds(const MG_State::GLState::ProgramObject& program, Vector<BindingKind>& outKinds) const; Bool ReflectBindingKinds(const MG_State::GLState::ProgramObject& program, Vector<BindingKind>& outKinds) const;
ProgramLayout* GetOrCreateProgramLayout(const MG_State::GLState::ProgramObject& program); 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<const void*>& outData, Bool GatherBindingPayloads(const MG_State::GLState::ProgramObject& program, Vector<const void*>& outData,
Vector<VkDeviceSize>& outSizes) const; Vector<VkDeviceSize>& outSizes) const;
Bool CreateDescriptorPool(Uint32 maxSets, VkDescriptorPool& outPool) const; Bool CreateDescriptorPool(Uint32 maxSets, VkDescriptorPool& outPool) const;
@@ -102,12 +98,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void DestroyProgramLayouts(); void DestroyProgramLayouts();
VkDevice m_device = VK_NULL_HANDLE; VkDevice m_device = VK_NULL_HANDLE;
VmaAllocator m_allocator = nullptr; VkBufferManager* m_bufferManager = nullptr;
Vector<FrameResources> m_frames; Vector<FrameResources> m_frames;
UnorderedMap<Uint64, ProgramLayout> m_programLayouts; UnorderedMap<Uint64, ProgramLayout> m_programLayouts;
VkDeviceSize m_minDynamicOffsetAlignment = 1; VkDeviceSize m_minDynamicOffsetAlignment = 1;
VkDeviceSize m_perFrameUploadBytes = 0;
Uint32 m_frameCount = 0; Uint32 m_frameCount = 0;
Uint32 m_maxBindings = 0; Uint32 m_maxBindings = 0;
Uint32 m_setsPerFrame = 0; Uint32 m_setsPerFrame = 0;
@@ -22,6 +22,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void VkBufferManager::Shutdown() { void VkBufferManager::Shutdown() {
m_vertexUploadArena.Shutdown(); m_vertexUploadArena.Shutdown();
m_indexUploadArena.Shutdown(); m_indexUploadArena.Shutdown();
m_uniformUploadArena.Shutdown();
m_initInfo = {}; m_initInfo = {};
} }
@@ -31,6 +32,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
m_vertexUploadArena.Shutdown(); m_vertexUploadArena.Shutdown();
m_indexUploadArena.Shutdown(); m_indexUploadArena.Shutdown();
m_uniformUploadArena.Shutdown();
m_initInfo.frameCount = frameCount; m_initInfo.frameCount = frameCount;
return InitializeTransientArenas(); return InitializeTransientArenas();
} }
@@ -38,6 +40,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void VkBufferManager::BeginFrame(Uint32 frameIndex) { void VkBufferManager::BeginFrame(Uint32 frameIndex) {
m_vertexUploadArena.BeginFrame(frameIndex); m_vertexUploadArena.BeginFrame(frameIndex);
m_indexUploadArena.BeginFrame(frameIndex); m_indexUploadArena.BeginFrame(frameIndex);
m_uniformUploadArena.BeginFrame(frameIndex);
} }
Bool VkBufferManager::UploadTransient(TransientBufferKind kind, Uint32 frameIndex, const void* data, 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); return m_vertexUploadArena.Upload(frameIndex, data, size, alignment, outSlice);
case TransientBufferKind::Index: case TransientBufferKind::Index:
return m_indexUploadArena.Upload(frameIndex, data, size, alignment, outSlice); return m_indexUploadArena.Upload(frameIndex, data, size, alignment, outSlice);
case TransientBufferKind::Uniform:
return m_uniformUploadArena.Upload(frameIndex, data, size, alignment, outSlice);
default: default:
return false; return false;
} }
@@ -80,6 +85,21 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return false; 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; return true;
} }
} // namespace MobileGL::MG_Backend::DirectVulkan } // namespace MobileGL::MG_Backend::DirectVulkan
@@ -17,6 +17,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
enum class TransientBufferKind : Uint8 { enum class TransientBufferKind : Uint8 {
Vertex, Vertex,
Index, Index,
Uniform,
}; };
struct VkBufferManagerInitInfo { struct VkBufferManagerInitInfo {
@@ -24,6 +25,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Uint32 frameCount = 0; Uint32 frameCount = 0;
VkDeviceSize minVertexUploadBytes = 4 * 1024 * 1024; VkDeviceSize minVertexUploadBytes = 4 * 1024 * 1024;
VkDeviceSize minIndexUploadBytes = 1 * 1024 * 1024; VkDeviceSize minIndexUploadBytes = 1 * 1024 * 1024;
VkDeviceSize minUniformUploadBytes = 4 * 1024 * 1024;
VmaMemoryUsage transientMemoryUsage = VMA_MEMORY_USAGE_AUTO; VmaMemoryUsage transientMemoryUsage = VMA_MEMORY_USAGE_AUTO;
VmaAllocationCreateFlags transientAllocationFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; VmaAllocationCreateFlags transientAllocationFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
Bool transientPersistentMapping = false; Bool transientPersistentMapping = false;
@@ -47,5 +49,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkBufferManagerInitInfo m_initInfo{}; VkBufferManagerInitInfo m_initInfo{};
BufferArena m_vertexUploadArena; BufferArena m_vertexUploadArena;
BufferArena m_indexUploadArena; BufferArena m_indexUploadArena;
BufferArena m_uniformUploadArena;
}; };
} // namespace MobileGL::MG_Backend::DirectVulkan } // namespace MobileGL::MG_Backend::DirectVulkan
@@ -293,6 +293,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
.frameCount = m_frameContext.GetFrameCount(), .frameCount = m_frameContext.GetFrameCount(),
.minVertexUploadBytes = 4 * 1024 * 1024, .minVertexUploadBytes = 4 * 1024 * 1024,
.minIndexUploadBytes = 1 * 1024 * 1024, .minIndexUploadBytes = 1 * 1024 * 1024,
.minUniformUploadBytes = 4 * 1024 * 1024,
.transientMemoryUsage = VMA_MEMORY_USAGE_AUTO, .transientMemoryUsage = VMA_MEMORY_USAGE_AUTO,
.transientAllocationFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, .transientAllocationFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
.transientPersistentMapping = false, .transientPersistentMapping = false,
@@ -329,9 +330,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
m_uniformDescriptorBinder = MakeUnique<UniformDescriptorBinder>(); m_uniformDescriptorBinder = MakeUnique<UniformDescriptorBinder>();
MOBILEGL_ASSERT(m_uniformDescriptorBinder != nullptr, "UniformDescriptorBinder creation failed."); 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_physicalDevice.properties.limits.minUniformBufferOffsetAlignment,
m_config.MaxFramesInFlight, 16, 64, 4 * 1024 * 1024, m_config.MaxFramesInFlight, 16, 64,
m_textureManager.get(), m_samplerManager.get()); m_textureManager.get(), m_samplerManager.get());
MOBILEGL_ASSERT(succeeded, "UniformDescriptorBinder initialization failed."); MOBILEGL_ASSERT(succeeded, "UniformDescriptorBinder initialization failed.");
m_vertexInputStateFactory = MakeUnique<VertexInputStateFactory>(m_config); m_vertexInputStateFactory = MakeUnique<VertexInputStateFactory>(m_config);