From 0e559493092f98828b5dbf98d40250df0dc73cad Mon Sep 17 00:00:00 2001 From: Anish Bhobe Date: Tue, 17 Jun 2025 22:10:54 +0200 Subject: [PATCH] Fully bindless textures. --- Assets/Shaders/Bindless.slang | 25 ++++++ Assets/Shaders/Mesh.slang | 14 ++-- Blaze.vcxproj | 1 + Blaze.vcxproj.filters | 3 + Blaze/Blaze.cpp | 51 ++++++------ Blaze/EntityManager.cpp | 68 ++++------------ Blaze/EntityManager.h | 21 ++--- Blaze/MiscData.cpp | 37 +++------ Blaze/MiscData.h | 42 +++++----- Blaze/RenderDevice.cpp | 19 ++++- Blaze/TextureManager.cpp | 144 ++++++++++++++++++++++++++++++---- Blaze/TextureManager.h | 76 ++++++++++++++---- 12 files changed, 325 insertions(+), 176 deletions(-) create mode 100644 Assets/Shaders/Bindless.slang diff --git a/Assets/Shaders/Bindless.slang b/Assets/Shaders/Bindless.slang new file mode 100644 index 0000000..6489acb --- /dev/null +++ b/Assets/Shaders/Bindless.slang @@ -0,0 +1,25 @@ +[vk::binding(0, 0)] uniform __DynamicResource<__DynamicResourceKind.Sampler> gTextures[]; + +public struct RID where T : IOpaqueDescriptor { + private uint internal; + + private const static uint INDEX_MASK = 0x0007FFFF; + private const static uint GENERATION_MASK = ~INDEX_MASK; + + public property bool hasValue { + get { + return (internal & GENERATION_MASK) > 0; + } + } + + public property T value { + get { + // TODO: Check if has value else placeholder. + return gTextures[internal & INDEX_MASK].asOpaqueDescriptor(); + } + } +} + +public extension T where T: IOpaqueDescriptor { + public typealias RID = RID; +} \ No newline at end of file diff --git a/Assets/Shaders/Mesh.slang b/Assets/Shaders/Mesh.slang index 95784fb..118c001 100644 --- a/Assets/Shaders/Mesh.slang +++ b/Assets/Shaders/Mesh.slang @@ -1,3 +1,4 @@ +import Bindless; struct VertexOut { float4 outPosition : SV_Position; @@ -11,14 +12,11 @@ struct CameraData { float4x4 proj; }; -struct PerFrameData { -} - -[vk::binding(0, 0)] uniform ConstantBuffer camera; -[vk::binding(0, 1)] uniform Sampler2D texture; +uniform ParameterBlock camera; struct PerInstanceData { float4x4 transform; + Sampler2D.RID textureID; } [[vk::push_constant]] @@ -45,6 +43,10 @@ float4 FragmentMain( float4 interpolatedColors : CoarseColor, float2 uv0 : TexCoord0, ) : SV_Target0 { - return float4(texture.Sample(uv0).rgb, 1.0f) * interpolatedColors; + if (let texture = pcb.textureID) { + return float4(texture.Sample(uv0).rgb, 1.0f) * interpolatedColors; + } else { + return interpolatedColors; + } } diff --git a/Blaze.vcxproj b/Blaze.vcxproj index c3171e5..ba571d0 100644 --- a/Blaze.vcxproj +++ b/Blaze.vcxproj @@ -161,6 +161,7 @@ Compiling %(Filename).slang %(Filename).spv + diff --git a/Blaze.vcxproj.filters b/Blaze.vcxproj.filters index 317fcb2..80be8b5 100644 --- a/Blaze.vcxproj.filters +++ b/Blaze.vcxproj.filters @@ -45,6 +45,9 @@ Resource Files\Config + + Resource Files\Shader Files + diff --git a/Blaze/Blaze.cpp b/Blaze/Blaze.cpp index c59b11c..24f30ba 100644 --- a/Blaze/Blaze.cpp +++ b/Blaze/Blaze.cpp @@ -45,13 +45,6 @@ SDL_AppResult SDL_AppInit( void** appstate, int, char** ) // TODO: Integrate this - // Model Setup - - // modelTransform[1].position = { -1.0f, 0.0f, 0.0f }; - // modelTransform[1].scale = 1.0f; - // modelTransform[1].rotation = - // DirectX::XMQuaternionRotationAxis( DirectX::XMVectorSet( 1.0f, 0.0f, 0.0f, 0.0f ), 0.0f ); - // TL----TR // | \ | // | \ | @@ -89,7 +82,7 @@ SDL_AppResult SDL_AppInit( void** appstate, int, char** ) }; Transform modelTransform = { - .position = { 1.0f, 0.0f, 0.0f }, + .position = { 0.0f, 0.0f, 0.0f }, .scale = 1.0f, .rotation = DirectX::XMQuaternionRotationAxis( DirectX::XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f ), 0.0f ), }; @@ -98,11 +91,7 @@ SDL_AppResult SDL_AppInit( void** appstate, int, char** ) { modelTransform.position.x = static_cast( i ); appState.entityManager->createEntity( - modelTransform, - vertices, - "Assets/Textures/wall.jpg", - appState.miscData->descriptorSetLayout[1], - appState.miscData->descriptorPool ); + modelTransform, vertices, i > 0 ? "Assets/Textures/wall.jpg" : "Assets/Textures/container2.png" ); } return SDL_APP_CONTINUE; @@ -130,8 +119,7 @@ SDL_AppResult SDL_AppIterate( void* appstate ) { double deltaTimeMs = deltaTime * 1000.0; double fps = 1.0 / deltaTime; - ( void )sprintf_s<256>( - appState.sprintfBuffer, "%.2f fps %.5fms %llu -> %llu", fps, deltaTimeMs, previousCounter, currentCounter ); + ( void )sprintf_s<256>( appState.sprintfBuffer, "%.2f fps %.2f ms", fps, deltaTimeMs ); SDL_SetWindowTitle( appState.window, appState.sprintfBuffer ); } @@ -222,22 +210,24 @@ SDL_AppResult SDL_AppIterate( void* appstate ) // Render Something? vkCmdBindPipeline( cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, misc.meshPipeline ); + + vkCmdBindDescriptorSets( + cmd, + VK_PIPELINE_BIND_POINT_GRAPHICS, + misc.pipelineLayout, + 0, + 1, + &renderDevice.textureManager->descriptorSet(), + 0, + nullptr ); + + vkCmdBindDescriptorSets( + cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, misc.pipelineLayout, 1, 1, &misc.descriptorSet, 0, nullptr ); + for ( Entity const& entity : entityManager.iter() ) { VkDeviceSize constexpr offset = 0; vkCmdBindVertexBuffers( cmd, 0, 1, &entity.mesh().vertexBuffer, &offset ); - vkCmdBindDescriptorSets( - cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, misc.pipelineLayout, 0, 1, &misc.descriptorSet, 0, nullptr ); - - vkCmdBindDescriptorSets( - cmd, - VK_PIPELINE_BIND_POINT_GRAPHICS, - misc.pipelineLayout, - 1, - 1, - &entity.material().descriptorSet, - 0, - nullptr ); Transform const& localTransform = entity.transform(); DirectX::XMMATRIX worldTransform; @@ -250,6 +240,13 @@ SDL_AppResult SDL_AppIterate( void* appstate ) } vkCmdPushConstants( cmd, misc.pipelineLayout, VK_SHADER_STAGE_ALL_GRAPHICS, 0, sizeof worldTransform, &worldTransform ); + vkCmdPushConstants( + cmd, + misc.pipelineLayout, + VK_SHADER_STAGE_ALL_GRAPHICS, + sizeof worldTransform, + sizeof entity.material().texture, + &entity.material().texture ); vkCmdDraw( cmd, entity.mesh().vertexCount, 1, 0, 0 ); } } diff --git a/Blaze/EntityManager.cpp b/Blaze/EntityManager.cpp index 262b3bc..4678ad3 100644 --- a/Blaze/EntityManager.cpp +++ b/Blaze/EntityManager.cpp @@ -11,11 +11,7 @@ #include "TextureManager.h" Entity* EntityManager::createEntity( - Transform const& transform, - std::span const vertices, - const char* textureFile, - VkDescriptorSetLayout layout, - VkDescriptorPool pool ) + Transform const& transform, std::span const vertices, const char* textureFile ) { ASSERT( pRenderDevice ); RenderDevice& renderDevice = *pRenderDevice; @@ -91,17 +87,6 @@ Entity* EntityManager::createEntity( height = static_cast( h ); } - auto textureOpt = renderDevice.textureManager->createTexture( { width, height, 1 } ); - if ( not textureOpt ) - { - vmaDestroyBuffer( pRenderDevice->gpuAllocator, Take( mesh.vertexBuffer ), Take( mesh.vertexBufferAllocation ) ); - SDL_LogError( SDL_LOG_CATEGORY_ERROR, "%s", stbi_failure_reason() ); - stbi_image_free( textureData ); - return nullptr; - } - TextureID texture = textureOpt.value(); - VkImage textureImage = renderDevice.textureManager->fetchImage( texture ).value(); - VkSamplerCreateInfo constexpr samplerCreateInfo = { .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .pNext = nullptr, @@ -125,6 +110,18 @@ Entity* EntityManager::createEntity( VK_CHECK( vkCreateSampler( renderDevice.device, &samplerCreateInfo, nullptr, &sampler ) ); + auto textureOpt = renderDevice.textureManager->createTexture( { width, height, 1 }, sampler ); + if ( not textureOpt ) + { + vmaDestroyBuffer( pRenderDevice->gpuAllocator, Take( mesh.vertexBuffer ), Take( mesh.vertexBufferAllocation ) ); + SDL_LogError( SDL_LOG_CATEGORY_ERROR, "%s", stbi_failure_reason() ); + stbi_image_free( textureData ); + return nullptr; + } + + TextureID texture = std::move( textureOpt.value() ); + VkImage textureImage = renderDevice.textureManager->fetchImage( texture ).value(); + // Staging Buffer Create VkBuffer stagingBuffer; VmaAllocation stagingAllocation; @@ -460,43 +457,10 @@ Entity* EntityManager::createEntity( vmaDestroyBuffer( renderDevice.gpuAllocator, stagingBuffer, stagingAllocation ); - VkDescriptorSetAllocateInfo const descriptorSetAllocateInfo = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, - .pNext = nullptr, - .descriptorPool = pool, - .descriptorSetCount = 1, - .pSetLayouts = &layout, - }; - - VkDescriptorSet descriptorSet; - VK_CHECK( vkAllocateDescriptorSets( renderDevice.device, &descriptorSetAllocateInfo, &descriptorSet ) ); - - VkImageView textureView = renderDevice.textureManager->fetchImageView( texture ).value(); - - VkDescriptorImageInfo const descriptorImageInfo = { - .sampler = sampler, - .imageView = textureView, - .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - }; - VkWriteDescriptorSet writeDescriptorSet{ - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .pNext = nullptr, - .dstSet = descriptorSet, - .dstBinding = 0, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .pImageInfo = &descriptorImageInfo, - .pBufferInfo = nullptr, - .pTexelBufferView = nullptr, - }; - - vkUpdateDescriptorSets( renderDevice.device, 1, &writeDescriptorSet, 0, nullptr ); - - material = { texture, sampler, descriptorSet }; + material = { std::move( texture ), sampler }; } - entities[count++] = Entity( transform, mesh, material ); + entities[count++] = Entity( transform, mesh, std::move( material ) ); return entities + count; } @@ -511,7 +475,7 @@ void EntityManager::destroyEntity( Entity* entity ) vkDestroySampler( device, Take( entity->material().sampler ), nullptr ); - pRenderDevice->textureManager->freeTexture( entity->material().texture ); + pRenderDevice->textureManager->freeTexture( std::move( entity->material().texture ) ); vmaDestroyBuffer( allocator, Take( entity->mesh().vertexBuffer ), Take( entity->mesh().vertexBufferAllocation ) ); // TODO: Leaking descriptor set. diff --git a/Blaze/EntityManager.h b/Blaze/EntityManager.h index 9e47ee8..558e1e1 100644 --- a/Blaze/EntityManager.h +++ b/Blaze/EntityManager.h @@ -39,9 +39,8 @@ struct Mesh struct Material { - TextureID texture; - VkSampler sampler; // TODO: Reuse - VkDescriptorSet descriptorSet; + TextureID texture; + VkSampler sampler; // TODO: Reuse }; struct Entity @@ -87,8 +86,8 @@ public: return m_mesh.vertexBuffer or m_material.texture; } - Entity( Transform const& transform, Mesh const& mesh, Material const& material ) - : m_transform{ transform }, m_mesh{ mesh }, m_material{ material } + Entity( Transform const& transform, Mesh const& mesh, Material&& material ) + : m_transform{ transform }, m_mesh{ mesh }, m_material{ std::forward( material ) } {} }; @@ -130,17 +129,11 @@ struct EntityManager } // Make Entities return ID, make it a sparse indexing system. - // TODO: Remove the descriptor pool dependency. - Entity* createEntity( - Transform const& transform, - std::span vertices, - const char* textureFile, - VkDescriptorSetLayout layout, - VkDescriptorPool pool ); + Entity* createEntity( Transform const& transform, std::span vertices, const char* textureFile ); - void destroyEntity( Entity* entity ); + void destroyEntity( Entity* entity ); - void destroy(); + void destroy(); ~EntityManager(); }; diff --git a/Blaze/MiscData.cpp b/Blaze/MiscData.cpp index d10ba1b..3bf7473 100644 --- a/Blaze/MiscData.cpp +++ b/Blaze/MiscData.cpp @@ -47,14 +47,6 @@ bool MiscData::init( RenderDevice const& renderDevice ) .pImmutableSamplers = nullptr, }; - VkDescriptorSetLayoutBinding constexpr perMaterialDescriptorBinding{ - .binding = 0, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, - .pImmutableSamplers = nullptr, - }; - VkDescriptorSetLayoutCreateInfo perFrameDescriptorSetLayoutCreateInfo = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .pNext = nullptr, @@ -62,30 +54,26 @@ bool MiscData::init( RenderDevice const& renderDevice ) .bindingCount = 1, .pBindings = &perFrameDescriptorBinding, }; - VK_CHECK( vkCreateDescriptorSetLayout( - device, &perFrameDescriptorSetLayoutCreateInfo, nullptr, &descriptorSetLayout[0] ) ); - VkDescriptorSetLayoutCreateInfo perMaterialDescriptorSetLayoutCreateInfo = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .bindingCount = 1, - .pBindings = &perMaterialDescriptorBinding, - }; - VK_CHECK( vkCreateDescriptorSetLayout( - device, &perMaterialDescriptorSetLayoutCreateInfo, nullptr, &descriptorSetLayout[1] ) ); + VK_CHECK( + vkCreateDescriptorSetLayout( device, &perFrameDescriptorSetLayoutCreateInfo, nullptr, &descriptorSetLayout ) ); VkPushConstantRange const pushConstantRange = { .stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS, .offset = 0, - .size = sizeof( DirectX::XMMATRIX ), + .size = sizeof( DirectX::XMMATRIX ) + sizeof( uint32_t ), + }; + + std::array const descriptorSetLayouts = { + renderDevice.textureManager->descriptorLayout(), + descriptorSetLayout, }; VkPipelineLayoutCreateInfo const pipelineLayoutCreateInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .pNext = nullptr, .flags = 0, - .setLayoutCount = static_cast( descriptorSetLayout.size() ), - .pSetLayouts = descriptorSetLayout.data(), + .setLayoutCount = static_cast( descriptorSetLayouts.size() ), + .pSetLayouts = descriptorSetLayouts.data(), .pushConstantRangeCount = 1, .pPushConstantRanges = &pushConstantRange, }; @@ -364,7 +352,7 @@ bool MiscData::init( RenderDevice const& renderDevice ) .pNext = nullptr, .descriptorPool = descriptorPool, .descriptorSetCount = 1, - .pSetLayouts = &descriptorSetLayout[0], + .pSetLayouts = &descriptorSetLayout, }; VK_CHECK( vkAllocateDescriptorSets( device, &descriptorSetAllocateInfo, &descriptorSet ) ); @@ -469,6 +457,5 @@ void MiscData::destroy( RenderDevice const& renderDevice ) vkDestroyPipeline( device, Take( meshPipeline ), nullptr ); vkDestroyPipelineLayout( device, Take( pipelineLayout ), nullptr ); - vkDestroyDescriptorSetLayout( device, Take( descriptorSetLayout[1] ), nullptr ); - vkDestroyDescriptorSetLayout( device, Take( descriptorSetLayout[0] ), nullptr ); + vkDestroyDescriptorSetLayout( device, Take( descriptorSetLayout ), nullptr ); } diff --git a/Blaze/MiscData.h b/Blaze/MiscData.h index 70e7642..04a5d50 100644 --- a/Blaze/MiscData.h +++ b/Blaze/MiscData.h @@ -18,30 +18,28 @@ struct MiscData DirectX::XMMATRIX projectionMatrix; }; - uint64_t previousCounter; + uint64_t previousCounter; - std::array descriptorSetLayout; - VkPipelineLayout pipelineLayout; - VkPipeline meshPipeline; + VkDescriptorSetLayout descriptorSetLayout; + VkPipelineLayout pipelineLayout; + VkPipeline meshPipeline; - uint64_t _padding; // TODO: Optimize out? + DirectX::XMVECTOR cameraPosition; + DirectX::XMVECTOR cameraTarget; + DirectX::XMVECTOR cameraUp; + CameraData cameraData; + VkBuffer cameraUniformBuffer; + VmaAllocation cameraUniformBufferAllocation; + size_t cameraUniformBufferSize; + uint8_t* cameraUniformBufferPtr; + VkDescriptorPool descriptorPool; + VkDescriptorSet descriptorSet; - DirectX::XMVECTOR cameraPosition; - DirectX::XMVECTOR cameraTarget; - DirectX::XMVECTOR cameraUp; - CameraData cameraData; - VkBuffer cameraUniformBuffer; - VmaAllocation cameraUniformBufferAllocation; - size_t cameraUniformBufferSize; - uint8_t* cameraUniformBufferPtr; - VkDescriptorPool descriptorPool; - VkDescriptorSet descriptorSet; + VkImageMemoryBarrier2 acquireToRenderBarrier; + VkDependencyInfo acquireToRenderDependency; + VkImageMemoryBarrier2 renderToPresentBarrier; + VkDependencyInfo renderToPresentDependency; - VkImageMemoryBarrier2 acquireToRenderBarrier; - VkDependencyInfo acquireToRenderDependency; - VkImageMemoryBarrier2 renderToPresentBarrier; - VkDependencyInfo renderToPresentDependency; - - bool init( RenderDevice const& renderDevice ); - void destroy( RenderDevice const& renderDevice ); + bool init( RenderDevice const& renderDevice ); + void destroy( RenderDevice const& renderDevice ); }; diff --git a/Blaze/RenderDevice.cpp b/Blaze/RenderDevice.cpp index 41a6bf2..28820c1 100644 --- a/Blaze/RenderDevice.cpp +++ b/Blaze/RenderDevice.cpp @@ -171,13 +171,28 @@ RenderDevice* RenderDevice_Create( GlobalMemory* mem, RenderDevice::CreateInfo c .pQueuePriorities = &priority, }; - VkPhysicalDeviceVulkan13Features constexpr features13 = { + VkPhysicalDeviceVulkan13Features features13 = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, .pNext = nullptr, .synchronization2 = true, .dynamicRendering = true, }; + VkPhysicalDeviceVulkan12Features const features12 = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, + .pNext = &features13, + .descriptorIndexing = true, + .shaderSampledImageArrayNonUniformIndexing = true, + .shaderStorageImageArrayNonUniformIndexing = true, + .descriptorBindingUniformBufferUpdateAfterBind = true, + .descriptorBindingSampledImageUpdateAfterBind = true, + .descriptorBindingStorageImageUpdateAfterBind = true, + .descriptorBindingUpdateUnusedWhilePending = true, + .descriptorBindingPartiallyBound = true, + .descriptorBindingVariableDescriptorCount = true, + .runtimeDescriptorArray = true, + }; + VkPhysicalDeviceFeatures features = { .depthClamp = true, .samplerAnisotropy = true, @@ -187,7 +202,7 @@ RenderDevice* RenderDevice_Create( GlobalMemory* mem, RenderDevice::CreateInfo c VkDeviceCreateInfo const deviceCreateInfo = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, - .pNext = &features13, + .pNext = &features12, .flags = 0, .queueCreateInfoCount = 1, .pQueueCreateInfos = &queueCreateInfo, diff --git a/Blaze/TextureManager.cpp b/Blaze/TextureManager.cpp index 9c786e3..3cb1917 100644 --- a/Blaze/TextureManager.cpp +++ b/Blaze/TextureManager.cpp @@ -4,7 +4,7 @@ #include "GlobalMemory.h" #include "RenderDevice.h" -std::optional TextureManager::createTexture( VkExtent3D const extent ) +std::optional TextureManager::createTexture( VkExtent3D const extent, VkSampler sampler ) { if ( m_freeList.empty() ) { @@ -95,13 +95,37 @@ std::optional TextureManager::createTexture( VkExtent3D const extent .index = index, }; + uint32_t const innerIndex = index & INDEX_MASK; + + // TODO: Batch all writes. + VkDescriptorImageInfo const descriptorImageInfo = { + .sampler = sampler, + .imageView = textureView, + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }; + + VkWriteDescriptorSet const descriptorWrite = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext = nullptr, + .dstSet = m_descriptorSet, + .dstBinding = 0, + .dstArrayElement = innerIndex, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .pImageInfo = &descriptorImageInfo, + .pBufferInfo = nullptr, + .pTexelBufferView = nullptr, + }; + + vkUpdateDescriptorSets( renderDevice.device, 1, &descriptorWrite, 0, nullptr ); + // NOTE: Memory hackery to create TextureID; - return *reinterpret_cast( &index ); + return std::move( *reinterpret_cast( &index ) ); } -bool TextureManager::isValidID( TextureID rid ) const +bool TextureManager::isValidID( TextureID const& rid ) const { - uint32_t const index = *reinterpret_cast( &rid ); + uint32_t const index = *reinterpret_cast( &rid ); uint32_t const innerIndex = index & INDEX_MASK; if ( innerIndex > m_capacity ) return false; @@ -109,29 +133,41 @@ bool TextureManager::isValidID( TextureID rid ) const return m_aTextures[innerIndex].index == index; } -void TextureManager::freeTexture( TextureID const rid ) +void TextureManager::freeTexture( TextureID&& rid ) { if ( not isValidID( rid ) ) return; Texture& texture = fetchTextureUnchecked( rid ); destroyTexture( texture ); + + auto _ = std::move( rid ); } -std::optional TextureManager::fetchImage( TextureID const rid ) +std::optional TextureManager::fetchImage( TextureID const& rid ) { if ( not isValidID( rid ) ) return std::nullopt; return fetchTextureUnchecked( rid ).image; } -std::optional TextureManager::fetchImageView( TextureID const rid ) +std::optional TextureManager::fetchImageView( TextureID const& rid ) { if ( not isValidID( rid ) ) return std::nullopt; return fetchTextureUnchecked( rid ).view; } +VkDescriptorSetLayout const& TextureManager::descriptorLayout() const +{ + return m_descriptorSetLayout; +} + +VkDescriptorSet const& TextureManager::descriptorSet() const +{ + return m_descriptorSet; +} + void TextureManager::destroy() { #if defined( _DEBUG ) @@ -141,6 +177,9 @@ void TextureManager::destroy() } #endif + ASSERT( m_pRenderDevice ); + RenderDevice const& renderDevice = *m_pRenderDevice; + while ( not m_freeList.empty() ) { Texture* tex = reinterpret_cast( m_freeList.popFront() ); @@ -151,7 +190,12 @@ void TextureManager::destroy() { destroyTexture( tex ); } + + m_descriptorSet = nullptr; + vkDestroyDescriptorPool( renderDevice.device, Take( m_descriptorPool ), nullptr ); + vkDestroyDescriptorSetLayout( renderDevice.device, Take( m_descriptorSetLayout ), nullptr ); } + TextureManager::~TextureManager() { ASSERT( not m_aTextures ); @@ -190,34 +234,106 @@ uint32_t TextureManager::calculateRequiredMipLevels( uint32_t const w, uint32_t return 1 + static_cast( floorf( log2f( static_cast( maxDim ) ) ) ); } -Texture& TextureManager::fetchTextureUnchecked( TextureID rid ) +Texture& TextureManager::fetchTextureUnchecked( TextureID const& rid ) { - uint32_t const index = *reinterpret_cast( &rid ); + uint32_t const index = *reinterpret_cast( &rid ); uint32_t const innerIndex = index & INDEX_MASK; return m_aTextures[innerIndex]; } -TextureManager::TextureManager( RenderDevice* pRenderDevice, Texture* aTextures, uint32_t const capacity ) - : m_pRenderDevice{ pRenderDevice }, m_aTextures{ aTextures }, m_count{ 0 }, m_capacity{ capacity } + +TextureManager::TextureManager( + RenderDevice* pRenderDevice, + Texture* aTextures, + uint32_t const capacity, + VkDescriptorSetLayout const setLayout, + VkDescriptorPool const pool, + VkDescriptorSet const descriptorSet ) + : m_pRenderDevice{ pRenderDevice } + , m_aTextures{ aTextures } + , m_count{ 0 } + , m_capacity{ capacity } + , m_descriptorSetLayout{ setLayout } + , m_descriptorPool{ pool } + , m_descriptorSet{ descriptorSet } { uint32_t i = 0; for ( Texture& tex : std::span{ m_aTextures, m_capacity } ) { // Default Generation is 1 - // TODO: Fix this by creating 0,0 as a valid texture. tex.index = i++ | ( 1 << GENERATION_OFFSET ); m_freeList.pushFront( reinterpret_cast( &tex ) ); } } -TextureManager* TextureManager_Create( GlobalMemory* mem, RenderDevice* renderDevice, uint32_t maxCount ) +TextureManager* TextureManager_Create( GlobalMemory* mem, RenderDevice* renderDevice, uint32_t const maxCount ) { Texture* textures = reinterpret_cast( mem->allocate( maxCount * sizeof( Texture ), alignof( Texture ) ) ); if ( not textures ) return nullptr; + VkDescriptorSetLayoutBinding const descriptorSetLayoutBinding{ + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = maxCount, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + .pImmutableSamplers = nullptr, + }; + + VkDescriptorBindingFlags flags = VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | + VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT | + VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT; + + VkDescriptorSetLayoutBindingFlagsCreateInfo const bindlessBinding = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, + .pNext = nullptr, + .bindingCount = 1, + .pBindingFlags = &flags, + }; + + VkDescriptorSetLayoutCreateInfo const descriptorSetLayoutCreateInfo = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .pNext = &bindlessBinding, + .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT, + .bindingCount = 1, + .pBindings = &descriptorSetLayoutBinding, + }; + + VkDescriptorSetLayout descriptorSetLayout; + VK_CHECK( vkCreateDescriptorSetLayout( + renderDevice->device, &descriptorSetLayoutCreateInfo, nullptr, &descriptorSetLayout ) ); + + VkDescriptorPoolSize const poolSize = { + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = maxCount, + }; + + VkDescriptorPoolCreateInfo const descriptorPoolCreateInfo = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .pNext = nullptr, + .flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT, + .maxSets = 1, + .poolSizeCount = 1, + .pPoolSizes = &poolSize, + }; + + VkDescriptorPool descriptorPool; + VK_CHECK( vkCreateDescriptorPool( renderDevice->device, &descriptorPoolCreateInfo, nullptr, &descriptorPool ) ); + + VkDescriptorSetAllocateInfo const descriptorSetAllocateInfo = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .pNext = nullptr, + .descriptorPool = descriptorPool, + .descriptorSetCount = 1, + .pSetLayouts = &descriptorSetLayout, + }; + + VkDescriptorSet descriptorSet; + VK_CHECK( vkAllocateDescriptorSets( renderDevice->device, &descriptorSetAllocateInfo, &descriptorSet ) ); + std::byte* allocation = mem->allocate( sizeof( TextureManager ), alignof( TextureManager ) ); if ( not allocation ) return nullptr; - return new ( allocation ) TextureManager{ renderDevice, textures, maxCount }; + return new ( allocation ) + TextureManager{ renderDevice, textures, maxCount, descriptorSetLayout, descriptorPool, descriptorSet }; } diff --git a/Blaze/TextureManager.h b/Blaze/TextureManager.h index a108433..a786d6c 100644 --- a/Blaze/TextureManager.h +++ b/Blaze/TextureManager.h @@ -117,10 +117,20 @@ struct RID private: uint32_t m_index = 0; - explicit RID( uint32_t const index ) : m_index{ index } {}; + 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 {}; @@ -132,6 +142,22 @@ public: } }; +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; @@ -160,28 +186,50 @@ private: RenderDevice* m_pRenderDevice; - Texture* m_aTextures; - uint32_t m_count; - uint32_t m_capacity; - FreeList m_freeList; + // Texture Manager + Texture* m_aTextures; + uint32_t m_count; + uint32_t m_capacity; + FreeList m_freeList; - void destroyTexture( Texture& tex ); + // Bindless Descriptor Info + VkDescriptorSetLayout m_descriptorSetLayout; + VkDescriptorPool m_descriptorPool; + VkDescriptorSet m_descriptorSet; - Texture& fetchTextureUnchecked( TextureID rid ); + void destroyTexture( Texture& tex ); + + Texture& fetchTextureUnchecked( TextureID const& rid ); public: - static uint32_t calculateRequiredMipLevels( uint32_t w, uint32_t h, uint32_t d ); + static uint32_t calculateRequiredMipLevels( uint32_t w, uint32_t h, uint32_t d ); - [[nodiscard]] bool isValidID( TextureID rid ) const; - [[nodiscard]] std::optional createTexture( VkExtent3D extent ); - void freeTexture( TextureID rid ); + [[nodiscard]] bool isValidID( TextureID const& rid ) const; + + // [[nodiscard]] std::optional createTexture( VkExtent3D extent ); + + void freeTexture( TextureID&& rid ); DEPRECATE_JULY_2025 - std::optional fetchImage( TextureID rid ); - std::optional fetchImageView( TextureID rid ); + [[nodiscard]] std::optional createTexture( VkExtent3D extent, VkSampler sampler ); + + DEPRECATE_JULY_2025 + std::optional fetchImage( TextureID const& rid ); + + DEPRECATE_JULY_2025 + std::optional fetchImageView( TextureID const& rid ); + + [[nodiscard]] VkDescriptorSetLayout const& descriptorLayout() const; + [[nodiscard]] VkDescriptorSet const& descriptorSet() const; // - TextureManager( RenderDevice* pRenderDevice, Texture* aTextures, uint32_t capacity ); + TextureManager( + RenderDevice* pRenderDevice, + Texture* aTextures, + uint32_t capacity, + VkDescriptorSetLayout setLayout, + VkDescriptorPool pool, + VkDescriptorSet descriptorSet ); void destroy(); TextureManager( TextureManager const& other ) = delete;