mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-10 05:08:31 +09:00
[Feat] (MG_Backend/DirectVulkan): Buffer arena, buffer slice
This commit is contained in:
@@ -224,6 +224,7 @@ set(SOURCE_FILES
|
||||
MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp
|
||||
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/VertexInputStateBuilder.cpp
|
||||
MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.cpp
|
||||
MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.cpp
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/BufferArena.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 "BufferArena.h"
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Bool BufferArena::Initialize(const BufferArenaDesc& desc) {
|
||||
Shutdown();
|
||||
|
||||
MOBILEGL_ASSERT(desc.allocator != nullptr, "BufferArena::Initialize requires valid allocator");
|
||||
MOBILEGL_ASSERT(desc.frameCount > 0, "BufferArena::Initialize requires non-zero frame count");
|
||||
MOBILEGL_ASSERT(desc.usage != 0, "BufferArena::Initialize requires non-zero buffer usage");
|
||||
|
||||
m_desc = desc;
|
||||
m_frames.clear();
|
||||
m_frames.resize(desc.frameCount);
|
||||
m_deferredReleases.resize(desc.frameCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
void BufferArena::Shutdown() {
|
||||
for (auto& frame : m_frames) {
|
||||
frame.buffer.Destroy();
|
||||
frame.writeCursor = 0;
|
||||
}
|
||||
m_frames.clear();
|
||||
m_deferredReleases.clear();
|
||||
m_desc = {};
|
||||
}
|
||||
|
||||
void BufferArena::BeginFrame(Uint32 frameIndex) {
|
||||
CollectDeferredReleases(frameIndex);
|
||||
ResetFrame(frameIndex);
|
||||
}
|
||||
|
||||
void BufferArena::ResetFrame(Uint32 frameIndex) {
|
||||
AssertValidFrameIndex(frameIndex);
|
||||
m_frames[frameIndex].writeCursor = 0;
|
||||
}
|
||||
|
||||
void BufferArena::CollectDeferredReleases(Uint32 frameIndex) {
|
||||
AssertValidFrameIndex(frameIndex);
|
||||
m_deferredReleases[frameIndex].clear();
|
||||
}
|
||||
|
||||
Bool BufferArena::Allocate(Uint32 frameIndex, VkDeviceSize size, VkDeviceSize alignment, BufferSlice& outSlice) {
|
||||
AssertValidFrameIndex(frameIndex);
|
||||
MOBILEGL_ASSERT(size > 0, "BufferArena::Allocate requires non-zero size");
|
||||
|
||||
auto& frame = m_frames[frameIndex];
|
||||
const VkDeviceSize resolvedAlignment = alignment > 0 ? alignment : 1;
|
||||
const VkDeviceSize offset = (frame.writeCursor + resolvedAlignment - 1) & ~(resolvedAlignment - 1);
|
||||
const VkDeviceSize endOffset = offset + size;
|
||||
|
||||
if (!EnsureCapacity(frameIndex, endOffset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
frame.writeCursor = endOffset;
|
||||
outSlice = frame.buffer.GetSlice(offset, size);
|
||||
return outSlice.IsValid();
|
||||
}
|
||||
|
||||
Bool BufferArena::Upload(Uint32 frameIndex, const void* data, VkDeviceSize size, VkDeviceSize alignment,
|
||||
BufferSlice& outSlice) {
|
||||
MOBILEGL_ASSERT(data != nullptr || size == 0, "BufferArena::Upload data pointer is null");
|
||||
if (!Allocate(frameIndex, size, alignment, outSlice)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (outSlice.mapped != nullptr) {
|
||||
Memcpy(outSlice.mapped, data, static_cast<SizeT>(size));
|
||||
return true;
|
||||
}
|
||||
|
||||
return m_frames[frameIndex].buffer.Upload(data, size, outSlice.offset);
|
||||
}
|
||||
|
||||
VkDeviceSize BufferArena::GetWriteCursor(Uint32 frameIndex) const {
|
||||
AssertValidFrameIndex(frameIndex);
|
||||
return m_frames[frameIndex].writeCursor;
|
||||
}
|
||||
|
||||
Uint32 BufferArena::GetFrameCount() const {
|
||||
return static_cast<Uint32>(m_frames.size());
|
||||
}
|
||||
|
||||
Bool BufferArena::EnsureCapacity(Uint32 frameIndex, VkDeviceSize requiredEndOffset) {
|
||||
AssertValidFrameIndex(frameIndex);
|
||||
auto& frame = m_frames[frameIndex];
|
||||
auto& buffer = frame.buffer;
|
||||
|
||||
if (buffer.IsValid() && buffer.GetSize() >= requiredEndOffset) {
|
||||
return true;
|
||||
}
|
||||
|
||||
VkDeviceSize newCapacity = buffer.IsValid() ? buffer.GetSize() : 0;
|
||||
if (newCapacity < m_desc.minBufferSize) {
|
||||
newCapacity = m_desc.minBufferSize;
|
||||
}
|
||||
if (newCapacity == 0) {
|
||||
newCapacity = requiredEndOffset;
|
||||
}
|
||||
while (newCapacity < requiredEndOffset) {
|
||||
newCapacity *= 2;
|
||||
}
|
||||
|
||||
if (buffer.IsValid()) {
|
||||
m_deferredReleases[frameIndex].push_back(std::move(buffer));
|
||||
}
|
||||
|
||||
VkBufferObjectDesc bufferDesc{};
|
||||
bufferDesc.allocator = m_desc.allocator;
|
||||
bufferDesc.size = newCapacity;
|
||||
bufferDesc.usage = m_desc.usage;
|
||||
bufferDesc.memoryUsage = m_desc.memoryUsage;
|
||||
bufferDesc.allocationFlags = m_desc.allocationFlags;
|
||||
if (!buffer.Create(bufferDesc)) {
|
||||
return false;
|
||||
}
|
||||
if (m_desc.persistentlyMapped && buffer.Map() == nullptr) {
|
||||
buffer.Destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
frame.writeCursor = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void BufferArena::AssertValidFrameIndex(Uint32 frameIndex) const {
|
||||
MOBILEGL_ASSERT(frameIndex < m_frames.size(), "BufferArena frame index out of range");
|
||||
}
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
@@ -0,0 +1,56 @@
|
||||
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/BufferArena.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 "BufferSlice.h"
|
||||
#include "VkBufferObject.h"
|
||||
#include "../VkIncludes.h"
|
||||
#include <Includes.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
struct BufferArenaDesc {
|
||||
VmaAllocator allocator = nullptr;
|
||||
Uint32 frameCount = 0;
|
||||
VkBufferUsageFlags usage = 0;
|
||||
VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
|
||||
VmaAllocationCreateFlags allocationFlags = 0;
|
||||
VkDeviceSize minBufferSize = 0;
|
||||
Bool persistentlyMapped = false;
|
||||
};
|
||||
|
||||
class BufferArena {
|
||||
public:
|
||||
Bool Initialize(const BufferArenaDesc& desc);
|
||||
void Shutdown();
|
||||
|
||||
void BeginFrame(Uint32 frameIndex);
|
||||
void ResetFrame(Uint32 frameIndex);
|
||||
void CollectDeferredReleases(Uint32 frameIndex);
|
||||
|
||||
Bool Allocate(Uint32 frameIndex, VkDeviceSize size, VkDeviceSize alignment, BufferSlice& outSlice);
|
||||
Bool Upload(Uint32 frameIndex, const void* data, VkDeviceSize size, VkDeviceSize alignment, BufferSlice& outSlice);
|
||||
|
||||
VkDeviceSize GetWriteCursor(Uint32 frameIndex) const;
|
||||
Uint32 GetFrameCount() const;
|
||||
|
||||
private:
|
||||
struct FrameResources {
|
||||
VkBufferObject buffer;
|
||||
VkDeviceSize writeCursor = 0;
|
||||
};
|
||||
|
||||
Bool EnsureCapacity(Uint32 frameIndex, VkDeviceSize requiredEndOffset);
|
||||
void AssertValidFrameIndex(Uint32 frameIndex) const;
|
||||
|
||||
BufferArenaDesc m_desc{};
|
||||
Vector<FrameResources> m_frames;
|
||||
Vector<Vector<VkBufferObject>> m_deferredReleases;
|
||||
};
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
@@ -0,0 +1,23 @@
|
||||
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/BufferSlice.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 <Includes.h>
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
struct BufferSlice {
|
||||
VkBuffer buffer = VK_NULL_HANDLE;
|
||||
VkDeviceSize offset = 0;
|
||||
VkDeviceSize size = 0;
|
||||
void* mapped = nullptr;
|
||||
|
||||
Bool IsValid() const { return buffer != VK_NULL_HANDLE; }
|
||||
};
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
@@ -13,11 +13,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_allocator = other.m_allocator;
|
||||
m_buffer = other.m_buffer;
|
||||
m_allocation = other.m_allocation;
|
||||
m_mappedData = other.m_mappedData;
|
||||
m_size = other.m_size;
|
||||
|
||||
other.m_allocator = nullptr;
|
||||
other.m_buffer = VK_NULL_HANDLE;
|
||||
other.m_allocation = nullptr;
|
||||
other.m_mappedData = nullptr;
|
||||
other.m_size = 0;
|
||||
}
|
||||
|
||||
@@ -31,11 +33,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_allocator = other.m_allocator;
|
||||
m_buffer = other.m_buffer;
|
||||
m_allocation = other.m_allocation;
|
||||
m_mappedData = other.m_mappedData;
|
||||
m_size = other.m_size;
|
||||
|
||||
other.m_allocator = nullptr;
|
||||
other.m_buffer = VK_NULL_HANDLE;
|
||||
other.m_allocation = nullptr;
|
||||
other.m_mappedData = nullptr;
|
||||
other.m_size = 0;
|
||||
return *this;
|
||||
}
|
||||
@@ -44,6 +48,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Destroy();
|
||||
}
|
||||
|
||||
Bool VkBufferObject::Create(const VkBufferObjectDesc& desc) {
|
||||
return Create(desc.allocator, desc.size, desc.usage, desc.memoryUsage, desc.allocationFlags);
|
||||
}
|
||||
|
||||
Bool VkBufferObject::Create(VmaAllocator allocator, VkDeviceSize size, VkBufferUsageFlags usage,
|
||||
VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags allocationFlags) {
|
||||
MOBILEGL_ASSERT(allocator != nullptr, "VkBufferObject::Create requires valid VMA allocator");
|
||||
@@ -78,6 +86,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
void VkBufferObject::Destroy() {
|
||||
Unmap();
|
||||
if (m_allocator != nullptr && m_buffer != VK_NULL_HANDLE && m_allocation != nullptr) {
|
||||
vmaDestroyBuffer(m_allocator, m_buffer, m_allocation);
|
||||
}
|
||||
@@ -87,6 +96,33 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
void* VkBufferObject::Map() {
|
||||
MOBILEGL_ASSERT(IsValid(), "VkBufferObject::Map called on invalid buffer");
|
||||
|
||||
if (m_mappedData != nullptr) {
|
||||
return m_mappedData;
|
||||
}
|
||||
|
||||
const VkResult mapResult = vmaMapMemory(m_allocator, m_allocation, &m_mappedData);
|
||||
if (mapResult != VK_SUCCESS || m_mappedData == nullptr) {
|
||||
MGLOG_E("VkBufferObject::Map failed: vmaMapMemory returned %d", mapResult);
|
||||
m_mappedData = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return m_mappedData;
|
||||
}
|
||||
|
||||
void VkBufferObject::Unmap() {
|
||||
if (!IsValid() || m_mappedData == nullptr) {
|
||||
m_mappedData = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
vmaUnmapMemory(m_allocator, m_allocation);
|
||||
m_mappedData = nullptr;
|
||||
}
|
||||
|
||||
Bool VkBufferObject::Upload(const void* data, VkDeviceSize size, VkDeviceSize offset) {
|
||||
MOBILEGL_ASSERT(IsValid(), "VkBufferObject::Upload called on invalid buffer");
|
||||
MOBILEGL_ASSERT(data != nullptr || size == 0, "VkBufferObject::Upload data pointer is null");
|
||||
@@ -96,15 +132,30 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return true;
|
||||
}
|
||||
|
||||
void* mapped = nullptr;
|
||||
const VkResult mapResult = vmaMapMemory(m_allocator, m_allocation, &mapped);
|
||||
if (mapResult != VK_SUCCESS || mapped == nullptr) {
|
||||
MGLOG_E("VkBufferObject::Upload failed: vmaMapMemory returned %d", mapResult);
|
||||
const Bool wasMapped = IsMapped();
|
||||
void* mapped = wasMapped ? m_mappedData : Map();
|
||||
if (mapped == nullptr) {
|
||||
MGLOG_E("VkBufferObject::Upload failed: unable to map buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
Memcpy(static_cast<Uint8*>(mapped) + offset, data, static_cast<SizeT>(size));
|
||||
vmaUnmapMemory(m_allocator, m_allocation);
|
||||
if (!wasMapped) {
|
||||
Unmap();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
BufferSlice VkBufferObject::GetSlice(VkDeviceSize offset, VkDeviceSize size) const {
|
||||
MOBILEGL_ASSERT(offset <= m_size, "VkBufferObject::GetSlice offset out of range");
|
||||
const VkDeviceSize resolvedSize = (size == VK_WHOLE_SIZE) ? (m_size - offset) : size;
|
||||
MOBILEGL_ASSERT(offset + resolvedSize <= m_size, "VkBufferObject::GetSlice range out of bounds");
|
||||
|
||||
BufferSlice slice{};
|
||||
slice.buffer = m_buffer;
|
||||
slice.offset = offset;
|
||||
slice.size = resolvedSize;
|
||||
slice.mapped = (m_mappedData != nullptr) ? static_cast<Uint8*>(m_mappedData) + offset : nullptr;
|
||||
return slice;
|
||||
}
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
@@ -8,11 +8,20 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BufferSlice.h"
|
||||
#include "../VkIncludes.h"
|
||||
#include <Includes.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
struct VkBufferObjectDesc {
|
||||
VmaAllocator allocator = nullptr;
|
||||
VkDeviceSize size = 0;
|
||||
VkBufferUsageFlags usage = 0;
|
||||
VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
|
||||
VmaAllocationCreateFlags allocationFlags = 0;
|
||||
};
|
||||
|
||||
class VkBufferObject {
|
||||
public:
|
||||
VkBufferObject() = default;
|
||||
@@ -23,20 +32,27 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkBufferObject(VkBufferObject&& other) noexcept;
|
||||
VkBufferObject& operator=(VkBufferObject&& other) noexcept;
|
||||
|
||||
Bool Create(const VkBufferObjectDesc& desc);
|
||||
Bool Create(VmaAllocator allocator, VkDeviceSize size, VkBufferUsageFlags usage,
|
||||
VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags allocationFlags = 0);
|
||||
void Destroy();
|
||||
|
||||
void* Map();
|
||||
void Unmap();
|
||||
Bool Upload(const void* data, VkDeviceSize size, VkDeviceSize offset = 0);
|
||||
|
||||
VkBuffer GetHandle() const { return m_buffer; }
|
||||
VkDeviceSize GetSize() const { return m_size; }
|
||||
BufferSlice GetSlice(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) const;
|
||||
void* GetMappedData() const { return m_mappedData; }
|
||||
Bool IsMapped() const { return m_mappedData != nullptr; }
|
||||
Bool IsValid() const { return m_allocator != nullptr && m_buffer != VK_NULL_HANDLE && m_allocation != nullptr; }
|
||||
|
||||
private:
|
||||
VmaAllocator m_allocator = nullptr;
|
||||
VkBuffer m_buffer = VK_NULL_HANDLE;
|
||||
VmaAllocation m_allocation = nullptr;
|
||||
void* m_mappedData = nullptr;
|
||||
VkDeviceSize m_size = 0;
|
||||
};
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
@@ -334,16 +334,32 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
MOBILEGL_ASSERT(m_vertexInputStateFactory != nullptr, "VertexInputStateFactory creation failed.");
|
||||
|
||||
CreateFrameContexts();
|
||||
m_frameVertexUploadBuffers.resize(m_frameContext.GetFrameCount());
|
||||
m_frameVertexUploadHeads.assign(m_frameContext.GetFrameCount(), 0);
|
||||
m_frameIndexUploadBuffers.resize(m_frameContext.GetFrameCount());
|
||||
m_frameIndexUploadHeads.assign(m_frameContext.GetFrameCount(), 0);
|
||||
m_deferredBufferReleases.clear();
|
||||
m_deferredBufferReleases.resize(m_frameContext.GetFrameCount());
|
||||
succeeded = 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(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.");
|
||||
|
||||
// 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());
|
||||
|
||||
MGLOG_D("VulkanRenderer initialized");
|
||||
}
|
||||
@@ -363,17 +379,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_textureManager.reset();
|
||||
}
|
||||
m_vertexInputStateFactory.reset();
|
||||
for (auto& buffer : m_frameVertexUploadBuffers) {
|
||||
buffer.Destroy();
|
||||
}
|
||||
for (auto& buffer : m_frameIndexUploadBuffers) {
|
||||
buffer.Destroy();
|
||||
}
|
||||
m_frameVertexUploadBuffers.clear();
|
||||
m_frameVertexUploadHeads.clear();
|
||||
m_frameIndexUploadBuffers.clear();
|
||||
m_frameIndexUploadHeads.clear();
|
||||
m_deferredBufferReleases.clear();
|
||||
m_vertexUploadArena.Shutdown();
|
||||
m_indexUploadArena.Shutdown();
|
||||
|
||||
m_frameContext.Destroy(m_device, m_commandPool);
|
||||
|
||||
@@ -418,58 +425,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
MGLOG_I("VulkanRenderer shut down completed");
|
||||
}
|
||||
|
||||
void VulkanRenderer::DeferDestroyBuffer(VkBufferObject& buffer) {
|
||||
if (!buffer.IsValid()) {
|
||||
return;
|
||||
}
|
||||
const Uint32 frameIndex = m_frameContext.GetCurrentFrameIndex();
|
||||
if (m_deferredBufferReleases.size() < m_frameContext.GetFrameCount()) {
|
||||
m_deferredBufferReleases.resize(m_frameContext.GetFrameCount());
|
||||
}
|
||||
m_deferredBufferReleases[frameIndex].push_back(std::move(buffer));
|
||||
}
|
||||
|
||||
void VulkanRenderer::CollectDeferredBufferReleases(Uint32 frameIndex) {
|
||||
if (frameIndex >= m_deferredBufferReleases.size()) {
|
||||
return;
|
||||
}
|
||||
m_deferredBufferReleases[frameIndex].clear();
|
||||
}
|
||||
|
||||
Bool VulkanRenderer::EnsureFrameUploadBufferCapacity(Uint32 frameIndex, Bool isIndexBuffer,
|
||||
VkDeviceSize requiredEndOffset, VkDeviceSize minCapacity,
|
||||
VkBufferUsageFlags usage) {
|
||||
auto& buffers = isIndexBuffer ? m_frameIndexUploadBuffers : m_frameVertexUploadBuffers;
|
||||
auto& heads = isIndexBuffer ? m_frameIndexUploadHeads : m_frameVertexUploadHeads;
|
||||
if (frameIndex >= buffers.size() || frameIndex >= heads.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& uploadBuffer = buffers[frameIndex];
|
||||
if (uploadBuffer.IsValid() && uploadBuffer.GetSize() >= requiredEndOffset) {
|
||||
return true;
|
||||
}
|
||||
|
||||
VkDeviceSize newCapacity = uploadBuffer.IsValid() ? uploadBuffer.GetSize() : 0;
|
||||
if (newCapacity < minCapacity) {
|
||||
newCapacity = minCapacity;
|
||||
}
|
||||
while (newCapacity < requiredEndOffset) {
|
||||
newCapacity *= 2;
|
||||
}
|
||||
|
||||
DeferDestroyBuffer(uploadBuffer);
|
||||
if (!uploadBuffer.Create(m_allocator, newCapacity, usage, VMA_MEMORY_USAGE_AUTO,
|
||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT)) {
|
||||
MGLOG_E("EnsureFrameUploadBufferCapacity failed: create upload buffer (index=%d, capacity=%zu)",
|
||||
isIndexBuffer, static_cast<SizeT>(newCapacity));
|
||||
return false;
|
||||
}
|
||||
|
||||
heads[frameIndex] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool VulkanRenderer::UploadAndBindVertexStreams(
|
||||
VkCommandBuffer commandBuffer, const MG_State::GLState::VertexArrayObject& vao) {
|
||||
auto& vertexInputState = m_vertexInputStateFactory->GetOrCreateVertexInputState(vao);
|
||||
@@ -498,26 +453,19 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
for (SizeT binding = 0; binding < bindingCount; ++binding) {
|
||||
const SizeT bufferKey = vertexInputState.bindingBufferKeys[binding];
|
||||
const MG_State::GLState::BufferObject* sourceBuffer = findBufferByKey(bufferKey);
|
||||
MOBILEGL_ASSERT(sourceBuffer != nullptr, "UploadAndBindVertexStreams failed to resolve source buffer");
|
||||
|
||||
const auto sourceData = sourceBuffer->GetDataReadOnly();
|
||||
|
||||
const SizeT sourceSize = sourceBuffer->GetSize();
|
||||
VkDeviceSize& frameHead = m_frameVertexUploadHeads[frameIndex];
|
||||
const VkDeviceSize writeOffset = (frameHead + 0x0F) & ~VkDeviceSize(0x0F);
|
||||
const VkDeviceSize writeEnd = writeOffset + static_cast<VkDeviceSize>(sourceSize);
|
||||
if (!EnsureFrameUploadBufferCapacity(frameIndex, false, writeEnd, 4 * 1024 * 1024,
|
||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)) {
|
||||
return false;
|
||||
}
|
||||
auto& frameUploadBuffer = m_frameVertexUploadBuffers[frameIndex];
|
||||
if (!frameUploadBuffer.Upload(sourceData->data(), static_cast<VkDeviceSize>(sourceSize), writeOffset)) {
|
||||
BufferSlice slice{};
|
||||
if (!m_vertexUploadArena.Upload(frameIndex, sourceData->data(), static_cast<VkDeviceSize>(sourceSize), 16,
|
||||
slice)) {
|
||||
MGLOG_E("UploadAndBindVertexStreams skipped: failed to upload binding %zu", binding);
|
||||
return false;
|
||||
}
|
||||
|
||||
frameHead = writeEnd;
|
||||
vkBuffers[binding] = frameUploadBuffer.GetHandle();
|
||||
vkOffsets[binding] = writeOffset;
|
||||
vkBuffers[binding] = slice.buffer;
|
||||
vkOffsets[binding] = slice.offset;
|
||||
}
|
||||
|
||||
vkCmdBindVertexBuffers(commandBuffer, 0, static_cast<Uint32>(bindingCount), vkBuffers.data(), vkOffsets.data());
|
||||
@@ -551,25 +499,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
"DrawElements index range out of bounds");
|
||||
|
||||
const Uint32 frameIndex = m_frameContext.GetCurrentFrameIndex();
|
||||
VkDeviceSize& frameIndexHead = m_frameIndexUploadHeads[frameIndex];
|
||||
const VkDeviceSize alignment = indexSize;
|
||||
const VkDeviceSize writeOffset = (frameIndexHead + alignment - 1) & ~(alignment - 1);
|
||||
const VkDeviceSize writeEnd = writeOffset + static_cast<VkDeviceSize>(indexDataSizeBytes);
|
||||
if (!EnsureFrameUploadBufferCapacity(frameIndex, true, writeEnd, 1 * 1024 * 1024,
|
||||
VK_BUFFER_USAGE_INDEX_BUFFER_BIT)) {
|
||||
BufferSlice slice{};
|
||||
if (!m_indexUploadArena.Upload(frameIndex, indexData->data() + pIndexBufferView->indexByteOffset,
|
||||
static_cast<VkDeviceSize>(indexDataSizeBytes), alignment, slice)) {
|
||||
MGLOG_E("DrawElements skipped: failed to prepare index upload buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& frameIndexUploadBuffer = m_frameIndexUploadBuffers[frameIndex];
|
||||
if (!frameIndexUploadBuffer.Upload(indexData->data() + pIndexBufferView->indexByteOffset,
|
||||
static_cast<VkDeviceSize>(indexDataSizeBytes), writeOffset)) {
|
||||
MGLOG_E("DrawElements skipped: failed to upload index data");
|
||||
return false;
|
||||
}
|
||||
|
||||
frameIndexHead = writeEnd;
|
||||
vkCmdBindIndexBuffer(frame.commandBuffer, frameIndexUploadBuffer.GetHandle(), writeOffset, vkIndexType);
|
||||
vkCmdBindIndexBuffer(frame.commandBuffer, slice.buffer, slice.offset, vkIndexType);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1346,13 +1283,8 @@ void main() {
|
||||
result = VK_SUCCESS;
|
||||
}
|
||||
VK_VERIFY(result, "Present, vkAcquireNextImageKHR");
|
||||
CollectDeferredBufferReleases(m_frameContext.GetCurrentFrameIndex());
|
||||
if (m_frameContext.GetCurrentFrameIndex() < m_frameVertexUploadHeads.size()) {
|
||||
m_frameVertexUploadHeads[m_frameContext.GetCurrentFrameIndex()] = 0;
|
||||
}
|
||||
if (m_frameContext.GetCurrentFrameIndex() < m_frameIndexUploadHeads.size()) {
|
||||
m_frameIndexUploadHeads[m_frameContext.GetCurrentFrameIndex()] = 0;
|
||||
}
|
||||
m_vertexUploadArena.BeginFrame(m_frameContext.GetCurrentFrameIndex());
|
||||
m_indexUploadArena.BeginFrame(m_frameContext.GetCurrentFrameIndex());
|
||||
}
|
||||
|
||||
void VulkanRenderer::CreateInstance() {
|
||||
@@ -1888,12 +1820,32 @@ void main() {
|
||||
m_frameContext.GetCurrent().isCommandRecording = false;
|
||||
m_frameContext.GetCurrent().hasCommandBufferRecorded = false;
|
||||
}
|
||||
m_deferredBufferReleases.clear();
|
||||
m_deferredBufferReleases.resize(m_frameContext.GetFrameCount());
|
||||
m_frameVertexUploadBuffers.resize(m_frameContext.GetFrameCount());
|
||||
m_frameVertexUploadHeads.assign(m_frameContext.GetFrameCount(), 0);
|
||||
m_frameIndexUploadBuffers.resize(m_frameContext.GetFrameCount());
|
||||
m_frameIndexUploadHeads.assign(m_frameContext.GetFrameCount(), 0);
|
||||
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");
|
||||
if (m_frameContext.GetFrameCount() > 0) {
|
||||
m_vertexUploadArena.BeginFrame(m_frameContext.GetCurrentFrameIndex());
|
||||
m_indexUploadArena.BeginFrame(m_frameContext.GetCurrentFrameIndex());
|
||||
}
|
||||
}
|
||||
|
||||
const PhysicalDevice& VulkanRenderer::GetPhysicalDevice() const {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
#include "Config.h"
|
||||
#include "BufferArena.h"
|
||||
#include "FrameContext.h"
|
||||
#include "PipelineFactory.h"
|
||||
#include "ProgramFactory.h"
|
||||
@@ -170,11 +171,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
VkCommandPool m_commandPool = VK_NULL_HANDLE;
|
||||
|
||||
Vector<VkBufferObject> m_frameVertexUploadBuffers;
|
||||
Vector<VkDeviceSize> m_frameVertexUploadHeads;
|
||||
Vector<VkBufferObject> m_frameIndexUploadBuffers;
|
||||
Vector<VkDeviceSize> m_frameIndexUploadHeads;
|
||||
Vector<Vector<VkBufferObject>> m_deferredBufferReleases;
|
||||
BufferArena m_vertexUploadArena;
|
||||
BufferArena m_indexUploadArena;
|
||||
|
||||
Uint m_imageIndexAcquired = 0;
|
||||
FrameContext m_frameContext;
|
||||
@@ -208,13 +206,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const MG_State::GLState::VertexArrayObject& vao,
|
||||
const RenderPassEntry& renderPassEntry);
|
||||
|
||||
void DeferDestroyBuffer(VkBufferObject& buffer);
|
||||
void CollectDeferredBufferReleases(Uint32 frameIndex);
|
||||
Bool EnsureFrameUploadBufferCapacity(Uint32 frameIndex, Bool isIndexBuffer, VkDeviceSize requiredEndOffset,
|
||||
VkDeviceSize minCapacity, VkBufferUsageFlags usage);
|
||||
Bool UploadAndBindVertexStreams(VkCommandBuffer commandBuffer, const MG_State::GLState::VertexArrayObject& vao);
|
||||
Bool UploadAndBindIndexBuffer(FrameContext::FrameData& frame,
|
||||
const MG_State::GLState::VertexArrayObject& vao,
|
||||
const MG_State::GLState::VertexArrayObject& vao,
|
||||
const IndexBufferView* pIndexBufferView = nullptr);
|
||||
Bool InitializeBlitResources();
|
||||
void ShutdownBlitResources();
|
||||
|
||||
Reference in New Issue
Block a user