Finally fixes issues with transfer manager and buffers

This commit is contained in:
2025-07-01 08:54:04 -04:00
parent 2ffebbfbca
commit ab47f756ea
13 changed files with 290 additions and 108 deletions

View File

@@ -1,6 +1,7 @@
#include <vulkan/vulkan.hpp>
#include "assets/transfer/TransferManager.hpp"
#include "core.hpp"
#include "engine/debug/profiling/counters.hpp"
#include "engine/debug/timing/FlameGraph.hpp"
@@ -91,6 +92,13 @@ namespace fgl::engine::gui
}
ImGui::Separator();
if ( ImGui::TreeNode( "Transfer Buffer" ) )
{
memory::TransferManager::getInstance().drawImGui();
ImGui::TreePop();
}
if ( ImGui::CollapsingHeader( "Buffers" ) )
{
/*

View File

@@ -74,6 +74,8 @@ int main()
constexpr std::string_view sponza_path {
"/home/kj16609/Desktop/Projects/cxx/Mecha/src/assets/khronos-sponza/Sponza.gltf"
// "/home/kj16609/Desktop/Projects/cxx/Mecha/src/assets/TransferTest/Orbs.gltf"
// "/home/kj16609/Desktop/Projects/cxx/Mecha/src/assets/Cube.gltf"
};
builder.loadScene( sponza_path );

View File

@@ -154,6 +154,7 @@ namespace fgl::engine::memory
HostVector< std::byte > vector { staging_buffer, std::get< RawData >( m_source ) };
m_source = vector.getHandle();
std::get< TransferBufferHandle >( m_source )->setReady( true );
return true;
}
@@ -162,55 +163,66 @@ namespace fgl::engine::memory
vk::raii::CommandBuffer& buffer,
Buffer& staging_buffer,
CopyRegionMap& copy_regions,
std::uint32_t transfer_idx,
std::uint32_t graphics_idx )
const std::uint32_t transfer_idx,
const std::uint32_t graphics_idx )
{
ZoneScoped;
switch ( m_type )
{
default:
throw std::runtime_error( "Invalid transfer type" );
case IMAGE_FROM_RAW:
return performRawImageStage( buffer, staging_buffer, transfer_idx, graphics_idx );
case IMAGE_FROM_BUFFER:
return performImageStage( buffer, transfer_idx, graphics_idx );
case BUFFER_FROM_RAW:
return performRawBufferStage( staging_buffer, copy_regions );
case BUFFER_FROM_BUFFER:
return performBufferStage( copy_regions );
case eImageFromRaw:
{
return performRawImageStage( buffer, staging_buffer, transfer_idx, graphics_idx );
}
case eImageFromBuffer:
{
if ( !std::get< TransferBufferHandle >( m_source )->transferReady() ) return false;
return performImageStage( buffer, transfer_idx, graphics_idx );
}
case eBufferFromRaw:
{
return performRawBufferStage( staging_buffer, copy_regions );
}
case eBufferFromBuffer:
{
if ( !std::get< TransferBufferHandle >( m_source )->transferReady() ) return false;
return performBufferStage( copy_regions );
}
}
FGL_UNREACHABLE();
}
void TransferData::markBad()
void TransferData::markBad() const
{
switch ( m_type )
{
case BUFFER_FROM_RAW:
case eBufferFromRaw:
[[fallthrough]];
case BUFFER_FROM_BUFFER:
case eBufferFromBuffer:
std::get< TransferBufferHandle >( m_target )->setReady( false );
break;
case IMAGE_FROM_RAW:
case eImageFromRaw:
[[fallthrough]];
case IMAGE_FROM_BUFFER:
case eImageFromBuffer:
std::get< TransferImageHandle >( m_target )->setReady( false );
}
}
void TransferData::markGood()
void TransferData::markGood() const
{
switch ( m_type )
{
case BUFFER_FROM_RAW:
case eBufferFromRaw:
[[fallthrough]];
case BUFFER_FROM_BUFFER:
case eBufferFromBuffer:
std::get< TransferBufferHandle >( m_target )->setReady( true );
break;
case IMAGE_FROM_RAW:
case eImageFromRaw:
[[fallthrough]];
case IMAGE_FROM_BUFFER:
case eImageFromBuffer:
std::get< TransferImageHandle >( m_target )->setReady( true );
}
}
@@ -221,12 +233,17 @@ namespace fgl::engine::memory
TransferData::TransferData(
const std::shared_ptr< BufferSuballocationHandle >& source,
const std::shared_ptr< BufferSuballocationHandle >& target,
const std::size_t offset ) :
m_type( BUFFER_FROM_BUFFER ),
const vk::DeviceSize size,
const vk::DeviceSize dst_offset,
const vk::DeviceSize src_offset ) :
m_type( eBufferFromBuffer ),
m_source( source ),
m_source_offset( src_offset ),
m_target( target ),
m_target_offset( offset )
m_target_offset( dst_offset ),
m_size( size == 0 ? source->m_size : size )
{
FGL_ASSERT( m_size <= target->m_size, "Attempting to copy to beyond size of target" );
markBad();
}
@@ -234,32 +251,48 @@ namespace fgl::engine::memory
TransferData::TransferData(
std::vector< std::byte >&& source,
const std::shared_ptr< BufferSuballocationHandle >& target,
const std::size_t offset ) :
m_type( BUFFER_FROM_RAW ),
const vk::DeviceSize size,
const vk::DeviceSize dst_offset,
const vk::DeviceSize src_offset ) :
m_type( eBufferFromRaw ),
m_source( std::forward< std::vector< std::byte > >( source ) ),
m_source_offset( src_offset ),
m_target( target ),
m_target_offset( offset )
m_target_offset( dst_offset ),
m_size( size == 0 ? source.size() : size )
{
FGL_ASSERT( m_size <= target->m_size, "Attempting to copy to beyond size of target" );
markBad();
}
//! IMAGE_FROM_BUFFER
TransferData::TransferData(
const std::shared_ptr< BufferSuballocationHandle >& source, const std::shared_ptr< ImageHandle >& target ) :
m_type( IMAGE_FROM_BUFFER ),
const std::shared_ptr< BufferSuballocationHandle >& source,
const std::shared_ptr< ImageHandle >& target,
const vk::DeviceSize size,
const vk::DeviceSize src_offset ) :
m_type( eImageFromBuffer ),
m_source( source ),
m_source_offset( src_offset ),
m_target( target ),
m_target_offset( 0 )
m_target_offset( 0 ),
m_size( size == 0 ? source->m_size : size )
{
markBad();
}
//! IMAGE_FROM_RAW
TransferData::TransferData( std::vector< std::byte >&& source, const std::shared_ptr< ImageHandle >& target ) :
m_type( IMAGE_FROM_RAW ),
TransferData::TransferData(
std::vector< std::byte >&& source,
const std::shared_ptr< ImageHandle >& target,
const vk::DeviceSize size,
const vk::DeviceSize src_offset ) :
m_type( eImageFromRaw ),
m_source( std::forward< std::vector< std::byte > >( source ) ),
m_source_offset( src_offset ),
m_target( target ),
m_target_offset( 0 )
m_target_offset( 0 ),
m_size( size == 0 ? source.size() : size )
{
assert( std::get< RawData >( m_source ).size() > 0 );
markBad();

View File

@@ -6,10 +6,10 @@
#include <memory>
#include <unordered_map>
#include <variant>
#include <vector>
#include "engine/memory/buffers/BufferHandle.hpp"
#include <variant>
namespace vk
{
@@ -25,7 +25,6 @@ namespace fgl::engine
{
class Texture;
class ImageHandle;
class Image;
namespace memory
{
@@ -57,10 +56,10 @@ namespace fgl::engine::memory
//! Type of transfer this data represents
enum TransferType
{
IMAGE_FROM_RAW,
IMAGE_FROM_BUFFER,
BUFFER_FROM_BUFFER,
BUFFER_FROM_RAW
eImageFromRaw,
eImageFromBuffer,
eBufferFromBuffer,
eBufferFromRaw
} m_type;
using RawData = std::vector< std::byte >;
@@ -72,11 +71,13 @@ namespace fgl::engine::memory
//! Source data. Data type depends on m_type
SourceData m_source;
vk::DeviceSize m_source_offset;
//! Target data. Data type depends on m_type
TargetData m_target;
vk::DeviceSize m_target_offset;
std::size_t m_target_offset;
vk::DeviceSize m_size;
//! Performs copy of raw data to the staging buffer
bool convertRawToBuffer( Buffer& staging_buffer );
@@ -120,27 +121,38 @@ namespace fgl::engine::memory
std::uint32_t graphics_idx );
//! Marks the target as not staged/not ready
void markBad();
void markBad() const;
//! Marks the target as staged/ready
void markGood();
void markGood() const;
//BUFFER_FROM_X
TransferData(
const std::shared_ptr< BufferSuballocationHandle >& source,
const std::shared_ptr< BufferSuballocationHandle >& target,
std::size_t offset );
vk::DeviceSize size = 0,
vk::DeviceSize dst_offset = 0,
vk::DeviceSize src_offset = 0 );
TransferData(
std::vector< std::byte >&& source,
const std::shared_ptr< BufferSuballocationHandle >& target,
std::size_t offset );
vk::DeviceSize size = 0,
vk::DeviceSize dst_offset = 0,
vk::DeviceSize src_offset = 0 );
//IMAGE_FROM_X
TransferData(
const std::shared_ptr< BufferSuballocationHandle >& source, const std::shared_ptr< ImageHandle >& target );
const std::shared_ptr< BufferSuballocationHandle >& source,
const std::shared_ptr< ImageHandle >& target,
vk::DeviceSize size = 0,
vk::DeviceSize src_offset = 0 );
TransferData( std::vector< std::byte >&& source, const std::shared_ptr< ImageHandle >& target );
TransferData(
std::vector< std::byte >&& source,
const std::shared_ptr< ImageHandle >& target,
vk::DeviceSize size = 0,
vk::DeviceSize src_offset = 0 );
};
} // namespace fgl::engine::memory

View File

@@ -12,6 +12,10 @@
#include "engine/memory/buffers/BufferSuballocation.hpp"
#include "engine/memory/buffers/vector/HostVector.hpp"
#ifdef ENABLE_IMGUI
#include "imgui.h"
#endif
namespace fgl::engine::memory
{
void TransferManager::recordCommands( vk::raii::CommandBuffer& command_buffer )
@@ -92,10 +96,10 @@ namespace fgl::engine::memory
void TransferManager::resizeBuffer( const std::uint64_t size )
{
m_staging_buffer->resize( size );
m_staging_buffer.resize( size );
}
void TransferManager::submitBuffer( vk::raii::CommandBuffer& command_buffer )
void TransferManager::submitBuffer( const vk::raii::CommandBuffer& command_buffer ) const
{
ZoneScoped;
@@ -299,6 +303,7 @@ namespace fgl::engine::memory
log::info( "Transfer manager created with size {}", literals::size_literals::toString( buffer_size ) );
GLOBAL_TRANSFER_MANAGER = this;
m_staging_buffer->setDebugName( "Staging buffer" );
}
void TransferManager::submitNow()
@@ -318,7 +323,13 @@ namespace fgl::engine::memory
submitBuffer( transfer_buffer );
if ( m_processing.size() > 0 ) log::debug( "Submitted {} objects to be transfered", m_processing.size() );
if ( m_processing.size() > 0 )
{
log::debug(
"Submitted {} objects to be transfered, Transfer buffer usage: {}",
m_processing.size(),
literals::size_literals::toString( m_staging_buffer->used() ) );
}
for ( auto& processed : m_processing )
{
@@ -329,4 +340,19 @@ namespace fgl::engine::memory
m_processing.clear();
}
void TransferManager::drawImGui() const
{
#ifdef ENABLE_IMGUI
ImGui::Text( "|- %s Allocated", literals::size_literals::toString( m_staging_buffer->size() ).c_str() );
ImGui::Text( "|- %s Used ", literals::size_literals::toString( m_staging_buffer->used() ).c_str() );
ImGui::Text(
"|- %s Unused",
literals::size_literals::toString( m_staging_buffer->size() - m_staging_buffer->used() ).c_str() );
ImGui::Text( "|- %i transfer remaining", m_queue.size() );
ImGui::Text( "|- %zu objects being processed", m_processing.size() );
ImGui::Text( "|- %zu total copy regions", m_copy_regions.size() );
#endif
}
} // namespace fgl::engine::memory

View File

@@ -14,6 +14,7 @@
namespace fgl::engine
{
class Image;
class Device;
namespace memory
@@ -64,7 +65,7 @@ namespace fgl::engine::memory
void recordCommands( vk::raii::CommandBuffer& command_buffer );
void submitBuffer( vk::raii::CommandBuffer& command_buffer );
void submitBuffer( const vk::raii::CommandBuffer& command_buffer ) const;
//! Creates barriers that releases ownership from the graphics family to the transfer queue.
std::vector< vk::BufferMemoryBarrier > createFromGraphicsBarriers();
@@ -103,15 +104,19 @@ namespace fgl::engine::memory
void copySuballocationRegion(
const std::shared_ptr< BufferSuballocationHandle >& src,
const std::shared_ptr< BufferSuballocationHandle >& dst,
const std::size_t offset = 0 )
const vk::DeviceSize size = 0,
const vk::DeviceSize dst_offset = 0,
const std::size_t src_offset = 0 )
{
FGL_ASSERT( src->m_size == dst->m_size, "Source and destination suballocations must be the same size" );
TransferData transfer_data {
src,
dst,
offset,
};
//! If the buffer has not been staged, Then there is nothing to copy in the first place.
if ( !src->m_staged ) return;
// Makes the dst as requiring the src to be stable (no pending writes) before it's used.
dst->markRequiresStable( src );
TransferData transfer_data { src, dst, size, dst_offset, src_offset };
m_queue.emplace( std::move( transfer_data ) );
}
@@ -119,12 +124,17 @@ namespace fgl::engine::memory
//! Queues a buffer to be transfered
template < typename DeviceVectorT >
requires is_device_vector< DeviceVectorT >
void copyToVector( std::vector< std::byte >&& data, DeviceVectorT& device_vector, std::size_t byte_offset = 0 )
void copyToVector(
std::vector< std::byte >&& data,
DeviceVectorT& device_vector,
const vk::DeviceSize size = 0,
const vk::DeviceSize dst_offset = 0,
const vk::DeviceSize src_offset = 0 )
{
assert( data.size() > 0 );
TransferData transfer_data { std::forward< std::vector< std::byte > >( data ),
device_vector.m_handle,
byte_offset };
assert( !data.empty() );
TransferData transfer_data {
std::forward< std::vector< std::byte > >( data ), device_vector.m_handle, size, dst_offset, src_offset
};
m_queue.emplace( std::move( transfer_data ) );
}
@@ -140,7 +150,10 @@ namespace fgl::engine::memory
std::memcpy( data.data(), &t, sizeof( T ) );
copyToVector( std::move( data ), device_vector, idx * sizeof( T ) );
const auto size { sizeof( T ) };
const auto dst_offset { idx * size };
copyToVector( std::move( data ), device_vector, size, dst_offset );
}
//! Queues a data copy from a STL vector to a device vector
@@ -154,7 +167,7 @@ namespace fgl::engine::memory
std::memcpy( punned_data.data(), data.data(), sizeof( T ) * data.size() );
copyToVector( std::move( punned_data ), device_vector, 0 );
copyToVector( std::move( punned_data ), device_vector, punned_data.size() );
}
void copyToVector( BufferVector& source, BufferVector& target, std::size_t target_offset );
@@ -163,6 +176,8 @@ namespace fgl::engine::memory
//! Forces the queue to be submitted now before the buffer is filled.
void submitNow();
void drawImGui() const;
};
} // namespace fgl::engine::memory

View File

@@ -44,7 +44,7 @@ namespace fgl::engine::components
{
return m_transform;
}
void updateTransform( TransformComponent & transform )
void updateTransform( const TransformComponent& transform )
{
m_transform = transform;
}

View File

@@ -14,6 +14,7 @@
#include "engine/memory/buffers/BufferHandle.hpp"
#include "engine/memory/buffers/exceptions.hpp"
#include "engine/rendering/devices/Device.hpp"
#include "math/literals/size.hpp"
#include "memory/DefferedCleanup.hpp"
namespace fgl::engine::memory
@@ -187,6 +188,67 @@ namespace fgl::engine::memory
} );
}
BufferSuballocation Buffer::allocate( const vk::DeviceSize desired_size, const std::uint32_t alignment )
{
auto allocation { operator->()->allocate( desired_size, alignment ) };
if ( !allocation )
{
// Resize to x1.5 the size, or the size plus the desired size x1.5, Whichever is bigger
const auto optimal_size { std::max( this->size() * 2, this->size() + desired_size * 2 ) };
this->resize( optimal_size );
FGL_ASSERT( optimal_size == this->size(), "Optimal size not met!" );
allocation = operator->()->allocate( desired_size, alignment );
}
return allocation;
}
void Buffer::resize( const vk::DeviceSize size )
{
const std::shared_ptr< BufferHandle > old_handle { *this };
const auto new_handle { old_handle->remake( size ) };
new_handle->setDebugName( old_handle->m_debug_name );
*this = new_handle;
}
std::shared_ptr< BufferHandle > BufferHandle::remake( vk::DeviceSize new_size )
{
ZoneScoped;
auto new_handle { std::make_shared< BufferHandle >( new_size, m_usage, m_memory_properties ) };
const auto& old_allocations { m_active_suballocations };
const auto& old_allocations_traces { m_allocation_traces };
// Remake all allocations into the new buffer
std::vector<
std::pair< std::shared_ptr< BufferSuballocationHandle >, std::shared_ptr< BufferSuballocationHandle > > >
allocations {};
allocations.reserve( old_allocations.size() );
for ( const auto& suballocation_weak : old_allocations )
{
if ( suballocation_weak.expired() ) continue;
auto suballocation { suballocation_weak.lock() };
if ( auto new_suballocation { new_handle->allocate( suballocation->m_size, suballocation->m_alignment ) } )
{
allocations.emplace_back( suballocation, new_suballocation );
// Copy the data from the old allocation to the new allocation
TransferManager::getInstance().copySuballocationRegion( suballocation, new_suballocation );
}
else
throw std::runtime_error( "The fuck" );
}
return new_handle;
}
/*
void BufferHandle::resize( const vk::DeviceSize new_size )
{
log::warn( "Resizing buffer from {} to {}", size(), new_size );
@@ -208,7 +270,7 @@ namespace fgl::engine::memory
auto suballocation { suballocation_weak.lock() };
auto new_suballocation { new_handle->allocate( suballocation->m_size, suballocation->m_alignment ) };
allocations.emplace_back( std::make_pair( suballocation, new_suballocation ) );
allocations.emplace_back( suballocation, new_suballocation );
}
catch ( std::bad_weak_ptr& e )
{
@@ -242,38 +304,28 @@ namespace fgl::engine::memory
TransferManager::getInstance().copySuballocationRegion( old_suballocation, new_suballocation );
}
}
*/
std::shared_ptr< BufferSuballocationHandle > BufferHandle::
allocate( vk::DeviceSize desired_memory_size, const std::uint32_t t_alignment )
allocate( vk::DeviceSize desired_memory_size, const vk::DeviceSize t_alignment )
{
ZoneScoped;
//Calculate alignment from alignment, ubo_alignment, and atom_size_alignment
desired_memory_size = align( desired_memory_size, alignment() );
assert( desired_memory_size <= this->size() );
//findAvailableBlock( memory_size, t_alignment );
if ( !canAllocate( desired_memory_size, t_alignment ) )
{
// Resize to x1.5 the size, or the size plus the desired size x1.5, Whichever is bigger
const auto optimal_size { std::
max( static_cast< vk::DeviceSize >( this->size() * 2 ),
this->size()
+ static_cast< vk::DeviceSize >( desired_memory_size * 2 ) ) };
this->resize( optimal_size );
FGL_ASSERT( optimal_size == this->size(), "Optimal size not met!" );
return { nullptr };
}
auto itter { findAvailableBlock( desired_memory_size, t_alignment ) };
if ( itter == m_free_blocks.end() )
{
throw BufferOOM();
// Could not find a block that is available.
return { nullptr };
}
//Allocate
auto [ selected_block_offset, selected_block_size ] = *itter;
m_free_blocks.erase( itter );
@@ -289,7 +341,7 @@ namespace fgl::engine::memory
//Insert the space left over before the block starts back into the free blocks
const std::size_t leftover_start_size { aligned_offset - selected_block_offset };
m_free_blocks.emplace_back( std::make_pair( selected_block_offset, leftover_start_size ) );
m_free_blocks.emplace_back( selected_block_offset, leftover_start_size );
mergeFreeBlocks();
@@ -325,7 +377,7 @@ namespace fgl::engine::memory
return suballocation_handle;
}
bool BufferHandle::canAllocate( const vk::DeviceSize memory_size, const std::uint32_t alignment )
bool BufferHandle::canAllocate( const vk::DeviceSize memory_size, const vk::DeviceSize alignment )
{
// TODO: This check can be optimized by itterating through and virtually combining blocks that would be combined.
// If the combined block is large enough then we should consider it being capable of allocation.
@@ -386,13 +438,14 @@ namespace fgl::engine::memory
void BufferHandle::setDebugName( const std::string& str )
{
m_debug_name = str;
std::string sized_name { std::format( "{}: {}", m_debug_name, literals::size_literals::toString( size() ) ) };
vk::DebugUtilsObjectNameInfoEXT info {};
info.objectType = vk::ObjectType::eBuffer;
info.pObjectName = str.c_str();
info.pObjectName = sized_name.c_str();
info.objectHandle = reinterpret_cast< std::uint64_t >( static_cast< VkBuffer >( this->m_buffer ) );
m_debug_name = str;
Device::getInstance().setDebugUtilsObjectName( info );
}
@@ -418,9 +471,9 @@ namespace fgl::engine::memory
#ifndef NDEBUG
//Check that we haven't lost any memory
std::size_t sum { 0 };
for ( const auto& free_blocks : this->m_free_blocks )
for ( const auto& size : this->m_free_blocks | std::views::values )
{
sum += free_blocks.second;
sum += size;
}
for ( auto& suballocation : m_active_suballocations )
@@ -441,6 +494,7 @@ namespace fgl::engine::memory
for ( auto& suballocation : m_active_suballocations )
{
if ( suballocation.expired() ) continue;
total_size += suballocation.lock()->m_size;
}
@@ -451,9 +505,9 @@ namespace fgl::engine::memory
{
vk::DeviceSize largest { 0 };
for ( const auto& blocks : m_free_blocks )
for ( const auto& size : m_free_blocks | std::views::values )
{
largest = std::max( largest, blocks.second );
largest = std::max( largest, size );
}
return largest;

View File

@@ -9,8 +9,7 @@
#include <cassert>
#include <cmath>
#include <cstdint>
#include <map>
#include <expected>
#include <memory>
#include <stacktrace>
#include <unordered_map>
@@ -36,6 +35,12 @@ namespace fgl::engine::memory
class BufferSuballocation;
struct BufferSuballocationHandle;
enum AllocationStatus : std::uint8_t
{
eFailedUnk,
eFailedOOM
};
//TODO: Dynamic/onDemand resizing of Buffer for suballocations
//TODO: Defragmentation
@@ -119,15 +124,20 @@ namespace fgl::engine::memory
bool isMappable() const { return m_alloc_info.pMappedData != nullptr; }
void resize( vk::DeviceSize new_size );
//! Returns a allocation block from this buffer. Block will be aligned with nonUniformBufferOffsetAlignment
//! and nonCoherentAtomSize if required (is_uniform_buffer and is_host_visible respectively)
private:
friend struct Buffer;
std::shared_ptr< BufferHandle > remake( vk::DeviceSize new_size );
// void resize( vk::DeviceSize new_size );
public:
/**
* @param desired_memory_size Size of each N
* @param alignment The alignment to use.
* @param t_alignment
* @param source_loc Source location.
* @return
* @brief Returns a allocation block from this buffer. Block will be aligned with nonUniformBufferOffsetAlignment and nonCoherentAtomSize if required (is_uniform_buffer and is_host_visible respectively)
*
* @note Alignment is forced to be at least the size of the largest alignment required by the device.
* (`alignment` vs `nonCoherentAtomSize` vs `minUniformBufferOffsetAlignment`)
@@ -137,11 +147,11 @@ namespace fgl::engine::memory
* @note Alignment for atom_size is 0 if buffer is not host visible
*/
std::shared_ptr< BufferSuballocationHandle >
allocate( vk::DeviceSize desired_memory_size, std::uint32_t alignment = 1 );
allocate( vk::DeviceSize desired_memory_size, vk::DeviceSize t_alignment );
bool canAllocate( vk::DeviceSize memory_size, std::uint32_t alignment = 1 );
bool canAllocate( vk::DeviceSize memory_size, vk::DeviceSize alignment = 1 );
//! Frees a given suballocation. After calling this the handle is invalid and accessing it is UB
//! Frees a given suballocation. After calling this, the handle is invalid and accessing it is UB
void free( BufferSuballocationHandle& info );
void mergeFreeBlocks();
@@ -166,6 +176,12 @@ namespace fgl::engine::memory
Buffer( const std::shared_ptr< BufferHandle >& buffer ) : std::shared_ptr< BufferHandle >( buffer ) {}
BufferSuballocation allocate( vk::DeviceSize desired_size, std::uint32_t alignment = 1 );
vk::DeviceSize size() const { return std::shared_ptr< BufferHandle >::operator->()->size(); }
void resize( vk::DeviceSize size );
~Buffer() = default;
};

View File

@@ -118,10 +118,11 @@ namespace fgl::engine::memory
}
BufferSuballocation::BufferSuballocation( const Buffer& buffer, const vk::DeviceSize size ) :
BufferSuballocation( buffer->allocate( size ) )
BufferSuballocation( buffer->allocate( size, 1 ) )
{}
BufferSuballocation::BufferSuballocation( const Buffer& buffer, const std::size_t t_size, const std::uint32_t t_align ) :
BufferSuballocation::
BufferSuballocation( const Buffer& buffer, const std::size_t t_size, const std::uint32_t t_align ) :
BufferSuballocation( buffer->allocate( t_size, t_align ) )
{}

View File

@@ -41,12 +41,12 @@ namespace fgl::engine::memory
}
vk::BufferCopy BufferSuballocationHandle::
copyRegion( const BufferSuballocationHandle& target, const std::size_t offset ) const
copyRegion( const BufferSuballocationHandle& target, const std::size_t suballocation_offset ) const
{
vk::BufferCopy copy {};
copy.size = std::min( this->m_size, target.m_size );
copy.srcOffset = this->getOffset();
copy.dstOffset = target.getOffset() + offset;
copy.dstOffset = target.getOffset() + suballocation_offset;
return copy;
}

View File

@@ -6,6 +6,8 @@
#include <vulkan/vulkan.hpp>
#include <queue>
#include "BufferHandle.hpp"
#include "engine/debug/Track.hpp"
@@ -37,6 +39,9 @@ namespace fgl::engine::memory
bool m_staged { false };
//! A list of all sources that are wanting to write to this buffer.
std::queue< std::shared_ptr< BufferSuballocationHandle > > m_pending_sources {};
BufferSuballocationHandle(
const Buffer& p_buffer, vk::DeviceSize offset, vk::DeviceSize memory_size, vk::DeviceSize alignment );
@@ -49,16 +54,27 @@ namespace fgl::engine::memory
[[nodiscard]] vk::Buffer getBuffer() const;
[[nodiscard]] vk::Buffer getVkBuffer() const;
[[nodiscard]] vk::BufferCopy copyRegion( const BufferSuballocationHandle& target, std::size_t offset ) const;
[[nodiscard]] vk::DeviceSize getOffset() const { return m_offset; }
[[nodiscard]] vk::BufferCopy
copyRegion( const BufferSuballocationHandle& target, std::size_t suballocation_offset ) const;
void copyTo(
const vk::raii::CommandBuffer& cmd_buffer,
const BufferSuballocationHandle& other,
std::size_t offset ) const;
bool ready() const { return m_staged; }
void markRequiresStable( const std::shared_ptr< BufferSuballocationHandle >& src )
{
m_pending_sources.push( src );
}
//! Returns true if this data is stable (No pending writes)
bool stable() const { return m_pending_sources.empty(); }
[[nodiscard]] vk::DeviceSize getOffset() const { return m_offset; }
bool transferReady() const { return m_staged && m_pending_sources.empty(); }
bool ready() const { return m_staged && m_pending_sources.empty(); }
void setReady( const bool value ) { m_staged = value; }
};

View File

@@ -6,15 +6,14 @@
#include "engine/assets/transfer/TransferManager.hpp"
#include "engine/memory/buffers/BufferHandle.hpp"
#include "memory/buffers/BufferHandle.hpp"
namespace fgl::engine::memory
{
constexpr std::uint32_t min_capacity { 1024 };
[[nodiscard]] BufferVector::BufferVector( Buffer& buffer, std::uint32_t count, std::uint32_t stride ) :
BufferSuballocation( buffer->allocate( std::max( count, min_capacity ) * stride, stride ) ),
[[nodiscard]] BufferVector::BufferVector( Buffer& buffer, const std::uint32_t count, const std::uint32_t stride ) :
BufferSuballocation( buffer.allocate( std::max( count, min_capacity ) * stride, stride ) ),
m_count( count ),
m_capacity( std::max( count, min_capacity ) ),
m_stride( stride )