[Feat] (MG_Backend/DirectVulkan): VkBufferManager

This commit is contained in:
2026-03-19 17:44:35 +08:00
parent b6ac6c182e
commit f2ab50b84e
6 changed files with 158 additions and 55 deletions
+1
View File
@@ -225,6 +225,7 @@ set(SOURCE_FILES
MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/BufferArena.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateBuilder.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.cpp
+1 -1
View File
@@ -34,7 +34,7 @@
#define MOBILEGL_EGL_API MOBILEGL_API
// ====================== MobileGL configurations ======================= //
#define MOBILEGL_LOG_ACTIVE_LEVEL MOBILEGL_LOG_LEVEL_INFO
#define MOBILEGL_LOG_ACTIVE_LEVEL MOBILEGL_LOG_LEVEL_DEBUG
#define MOBILEGL_LOG_ENABLE_CONSOLE 0
#define MOBILEGL_LOG_ENABLE_FILE 1
@@ -0,0 +1,85 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.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 "VkBufferManager.h"
namespace MobileGL::MG_Backend::DirectVulkan {
Bool VkBufferManager::Initialize(const VkBufferManagerInitInfo& initInfo) {
Shutdown();
MOBILEGL_ASSERT(initInfo.allocator != nullptr, "VkBufferManager::Initialize requires valid allocator");
MOBILEGL_ASSERT(initInfo.frameCount > 0, "VkBufferManager::Initialize requires non-zero frame count");
m_initInfo = initInfo;
return InitializeTransientArenas();
}
void VkBufferManager::Shutdown() {
m_vertexUploadArena.Shutdown();
m_indexUploadArena.Shutdown();
m_initInfo = {};
}
Bool VkBufferManager::RecreateTransientArenas(Uint32 frameCount) {
MOBILEGL_ASSERT(m_initInfo.allocator != nullptr, "VkBufferManager::RecreateTransientArenas requires initialized manager");
MOBILEGL_ASSERT(frameCount > 0, "VkBufferManager::RecreateTransientArenas requires non-zero frame count");
m_vertexUploadArena.Shutdown();
m_indexUploadArena.Shutdown();
m_initInfo.frameCount = frameCount;
return InitializeTransientArenas();
}
void VkBufferManager::BeginFrame(Uint32 frameIndex) {
m_vertexUploadArena.BeginFrame(frameIndex);
m_indexUploadArena.BeginFrame(frameIndex);
}
Bool VkBufferManager::UploadTransient(TransientBufferKind kind, Uint32 frameIndex, const void* data,
VkDeviceSize size, VkDeviceSize alignment, BufferSlice& outSlice) {
switch (kind) {
case TransientBufferKind::Vertex:
return m_vertexUploadArena.Upload(frameIndex, data, size, alignment, outSlice);
case TransientBufferKind::Index:
return m_indexUploadArena.Upload(frameIndex, data, size, alignment, outSlice);
default:
return false;
}
}
Bool VkBufferManager::InitializeTransientArenas() {
Bool ok = m_vertexUploadArena.Initialize({
.allocator = m_initInfo.allocator,
.frameCount = m_initInfo.frameCount,
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
.memoryUsage = m_initInfo.transientMemoryUsage,
.allocationFlags = m_initInfo.transientAllocationFlags,
.minBufferSize = m_initInfo.minVertexUploadBytes,
.persistentlyMapped = m_initInfo.transientPersistentMapping,
});
if (!ok) {
return false;
}
ok = m_indexUploadArena.Initialize({
.allocator = m_initInfo.allocator,
.frameCount = m_initInfo.frameCount,
.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
.memoryUsage = m_initInfo.transientMemoryUsage,
.allocationFlags = m_initInfo.transientAllocationFlags,
.minBufferSize = m_initInfo.minIndexUploadBytes,
.persistentlyMapped = m_initInfo.transientPersistentMapping,
});
if (!ok) {
m_vertexUploadArena.Shutdown();
return false;
}
return true;
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -0,0 +1,51 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.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 "BufferArena.h"
#include "../VkIncludes.h"
#include <Includes.h>
#include <vk_mem_alloc.h>
namespace MobileGL::MG_Backend::DirectVulkan {
enum class TransientBufferKind : Uint8 {
Vertex,
Index,
};
struct VkBufferManagerInitInfo {
VmaAllocator allocator = nullptr;
Uint32 frameCount = 0;
VkDeviceSize minVertexUploadBytes = 4 * 1024 * 1024;
VkDeviceSize minIndexUploadBytes = 1 * 1024 * 1024;
VmaMemoryUsage transientMemoryUsage = VMA_MEMORY_USAGE_AUTO;
VmaAllocationCreateFlags transientAllocationFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
Bool transientPersistentMapping = false;
};
class VkBufferManager {
public:
Bool Initialize(const VkBufferManagerInitInfo& initInfo);
void Shutdown();
// Recreate all per-frame transient arenas
Bool RecreateTransientArenas(Uint32 frameCount);
void BeginFrame(Uint32 frameIndex);
Bool UploadTransient(TransientBufferKind kind, Uint32 frameIndex, const void* data, VkDeviceSize size,
VkDeviceSize alignment, BufferSlice& outSlice);
private:
Bool InitializeTransientArenas();
VkBufferManagerInitInfo m_initInfo{};
BufferArena m_vertexUploadArena;
BufferArena m_indexUploadArena;
};
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -334,32 +334,21 @@ namespace MobileGL::MG_Backend::DirectVulkan {
MOBILEGL_ASSERT(m_vertexInputStateFactory != nullptr, "VertexInputStateFactory creation failed.");
CreateFrameContexts();
succeeded = m_vertexUploadArena.Initialize({
succeeded = m_bufferManager.Initialize({
.allocator = m_allocator,
.frameCount = m_frameContext.GetFrameCount(),
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
.memoryUsage = VMA_MEMORY_USAGE_AUTO,
.allocationFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
.minBufferSize = 4 * 1024 * 1024,
.persistentlyMapped = false,
.minVertexUploadBytes = 4 * 1024 * 1024,
.minIndexUploadBytes = 1 * 1024 * 1024,
.transientMemoryUsage = VMA_MEMORY_USAGE_AUTO,
.transientAllocationFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
.transientPersistentMapping = false,
});
MOBILEGL_ASSERT(succeeded, "Vertex upload arena initialization failed.");
succeeded = m_indexUploadArena.Initialize({
.allocator = m_allocator,
.frameCount = m_frameContext.GetFrameCount(),
.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
.memoryUsage = VMA_MEMORY_USAGE_AUTO,
.allocationFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
.minBufferSize = 1 * 1024 * 1024,
.persistentlyMapped = false,
});
MOBILEGL_ASSERT(succeeded, "Index upload arena initialization failed.");
MOBILEGL_ASSERT(succeeded, "Buffer manager initialization failed.");
// Prime the first frame so Render() always targets an acquired swapchain image.
VK_VERIFY(m_frameContext.WaitAndAcquireNextImage(m_device, m_swapchainObject.GetHandle(), m_imageIndexAcquired),
"Initialize, WaitAndAcquireNextImage");
m_vertexUploadArena.BeginFrame(m_frameContext.GetCurrentFrameIndex());
m_indexUploadArena.BeginFrame(m_frameContext.GetCurrentFrameIndex());
m_bufferManager.BeginFrame(m_frameContext.GetCurrentFrameIndex());
MGLOG_D("VulkanRenderer initialized");
}
@@ -379,8 +368,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
m_textureManager.reset();
}
m_vertexInputStateFactory.reset();
m_vertexUploadArena.Shutdown();
m_indexUploadArena.Shutdown();
m_bufferManager.Shutdown();
m_frameContext.Destroy(m_device, m_commandPool);
@@ -459,8 +447,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const SizeT sourceSize = sourceBuffer->GetSize();
BufferSlice slice{};
if (!m_vertexUploadArena.Upload(frameIndex, sourceData->data(), static_cast<VkDeviceSize>(sourceSize), 16,
slice)) {
if (!m_bufferManager.UploadTransient(TransientBufferKind::Vertex, frameIndex, sourceData->data(),
static_cast<VkDeviceSize>(sourceSize), 16, slice)) {
MGLOG_E("UploadAndBindVertexStreams skipped: failed to upload binding %zu", binding);
return false;
}
@@ -501,8 +489,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const Uint32 frameIndex = m_frameContext.GetCurrentFrameIndex();
const VkDeviceSize alignment = indexSize;
BufferSlice slice{};
if (!m_indexUploadArena.Upload(frameIndex, indexData->data() + pIndexBufferView->indexByteOffset,
static_cast<VkDeviceSize>(indexDataSizeBytes), alignment, slice)) {
if (!m_bufferManager.UploadTransient(TransientBufferKind::Index, frameIndex,
indexData->data() + pIndexBufferView->indexByteOffset,
static_cast<VkDeviceSize>(indexDataSizeBytes), alignment, slice)) {
MGLOG_E("DrawElements skipped: failed to prepare index upload buffer");
return false;
}
@@ -1283,8 +1272,7 @@ void main() {
result = VK_SUCCESS;
}
VK_VERIFY(result, "Present, vkAcquireNextImageKHR");
m_vertexUploadArena.BeginFrame(m_frameContext.GetCurrentFrameIndex());
m_indexUploadArena.BeginFrame(m_frameContext.GetCurrentFrameIndex());
m_bufferManager.BeginFrame(m_frameContext.GetCurrentFrameIndex());
}
void VulkanRenderer::CreateInstance() {
@@ -1820,31 +1808,10 @@ void main() {
m_frameContext.GetCurrent().isCommandRecording = false;
m_frameContext.GetCurrent().hasCommandBufferRecorded = false;
}
m_vertexUploadArena.Shutdown();
Bool okArena = m_vertexUploadArena.Initialize({
.allocator = m_allocator,
.frameCount = m_frameContext.GetFrameCount(),
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
.memoryUsage = VMA_MEMORY_USAGE_AUTO,
.allocationFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
.minBufferSize = 4 * 1024 * 1024,
.persistentlyMapped = false,
});
MOBILEGL_ASSERT(okArena, "RecreateSwapchain: vertex upload arena initialization failed");
m_indexUploadArena.Shutdown();
okArena = m_indexUploadArena.Initialize({
.allocator = m_allocator,
.frameCount = m_frameContext.GetFrameCount(),
.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
.memoryUsage = VMA_MEMORY_USAGE_AUTO,
.allocationFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
.minBufferSize = 1 * 1024 * 1024,
.persistentlyMapped = false,
});
MOBILEGL_ASSERT(okArena, "RecreateSwapchain: index upload arena initialization failed");
const Bool okArena = m_bufferManager.RecreateTransientArenas(m_frameContext.GetFrameCount());
MOBILEGL_ASSERT(okArena, "RecreateSwapchain: buffer manager transient arena initialization failed");
if (m_frameContext.GetFrameCount() > 0) {
m_vertexUploadArena.BeginFrame(m_frameContext.GetCurrentFrameIndex());
m_indexUploadArena.BeginFrame(m_frameContext.GetCurrentFrameIndex());
m_bufferManager.BeginFrame(m_frameContext.GetCurrentFrameIndex());
}
}
@@ -8,7 +8,6 @@
#pragma once
#include "Config.h"
#include "BufferArena.h"
#include "FrameContext.h"
#include "PipelineFactory.h"
#include "ProgramFactory.h"
@@ -16,6 +15,7 @@
#include "UniformDescriptorBinder.h"
#include "VertexInputStateFactory.h"
#include "VkBufferObject.h"
#include "VkBufferManager.h"
#include "VkClearManager.h"
#include "VkRenderPassManager.h"
#include "VkSamplerManager.h"
@@ -171,8 +171,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkCommandPool m_commandPool = VK_NULL_HANDLE;
BufferArena m_vertexUploadArena;
BufferArena m_indexUploadArena;
VkBufferManager m_bufferManager;
Uint m_imageIndexAcquired = 0;
FrameContext m_frameContext;