Fixup memory fragmentation issue in Buffer

This commit is contained in:
2024-02-23 04:43:34 -05:00
parent 34ceb1a43d
commit c995111ae1
3 changed files with 118 additions and 64 deletions

View File

@@ -127,20 +127,34 @@ namespace fgl::engine
//Calculate alignment from alignment, ubo_alignment, and atom_size_alignment
memory_size = align( memory_size, alignment() );
//Find a free space.
auto itter = std::find_if(
m_free_blocks.begin(),
m_free_blocks.end(),
[ this, memory_size, allignment ]( const std::pair< vk::DeviceSize, vk::DeviceSize >& pair )
{
auto [ offset, size ] = pair;
auto findBlock = [ this, memory_size, allignment ]()
{
//Find a free space.
return std::find_if(
m_free_blocks.begin(),
m_free_blocks.end(),
[ this, memory_size, allignment ]( const std::pair< vk::DeviceSize, vk::DeviceSize >& pair )
{
const auto [ offset, size ] = pair;
const auto new_offset = align( offset, alignment(), allignment );
size -= new_offset - offset;
const auto new_offset = align( offset, alignment(), allignment );
const auto after_size { size - ( new_offset - offset ) };
return size >= memory_size;
} );
// If the size of the block after alignment is greater than or equal to the size of the memory we want to allocate using it.
return after_size >= memory_size;
} );
};
auto itter = findBlock();
if ( itter == m_free_blocks.end() )
{
//If we can't find a block, then we need to merge the free blocks and try again
mergeFreeBlocks();
itter = findBlock();
}
//TODO: Move this error stuff into the exception message
if ( itter == m_free_blocks.end() )
{
std::cout << "========= !!! OOM !!! =========\n"
@@ -166,6 +180,34 @@ namespace fgl::engine
<< "Attempted to allocate block of size: "
<< fgl::literals::size_literals::to_string( memory_size ) << std::endl;
int allocated_memory_counter { 0 };
//Sum up all memory to check for leaks
for ( auto [ offset, size ] : m_suballocations )
{
allocated_memory_counter += size;
}
std::cout << "Total memory allocated: "
<< fgl::literals::size_literals::to_string( allocated_memory_counter ) << std::endl;
int free_memory_counter { 0 };
for ( auto [ offset, size ] : m_free_blocks )
{
free_memory_counter += size;
}
std::cout << "Total memory free: " << fgl::literals::size_literals::to_string( free_memory_counter )
<< std::endl;
std::cout << "Total memory: " << fgl::literals::size_literals::to_string( m_handle->m_memory_size )
<< std::endl;
if ( allocated_memory_counter + free_memory_counter != m_handle->m_memory_size )
{
std::cout << "Memory size mismatch detected! Difference of: "
<< ( allocated_memory_counter + free_memory_counter - memory_size ) << std::endl;
}
throw exceptions::AllocationException();
}
@@ -175,82 +217,89 @@ namespace fgl::engine
const auto aligned_offset { align( offset, alignment(), allignment ) };
//Fix the offset and size if they aren't alligned
if ( aligned_offset != offset )
{
//Insert the space left over before the block starts back into the free blocks
m_free_blocks.emplace_back( std::make_pair( offset, aligned_offset - offset ) );
mergeFreeBlocks();
offset = aligned_offset;
size -= aligned_offset - offset;
}
//Add the suballocation
m_suballocations.insert_or_assign( offset, memory_size );
//If there is any memory left over, Then add it back into the free blocks
if ( size - memory_size > 0 )
m_free_blocks.emplace_back( std::make_pair( offset + memory_size, size - memory_size ) );
return std::make_shared< BufferSuballocationHandle >( *this, offset, memory_size );
}
void Buffer::mergeFreeBlocks()
{
ZoneScoped;
//Can't combine blocks if there is only 1
if ( m_free_blocks.size() <= 1 ) return;
//Sort the blocks by offset
std::sort(
m_free_blocks.begin(),
m_free_blocks.end(),
[]( const auto& a, const auto& b ) -> bool { return a.first < b.first; } );
auto itter { m_free_blocks.begin() };
auto next_block { std::next( itter ) };
while ( next_block != m_free_blocks.end() && itter != m_free_blocks.end() )
{
//Is the next block adjacent to the current block?
auto& [ offset, size ] = *itter;
const auto& [ next_offset, next_size ] = *next_block;
const bool is_adjacent { offset + size == next_offset };
if ( is_adjacent )
{
//Combine the blocks
size += next_size;
//Remove the next block
m_free_blocks.erase( next_block );
//Reset the next block
next_block = std::next( itter );
continue;
}
else
{
//Move to the next block
itter = next_block;
next_block = std::next( itter );
continue;
}
}
return;
}
void Buffer::free( fgl::engine::BufferSuballocationHandle& info )
{
ZoneScoped;
{
//Find the suballocation
auto itter = m_suballocations.find( info.m_offset );
//Find the suballocation
auto itter = m_suballocations.find( info.m_offset );
if ( itter == m_suballocations.end() ) throw std::runtime_error( "Failed to find suballocation" );
if ( itter == m_suballocations.end() ) throw std::runtime_error( "Failed to find suballocation" );
//Remove the suballocation
m_suballocations.erase( itter );
}
//Remove the suballocation
m_suballocations.erase( itter );
// Forward check
{
ZoneScopedN( "Forward check" );
auto itter = std::find_if(
m_free_blocks.begin(),
m_free_blocks.end(),
[ &info ]( const std::pair< vk::DeviceSize, vk::DeviceSize >& pair )
{
auto& [ offset, size ] = pair;
return offset > info.m_offset && offset == info.m_offset + info.m_size;
} );
//Add the block back to the free blocks
m_free_blocks.emplace_back( std::make_pair( info.m_offset, info.m_size ) );
//If itter is not end then we have found a block where itter->offset > offset
if ( itter != m_free_blocks.end() )
{
auto& [ free_offset, free_size ] = *itter;
info.m_size += free_size; // Add their size to ours
//Nuke block
m_free_blocks.erase( itter );
}
}
// Backwards check
{
ZoneScopedN( "Backwards check" );
auto prev_block = std::find_if(
m_free_blocks.begin(),
m_free_blocks.end(),
[ &info ]( const std::pair< vk::DeviceSize, vk::DeviceSize >& pair )
{
auto& [ offset, size ] = pair;
return offset + size + 1 == info.m_offset;
} );
if ( prev_block != m_free_blocks.end() )
{
auto& [ offset, size ] = *prev_block;
size += info.m_size;
}
else
{
//No block before us. We are the free block
m_free_blocks.push_back( { info.m_offset, info.m_size } );
}
}
mergeFreeBlocks();
}
void* Buffer::map( BufferSuballocationHandle& handle )

View File

@@ -155,6 +155,8 @@ namespace fgl::engine
suballocate( vk::DeviceSize memory_size, std::uint32_t allignment = 1 );
void free( BufferSuballocationHandle& info );
void mergeFreeBlocks();
};
void initGlobalStagingBuffer( std::uint64_t size );

View File

@@ -32,12 +32,14 @@ namespace fgl::engine
HostVector( Buffer& buffer, const std::uint32_t count = 1 ) : BufferVector( buffer, count, sizeof( T ) )
{
ZoneScoped;
assert( count != 0 && "BufferSuballocationVector::BufferSuballocationVector() called with count == 0" );
}
HostVector( Buffer& buffer, const std::vector< T >& vec ) :
HostVector( buffer, static_cast< std::uint32_t >( vec.size() ) )
{
ZoneScoped;
if ( this->m_stride == sizeof( T ) )
{
std::memcpy( this->ptr(), vec.data(), this->count() * sizeof( T ) );
@@ -52,6 +54,7 @@ namespace fgl::engine
void flushRange( const std::uint32_t start_idx, const std::uint32_t end_idx )
{
ZoneScoped;
assert(
start_idx < this->m_count && "BufferSuballocationVector::flushRange start_idx index out of bounds" );
assert( end_idx <= this->m_count && "BufferSuballocationVector::flushRange end_idx index out of bounds" );