340 lines
11 KiB
C++
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 };
|
|
}
|