You're breathtaking!

This commit is contained in:
Wojtek Figat
2020-12-07 23:40:54 +01:00
commit 6fb9eee74c
5143 changed files with 1153594 additions and 0 deletions

View File

@@ -0,0 +1,614 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#if GRAPHICS_API_VULKAN
#include "DescriptorSetVulkan.h"
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Utilities/Crc.h"
#include "GPUDeviceVulkan.h"
#include "RenderToolsVulkan.h"
#include "GPUContextVulkan.h"
#include "GPUAdapterVulkan.h"
#include "CmdBufferVulkan.h"
#include "Engine/Threading/Threading.h"
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
#include "Engine/Engine/Engine.h"
#endif
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
void DescriptorSetLayoutInfoVulkan::CacheTypesUsageID()
{
static CriticalSection locker;
static uint32 uniqueID = 1;
static Dictionary<uint32, uint32> typesUsageHashMap;
const uint32 typesUsageHash = Crc::MemCrc32(LayoutTypes, sizeof(LayoutTypes));
ScopeLock lock(locker);
uint32 id;
if (!typesUsageHashMap.TryGet(typesUsageHash, id))
{
id = uniqueID++;
typesUsageHashMap.Add(typesUsageHash, id);
}
_typesUsageID = id;
}
#endif
void DescriptorSetLayoutInfoVulkan::AddDescriptor(int32 descriptorSetIndex, const VkDescriptorSetLayoutBinding& descriptor)
{
// Increment type usage
LayoutTypes[descriptor.descriptorType]++;
if (descriptorSetIndex >= SetLayouts.Count())
{
SetLayouts.Resize(descriptorSetIndex + 1);
}
SetLayout& descSetLayout = SetLayouts[descriptorSetIndex];
descSetLayout.LayoutBindings.Add(descriptor);
// TODO: manual hash update method?
_hash = Crc::MemCrc32(&descriptor, sizeof(descriptor), _hash);
}
void DescriptorSetLayoutInfoVulkan::AddBindingsForStage(VkShaderStageFlagBits stageFlags, DescriptorSet::Stage descSet, const SpirvShaderDescriptorInfo* descriptorInfo)
{
const int32 descriptorSetIndex = (int32)descSet;
VkDescriptorSetLayoutBinding binding;
binding.descriptorCount = 1;
binding.stageFlags = stageFlags;
binding.pImmutableSamplers = nullptr;
for (uint32 descriptorIndex = 0; descriptorIndex < descriptorInfo->DescriptorTypesCount; descriptorIndex++)
{
auto& descriptor = descriptorInfo->DescriptorTypes[descriptorIndex];
binding.binding = descriptorIndex;
binding.descriptorType = descriptor.DescriptorType;
AddDescriptor(descriptorSetIndex, binding);
}
}
DescriptorSetLayoutVulkan::DescriptorSetLayoutVulkan(GPUDeviceVulkan* device)
: _device(device)
{
}
DescriptorSetLayoutVulkan::~DescriptorSetLayoutVulkan()
{
for (VkDescriptorSetLayout& handle : _handles)
{
_device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::DescriptorSetLayout, handle);
}
}
void DescriptorSetLayoutVulkan::Compile()
{
ASSERT(_handles.IsEmpty());
// Check if we obey limits
const VkPhysicalDeviceLimits& limits = _device->PhysicalDeviceLimits;
// Check for maxDescriptorSetSamplers
ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_SAMPLER]
+ LayoutTypes[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER]
< limits.maxDescriptorSetSamplers);
// Check for maxDescriptorSetUniformBuffers
ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER]
+ LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC]
< limits.maxDescriptorSetUniformBuffers);
// Check for maxDescriptorSetUniformBuffersDynamic
if (!_device->Adapter->IsAMD())
{
ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC]
< limits.maxDescriptorSetUniformBuffersDynamic);
}
// Check for maxDescriptorSetStorageBuffers
ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER]
+ LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC]
< limits.maxDescriptorSetStorageBuffers);
// Check for maxDescriptorSetStorageBuffersDynamic
if (LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC] > limits.maxDescriptorSetUniformBuffersDynamic)
{
// TODO: Downgrade to non-dynamic?
}
ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC]
< limits.maxDescriptorSetStorageBuffersDynamic);
// Check for maxDescriptorSetSampledImages
ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER]
+ LayoutTypes[VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE]
+ LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER]
< limits.maxDescriptorSetSampledImages);
// Check for maxDescriptorSetStorageImages
ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_IMAGE]
+ LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER]
< limits.maxDescriptorSetStorageImages);
_handles.Resize(SetLayouts.Count());
for (int32 i = 0; i < SetLayouts.Count(); i++)
{
auto& layout = SetLayouts[i];
VkDescriptorSetLayoutCreateInfo layoutInfo;
RenderToolsVulkan::ZeroStruct(layoutInfo, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO);
layoutInfo.bindingCount = layout.LayoutBindings.Count();
layoutInfo.pBindings = layout.LayoutBindings.Get();
VALIDATE_VULKAN_RESULT(vkCreateDescriptorSetLayout(_device->Device, &layoutInfo, nullptr, &_handles[i]));
}
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
if (_typesUsageID == ~0)
{
CacheTypesUsageID();
}
RenderToolsVulkan::ZeroStruct(_allocateInfo, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO);
_allocateInfo.descriptorSetCount = _handles.Count();
_allocateInfo.pSetLayouts = _handles.Get();
#endif
}
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
DescriptorPoolVulkan::DescriptorPoolVulkan(GPUDeviceVulkan* device, const DescriptorSetLayoutVulkan& layout)
#else
DescriptorPoolVulkan::DescriptorPoolVulkan(GPUDeviceVulkan* device)
#endif
: _device(device)
, _handle(VK_NULL_HANDLE)
, MaxDescriptorSets(0)
, NumAllocatedDescriptorSets(0)
, PeakAllocatedDescriptorSets(0)
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
, Layout(layout)
#endif
{
Array<VkDescriptorPoolSize, FixedAllocation<VK_DESCRIPTOR_TYPE_RANGE_SIZE>> types;
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
// Max number of descriptor sets layout allocations
const uint32 MaxSetsAllocations = 256;
// Descriptor sets number required to allocate the max number of descriptor sets layout.
// When we're hashing pools with types usage ID the descriptor pool can be used for different layouts so the initial layout does not make much sense.
// In the latter case we'll be probably overallocating the descriptor types but given the relatively small number of max allocations this should not have
// a serious impact.
MaxDescriptorSets = MaxSetsAllocations * (VULKAN_HASH_POOLS_WITH_TYPES_USAGE_ID ? 1 : Layout.GetLayouts().Count());
for (uint32 typeIndex = VK_DESCRIPTOR_TYPE_BEGIN_RANGE; typeIndex < VK_DESCRIPTOR_TYPE_END_RANGE; ++typeIndex)
{
const VkDescriptorType descriptorType = (VkDescriptorType)typeIndex;
const uint32 typesUsed = Layout.GetTypesUsed(descriptorType);
if (typesUsed > 0)
{
VkDescriptorPoolSize& type = types.AddOne();
Platform::MemoryClear(&type, sizeof(type));
type.type = descriptorType;
type.descriptorCount = typesUsed * MaxSetsAllocations;
}
}
#else
MaxDescriptorSets = 16384;
Platform::MemoryClear(MaxAllocatedTypes, sizeof(MaxAllocatedTypes));
Platform::MemoryClear(NumAllocatedTypes, sizeof(NumAllocatedTypes));
Platform::MemoryClear(PeakAllocatedTypes, sizeof(PeakAllocatedTypes));
VkDescriptorPoolSize type;
type.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
type.descriptorCount = 2048;
types.Add(type);
type.type = VK_DESCRIPTOR_TYPE_SAMPLER;
type.descriptorCount = 1024;
types.Add(type);
type.type = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
type.descriptorCount = 512;
types.Add(type);
type.type = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
type.descriptorCount = 512;
types.Add(type);
type.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
type.descriptorCount = 512;
types.Add(type);
type.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
type.descriptorCount = 512;
types.Add(type);
type.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
type.descriptorCount = 2048;
types.Add(type);
for (const VkDescriptorPoolSize& poolSize : types)
{
MaxAllocatedTypes[poolSize.type] = poolSize.descriptorCount;
}
#endif
VkDescriptorPoolCreateInfo createInfo;
RenderToolsVulkan::ZeroStruct(createInfo, VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO);
createInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
createInfo.poolSizeCount = types.Count();
createInfo.pPoolSizes = types.Get();
createInfo.maxSets = MaxDescriptorSets;
VALIDATE_VULKAN_RESULT(vkCreateDescriptorPool(_device->Device, &createInfo, nullptr, &_handle));
}
DescriptorPoolVulkan::~DescriptorPoolVulkan()
{
if (_handle != VK_NULL_HANDLE)
{
vkDestroyDescriptorPool(_device->Device, _handle, nullptr);
}
}
void DescriptorPoolVulkan::TrackAddUsage(const DescriptorSetLayoutVulkan& layout)
{
// Check and increment our current type usage
for (uint32 typeIndex = VK_DESCRIPTOR_TYPE_BEGIN_RANGE; typeIndex < VK_DESCRIPTOR_TYPE_END_RANGE; typeIndex++)
{
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
ASSERT(Layout.GetTypesUsed((VkDescriptorType)typeIndex) == layout.GetTypesUsed((VkDescriptorType)typeIndex));
#else
NumAllocatedTypes[typeIndex] += (int32)layout.GetTypesUsed((VkDescriptorType)typeIndex);
PeakAllocatedTypes[typeIndex] = Math::Max(PeakAllocatedTypes[typeIndex], NumAllocatedTypes[typeIndex]);
#endif
}
NumAllocatedDescriptorSets += layout.GetLayouts().Count();
PeakAllocatedDescriptorSets = Math::Max(NumAllocatedDescriptorSets, PeakAllocatedDescriptorSets);
}
void DescriptorPoolVulkan::TrackRemoveUsage(const DescriptorSetLayoutVulkan& layout)
{
// Check and increment our current type usage
for (uint32 typeIndex = VK_DESCRIPTOR_TYPE_BEGIN_RANGE; typeIndex < VK_DESCRIPTOR_TYPE_END_RANGE; typeIndex++)
{
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
ASSERT(Layout.GetTypesUsed((VkDescriptorType)typeIndex) == layout.GetTypesUsed((VkDescriptorType)typeIndex));
#else
NumAllocatedTypes[typeIndex] -= (int32)layout.GetTypesUsed((VkDescriptorType)typeIndex);
ASSERT(NumAllocatedTypes[typeIndex] >= 0);
#endif
}
NumAllocatedDescriptorSets -= layout.GetLayouts().Count();
}
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
void DescriptorPoolVulkan::Reset()
{
if (_handle != VK_NULL_HANDLE)
{
VALIDATE_VULKAN_RESULT(vkResetDescriptorPool(_device->Device, _handle, 0));
}
NumAllocatedDescriptorSets = 0;
}
bool DescriptorPoolVulkan::AllocateDescriptorSets(const VkDescriptorSetAllocateInfo& descriptorSetAllocateInfo, VkDescriptorSet* result)
{
VkDescriptorSetAllocateInfo allocateInfo = descriptorSetAllocateInfo;
allocateInfo.descriptorPool = _handle;
return vkAllocateDescriptorSets(_device->Device, &allocateInfo, result) == VK_SUCCESS;
}
TypedDescriptorPoolSetVulkan::~TypedDescriptorPoolSetVulkan()
{
for (auto pool = _poolListHead; pool;)
{
const auto next = pool->Next;
Delete(pool->Element);
Delete(pool);
pool = next;
}
}
bool TypedDescriptorPoolSetVulkan::AllocateDescriptorSets(const DescriptorSetLayoutVulkan& layout, VkDescriptorSet* outSets)
{
const auto& layoutHandles = layout.GetHandles();
if (layoutHandles.HasItems())
{
auto* pool = _poolListCurrent->Element;
while (!pool->AllocateDescriptorSets(layout.GetAllocateInfo(), outSets))
{
pool = GetFreePool(true);
}
return true;
}
return true;
}
DescriptorPoolVulkan* TypedDescriptorPoolSetVulkan::GetFreePool(bool forceNewPool)
{
if (!forceNewPool)
{
return _poolListCurrent->Element;
}
if (_poolListCurrent->Next)
{
_poolListCurrent = _poolListCurrent->Next;
return _poolListCurrent->Element;
}
return PushNewPool();
}
DescriptorPoolVulkan* TypedDescriptorPoolSetVulkan::PushNewPool()
{
auto* newPool = New<DescriptorPoolVulkan>(_device, _layout);
if (_poolListCurrent)
{
_poolListCurrent->Next = New<PoolList>(newPool);
_poolListCurrent = _poolListCurrent->Next;
}
else
{
_poolListCurrent = _poolListHead = New<PoolList>(newPool);
}
return newPool;
}
void TypedDescriptorPoolSetVulkan::Reset()
{
for (PoolList* pool = _poolListHead; pool; pool = pool->Next)
{
pool->Element->Reset();
}
_poolListCurrent = _poolListHead;
}
#endif
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
DescriptorPoolSetContainerVulkan::DescriptorPoolSetContainerVulkan(GPUDeviceVulkan* device)
: _device(device)
, _lastFrameUsed(Engine::FrameCount)
, _used(true)
{
}
DescriptorPoolSetContainerVulkan::~DescriptorPoolSetContainerVulkan()
{
_typedDescriptorPools.ClearDelete();
}
TypedDescriptorPoolSetVulkan* DescriptorPoolSetContainerVulkan::AcquireTypedPoolSet(const DescriptorSetLayoutVulkan& layout)
{
const uint32 hash = VULKAN_HASH_POOLS_WITH_TYPES_USAGE_ID ? layout.GetTypesUsageID() : GetHash(layout);
TypedDescriptorPoolSetVulkan* typedPool;
if (!_typedDescriptorPools.TryGet(hash, typedPool))
{
typedPool = New<TypedDescriptorPoolSetVulkan>(_device, this, layout);
_typedDescriptorPools.Add(hash, typedPool);
}
return typedPool;
}
void DescriptorPoolSetContainerVulkan::Reset()
{
for (auto i = _typedDescriptorPools.Begin(); i.IsNotEnd(); ++i)
{
TypedDescriptorPoolSetVulkan* typedPool = i->Value;
typedPool->Reset();
}
}
void DescriptorPoolSetContainerVulkan::SetUsed(bool used)
{
_used = used;
_lastFrameUsed = used ? Engine::FrameCount : _lastFrameUsed;
}
DescriptorPoolsManagerVulkan::DescriptorPoolsManagerVulkan(GPUDeviceVulkan* device)
: _device(device)
{
}
DescriptorPoolsManagerVulkan::~DescriptorPoolsManagerVulkan()
{
_poolSets.ClearDelete();
}
DescriptorPoolSetContainerVulkan& DescriptorPoolsManagerVulkan::AcquirePoolSetContainer()
{
ScopeLock lock(_locker);
for (auto* poolSet : _poolSets)
{
if (poolSet->IsUnused())
{
poolSet->SetUsed(true);
poolSet->Reset();
return *poolSet;
}
}
const auto poolSet = New<DescriptorPoolSetContainerVulkan>(_device);
_poolSets.Add(poolSet);
return *poolSet;
}
void DescriptorPoolsManagerVulkan::ReleasePoolSet(DescriptorPoolSetContainerVulkan& poolSet)
{
poolSet.SetUsed(false);
}
void DescriptorPoolsManagerVulkan::GC()
{
ScopeLock lock(_locker);
// Pool sets are forward allocated - iterate from the back to increase the chance of finding an unused one
for (int32 i = _poolSets.Count() - 1; i >= 0; i--)
{
const auto poolSet = _poolSets[i];
if (poolSet->IsUnused() && Engine::FrameCount - poolSet->GetLastFrameUsed() > VULKAN_RESOURCE_DELETE_SAFE_FRAMES_COUNT)
{
_poolSets.RemoveAt(i);
Delete(poolSet);
break;
}
}
}
#endif
PipelineLayoutVulkan::PipelineLayoutVulkan(GPUDeviceVulkan* device, const DescriptorSetLayoutInfoVulkan& layout)
: _device(device)
, _handle(VK_NULL_HANDLE)
, _descriptorSetLayout(device)
{
_descriptorSetLayout.CopyFrom(layout);
_descriptorSetLayout.Compile();
const auto& layoutHandles = _descriptorSetLayout.GetHandles();
VkPipelineLayoutCreateInfo createInfo;
RenderToolsVulkan::ZeroStruct(createInfo, VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO);
createInfo.setLayoutCount = layoutHandles.Count();
createInfo.pSetLayouts = layoutHandles.Get();
VALIDATE_VULKAN_RESULT(vkCreatePipelineLayout(_device->Device, &createInfo, nullptr, &_handle));
}
PipelineLayoutVulkan::~PipelineLayoutVulkan()
{
if (_handle != VK_NULL_HANDLE)
{
_device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::PipelineLayout, _handle);
}
}
#if !VULKAN_USE_DESCRIPTOR_POOL_MANAGER
DescriptorSetsVulkan::DescriptorSetsVulkan(GPUDeviceVulkan* device, const DescriptorSetLayoutVulkan& layout, GPUContextVulkan* context)
: _device(device)
, _pool(nullptr)
, _layout(&layout)
{
const auto& layoutHandles = layout.GetHandles();
if (layoutHandles.HasItems())
{
VkDescriptorSetAllocateInfo allocateInfo;
RenderToolsVulkan::ZeroStruct(allocateInfo, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO);
allocateInfo.descriptorSetCount = layoutHandles.Count();
allocateInfo.pSetLayouts = layoutHandles.Get();
_sets.AddZeroed(layoutHandles.Count());
_pool = context->AllocateDescriptorSets(allocateInfo, layout, _sets.Get());
_pool->TrackAddUsage(layout);
}
}
DescriptorSetsVulkan::~DescriptorSetsVulkan()
{
}
DescriptorSetRingBufferVulkan::DescriptorSetRingBufferVulkan(GPUDeviceVulkan* device)
: _device(device)
, _currDescriptorSets(nullptr)
{
}
DescriptorSetsVulkan* DescriptorSetRingBufferVulkan::RequestDescriptorSets(GPUContextVulkan* context, CmdBufferVulkan* cmdBuffer, const PipelineLayoutVulkan* layout)
{
DescriptorSetsEntry* foundEntry = nullptr;
for (DescriptorSetsEntry* descriptorSetsEntry : DescriptorSetsEntries)
{
if (descriptorSetsEntry->CmdBuffer == cmdBuffer)
{
foundEntry = descriptorSetsEntry;
}
}
if (!foundEntry)
{
if (!layout->HasDescriptors())
{
return nullptr;
}
foundEntry = New<DescriptorSetsEntry>(cmdBuffer);
DescriptorSetsEntries.Add(foundEntry);
}
const uint64 cmdBufferFenceSignaledCounter = cmdBuffer->GetFenceSignaledCounter();
for (int32 i = 0; i < foundEntry->Pairs.Count(); i++)
{
DescriptorSetsPair& entry = foundEntry->Pairs[i];
if (entry.FenceCounter < cmdBufferFenceSignaledCounter)
{
entry.FenceCounter = cmdBufferFenceSignaledCounter;
return entry.DescriptorSets;
}
}
DescriptorSetsPair newEntry;
newEntry.DescriptorSets = New<DescriptorSetsVulkan>(_device, layout->GetDescriptorSetLayout(), context);
newEntry.FenceCounter = cmdBufferFenceSignaledCounter;
foundEntry->Pairs.Add(newEntry);
return newEntry.DescriptorSets;
}
#endif
uint32 DescriptorSetWriterVulkan::SetupDescriptorWrites(const SpirvShaderDescriptorInfo& info, VkWriteDescriptorSet* writeDescriptors, VkDescriptorImageInfo* imageInfo, VkDescriptorBufferInfo* bufferInfo, uint8* bindingToDynamicOffsetMap)
{
WriteDescriptors = writeDescriptors;
NumWrites = info.DescriptorTypesCount;
ASSERT(info.DescriptorTypesCount <= 64 && TEXT("Out of bits for Dirty Mask! More than 64 resources in one descriptor set!"));
BindingToDynamicOffsetMap = bindingToDynamicOffsetMap;
uint32 dynamicOffsetIndex = 0;
for (uint32 i = 0; i < info.DescriptorTypesCount; i++)
{
writeDescriptors->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeDescriptors->dstBinding = i;
writeDescriptors->descriptorCount = 1;
writeDescriptors->descriptorType = info.DescriptorTypes[i].DescriptorType;
switch (writeDescriptors->descriptorType)
{
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
BindingToDynamicOffsetMap[i] = dynamicOffsetIndex;
++dynamicOffsetIndex;
writeDescriptors->pBufferInfo = bufferInfo++;
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
writeDescriptors->pBufferInfo = bufferInfo++;
break;
case VK_DESCRIPTOR_TYPE_SAMPLER:
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
writeDescriptors->pImageInfo = imageInfo++;
break;
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
break;
default:
CRASH;
break;
}
writeDescriptors++;
}
return dynamicOffsetIndex;
}
#endif