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",