From ab47f756ea385ec0f37578d91f5035c261c90957 Mon Sep 17 00:00:00 2001 From: kj16609 Date: Tue, 1 Jul 2025 08:54:04 -0400 Subject: [PATCH] Finally fixes issues with transfer manager and buffers --- src/editor/src/gui/drawStats.cpp | 8 ++ src/editor/src/main.cpp | 2 + src/engine/assets/transfer/TransferData.cpp | 97 ++++++++++------ src/engine/assets/transfer/TransferData.hpp | 38 ++++--- .../assets/transfer/TransferManager.cpp | 32 +++++- .../assets/transfer/TransferManager.hpp | 43 +++++--- .../gameobjects/components/ModelComponent.hpp | 2 +- src/engine/memory/buffers/BufferHandle.cpp | 104 +++++++++++++----- src/engine/memory/buffers/BufferHandle.hpp | 34 ++++-- .../memory/buffers/BufferSuballocation.cpp | 5 +- .../buffers/BufferSuballocationHandle.cpp | 4 +- .../buffers/BufferSuballocationHandle.hpp | 24 +++- .../memory/buffers/vector/BufferVector.cpp | 5 +- 13 files changed, 290 insertions(+), 108 deletions(-) diff --git a/src/editor/src/gui/drawStats.cpp b/src/editor/src/gui/drawStats.cpp index 64213a4..c2d9ffc 100644 --- a/src/editor/src/gui/drawStats.cpp +++ b/src/editor/src/gui/drawStats.cpp @@ -1,6 +1,7 @@ #include +#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" ) ) { /* diff --git a/src/editor/src/main.cpp b/src/editor/src/main.cpp index b858a33..f9cf690 100644 --- a/src/editor/src/main.cpp +++ b/src/editor/src/main.cpp @@ -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 ); diff --git a/src/engine/assets/transfer/TransferData.cpp b/src/engine/assets/transfer/TransferData.cpp index 6eb3334..a686b89 100644 --- a/src/engine/assets/transfer/TransferData.cpp +++ b/src/engine/assets/transfer/TransferData.cpp @@ -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(); diff --git a/src/engine/assets/transfer/TransferData.hpp b/src/engine/assets/transfer/TransferData.hpp index 92b6b1a..785eb7c 100644 --- a/src/engine/assets/transfer/TransferData.hpp +++ b/src/engine/assets/transfer/TransferData.hpp @@ -6,10 +6,10 @@ #include #include +#include #include #include "engine/memory/buffers/BufferHandle.hpp" -#include 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 \ No newline at end of file diff --git a/src/engine/assets/transfer/TransferManager.cpp b/src/engine/assets/transfer/TransferManager.cpp index 993c1bb..4f96578 100644 --- a/src/engine/assets/transfer/TransferManager.cpp +++ b/src/engine/assets/transfer/TransferManager.cpp @@ -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 diff --git a/src/engine/assets/transfer/TransferManager.hpp b/src/engine/assets/transfer/TransferManager.hpp index d9a6524..5f9ecca 100644 --- a/src/engine/assets/transfer/TransferManager.hpp +++ b/src/engine/assets/transfer/TransferManager.hpp @@ -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 diff --git a/src/engine/gameobjects/components/ModelComponent.hpp b/src/engine/gameobjects/components/ModelComponent.hpp index e52f362..60e056f 100644 --- a/src/engine/gameobjects/components/ModelComponent.hpp +++ b/src/engine/gameobjects/components/ModelComponent.hpp @@ -44,7 +44,7 @@ namespace fgl::engine::components { return m_transform; } - void updateTransform( TransformComponent & transform ) + void updateTransform( const TransformComponent& transform ) { m_transform = transform; } diff --git a/src/engine/memory/buffers/BufferHandle.cpp b/src/engine/memory/buffers/BufferHandle.cpp index 90fb272..c7df3fc 100644 --- a/src/engine/memory/buffers/BufferHandle.cpp +++ b/src/engine/memory/buffers/BufferHandle.cpp @@ -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; diff --git a/src/engine/memory/buffers/BufferHandle.hpp b/src/engine/memory/buffers/BufferHandle.hpp index ca958d1..e4618b7 100644 --- a/src/engine/memory/buffers/BufferHandle.hpp +++ b/src/engine/memory/buffers/BufferHandle.hpp @@ -9,8 +9,7 @@ #include #include -#include -#include +#include #include #include #include @@ -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; }; diff --git a/src/engine/memory/buffers/BufferSuballocation.cpp b/src/engine/memory/buffers/BufferSuballocation.cpp index d3e6e1d..53cb9de 100644 --- a/src/engine/memory/buffers/BufferSuballocation.cpp +++ b/src/engine/memory/buffers/BufferSuballocation.cpp @@ -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 ) ) {} diff --git a/src/engine/memory/buffers/BufferSuballocationHandle.cpp b/src/engine/memory/buffers/BufferSuballocationHandle.cpp index ec30a6f..0a5ac53 100644 --- a/src/engine/memory/buffers/BufferSuballocationHandle.cpp +++ b/src/engine/memory/buffers/BufferSuballocationHandle.cpp @@ -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; } diff --git a/src/engine/memory/buffers/BufferSuballocationHandle.hpp b/src/engine/memory/buffers/BufferSuballocationHandle.hpp index e2712d0..639efac 100644 --- a/src/engine/memory/buffers/BufferSuballocationHandle.hpp +++ b/src/engine/memory/buffers/BufferSuballocationHandle.hpp @@ -6,6 +6,8 @@ #include +#include + #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; } }; diff --git a/src/engine/memory/buffers/vector/BufferVector.cpp b/src/engine/memory/buffers/vector/BufferVector.cpp index 4d09abe..dbafe7d 100644 --- a/src/engine/memory/buffers/vector/BufferVector.cpp +++ b/src/engine/memory/buffers/vector/BufferVector.cpp @@ -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 )