399 lines
12 KiB
C++
399 lines
12 KiB
C++
|
|
#include "BufferManager.h"
|
|
|
|
#include "GlobalMemory.h"
|
|
|
|
using Blaze::BufferManager;
|
|
|
|
void BufferManager::DestroyBuffer( Buffer& buf )
|
|
{
|
|
if ( not buf.buffer ) return;
|
|
|
|
ASSERT( m_renderDevice );
|
|
|
|
uint32_t const index = buf.index;
|
|
uint32_t const inner_index = index & kIndexMask;
|
|
uint32_t const generation = ( index & kGenerationMask ) >> kGenerationOffset;
|
|
|
|
RenderDevice const& render_device = *m_renderDevice;
|
|
|
|
vmaDestroyBuffer( render_device.gpuAllocator, Take( buf.buffer ), Take( buf.allocation ) );
|
|
|
|
buf.size = 0;
|
|
buf.mappedData = nullptr;
|
|
buf.index = inner_index | ( generation + 1 ) << kGenerationOffset;
|
|
|
|
// NOTE: DO NOT EDIT INNER INDEX.
|
|
ASSERT( inner_index == ( buf.index & kIndexMask ) and "Index should not be modified" );
|
|
ASSERT( buf.index > index and "Generation should increase." );
|
|
|
|
m_freeList.PushBack( reinterpret_cast<Util::FreeList::Node*>( &buf ) );
|
|
|
|
--m_count;
|
|
}
|
|
|
|
Blaze::Buffer& BufferManager::FetchBufferUnchecked( BufferID const& rid )
|
|
{
|
|
uint32_t const index = *reinterpret_cast<uint32_t const*>( &rid );
|
|
uint32_t const inner_index = index & kIndexMask;
|
|
|
|
return m_buffers[inner_index];
|
|
}
|
|
|
|
void BufferManager::WriteToBuffer( BufferID const& rid, void const* data, size_t const size, size_t const offset )
|
|
{
|
|
ASSERT( IsValidID( rid ) );
|
|
|
|
Buffer const& buffer = FetchBufferUnchecked( rid );
|
|
|
|
ASSERT( size <= buffer.size );
|
|
|
|
memcpy( buffer.mappedData + offset, data, size );
|
|
}
|
|
|
|
bool BufferManager::IsValidID( BufferID const& rid ) const
|
|
{
|
|
uint32_t const index = *reinterpret_cast<uint32_t const*>( &rid );
|
|
uint32_t const inner_index = index & kIndexMask;
|
|
|
|
if ( inner_index > m_capacity ) return false;
|
|
|
|
return m_buffers[inner_index].index == index;
|
|
}
|
|
|
|
Blaze::BufferID BufferManager::CreateVertexBuffer( size_t const size )
|
|
{
|
|
ASSERT( not m_freeList.Empty() );
|
|
|
|
Buffer* buffer_slot = reinterpret_cast<Buffer*>( m_freeList.PopFront() );
|
|
++m_count;
|
|
|
|
ASSERT( m_renderDevice );
|
|
RenderDevice const& render_device = *m_renderDevice;
|
|
|
|
VkBufferCreateInfo const buffer_create_info = {
|
|
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
|
.pNext = nullptr,
|
|
.flags = 0,
|
|
.size = size,
|
|
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
|
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
|
.queueFamilyIndexCount = 0,
|
|
.pQueueFamilyIndices = nullptr,
|
|
};
|
|
|
|
VmaAllocationCreateInfo constexpr allocation_create_info = {
|
|
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
|
|
.usage = VMA_MEMORY_USAGE_AUTO,
|
|
.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
|
|
.preferredFlags = 0,
|
|
.memoryTypeBits = 0,
|
|
.pool = nullptr,
|
|
.pUserData = nullptr,
|
|
.priority = 1.0f,
|
|
};
|
|
|
|
VmaAllocationInfo allocation_info;
|
|
VkBuffer vertex_buffer;
|
|
VmaAllocation vertex_buffer_allocation;
|
|
|
|
VK_CHECK( vmaCreateBuffer(
|
|
render_device.gpuAllocator,
|
|
&buffer_create_info,
|
|
&allocation_create_info,
|
|
&vertex_buffer,
|
|
&vertex_buffer_allocation,
|
|
&allocation_info ) );
|
|
|
|
// NOTE: textureSlot preserves index between uses.
|
|
uint32_t index = buffer_slot->index;
|
|
new ( buffer_slot ) Buffer{
|
|
.buffer = vertex_buffer,
|
|
.allocation = vertex_buffer_allocation,
|
|
.mappedData = static_cast<std::byte*>( allocation_info.pMappedData ),
|
|
.deviceAddress = 0,
|
|
.size = size,
|
|
.index = index,
|
|
};
|
|
|
|
// NOTE: Memory hackery to create TextureID;
|
|
return *reinterpret_cast<BufferID*>( &index );
|
|
}
|
|
|
|
Blaze::BufferID BufferManager::CreateIndexBuffer( size_t const size )
|
|
{
|
|
ASSERT( not m_freeList.Empty() );
|
|
|
|
Buffer* buffer_slot = reinterpret_cast<Buffer*>( m_freeList.PopFront() );
|
|
++m_count;
|
|
|
|
ASSERT( m_renderDevice );
|
|
RenderDevice const& render_device = *m_renderDevice;
|
|
|
|
|
|
VkBufferCreateInfo const buffer_create_info = {
|
|
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
|
.pNext = nullptr,
|
|
.flags = 0,
|
|
.size = size,
|
|
.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
|
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
|
.queueFamilyIndexCount = 0,
|
|
.pQueueFamilyIndices = nullptr,
|
|
};
|
|
|
|
VmaAllocationCreateInfo constexpr allocation_create_info = {
|
|
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
|
|
.usage = VMA_MEMORY_USAGE_AUTO,
|
|
.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
|
|
.preferredFlags = 0,
|
|
.memoryTypeBits = 0,
|
|
.pool = nullptr,
|
|
.pUserData = nullptr,
|
|
.priority = 1.0f,
|
|
};
|
|
|
|
VmaAllocationInfo allocation_info;
|
|
VkBuffer index_buffer;
|
|
VmaAllocation index_buffer_allocation;
|
|
|
|
VK_CHECK( vmaCreateBuffer(
|
|
render_device.gpuAllocator,
|
|
&buffer_create_info,
|
|
&allocation_create_info,
|
|
&index_buffer,
|
|
&index_buffer_allocation,
|
|
&allocation_info ) );
|
|
|
|
// NOTE: bufferSlot preserves index between uses.
|
|
uint32_t index = buffer_slot->index;
|
|
new ( buffer_slot ) Buffer{
|
|
.buffer = index_buffer,
|
|
.allocation = index_buffer_allocation,
|
|
.mappedData = static_cast<std::byte*>( allocation_info.pMappedData ),
|
|
.deviceAddress = 0,
|
|
.size = size,
|
|
.index = index,
|
|
};
|
|
|
|
// NOTE: Memory hackery to create BufferID;
|
|
return *reinterpret_cast<BufferID*>( &index );
|
|
}
|
|
|
|
Blaze::BufferID BufferManager::CreateStorageBuffer( size_t const size )
|
|
{
|
|
ASSERT( not m_freeList.Empty() );
|
|
|
|
Buffer* buffer_slot = reinterpret_cast<Buffer*>( m_freeList.PopFront() );
|
|
++m_count;
|
|
|
|
ASSERT( m_renderDevice );
|
|
RenderDevice const& render_device = *m_renderDevice;
|
|
|
|
|
|
VkBufferCreateInfo const buffer_create_info = {
|
|
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
|
.pNext = nullptr,
|
|
.flags = 0,
|
|
.size = size,
|
|
.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
|
|
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
|
.queueFamilyIndexCount = 0,
|
|
.pQueueFamilyIndices = nullptr,
|
|
};
|
|
|
|
VmaAllocationCreateInfo constexpr allocation_create_info = {
|
|
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
|
|
.usage = VMA_MEMORY_USAGE_AUTO,
|
|
.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
|
|
.preferredFlags = 0,
|
|
.memoryTypeBits = 0,
|
|
.pool = nullptr,
|
|
.pUserData = nullptr,
|
|
.priority = 1.0f,
|
|
};
|
|
|
|
VmaAllocationInfo allocation_info;
|
|
VkBuffer storage_buffer;
|
|
VmaAllocation storage_buffer_allocation;
|
|
|
|
VK_CHECK( vmaCreateBuffer(
|
|
render_device.gpuAllocator,
|
|
&buffer_create_info,
|
|
&allocation_create_info,
|
|
&storage_buffer,
|
|
&storage_buffer_allocation,
|
|
&allocation_info ) );
|
|
|
|
VkBufferDeviceAddressInfo const device_address_info = {
|
|
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
|
|
.pNext = nullptr,
|
|
.buffer = storage_buffer,
|
|
};
|
|
|
|
VkDeviceAddress const device_address = vkGetBufferDeviceAddress( render_device.device, &device_address_info );
|
|
|
|
// NOTE: bufferSlot preserves index between uses.
|
|
uint32_t index = buffer_slot->index;
|
|
new ( buffer_slot ) Buffer{
|
|
.buffer = storage_buffer,
|
|
.allocation = storage_buffer_allocation,
|
|
.mappedData = static_cast<std::byte*>( allocation_info.pMappedData ),
|
|
.deviceAddress = device_address,
|
|
.size = size,
|
|
.index = index,
|
|
};
|
|
|
|
// NOTE: Memory hackery to create BufferID;
|
|
return *reinterpret_cast<BufferID*>( &index );
|
|
}
|
|
|
|
Blaze::BufferID BufferManager::CreateUniformBuffer( size_t const size )
|
|
{
|
|
ASSERT( not m_freeList.Empty() );
|
|
|
|
Buffer* buffer_slot = reinterpret_cast<Buffer*>( m_freeList.PopFront() );
|
|
++m_count;
|
|
|
|
ASSERT( m_renderDevice );
|
|
RenderDevice const& render_device = *m_renderDevice;
|
|
|
|
VkBufferCreateInfo const buffer_create_info = {
|
|
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
|
.pNext = nullptr,
|
|
.flags = 0,
|
|
.size = size,
|
|
.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
|
|
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
|
.queueFamilyIndexCount = 0,
|
|
.pQueueFamilyIndices = nullptr,
|
|
};
|
|
|
|
VmaAllocationCreateInfo constexpr allocation_create_info = {
|
|
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
|
|
.usage = VMA_MEMORY_USAGE_AUTO,
|
|
.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
|
|
.preferredFlags = 0,
|
|
.memoryTypeBits = 0,
|
|
.pool = nullptr,
|
|
.pUserData = nullptr,
|
|
.priority = 1.0f,
|
|
};
|
|
|
|
VmaAllocationInfo allocation_info;
|
|
VkBuffer storage_buffer;
|
|
VmaAllocation storage_buffer_allocation;
|
|
|
|
VK_CHECK( vmaCreateBuffer(
|
|
render_device.gpuAllocator,
|
|
&buffer_create_info,
|
|
&allocation_create_info,
|
|
&storage_buffer,
|
|
&storage_buffer_allocation,
|
|
&allocation_info ) );
|
|
|
|
VkBufferDeviceAddressInfo const device_address_info = {
|
|
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
|
|
.pNext = nullptr,
|
|
.buffer = storage_buffer,
|
|
};
|
|
|
|
VkDeviceAddress const device_address = vkGetBufferDeviceAddress( render_device.device, &device_address_info );
|
|
|
|
// NOTE: bufferSlot preserves index between uses.
|
|
uint32_t index = buffer_slot->index;
|
|
new ( buffer_slot ) Buffer{
|
|
.buffer = storage_buffer,
|
|
.allocation = storage_buffer_allocation,
|
|
.mappedData = static_cast<std::byte*>( allocation_info.pMappedData ),
|
|
.deviceAddress = device_address,
|
|
.size = size,
|
|
.index = index,
|
|
};
|
|
|
|
// NOTE: Memory hackery to create BufferID;
|
|
return *reinterpret_cast<BufferID*>( &index );
|
|
}
|
|
|
|
void BufferManager::FreeBuffer( BufferID* rid )
|
|
{
|
|
if ( not IsValidID( *rid ) ) return;
|
|
|
|
Buffer& buffer = FetchBufferUnchecked( *rid );
|
|
|
|
DestroyBuffer( buffer );
|
|
|
|
*rid = {};
|
|
}
|
|
|
|
std::optional<VkBuffer> BufferManager::FetchBuffer( BufferID const& rid )
|
|
{
|
|
if ( not IsValidID( rid ) ) return std::nullopt;
|
|
|
|
return FetchBufferUnchecked( rid ).buffer;
|
|
}
|
|
|
|
std::optional<VkDeviceAddress> BufferManager::FetchDeviceAddress( BufferID const& rid )
|
|
{
|
|
if ( not IsValidID( rid ) ) return std::nullopt;
|
|
Buffer const& buffer = FetchBufferUnchecked( rid );
|
|
|
|
if ( buffer.deviceAddress == 0 ) return std::nullopt;
|
|
|
|
return buffer.deviceAddress;
|
|
}
|
|
|
|
BufferManager::BufferManager( RenderDevice* render_device, Buffer* buffers, uint32_t const capacity )
|
|
: m_renderDevice{ render_device }
|
|
, m_buffers{ buffers }
|
|
, m_count{ 0 }
|
|
, m_capacity{ capacity }
|
|
{
|
|
uint32_t i = 0;
|
|
for ( Buffer& tex : std::span{ m_buffers, m_capacity } )
|
|
{
|
|
// Default Generation is 1
|
|
tex.index = i++ | ( 1 << kGenerationOffset );
|
|
m_freeList.PushFront( reinterpret_cast<Util::FreeList::Node*>( &tex ) );
|
|
}
|
|
}
|
|
|
|
void BufferManager::Destroy()
|
|
{
|
|
|
|
#if defined( _DEBUG )
|
|
if ( m_count > 0 )
|
|
{
|
|
SDL_LogError( SDL_LOG_CATEGORY_ERROR, "%u buffers still allocated.", m_count );
|
|
}
|
|
#endif
|
|
|
|
while ( not m_freeList.Empty() )
|
|
{
|
|
Buffer* buf = reinterpret_cast<Buffer*>( m_freeList.PopFront() );
|
|
memset( buf, 0, sizeof *buf );
|
|
}
|
|
|
|
for ( Buffer& buf : std::span{ m_buffers, m_count } )
|
|
{
|
|
DestroyBuffer( buf );
|
|
}
|
|
}
|
|
|
|
BufferManager::~BufferManager()
|
|
{
|
|
ASSERT( not m_buffers );
|
|
}
|
|
|
|
|
|
BufferManager* BufferManager::Create( GlobalMemory* mem, RenderDevice* render_device, uint32_t const max_count )
|
|
{
|
|
Buffer* buffers = reinterpret_cast<Buffer*>( mem->Allocate( max_count * sizeof( Buffer ), alignof( Buffer ) ) );
|
|
if ( not buffers ) return nullptr;
|
|
|
|
std::byte* allocation = mem->Allocate( sizeof( BufferManager ), alignof( BufferManager ) );
|
|
if ( not allocation ) return nullptr;
|
|
|
|
return new ( allocation ) BufferManager{ render_device, buffers, max_count };
|
|
}
|