Add memory buffer allocation count into the stats screen

This commit is contained in:
2024-08-07 04:16:21 -04:00
parent 48087b53ed
commit 32d452a568
8 changed files with 119 additions and 26 deletions

View File

@@ -1,16 +1,89 @@
#include <vulkan/vulkan.hpp>
#include "core.hpp"
#include "engine/buffers/Buffer.hpp"
#include "safe_include.hpp"
namespace fgl::engine::gui
{
struct AllocationList
{
struct AllocationInfo
{
vk::DeviceSize m_total;
vk::DeviceSize m_used;
inline vk::DeviceSize free() const { return m_total - m_used; }
vk::DeviceSize m_largest_free_block;
};
AllocationInfo gpu {};
AllocationInfo host {};
};
const AllocationList getTotalAllocated()
{
AllocationList info {};
auto& [ gpu_allocated, gpu_used, gpu_largest_free ] = info.gpu;
auto& [ host_allocated, host_used, host_largest_free ] = info.host;
for ( const std::weak_ptr< memory::BufferHandle >& buffer_weak :
fgl::engine::memory::Buffer::getActiveBufferHandles() )
{
if ( auto locked = buffer_weak.lock(); locked )
{
// The buffer is still active.
if ( locked->m_memory_properties & vk::MemoryPropertyFlagBits::eDeviceLocal )
{
gpu_allocated += locked->size();
}
else if ( locked->m_memory_properties & vk::MemoryPropertyFlagBits::eHostVisible )
{
host_allocated += locked->size();
}
else
throw std::
runtime_error( "Unknown memory property flag choice. Could not determine host vs device" );
}
}
return info;
}
void drawMemoryStats()
{
const auto [ gpu, host ] = getTotalAllocated();
const auto& [ gpu_allocated, gpu_used, gpu_largest_block ] = gpu;
const auto& [ host_allocated, host_used, host_largest_block ] = host;
ImGui::Text( "Device" );
ImGui::Text( "|- %0.1f MB Allocated", static_cast< float >( gpu_allocated ) / 1000.0f / 1000.0f );
ImGui::Text( "|- %0.1f MB Used ", static_cast< float >( gpu_used ) / 1000.0f / 1000.0f );
ImGui::Text( "|- %0.1f MB Unused", static_cast< float >( gpu.free() ) / 1000.0f / 1000.0f );
ImGui::Separator();
ImGui::Text( "Host" );
ImGui::Text( "|- %0.1f MB Allocated", static_cast< float >( host_allocated ) / 1000.0f / 1000.0f );
ImGui::Text( "|- %0.1f MB Used ", static_cast< float >( host_used ) / 1000.0f / 1000.0f );
ImGui::Text( "|- %0.1f MB Unused", static_cast< float >( host.free() ) / 1000.0f / 1000.0f );
ImGui::Separator();
}
void drawStats( const FrameInfo& info )
{
ImGui::Begin( "Stats" );
ImGui::Text( "FPS: %0.1f", ImGui::GetIO().Framerate );
if ( ImGui::CollapsingHeader( "Memory" ) )
{
drawMemoryStats();
}
ImGui::End();
}

View File

@@ -65,6 +65,10 @@ else ()
target_compile_definitions(FGLEngine PUBLIC FGL_TESTS=0)
endif ()
# Enable tracking for buffers, I need to find some way to disable this when trying to link
# with the game itself
target_compile_definitions(FGLEngine PUBLIC TRACK_BUFFERS)
#GLM settings
# GLM_FORCE_NO_CTOR_INIT

View File

@@ -3,6 +3,7 @@
//
#include "Buffer.hpp"
#include "BufferSuballocationHandle.hpp"
#include "align.hpp"
#include "engine/buffers/exceptions.hpp"
@@ -78,6 +79,7 @@ namespace fgl::engine::memory
vmaGetAllocationInfo( Device::getInstance().allocator(), m_allocation, &m_alloc_info );
}
#ifdef TRACK_BUFFERS
std::vector< std::weak_ptr< BufferHandle > > Buffer::getActiveBufferHandles()
{
std::vector< std::weak_ptr< BufferHandle > > handles;
@@ -94,6 +96,7 @@ namespace fgl::engine::memory
m_buffer_handles = handles;
return handles;
}
#endif
vk::DeviceSize Buffer::alignment()
{
@@ -113,23 +116,23 @@ namespace fgl::engine::memory
}
std::shared_ptr< BufferSuballocationHandle > Buffer::
suballocate( vk::DeviceSize memory_size, std::uint32_t allignment )
allocate( vk::DeviceSize memory_size, std::uint32_t t_alignment )
{
ZoneScoped;
//Calculate alignment from alignment, ubo_alignment, and atom_size_alignment
memory_size = align( memory_size, alignment() );
auto findBlock = [ this, memory_size, allignment ]()
auto findBlock = [ this, memory_size, t_alignment ]()
{
//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 )
[ this, memory_size, t_alignment ]( const std::pair< vk::DeviceSize, vk::DeviceSize >& pair )
{
const auto [ offset, size ] = pair;
const auto new_offset = align( offset, alignment(), allignment );
const auto new_offset = align( offset, alignment(), t_alignment );
const auto after_size { size - ( new_offset - offset ) };
// 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.
@@ -156,14 +159,14 @@ namespace fgl::engine::memory
{
std::cout << "Offset: " << std::hex << offset << " Size: " << std::dec << size << "\n";
std::cout << "Aligned offset: " << std::hex << align( offset, alignment(), allignment )
std::cout << "Aligned offset: " << std::hex << align( offset, alignment(), t_alignment )
<< " Size: " << std::dec << size << "\n"
<< std::endl;
}
std::cout << "====== Suballocations ======\n";
for ( auto [ offset, size ] : m_suballocations )
for ( auto [ offset, size ] : m_allocations )
{
std::cout << "Offset: " << std::hex << offset << " Size: " << std::dec << size << "\n";
}
@@ -174,7 +177,7 @@ namespace fgl::engine::memory
std::uint64_t allocated_memory_counter { 0 };
//Sum up all memory to check for leaks
for ( auto [ offset, size ] : m_suballocations )
for ( auto [ offset, size ] : m_allocations )
{
allocated_memory_counter += size;
}
@@ -207,7 +210,7 @@ namespace fgl::engine::memory
auto [ offset, size ] = *itter;
m_free_blocks.erase( itter );
const auto aligned_offset { align( offset, alignment(), allignment ) };
const auto aligned_offset { align( offset, alignment(), t_alignment ) };
//Fix the offset and size if they aren't alligned
if ( aligned_offset != offset )
@@ -224,7 +227,7 @@ namespace fgl::engine::memory
}
//Add the suballocation
m_suballocations.insert_or_assign( offset, memory_size );
m_allocations.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 )
@@ -238,7 +241,7 @@ namespace fgl::engine::memory
sum += free_blocks.second;
}
for ( const auto& allocated : this->m_suballocations )
for ( const auto& allocated : this->m_allocations )
{
sum += allocated.second;
}
@@ -310,12 +313,12 @@ namespace fgl::engine::memory
ZoneScoped;
//Find the suballocation
auto itter = m_suballocations.find( info.m_offset );
auto itter = m_allocations.find( info.m_offset );
if ( itter == m_suballocations.end() ) throw std::runtime_error( "Failed to find suballocation" );
if ( itter == m_allocations.end() ) throw std::runtime_error( "Failed to find suballocation" );
//Remove the suballocation
m_suballocations.erase( itter );
m_allocations.erase( itter );
//Add the block back to the free blocks
m_free_blocks.emplace_back( std::make_pair( info.m_offset, info.m_size ) );
@@ -330,7 +333,7 @@ namespace fgl::engine::memory
sum += free_blocks.second;
}
for ( const auto& allocated : this->m_suballocations )
for ( const auto& allocated : this->m_allocations )
{
sum += allocated.second;
}
@@ -351,12 +354,14 @@ namespace fgl::engine::memory
m_memory_properties( memory_properties )
{
m_free_blocks.insert( m_free_blocks.begin(), { 0, memory_size } );
#ifdef TRACK_BUFFERS
m_buffer_handles.emplace_back( m_handle );
#endif
}
Buffer::~Buffer()
{
assert( m_suballocations.size() == 0 && "Buffer destructed while allocations still present" );
assert( m_allocations.size() == 0 && "Buffer destructed while allocations still present" );
}
} // namespace fgl::engine::memory

View File

@@ -32,7 +32,9 @@ namespace fgl::engine::memory
//TODO: Dynamic/onDemand resizing of Buffer for suballocations
//TODO: Defragmentation
class BufferHandle
//TODO: Ensure this class can't be directly accessed from within Buffer unless we are trying
// to access it in a debug manner (IE the drawStats menu)
struct BufferHandle
{
vk::Buffer m_buffer { VK_NULL_HANDLE };
VmaAllocation m_allocation {};
@@ -46,8 +48,6 @@ namespace fgl::engine::memory
void alloc( vk::DeviceSize memory_size );
void dealloc();
public:
BufferHandle() = delete;
BufferHandle( const BufferHandle& other ) = delete;
BufferHandle& operator=( const BufferHandle& other ) = delete;
@@ -70,8 +70,21 @@ namespace fgl::engine::memory
class Buffer
{
#ifdef TRACK_BUFFERS
//! Tracking pointer for all buffers
inline static std::vector< std::weak_ptr< BufferHandle > > m_buffer_handles {};
public:
static std::vector< std::weak_ptr< BufferHandle > > getActiveBufferHandles();
private:
#endif
//TODO: Switch this to being a non pointer if we aren't tracking
// Since we don't need this to be a pointer at all if we don't need to track anything
// It'll just be wasted pointer dereferencing
std::shared_ptr< BufferHandle > m_handle;
public:
@@ -91,8 +104,6 @@ namespace fgl::engine::memory
Buffer( Buffer&& other ) = default;
Buffer& operator=( Buffer&& other ) = default;
static std::vector< std::weak_ptr< BufferHandle > > getActiveBufferHandles();
inline vk::Buffer& getVkBuffer() noexcept { return m_handle->m_buffer; }
//! Returns the required alignment for this buffer.
@@ -104,7 +115,7 @@ namespace fgl::engine::memory
//! @brief List of all active suballocations
//! <offset, size>
std::map< vk::DeviceSize, vk::DeviceSize > m_suballocations {};
std::map< vk::DeviceSize, vk::DeviceSize > m_allocations {};
//! @brief list of any free blocks
//! @note All blocks are amalgamated to the largest they can expand to.
@@ -164,7 +175,7 @@ namespace fgl::engine::memory
* @note Alignment for atom_size is 0 if buffer is not host visible
*/
std::shared_ptr< BufferSuballocationHandle >
suballocate( vk::DeviceSize memory_size, std::uint32_t alignment = 1 );
allocate( vk::DeviceSize memory_size, std::uint32_t alignment = 1 );
//! Frees a given suballocation. After calling this the handle is invalid and accessing it is UB
void free( BufferSuballocationHandle& info );

View File

@@ -109,7 +109,7 @@ namespace fgl::engine::memory
}
BufferSuballocation::BufferSuballocation( Buffer& buffer, const vk::DeviceSize size ) :
BufferSuballocation( buffer.suballocate( size ) )
BufferSuballocation( buffer.allocate( size ) )
{}
} // namespace fgl::engine

View File

@@ -24,7 +24,7 @@ namespace fgl::engine
HostSingleT& operator=( const HostSingleT& ) = delete;
HostSingleT( memory::Buffer& buffer ) :
memory::BufferSuballocation( buffer.suballocate( sizeof( T ), alignof( T ) ) )
memory::BufferSuballocation( buffer.allocate( sizeof( T ), alignof( T ) ) )
{}
HostSingleT& operator=( T& t )

View File

@@ -11,7 +11,7 @@ namespace fgl::engine::memory
{
[[nodiscard]] BufferVector::BufferVector( Buffer& buffer, std::uint32_t count, std::uint32_t stride ) :
BufferSuballocation( buffer.suballocate( count * stride ) ),
BufferSuballocation( buffer.allocate( count * stride ) ),
m_count( count ),
m_stride( stride )
{}

View File

@@ -34,7 +34,7 @@ namespace fgl::engine
/**
* @brief Constructs a new DeviceVector from a vector using an allocation of the supplied buffer
* @param buffer buffer to suballocate from
* @param buffer buffer to allocate from
* @param data
*/
DeviceVector( memory::Buffer& buffer, const std::vector< T >& data ) :