Blaze/Blaze/Source/TextureManager.cpp

340 lines
11 KiB
C++

#include "TextureManager.h"
#include "FreeList.h"
#include "GlobalMemory.h"
#include "RenderDevice.h"
template struct RID<Texture>;
TextureID TextureManager::createTexture( VkExtent3D const extent, VkSampler const sampler, VkFormat const format )
{
ASSERT( not m_freeList.empty() );
Texture* textureSlot = reinterpret_cast<Texture*>( m_freeList.popFront() );
++m_count;
ASSERT( m_pRenderDevice );
RenderDevice const& renderDevice = *m_pRenderDevice;
VkImage texture;
VmaAllocation textureAllocation;
VkImageView textureView;
uint32_t const mipLevels = calculateRequiredMipLevels( extent.width, extent.height, extent.depth );
VkImageCreateInfo const imageCreateInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.imageType = VK_IMAGE_TYPE_2D,
.format = format,
.extent = extent,
.mipLevels = mipLevels,
.arrayLayers = 1,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
};
VmaAllocationCreateInfo constexpr allocationCreateInfo = {
.flags = 0,
.usage = VMA_MEMORY_USAGE_AUTO,
.requiredFlags = 0,
.preferredFlags = 0,
.memoryTypeBits = 0,
.pool = nullptr,
.pUserData = nullptr,
.priority = 1.0f,
};
VK_CHECK( vmaCreateImage(
renderDevice.gpuAllocator, &imageCreateInfo, &allocationCreateInfo, &texture, &textureAllocation, nullptr ) );
VkImageSubresourceRange const subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = mipLevels,
.baseArrayLayer = 0,
.layerCount = 1,
};
VkComponentMapping constexpr componentMapping = {
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
};
VkImageViewCreateInfo const imageViewCreateInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.image = texture,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = imageCreateInfo.format,
.components = componentMapping,
.subresourceRange = subresourceRange,
};
VK_CHECK( vkCreateImageView( renderDevice.device, &imageViewCreateInfo, nullptr, &textureView ) );
// NOTE: textureSlot preserves index between uses.
uint32_t index = textureSlot->index;
new ( textureSlot ) Texture{
.image = texture,
.allocation = textureAllocation,
.view = textureView,
.extent = extent,
.format = format,
.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 );
}
bool TextureManager::isValidID( TextureID const& rid ) const
{
uint32_t const index = *reinterpret_cast<uint32_t const*>( &rid );
uint32_t const innerIndex = index & INDEX_MASK;
if ( innerIndex > m_capacity ) return false;
return m_aTextures[innerIndex].index == index;
}
void TextureManager::freeTexture( TextureID* rid )
{
if ( not isValidID( *rid ) ) return;
Texture& texture = fetchTextureUnchecked( *rid );
destroyTexture( texture );
*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 )
{
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 )
if ( m_count > 0 )
{
SDL_LogError( SDL_LOG_CATEGORY_ERROR, "%u textures still allocated.", m_count );
}
#endif
ASSERT( m_pRenderDevice );
RenderDevice const& renderDevice = *m_pRenderDevice;
while ( not m_freeList.empty() )
{
Texture* tex = reinterpret_cast<Texture*>( m_freeList.popFront() );
memset( tex, 0, sizeof *tex );
}
for ( Texture& tex : std::span{ m_aTextures, m_count } )
{
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 );
}
void TextureManager::destroyTexture( Texture& tex )
{
if ( not tex.image ) return;
ASSERT( m_pRenderDevice );
uint32_t const index = tex.index;
uint32_t const innerIndex = index & INDEX_MASK;
uint32_t const generation = ( index & GENERATION_MASK ) >> GENERATION_OFFSET;
RenderDevice const& renderDevice = *m_pRenderDevice;
vkDestroyImageView( renderDevice.device, Take( tex.view ), nullptr );
vmaDestroyImage( renderDevice.gpuAllocator, Take( tex.image ), Take( tex.allocation ) );
tex.extent = {};
tex.format = VK_FORMAT_UNDEFINED;
tex.index = innerIndex | ( generation + 1 ) << GENERATION_OFFSET;
// NOTE: DO NOT EDIT INNER INDEX.
ASSERT( innerIndex == ( tex.index & INDEX_MASK ) and "Index should not be modified" );
ASSERT( tex.index > index and "Generation should increase." );
m_freeList.pushBack( reinterpret_cast<FreeList::Node*>( &tex ) );
--m_count;
}
uint32_t TextureManager::calculateRequiredMipLevels( uint32_t const w, uint32_t const h, uint32_t const d )
{
uint32_t const maxDim = std::max( std::max( w, h ), d );
return 1 + static_cast<uint32_t>( floorf( log2f( static_cast<float>( maxDim ) ) ) );
}
Texture& TextureManager::fetchTextureUnchecked( TextureID const& 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,
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
tex.index = i++ | ( 1 << GENERATION_OFFSET );
m_freeList.pushFront( reinterpret_cast<FreeList::Node*>( &tex ) );
}
}
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, descriptorSetLayout, descriptorPool, descriptorSet };
}