Fixup memory fragmentation issue in Buffer
This commit is contained in:
@@ -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 )
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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" );
|
||||
|
||||
Reference in New Issue
Block a user