Finally fixes issues with transfer manager and buffers
This commit is contained in:
@@ -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" ) )
|
||||
{
|
||||
/*
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace fgl::engine::components
|
||||
{
|
||||
return m_transform;
|
||||
}
|
||||
void updateTransform( TransformComponent & transform )
|
||||
void updateTransform( const TransformComponent& transform )
|
||||
{
|
||||
m_transform = transform;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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 ) )
|
||||
{}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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; }
|
||||
};
|
||||
|
||||
@@ -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 )
|
||||
|
||||
Reference in New Issue
Block a user