Blaze/Blaze/Source/BufferManager.cpp

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 };
}