Compare commits

...

1 Commits

Author SHA1 Message Date
Anish Bhobe 297548d2c9 Fully bindless textures. 2025-06-17 22:10:54 +02:00
12 changed files with 325 additions and 164 deletions

View File

@ -0,0 +1,25 @@
[vk::binding(0, 0)] uniform __DynamicResource<__DynamicResourceKind.Sampler> gTextures[];
public struct RID<T> 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<T>();
}
}
}
public extension <T> T where T: IOpaqueDescriptor {
public typealias RID = RID<T>;
}

View File

@ -1,3 +1,4 @@
import Bindless;
struct VertexOut {
float4 outPosition : SV_Position;
@ -11,14 +12,13 @@ struct CameraData {
float4x4 proj;
};
struct PerFrameData {
}
[vk::binding(0, 1)] uniform ConstantBuffer<CameraData> camera;
[vk::binding(0, 0)] uniform ConstantBuffer<CameraData> camera;
[vk::binding(0, 1)] uniform Sampler2D texture;
#define INDEX_MASK 0x0007FFFF;
struct PerInstanceData {
float4x4 transform;
Sampler2D.RID textureID;
}
[[vk::push_constant]]
@ -45,6 +45,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;
}
}

View File

@ -161,6 +161,7 @@
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Compiling %(Filename).slang</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(Filename).spv</Outputs>
</CustomBuild>
<None Include="Assets\Shaders\Bindless.slang" />
<None Include="PLAN.md">
<SubType>
</SubType>

View File

@ -45,6 +45,9 @@
<None Include="vcpkg-configuration.json">
<Filter>Resource Files\Config</Filter>
</None>
<None Include="Assets\Shaders\Bindless.slang">
<Filter>Resource Files\Shader Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Blaze\AppState.h">

View File

@ -98,11 +98,7 @@ SDL_AppResult SDL_AppInit( void** appstate, int, char** )
{
modelTransform.position.x = static_cast<float>( 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;
@ -222,22 +218,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 +248,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 );
}
}

View File

@ -11,11 +11,7 @@
#include "TextureManager.h"
Entity* EntityManager::createEntity(
Transform const& transform,
std::span<Vertex> const vertices,
const char* textureFile,
VkDescriptorSetLayout layout,
VkDescriptorPool pool )
Transform const& transform, std::span<Vertex> const vertices, const char* textureFile )
{
ASSERT( pRenderDevice );
RenderDevice& renderDevice = *pRenderDevice;
@ -91,17 +87,6 @@ Entity* EntityManager::createEntity(
height = static_cast<uint32_t>( 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.

View File

@ -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>( 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<Vertex> vertices,
const char* textureFile,
VkDescriptorSetLayout layout,
VkDescriptorPool pool );
Entity* createEntity( Transform const& transform, std::span<Vertex> vertices, const char* textureFile );
void destroyEntity( Entity* entity );
void destroyEntity( Entity* entity );
void destroy();
void destroy();
~EntityManager();
};

View File

@ -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<uint32_t>( descriptorSetLayout.size() ),
.pSetLayouts = descriptorSetLayout.data(),
.setLayoutCount = static_cast<uint32_t>( 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 );
}

View File

@ -18,30 +18,30 @@ struct MiscData
DirectX::XMMATRIX projectionMatrix;
};
uint64_t previousCounter;
uint64_t previousCounter;
std::array<VkDescriptorSetLayout, 2> descriptorSetLayout;
VkPipelineLayout pipelineLayout;
VkPipeline meshPipeline;
VkDescriptorSetLayout descriptorSetLayout;
VkPipelineLayout pipelineLayout;
VkPipeline meshPipeline;
uint64_t _padding; // TODO: Optimize out?
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 );
};

View File

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

View File

@ -4,7 +4,7 @@
#include "GlobalMemory.h"
#include "RenderDevice.h"
std::optional<TextureID> TextureManager::createTexture( VkExtent3D const extent )
std::optional<TextureID> TextureManager::createTexture( VkExtent3D const extent, VkSampler sampler )
{
if ( m_freeList.empty() )
{
@ -95,13 +95,37 @@ std::optional<TextureID> 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<TextureID*>( &index );
return std::move( *reinterpret_cast<TextureID*>( &index ) );
}
bool TextureManager::isValidID( TextureID rid ) const
bool TextureManager::isValidID( TextureID const& rid ) const
{
uint32_t const index = *reinterpret_cast<uint32_t*>( &rid );
uint32_t const index = *reinterpret_cast<uint32_t const*>( &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<VkImage> TextureManager::fetchImage( TextureID const rid )
std::optional<VkImage> TextureManager::fetchImage( TextureID const& rid )
{
if ( not isValidID( rid ) ) return std::nullopt;
return fetchTextureUnchecked( rid ).image;
}
std::optional<VkImageView> TextureManager::fetchImageView( TextureID const rid )
std::optional<VkImageView> 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<Texture*>( 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<uint32_t>( floorf( log2f( static_cast<float>( maxDim ) ) ) );
}
Texture& TextureManager::fetchTextureUnchecked( TextureID rid )
Texture& TextureManager::fetchTextureUnchecked( TextureID const& rid )
{
uint32_t const index = *reinterpret_cast<uint32_t*>( &rid );
uint32_t const index = *reinterpret_cast<uint32_t const*>( &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<FreeList::Node*>( &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<Texture*>( 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 };
}

View File

@ -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 <typename T>
RID<T>::RID( RID&& other ) noexcept : m_index{ other.m_index }
{
other.m_index = 0;
}
template <typename T>
RID<T>& RID<T>::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<TextureID> createTexture( VkExtent3D extent );
void freeTexture( TextureID rid );
[[nodiscard]] bool isValidID( TextureID const& rid ) const;
// [[nodiscard]] std::optional<TextureID> createTexture( VkExtent3D extent );
void freeTexture( TextureID&& rid );
DEPRECATE_JULY_2025
std::optional<VkImage> fetchImage( TextureID rid );
std::optional<VkImageView> fetchImageView( TextureID rid );
[[nodiscard]] std::optional<TextureID> createTexture( VkExtent3D extent, VkSampler sampler );
DEPRECATE_JULY_2025
std::optional<VkImage> fetchImage( TextureID const& rid );
DEPRECATE_JULY_2025
std::optional<VkImageView> 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;