diff --git a/CMakeLists.txt b/CMakeLists.txt index dad53933..c6f35a10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/MobileGL/Defines.h b/MobileGL/Defines.h index 80c7366e..a183d4f8 100644 --- a/MobileGL/Defines.h +++ b/MobileGL/Defines.h @@ -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 diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp new file mode 100644 index 00000000..b7ab8aa8 --- /dev/null +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp @@ -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 diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.h new file mode 100644 index 00000000..20469123 --- /dev/null +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.h @@ -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 +#include + +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 diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index 6204e0d2..c32ba3e2 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -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(sourceSize), 16, - slice)) { + if (!m_bufferManager.UploadTransient(TransientBufferKind::Vertex, frameIndex, sourceData->data(), + static_cast(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(indexDataSizeBytes), alignment, slice)) { + if (!m_bufferManager.UploadTransient(TransientBufferKind::Index, frameIndex, + indexData->data() + pIndexBufferView->indexByteOffset, + static_cast(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()); } } diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h index ec18114b..778771e9 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h @@ -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;