From dfdc0dd6d63caf685f7a3df007d4f124ad749f17 Mon Sep 17 00:00:00 2001 From: Anish Bhobe Date: Sat, 21 Jun 2025 16:07:40 +0200 Subject: [PATCH] Added BufferManager and Entity Hierarchy. --- Blaze.sln | 2 +- Blaze.vcxproj | 5 + Blaze.vcxproj.filters | 21 +++++ Blaze/Blaze.cpp | 38 ++++++-- Blaze/BufferManager.cpp | 192 +++++++++++++++++++++++++++++++++++++++ Blaze/BufferManager.h | 81 +++++++++++++++++ Blaze/EntityManager.cpp | 169 ++++++++++++++++++++++++---------- Blaze/EntityManager.h | 65 +++++++++++-- Blaze/FreeList.cpp | 72 +++++++++++++++ Blaze/FreeList.h | 42 +++++++++ Blaze/RID.cpp | 0 Blaze/RID.h | 50 ++++++++++ Blaze/RenderDevice.cpp | 14 +++ Blaze/RenderDevice.h | 4 +- Blaze/TextureManager.cpp | 5 + Blaze/TextureManager.h | 154 ++----------------------------- vcpkg.json | 1 + 17 files changed, 702 insertions(+), 213 deletions(-) create mode 100644 Blaze/BufferManager.cpp create mode 100644 Blaze/BufferManager.h create mode 100644 Blaze/FreeList.cpp create mode 100644 Blaze/FreeList.h create mode 100644 Blaze/RID.cpp create mode 100644 Blaze/RID.h diff --git a/Blaze.sln b/Blaze.sln index acd7e1c..dc95fe3 100644 --- a/Blaze.sln +++ b/Blaze.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.13.36105.23 d17.13 +VisualStudioVersion = 17.13.36105.23 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Blaze", "Blaze.vcxproj", "{92E725FE-D87B-4FDE-8371-5B2CE60945FD}" EndProject diff --git a/Blaze.vcxproj b/Blaze.vcxproj index 05677ab..f5d9d1c 100644 --- a/Blaze.vcxproj +++ b/Blaze.vcxproj @@ -172,21 +172,26 @@ + + + + + diff --git a/Blaze.vcxproj.filters b/Blaze.vcxproj.filters index c297453..5183898 100644 --- a/Blaze.vcxproj.filters +++ b/Blaze.vcxproj.filters @@ -80,6 +80,18 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + @@ -112,6 +124,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + diff --git a/Blaze/Blaze.cpp b/Blaze/Blaze.cpp index 4ef4f04..036378e 100644 --- a/Blaze/Blaze.cpp +++ b/Blaze/Blaze.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -82,18 +83,19 @@ SDL_AppResult SDL_AppInit( void** appstate, int, char** ) }; Transform modelTransform = { - .position = { 0.0f, 0.0f, 0.0f }, + .position = { 0.0f, 0.0f, 2.0f }, .scale = 1.0f, .rotation = DirectX::XMQuaternionRotationAxis( DirectX::XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f ), 0.0f ), }; - for ( int i = -3; i <= 3; ++i ) + for ( int i = -1; i <= 1; ++i ) { modelTransform.position.x = static_cast( i ); appState.entityManager->createEntity( modelTransform, vertices, i > 0 ? "Assets/Textures/wall.jpg" : "Assets/Textures/container2.png" ); } + return SDL_APP_CONTINUE; } @@ -249,11 +251,9 @@ SDL_AppResult SDL_AppIterate( void* appstate ) vkCmdBindDescriptorSets( cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, misc.pipelineLayout, 1, 1, &misc.descriptorSet, 0, nullptr ); - for ( Entity const& entity : entityManager.iter() ) + std::function drawEntity = + [&]( Entity const& entity, DirectX::XMMATRIX const& parent ) { - VkDeviceSize constexpr offset = 0; - vkCmdBindVertexBuffers( cmd, 0, 1, &entity.mesh().vertexBuffer, &offset ); - Transform const& localTransform = entity.transform(); DirectX::XMMATRIX worldTransform; { @@ -261,8 +261,17 @@ SDL_AppResult SDL_AppIterate( void* appstate ) auto scale = localTransform.scale; worldTransform = DirectX::XMMatrixScaling( scale, scale, scale ) * DirectX::XMMatrixRotationQuaternion( localTransform.rotation ) * - DirectX::XMMatrixTranslation( x, y, z ); + DirectX::XMMatrixTranslation( x, y, z ) * parent; } + + VkBuffer const vertexBuffer = + renderDevice.bufferManager->fetchBuffer( entity.mesh().buffer ).value_or( nullptr ); + if ( vertexBuffer ) + { + VkDeviceSize constexpr offset = 0; + vkCmdBindVertexBuffers( cmd, 0, 1, &vertexBuffer, &offset ); + } + vkCmdPushConstants( cmd, misc.pipelineLayout, VK_SHADER_STAGE_ALL_GRAPHICS, 0, sizeof worldTransform, &worldTransform ); vkCmdPushConstants( @@ -273,6 +282,21 @@ SDL_AppResult SDL_AppIterate( void* appstate ) sizeof entity.material().texture, &entity.material().texture ); vkCmdDraw( cmd, entity.mesh().vertexCount, 1, 0, 0 ); + + for ( Entity& child : entity.children() ) + { + drawEntity( child, worldTransform ); + } + }; + + for ( Entity const& entity : entityManager.iter() ) + { + if ( not entity.isRoot() ) + { + continue; + } + + drawEntity( entity, DirectX::XMMatrixIdentity() ); } } vkCmdEndRendering( cmd ); diff --git a/Blaze/BufferManager.cpp b/Blaze/BufferManager.cpp new file mode 100644 index 0000000..b31df30 --- /dev/null +++ b/Blaze/BufferManager.cpp @@ -0,0 +1,192 @@ + +#include "BufferManager.h" + +#include "GlobalMemory.h" + +template struct RID; + +void BufferManager::destroyBuffer( Buffer& buf ) +{ + if ( not buf.buffer ) return; + + ASSERT( m_pRenderDevice ); + + uint32_t const index = buf.index; + uint32_t const innerIndex = index & INDEX_MASK; + uint32_t const generation = ( index & GENERATION_MASK ) >> GENERATION_OFFSET; + + RenderDevice const& renderDevice = *m_pRenderDevice; + + vmaDestroyBuffer( renderDevice.gpuAllocator, Take( buf.buffer ), Take( buf.allocation ) ); + + buf.size = 0; + buf.mappedData = nullptr; + buf.index = innerIndex | ( generation + 1 ) << GENERATION_OFFSET; + + // NOTE: DO NOT EDIT INNER INDEX. + ASSERT( innerIndex == ( buf.index & INDEX_MASK ) and "Index should not be modified" ); + ASSERT( buf.index > index and "Generation should increase." ); + + m_freeList.pushBack( reinterpret_cast( &buf ) ); + + --m_count; +} + +Buffer& BufferManager::fetchBufferUnchecked( BufferID const& rid ) +{ + uint32_t const index = *reinterpret_cast( &rid ); + uint32_t const innerIndex = index & INDEX_MASK; + + return m_aBuffers[innerIndex]; +} + +void BufferManager::writeToBufferImpl( BufferID const& rid, void const* data, size_t const size ) +{ + ASSERT( isValidID( rid ) ); + + Buffer const& buffer = fetchBufferUnchecked( rid ); + + ASSERT( buffer.size <= size ); + + memcpy( buffer.mappedData, data, size ); +} + +bool BufferManager::isValidID( BufferID const& rid ) const +{ + uint32_t const index = *reinterpret_cast( &rid ); + uint32_t const innerIndex = index & INDEX_MASK; + + if ( innerIndex > m_capacity ) return false; + + return m_aBuffers[innerIndex].index == index; +} + +std::optional BufferManager::createVertexBuffer( size_t const size ) +{ + if ( m_freeList.empty() ) + { + return std::nullopt; + } + + Buffer* bufferSlot = reinterpret_cast( m_freeList.popFront() ); + ++m_count; + + ASSERT( m_pRenderDevice ); + RenderDevice const& renderDevice = *m_pRenderDevice; + + + VkBufferCreateInfo const bufferCreateInfo = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .size = size, + .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + }; + + VmaAllocationCreateInfo constexpr allocationCreateInfo = { + .flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, + .usage = VMA_MEMORY_USAGE_AUTO, + .requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + .preferredFlags = 0, + .memoryTypeBits = 0, + .pool = nullptr, + .pUserData = nullptr, + .priority = 1.0f, + }; + + VmaAllocationInfo allocationInfo; + VkBuffer vertexBuffer; + VmaAllocation vertexBufferAllocation; + + VK_CHECK( vmaCreateBuffer( + renderDevice.gpuAllocator, + &bufferCreateInfo, + &allocationCreateInfo, + &vertexBuffer, + &vertexBufferAllocation, + &allocationInfo ) ); + + // NOTE: textureSlot preserves index between uses. + uint32_t index = bufferSlot->index; + new ( bufferSlot ) Buffer{ + .buffer = vertexBuffer, + .allocation = vertexBufferAllocation, + .mappedData = static_cast( allocationInfo.pMappedData ), + .size = size, + .index = index, + }; + + // NOTE: Memory hackery to create TextureID; + return std::move( *reinterpret_cast( &index ) ); +} + +void BufferManager::freeBuffer( BufferID&& rid ) +{ + if ( not isValidID( rid ) ) return; + + Buffer& buffer = fetchBufferUnchecked( rid ); + + destroyBuffer( buffer ); + + auto _ = std::move( rid ); +} + +std::optional BufferManager::fetchBuffer( BufferID const& rid ) +{ + if ( not isValidID( rid ) ) return std::nullopt; + + return fetchBufferUnchecked( rid ).buffer; +} + +BufferManager::BufferManager( RenderDevice* pRenderDevice, Buffer* aBuffers, uint32_t const capacity ) + : m_pRenderDevice{ pRenderDevice }, m_aBuffers{ aBuffers }, m_count{ 0 }, m_capacity{ capacity } +{ + uint32_t i = 0; + for ( Buffer& tex : std::span{ m_aBuffers, m_capacity } ) + { + // Default Generation is 1 + tex.index = i++ | ( 1 << GENERATION_OFFSET ); + m_freeList.pushFront( reinterpret_cast( &tex ) ); + } +} + +void BufferManager::destroy() +{ + +#if defined( _DEBUG ) + if ( m_count > 0 ) + { + SDL_LogError( SDL_LOG_CATEGORY_ERROR, "%u buffers still allocated.", m_count ); + } +#endif + + while ( not m_freeList.empty() ) + { + Buffer* buf = reinterpret_cast( m_freeList.popFront() ); + memset( buf, 0, sizeof *buf ); + } + + for ( Buffer& buf : std::span{ m_aBuffers, m_count } ) + { + destroyBuffer( buf ); + } +} + +BufferManager::~BufferManager() +{ + ASSERT( not m_aBuffers ); +} + +BufferManager* BufferManager_Create( GlobalMemory* mem, RenderDevice* renderDevice, uint32_t maxCount ) +{ + Buffer* buffers = reinterpret_cast( mem->allocate( maxCount * sizeof( Buffer ), alignof( Buffer ) ) ); + if ( not buffers ) return nullptr; + + std::byte* allocation = mem->allocate( sizeof( BufferManager ), alignof( BufferManager ) ); + if ( not allocation ) return nullptr; + + return new ( allocation ) BufferManager{ renderDevice, buffers, maxCount }; +} diff --git a/Blaze/BufferManager.h b/Blaze/BufferManager.h new file mode 100644 index 0000000..cf2f0d1 --- /dev/null +++ b/Blaze/BufferManager.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include + +#include "FreeList.h" +#include "MacroUtils.h" +#include "RID.h" +#include "RenderDevice.h" +#include "VulkanHeader.h" + +struct GlobalMemory; +struct RenderDevice; + +struct Buffer +{ + VkBuffer buffer; + VmaAllocation allocation; + std::byte* mappedData; // Assume the system has ReBAR/SAM enabled. + size_t size; + uint32_t index; +}; + +static_assert( sizeof( Buffer ) > sizeof( FreeList::Node ) and "Buffer is used intrusively by FreeList" ); +static_assert( + offsetof( Buffer, index ) >= sizeof( FreeList::Node ) and "Index should not be overwritten even in invalid state" ); + +extern template struct RID; +using BufferID = RID; + +struct BufferManager +{ +private: + constexpr static uint32_t INDEX_MASK = 0x0007FFFF; + constexpr static uint32_t GENERATION_MASK = ~INDEX_MASK; + constexpr static uint32_t GENERATION_OFFSET = 19; + static_assert( + ( ( GENERATION_MASK >> GENERATION_OFFSET & 0x1 ) == 0x1 ) and + ( ( GENERATION_MASK >> ( GENERATION_OFFSET - 1 ) & 0x1 ) != 0x1 ) and "Checks boundary" ); + + RenderDevice* m_pRenderDevice; + + // Texture Manager + Buffer* m_aBuffers; + uint32_t m_count; + uint32_t m_capacity; + FreeList m_freeList; + + void destroyBuffer( Buffer& buf ); + + Buffer& fetchBufferUnchecked( BufferID const& rid ); + void writeToBufferImpl( BufferID const& rid, void const* data, size_t size ); + +public: + [[nodiscard]] bool isValidID( BufferID const& rid ) const; + + std::optional createVertexBuffer( size_t size ); + + void freeBuffer( BufferID&& rid ); + + DEPRECATE_JULY_2025 + std::optional fetchBuffer( BufferID const& rid ); + + template + void writeToBuffer( BufferID const& rid, std::span const& data ) + { + writeToBufferImpl( rid, data.data(), data.size_bytes() ); + } + + // + BufferManager( RenderDevice* pRenderDevice, Buffer* aBuffers, uint32_t capacity ); + void destroy(); + + BufferManager( BufferManager const& other ) = delete; + BufferManager( BufferManager&& other ) noexcept = delete; + BufferManager& operator=( BufferManager const& other ) = delete; + BufferManager& operator=( BufferManager&& other ) noexcept = delete; + ~BufferManager(); +}; + +BufferManager* BufferManager_Create( GlobalMemory* mem, RenderDevice* renderDevice, uint32_t maxCount ); diff --git a/Blaze/EntityManager.cpp b/Blaze/EntityManager.cpp index 4678ad3..a9f14ee 100644 --- a/Blaze/EntityManager.cpp +++ b/Blaze/EntityManager.cpp @@ -10,6 +10,109 @@ #include "Frame.h" #include "TextureManager.h" +Entity& EntitySiblingIterable::Iterator::operator++() +{ + current = current->nextSibling(); + return *current; +} + +bool EntitySiblingIterable::Iterator::operator==( Iterator const& other ) const +{ + return current == other.current; +} + +Entity& EntitySiblingIterable::Iterator::operator*() const +{ + return *current; +} + +EntitySiblingIterable::Iterator EntitySiblingIterable::begin() +{ + return { current }; +} + +EntitySiblingIterable::Iterator EntitySiblingIterable::end() +{ + return {}; +} + + +void Entity::setParent( Entity* parent ) +{ + ASSERT( parent ); + + if ( m_parent == parent ) return; + + removeParent(); + + // Insert self into parent. + m_parent = parent; + + Entity* oldHead = parent->m_firstChild; + + if ( oldHead ) + { + // Old head is next after this + this->m_nextSibling = oldHead; + + // This is prev to old head + oldHead->m_prevSibling = this; + } + // We are the head now. + m_parent->m_firstChild = this; +} +void Entity::addChild( Entity* child ) +{ + child->setParent( this ); +} + +void Entity::removeChild( Entity* child ) +{ + ASSERT( child ); + child->removeParent(); +} + +void Entity::removeParent() +{ + if ( m_parent ) + { + + // Replace prev of next with prev of self + if ( m_nextSibling ) m_nextSibling->m_prevSibling = m_prevSibling; + + // Replace next of prev with next of self + if ( m_prevSibling ) + { + m_prevSibling->m_nextSibling = m_nextSibling; + } + else + { + // We are head of chain + m_parent->m_firstChild = m_nextSibling; + } + + m_nextSibling = nullptr; + m_prevSibling = nullptr; + m_parent = nullptr; + } +} + +EntitySiblingIterable Entity::children() const +{ + return { m_firstChild }; +} + +Entity::Entity( Transform const& transform, Mesh&& mesh, Material&& material ) + : m_transform{ transform } + , m_mesh{ std::forward( mesh ) } + , m_material{ std::forward( material ) } + , m_parent{ nullptr } + , m_firstChild{ nullptr } + , m_prevSibling{ nullptr } + , m_nextSibling{ nullptr } + , m_padding0{ 0 } +{} + Entity* EntityManager::createEntity( Transform const& transform, std::span const vertices, const char* textureFile ) { @@ -18,45 +121,16 @@ Entity* EntityManager::createEntity( Mesh mesh; { - mesh.vertexCount = static_cast( vertices.size() ); - mesh.vertexBufferSize = static_cast( vertices.size_bytes() ); + mesh.vertexCount = static_cast( vertices.size() ); - VkBufferCreateInfo const bufferCreateInfo = { - .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .size = mesh.vertexBufferSize, - .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = 0, - .pQueueFamilyIndices = nullptr, - }; - - VmaAllocationCreateInfo constexpr allocationCreateInfo = { - .flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, - .usage = VMA_MEMORY_USAGE_AUTO, - .requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, - .preferredFlags = 0, - .memoryTypeBits = 0, - .pool = nullptr, - .pUserData = nullptr, - .priority = 1.0f, - }; - - VmaAllocationInfo allocationInfo; - - VK_CHECK( vmaCreateBuffer( - pRenderDevice->gpuAllocator, - &bufferCreateInfo, - &allocationCreateInfo, - &mesh.vertexBuffer, - &mesh.vertexBufferAllocation, - &allocationInfo ) ); - - if ( allocationInfo.pMappedData ) + auto vertexBuffer = renderDevice.bufferManager->createVertexBuffer( vertices.size_bytes() ); + if ( not vertexBuffer ) { - memcpy( allocationInfo.pMappedData, vertices.data(), vertices.size_bytes() ); + return nullptr; } + mesh.buffer = std::move( vertexBuffer.value() ); + + renderDevice.bufferManager->writeToBuffer( mesh.buffer, vertices ); } Material material; @@ -78,7 +152,7 @@ Entity* EntityManager::createEntity( if ( not textureData ) { - vmaDestroyBuffer( pRenderDevice->gpuAllocator, Take( mesh.vertexBuffer ), Take( mesh.vertexBufferAllocation ) ); + renderDevice.bufferManager->freeBuffer( std::move( mesh.buffer ) ); SDL_LogError( SDL_LOG_CATEGORY_ERROR, "%s", stbi_failure_reason() ); return nullptr; } @@ -113,7 +187,7 @@ Entity* EntityManager::createEntity( auto textureOpt = renderDevice.textureManager->createTexture( { width, height, 1 }, sampler ); if ( not textureOpt ) { - vmaDestroyBuffer( pRenderDevice->gpuAllocator, Take( mesh.vertexBuffer ), Take( mesh.vertexBufferAllocation ) ); + renderDevice.bufferManager->freeBuffer( std::move( mesh.buffer ) ); SDL_LogError( SDL_LOG_CATEGORY_ERROR, "%s", stbi_failure_reason() ); stbi_image_free( textureData ); return nullptr; @@ -457,28 +531,29 @@ Entity* EntityManager::createEntity( vmaDestroyBuffer( renderDevice.gpuAllocator, stagingBuffer, stagingAllocation ); - material = { std::move( texture ), sampler }; + material = { + sampler, + std::move( texture ), + }; } - entities[count++] = Entity( transform, mesh, std::move( material ) ); + Entity& alloc = entities[count++]; + alloc = Entity( transform, std::move( mesh ), std::move( material ) ); - return entities + count; + return &alloc; } void EntityManager::destroyEntity( Entity* entity ) { ASSERT( entity ); - if ( !entity->isInit() ) return; + if ( not entity->isInit() ) return; - VkDevice const device = pRenderDevice->device; - VmaAllocator const allocator = pRenderDevice->gpuAllocator; + VkDevice const device = pRenderDevice->device; vkDestroySampler( device, Take( entity->material().sampler ), nullptr ); pRenderDevice->textureManager->freeTexture( std::move( entity->material().texture ) ); - vmaDestroyBuffer( allocator, Take( entity->mesh().vertexBuffer ), Take( entity->mesh().vertexBufferAllocation ) ); - - // TODO: Leaking descriptor set. + pRenderDevice->bufferManager->freeBuffer( std::move( entity->mesh().buffer ) ); } void EntityManager::destroy() diff --git a/Blaze/EntityManager.h b/Blaze/EntityManager.h index 2497f78..5e2aed4 100644 --- a/Blaze/EntityManager.h +++ b/Blaze/EntityManager.h @@ -7,8 +7,10 @@ #include "VulkanHeader.h" // TODO: Remove this dependency +#include "BufferManager.h" #include "TextureManager.h" +struct Entity; struct RenderDevice; struct GlobalMemory; @@ -28,16 +30,32 @@ struct Transform struct Mesh { - VkBuffer vertexBuffer; - VmaAllocation vertexBufferAllocation; - uint32_t vertexBufferSize; - uint32_t vertexCount; + BufferID buffer; + uint32_t vertexCount; }; struct Material { + VkSampler sampler; // TODO: Reuse TextureID texture; - VkSampler sampler; // TODO: Reuse + uint32_t padding0; // FIXME: Wasting space. +}; + +struct EntitySiblingIterable +{ + Entity* current; + + struct Iterator + { + Entity* current = nullptr; + + Entity& operator++(); + bool operator==( Iterator const& ) const; + Entity& operator*() const; + }; + + Iterator begin(); + Iterator end(); }; struct Entity @@ -46,6 +64,11 @@ private: Transform m_transform; Mesh m_mesh; Material m_material; + Entity* m_parent; // TODO: Switch to EntityIndex. + Entity* m_firstChild; + Entity* m_prevSibling; + Entity* m_nextSibling; + uint64_t m_padding0; // FIXME: Wasting space. public: [[nodiscard]] Transform& transform() @@ -80,12 +103,36 @@ public: [[nodiscard]] bool isInit() const { - return m_mesh.vertexBuffer or m_material.texture; + return m_mesh.buffer or m_material.texture; } - Entity( Transform const& transform, Mesh const& mesh, Material&& material ) - : m_transform{ transform }, m_mesh{ mesh }, m_material{ std::forward( material ) } - {} + [[nodiscard]] bool isRoot() const + { + return not m_parent; + } + + [[nodiscard]] Entity* parent() const + { + return m_parent; + } + + [[nodiscard]] Entity* nextSibling() const + { + return m_nextSibling; + } + + void setParent( Entity* parent ); + + void addChild( Entity* child ); + + void removeChild( Entity* child ); + + // Remove self from parent + void removeParent(); + + [[nodiscard]] EntitySiblingIterable children() const; + + Entity( Transform const& transform, Mesh&& mesh, Material&& material ); }; struct EntityManager diff --git a/Blaze/FreeList.cpp b/Blaze/FreeList.cpp new file mode 100644 index 0000000..4cea0a3 --- /dev/null +++ b/Blaze/FreeList.cpp @@ -0,0 +1,72 @@ + +#include "FreeList.h" + +FreeList::Iterator& FreeList::Iterator::operator++() +{ + pIter = pIter->pNext; + return *this; +} + +bool FreeList::Iterator::operator==( Iterator const& other ) const +{ + return this->pIter == other.pIter; +} + +FreeList::Node& FreeList::Iterator::operator*() +{ + return *pIter; +} + +FreeList::FreeList() : m_head{ .pNext = &m_tail, .pPrev = nullptr }, m_tail{ .pNext = nullptr, .pPrev = &m_head } +{} + +void FreeList::pushBack( Node* pNode ) +{ + Node* prev = m_tail.pPrev; + + // Set prev as previous of pNode + prev->pNext = pNode; + pNode->pPrev = prev; + + // Set tail as next of pNode + pNode->pNext = &m_tail; + m_tail.pPrev = pNode; +} + +void FreeList::pushFront( Node* pNode ) +{ + Node* next = m_head.pNext; + + // Set next as next of pNode + next->pPrev = pNode; + pNode->pNext = next; + + // Set head as prev of pNode + pNode->pPrev = &m_head; + m_head.pNext = pNode; +} + +FreeList::Node* FreeList::popFront() +{ + ASSERT( not empty() ); + + Node* element = m_head.pNext; + element->pPrev->pNext = element->pNext; + element->pNext->pPrev = element->pPrev; + return element; +} + +bool FreeList::empty() const +{ + return m_head.pNext == &m_tail; +} + +FreeList::Iterator FreeList::begin() +{ + return { m_head.pNext }; +} + +FreeList::Iterator FreeList::end() +{ + return { &m_tail }; +} diff --git a/Blaze/FreeList.h b/Blaze/FreeList.h new file mode 100644 index 0000000..e7cd2e9 --- /dev/null +++ b/Blaze/FreeList.h @@ -0,0 +1,42 @@ +#pragma once +#include "MacroUtils.h" + +struct FreeList +{ + struct Node + { + Node* pNext; + Node* pPrev; + }; + + struct Iterator + { + Node* pIter; + + Iterator& operator++(); + bool operator==( Iterator const& other ) const; + Node& operator*(); + }; + +private: + Node m_head; + Node m_tail; + +public: + FreeList(); + + void pushBack( Node* pNode ); + void pushFront( Node* pNode ); + Node* popFront(); + [[nodiscard]] bool empty() const; + + Iterator begin(); + Iterator end(); + + FreeList( FreeList&& ) = delete; + FreeList( FreeList const& ) = delete; + FreeList& operator=( FreeList const& ) = delete; + FreeList& operator=( FreeList&& ) = delete; + + ~FreeList() = default; +}; diff --git a/Blaze/RID.cpp b/Blaze/RID.cpp new file mode 100644 index 0000000..e69de29 diff --git a/Blaze/RID.h b/Blaze/RID.h new file mode 100644 index 0000000..cc4774e --- /dev/null +++ b/Blaze/RID.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +template +struct RID +{ +private: + uint32_t m_index = 0; + + explicit RID( uint32_t const index ) : m_index{ index } + {} + +public: + RID() = default; + + // No copy + RID( RID const& ) = delete; + RID& operator=( RID const& ) = delete; + + // Move allowed + RID( RID&& other ) noexcept; + RID& operator=( RID&& other ) noexcept; + + static RID null() + { + return {}; + } + + operator bool() const + { + return m_index != 0; + } +}; + +template +RID::RID( RID&& other ) noexcept : m_index{ other.m_index } +{ + other.m_index = 0; +} + +template +RID& RID::operator=( RID&& other ) noexcept +{ + if ( this == &other ) return *this; + + m_index = other.m_index; + other.m_index = 0; + return *this; +} diff --git a/Blaze/RenderDevice.cpp b/Blaze/RenderDevice.cpp index d95993c..5a4e029 100644 --- a/Blaze/RenderDevice.cpp +++ b/Blaze/RenderDevice.cpp @@ -8,6 +8,7 @@ #include #include +#include "BufferManager.h" #include "Frame.h" #include "GlobalMemory.h" #include "MathUtil.h" @@ -424,6 +425,18 @@ RenderDevice* RenderDevice_Create( GlobalMemory* mem, RenderDevice::CreateInfo c ASSERT( renderDevice->textureManager ); + BufferManager* bufferManager = BufferManager_Create( mem, renderDevice, 10000 ); + if ( !bufferManager ) + { + SDL_LogError( SDL_LOG_CATEGORY_APPLICATION, "BufferManager failed to init" ); + renderDevice->destroy(); + return nullptr; + } + + renderDevice->bufferManager = bufferManager; + + ASSERT( renderDevice->bufferManager ); + return renderDevice; } @@ -436,6 +449,7 @@ void RenderDevice::destroy() { if ( not isInit() ) return; + Take( bufferManager )->destroy(); Take( textureManager )->destroy(); for ( Frame& frame : std::span{ Take( frames ), swapchainImageCount } ) diff --git a/Blaze/RenderDevice.h b/Blaze/RenderDevice.h index 95c1564..db54843 100644 --- a/Blaze/RenderDevice.h +++ b/Blaze/RenderDevice.h @@ -3,9 +3,10 @@ #include #include -#include "TextureManager.h" #include "VulkanHeader.h" + +struct BufferManager; struct GlobalMemory; struct Frame; struct TextureManager; @@ -42,6 +43,7 @@ struct RenderDevice uint32_t frameIndex = 0; TextureManager* textureManager; + BufferManager* bufferManager; [[nodiscard]] bool isInit() const; void destroy(); diff --git a/Blaze/TextureManager.cpp b/Blaze/TextureManager.cpp index 3cb1917..29e4d17 100644 --- a/Blaze/TextureManager.cpp +++ b/Blaze/TextureManager.cpp @@ -1,9 +1,12 @@ #include "TextureManager.h" +#include "FreeList.h" #include "GlobalMemory.h" #include "RenderDevice.h" +template struct RID; + std::optional TextureManager::createTexture( VkExtent3D const extent, VkSampler sampler ) { if ( m_freeList.empty() ) @@ -12,6 +15,7 @@ std::optional TextureManager::createTexture( VkExtent3D const extent, } Texture* textureSlot = reinterpret_cast( m_freeList.popFront() ); + ++m_count; ASSERT( m_pRenderDevice ); RenderDevice const& renderDevice = *m_pRenderDevice; @@ -226,6 +230,7 @@ void TextureManager::destroyTexture( Texture& tex ) ASSERT( tex.index > index and "Generation should increase." ); m_freeList.pushBack( reinterpret_cast( &tex ) ); + --m_count; } uint32_t TextureManager::calculateRequiredMipLevels( uint32_t const w, uint32_t const h, uint32_t const d ) diff --git a/Blaze/TextureManager.h b/Blaze/TextureManager.h index 75e6457..7f787e9 100644 --- a/Blaze/TextureManager.h +++ b/Blaze/TextureManager.h @@ -3,159 +3,15 @@ #include #include +#include "FreeList.h" #include "MacroUtils.h" +#include "RID.h" #include "RenderDevice.h" #include "VulkanHeader.h" - struct GlobalMemory; struct RenderDevice; -struct FreeListNode -{ - FreeListNode* pNext; - FreeListNode* pPrev; -}; - -struct FreeList -{ - using Node = FreeListNode; - - struct Iterator - { - FreeListNode* pIter; - - Iterator& operator++() - { - pIter = pIter->pNext; - return *this; - } - - bool operator==( Iterator const& other ) const - { - return this->pIter == other.pIter; - } - - FreeListNode& operator*() - { - return *pIter; - } - }; - -private: - FreeListNode m_head; - FreeListNode m_tail; - -public: - FreeList() : m_head{ .pNext = &m_tail, .pPrev = nullptr }, m_tail{ .pNext = nullptr, .pPrev = &m_head } - {} - - void pushBack( Node* pNode ) - { - Node* prev = m_tail.pPrev; - - // Set prev as previous of pNode - prev->pNext = pNode; - pNode->pPrev = prev; - - // Set tail as next of pNode - pNode->pNext = &m_tail; - m_tail.pPrev = pNode; - } - - void pushFront( Node* pNode ) - { - Node* next = m_head.pNext; - - // Set next as next of pNode - next->pPrev = pNode; - pNode->pNext = next; - - // Set head as prev of pNode - pNode->pPrev = &m_head; - m_head.pNext = pNode; - } - - Node* popFront() - { - ASSERT( not empty() ); - - Node* element = m_head.pNext; - element->pPrev->pNext = element->pNext; - element->pNext->pPrev = element->pPrev; - return element; - } - - [[nodiscard]] bool empty() const - { - return m_head.pNext == &m_tail; - } - - Iterator begin() - { - return { m_head.pNext }; - } - - Iterator end() - { - return { &m_tail }; - } - - FreeList( FreeList&& ) = delete; - FreeList( FreeList const& ) = delete; - FreeList& operator=( FreeList const& ) = delete; - FreeList& operator=( FreeList&& ) = delete; - - ~FreeList() = default; -}; - -template -struct RID -{ -private: - uint32_t m_index = 0; - - explicit RID( uint32_t const index ) : m_index{ index } - {} - -public: - RID() = default; - - // No copy - RID( RID const& ) = delete; - RID& operator=( RID const& ) = delete; - - // Move allowed - RID( RID&& other ) noexcept; - RID& operator=( RID&& other ) noexcept; - - static RID null() - { - return {}; - } - - operator bool() const - { - return m_index == 0; - } -}; - -template -RID::RID( RID&& other ) noexcept : m_index{ other.m_index } -{ - other.m_index = 0; -} - -template -RID& RID::operator=( RID&& other ) noexcept -{ - if ( this == &other ) return *this; - - m_index = other.m_index; - other.m_index = 0; - return *this; -} - struct Texture { VkImage image; @@ -166,10 +22,12 @@ struct Texture uint32_t index; }; -static_assert( sizeof( Texture ) > sizeof( FreeListNode ) and "Texture is used intrusively by FreeList" ); +static_assert( sizeof( Texture ) > sizeof( FreeList::Node ) and "Texture is used intrusively by FreeList" ); static_assert( - offsetof( Texture, index ) >= sizeof( FreeListNode ) and "Index should not be overwritten even in invalid state" ); + offsetof( Texture, index ) >= sizeof( FreeList::Node ) and + "Index should not be overwritten even in invalid state" ); +extern template struct RID; using TextureID = RID; struct TextureManager diff --git a/vcpkg.json b/vcpkg.json index 89d99fc..a5f424b 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -4,6 +4,7 @@ "shader-slang", "vulkan-memory-allocator", "directxmath", + "cgltf", "stb", { "name": "sdl3",