From 8517c9713a304c2270038a4d113b9cf518ba6595 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Mon, 6 Jun 2022 21:30:55 +0200 Subject: [PATCH] Move descriptor sets from VkRenderPassManager into a new class called VkDescriptorSetManager --- src/CMakeLists.txt | 1 + .../vulkan/renderer/vk_descriptorset.cpp | 213 ++++++++++++++++++ .../vulkan/renderer/vk_descriptorset.h | 51 +++++ .../rendering/vulkan/renderer/vk_raytrace.cpp | 26 ++- .../vulkan/renderer/vk_renderpass.cpp | 177 +-------------- .../rendering/vulkan/renderer/vk_renderpass.h | 31 +-- .../vulkan/renderer/vk_renderstate.cpp | 14 +- .../rendering/vulkan/shaders/vk_shader.cpp | 51 +++-- .../rendering/vulkan/system/vk_buffers.cpp | 6 +- .../rendering/vulkan/system/vk_device.cpp | 2 +- .../vulkan/system/vk_framebuffer.cpp | 10 +- .../rendering/vulkan/system/vk_framebuffer.h | 4 + .../vulkan/textures/vk_hwtexture.cpp | 8 +- 13 files changed, 344 insertions(+), 250 deletions(-) create mode 100644 src/common/rendering/vulkan/renderer/vk_descriptorset.cpp create mode 100644 src/common/rendering/vulkan/renderer/vk_descriptorset.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 28385adbc7..645f3fc7a7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -803,6 +803,7 @@ set (VULKAN_SOURCES common/rendering/vulkan/renderer/vk_streambuffer.cpp common/rendering/vulkan/renderer/vk_postprocess.cpp common/rendering/vulkan/renderer/vk_renderbuffers.cpp + common/rendering/vulkan/renderer/vk_descriptorset.cpp common/rendering/vulkan/renderer/vk_raytrace.cpp common/rendering/vulkan/shaders/vk_shader.cpp common/rendering/vulkan/textures/vk_samplers.cpp diff --git a/src/common/rendering/vulkan/renderer/vk_descriptorset.cpp b/src/common/rendering/vulkan/renderer/vk_descriptorset.cpp new file mode 100644 index 0000000000..af837df9a4 --- /dev/null +++ b/src/common/rendering/vulkan/renderer/vk_descriptorset.cpp @@ -0,0 +1,213 @@ +/* +** Vulkan backend +** Copyright (c) 2016-2020 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include "vk_descriptorset.h" +#include "vk_streambuffer.h" +#include "vk_renderbuffers.h" +#include "vk_raytrace.h" +#include "vulkan/shaders/vk_shader.h" +#include "vulkan/textures/vk_samplers.h" +#include "vulkan/system/vk_builders.h" +#include "vulkan/system/vk_framebuffer.h" +#include "vulkan/system/vk_buffers.h" +#include "flatvertices.h" +#include "hw_viewpointuniforms.h" +#include "v_2ddrawer.h" + +VkDescriptorSetManager::VkDescriptorSetManager() +{ +} + +VkDescriptorSetManager::~VkDescriptorSetManager() +{ +} + +void VkDescriptorSetManager::Init() +{ + CreateFixedSet(); + CreateDynamicSet(); + CreateNullTexture(); +} + +void VkDescriptorSetManager::CreateDynamicSet() +{ + DescriptorSetLayoutBuilder builder; + builder.addBinding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT); + builder.addBinding(1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT); + builder.addBinding(2, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT); + DynamicSetLayout = builder.create(GetVulkanFrameBuffer()->device); + DynamicSetLayout->SetDebugName("VkDescriptorSetManager.DynamicSetLayout"); + + DescriptorPoolBuilder poolbuilder; + poolbuilder.addPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 3); + poolbuilder.setMaxSets(1); + DynamicDescriptorPool = poolbuilder.create(GetVulkanFrameBuffer()->device); + DynamicDescriptorPool->SetDebugName("VkDescriptorSetManager.DynamicDescriptorPool"); + + DynamicSet = DynamicDescriptorPool->allocate(DynamicSetLayout.get()); + if (!DynamicSet) + I_FatalError("CreateDynamicSet failed.\n"); +} + +void VkDescriptorSetManager::UpdateDynamicSet() +{ + auto fb = GetVulkanFrameBuffer(); + + WriteDescriptors update; + update.addBuffer(DynamicSet.get(), 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, fb->ViewpointUBO->mBuffer.get(), 0, sizeof(HWViewpointUniforms)); + update.addBuffer(DynamicSet.get(), 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, fb->MatrixBuffer->UniformBuffer->mBuffer.get(), 0, sizeof(MatricesUBO)); + update.addBuffer(DynamicSet.get(), 2, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, fb->StreamBuffer->UniformBuffer->mBuffer.get(), 0, sizeof(StreamUBO)); + update.updateSets(fb->device); +} + +void VkDescriptorSetManager::CreateFixedSet() +{ + DescriptorSetLayoutBuilder builder; + builder.addBinding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT); + builder.addBinding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT); + builder.addBinding(2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT); + if (GetVulkanFrameBuffer()->device->SupportsDeviceExtension(VK_KHR_RAY_QUERY_EXTENSION_NAME)) + builder.addBinding(3, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1, VK_SHADER_STAGE_FRAGMENT_BIT); + FixedSetLayout = builder.create(GetVulkanFrameBuffer()->device); + FixedSetLayout->SetDebugName("VkDescriptorSetManager.FixedSetLayout"); + + DescriptorPoolBuilder poolbuilder; + poolbuilder.addPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1); + poolbuilder.addPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2); + if (GetVulkanFrameBuffer()->device->SupportsDeviceExtension(VK_KHR_RAY_QUERY_EXTENSION_NAME)) + poolbuilder.addPoolSize(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1); + poolbuilder.setMaxSets(1); + FixedDescriptorPool = poolbuilder.create(GetVulkanFrameBuffer()->device); + FixedDescriptorPool->SetDebugName("VkDescriptorSetManager.FixedDescriptorPool"); + + FixedSet = FixedDescriptorPool->allocate(FixedSetLayout.get()); + if (!FixedSet) + I_FatalError("CreateFixedSet failed.\n"); +} + +void VkDescriptorSetManager::UpdateFixedSet() +{ + auto fb = GetVulkanFrameBuffer(); + + WriteDescriptors update; + update.addCombinedImageSampler(FixedSet.get(), 0, fb->GetBuffers()->Shadowmap.View.get(), fb->GetBuffers()->ShadowmapSampler.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + update.addCombinedImageSampler(FixedSet.get(), 1, fb->GetBuffers()->Lightmap.View.get(), fb->GetBuffers()->LightmapSampler.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + update.addBuffer(FixedSet.get(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->LightBufferSSO->mBuffer.get()); + if (fb->device->SupportsDeviceExtension(VK_KHR_RAY_QUERY_EXTENSION_NAME)) + update.addAccelerationStructure(FixedSet.get(), 3, fb->GetRaytrace()->GetAccelStruct()); + update.updateSets(fb->device); +} + +void VkDescriptorSetManager::TextureSetPoolReset() +{ + if (auto fb = GetVulkanFrameBuffer()) + { + auto& deleteList = fb->FrameDeleteList; + + for (auto& desc : TextureDescriptorPools) + { + deleteList.DescriptorPools.push_back(std::move(desc)); + } + deleteList.Descriptors.push_back(std::move(NullTextureDescriptorSet)); + } + NullTextureDescriptorSet.reset(); + TextureDescriptorPools.clear(); + TextureDescriptorSetsLeft = 0; + TextureDescriptorsLeft = 0; +} + +void VkDescriptorSetManager::CreateNullTexture() +{ + auto fb = GetVulkanFrameBuffer(); + + ImageBuilder imgbuilder; + imgbuilder.setFormat(VK_FORMAT_R8G8B8A8_UNORM); + imgbuilder.setSize(1, 1); + imgbuilder.setUsage(VK_IMAGE_USAGE_SAMPLED_BIT); + NullTexture = imgbuilder.create(fb->device); + NullTexture->SetDebugName("VkDescriptorSetManager.NullTexture"); + + ImageViewBuilder viewbuilder; + viewbuilder.setImage(NullTexture.get(), VK_FORMAT_R8G8B8A8_UNORM); + NullTextureView = viewbuilder.create(fb->device); + NullTextureView->SetDebugName("VkDescriptorSetManager.NullTextureView"); + + PipelineBarrier barrier; + barrier.addImage(NullTexture.get(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 0, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT); + barrier.execute(fb->GetTransferCommands(), VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); +} + +VulkanDescriptorSet* VkDescriptorSetManager::GetNullTextureDescriptorSet() +{ + if (!NullTextureDescriptorSet) + { + NullTextureDescriptorSet = AllocateTextureDescriptorSet(SHADER_MIN_REQUIRED_TEXTURE_LAYERS); + + auto fb = GetVulkanFrameBuffer(); + WriteDescriptors update; + for (int i = 0; i < SHADER_MIN_REQUIRED_TEXTURE_LAYERS; i++) + { + update.addCombinedImageSampler(NullTextureDescriptorSet.get(), i, NullTextureView.get(), fb->GetSamplerManager()->Get(CLAMP_XY_NOMIP), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + } + update.updateSets(fb->device); + } + + return NullTextureDescriptorSet.get(); +} + +std::unique_ptr VkDescriptorSetManager::AllocateTextureDescriptorSet(int numLayers) +{ + if (TextureDescriptorSetsLeft == 0 || TextureDescriptorsLeft < numLayers) + { + TextureDescriptorSetsLeft = 1000; + TextureDescriptorsLeft = 2000; + + DescriptorPoolBuilder builder; + builder.addPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, TextureDescriptorsLeft); + builder.setMaxSets(TextureDescriptorSetsLeft); + TextureDescriptorPools.push_back(builder.create(GetVulkanFrameBuffer()->device)); + TextureDescriptorPools.back()->SetDebugName("VkDescriptorSetManager.TextureDescriptorPool"); + } + + TextureDescriptorSetsLeft--; + TextureDescriptorsLeft -= numLayers; + return TextureDescriptorPools.back()->allocate(GetTextureSetLayout(numLayers)); +} + +VulkanDescriptorSetLayout* VkDescriptorSetManager::GetTextureSetLayout(int numLayers) +{ + if (TextureSetLayouts.size() < (size_t)numLayers) + TextureSetLayouts.resize(numLayers); + + auto& layout = TextureSetLayouts[numLayers - 1]; + if (layout) + return layout.get(); + + DescriptorSetLayoutBuilder builder; + for (int i = 0; i < numLayers; i++) + { + builder.addBinding(i, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT); + } + layout = builder.create(GetVulkanFrameBuffer()->device); + layout->SetDebugName("VkDescriptorSetManager.TextureSetLayout"); + return layout.get(); +} diff --git a/src/common/rendering/vulkan/renderer/vk_descriptorset.h b/src/common/rendering/vulkan/renderer/vk_descriptorset.h new file mode 100644 index 0000000000..ea85bd58c5 --- /dev/null +++ b/src/common/rendering/vulkan/renderer/vk_descriptorset.h @@ -0,0 +1,51 @@ + +#pragma once + +#include "vulkan/system/vk_objects.h" + +class VkDescriptorSetManager +{ +public: + VkDescriptorSetManager(); + ~VkDescriptorSetManager(); + + void Init(); + void UpdateFixedSet(); + void UpdateDynamicSet(); + void TextureSetPoolReset(); + + VulkanDescriptorSetLayout* GetDynamicSetLayout() { return DynamicSetLayout.get(); } + VulkanDescriptorSetLayout* GetFixedSetLayout() { return FixedSetLayout.get(); } + VulkanDescriptorSetLayout* GetTextureSetLayout(int numLayers); + + VulkanDescriptorSet* GetDynamicDescriptorSet() { return DynamicSet.get(); } + VulkanDescriptorSet* GetFixedDescriptorSet() { return FixedSet.get(); } + VulkanDescriptorSet* GetNullTextureDescriptorSet(); + + std::unique_ptr AllocateTextureDescriptorSet(int numLayers); + + VulkanImageView* GetNullTextureView() { return NullTextureView.get(); } + +private: + void CreateDynamicSet(); + void CreateFixedSet(); + void CreateNullTexture(); + + std::unique_ptr DynamicSetLayout; + std::unique_ptr FixedSetLayout; + std::vector> TextureSetLayouts; + + std::unique_ptr DynamicDescriptorPool; + std::unique_ptr FixedDescriptorPool; + + int TextureDescriptorSetsLeft = 0; + int TextureDescriptorsLeft = 0; + std::vector> TextureDescriptorPools; + + std::unique_ptr DynamicSet; + std::unique_ptr FixedSet; + std::unique_ptr NullTextureDescriptorSet; + + std::unique_ptr NullTexture; + std::unique_ptr NullTextureView; +}; diff --git a/src/common/rendering/vulkan/renderer/vk_raytrace.cpp b/src/common/rendering/vulkan/renderer/vk_raytrace.cpp index 6f9fbb2e58..01986572d8 100644 --- a/src/common/rendering/vulkan/renderer/vk_raytrace.cpp +++ b/src/common/rendering/vulkan/renderer/vk_raytrace.cpp @@ -76,19 +76,23 @@ VulkanAccelerationStructure* VkRaytrace::GetAccelStruct() void VkRaytrace::Reset() { - vertexBuffer.reset(); - indexBuffer.reset(); - transferBuffer.reset(); + auto fb = GetVulkanFrameBuffer(); + if (fb) + { + fb->FrameDeleteList.Buffers.push_back(std::move(vertexBuffer)); + fb->FrameDeleteList.Buffers.push_back(std::move(indexBuffer)); + fb->FrameDeleteList.Buffers.push_back(std::move(transferBuffer)); - blScratchBuffer.reset(); - blAccelStructBuffer.reset(); - blAccelStruct.reset(); + fb->FrameDeleteList.Buffers.push_back(std::move(blScratchBuffer)); + fb->FrameDeleteList.Buffers.push_back(std::move(blAccelStructBuffer)); + fb->FrameDeleteList.AccelStructs.push_back(std::move(blAccelStruct)); - tlTransferBuffer.reset(); - tlScratchBuffer.reset(); - tlInstanceBuffer.reset(); - tlAccelStructBuffer.reset(); - tlAccelStruct.reset(); + fb->FrameDeleteList.Buffers.push_back(std::move(tlTransferBuffer)); + fb->FrameDeleteList.Buffers.push_back(std::move(tlScratchBuffer)); + fb->FrameDeleteList.Buffers.push_back(std::move(tlInstanceBuffer)); + fb->FrameDeleteList.Buffers.push_back(std::move(tlAccelStructBuffer)); + fb->FrameDeleteList.AccelStructs.push_back(std::move(tlAccelStruct)); + } } void VkRaytrace::CreateVulkanObjects() diff --git a/src/common/rendering/vulkan/renderer/vk_renderpass.cpp b/src/common/rendering/vulkan/renderer/vk_renderpass.cpp index 0e5687ba01..85d876c380 100644 --- a/src/common/rendering/vulkan/renderer/vk_renderpass.cpp +++ b/src/common/rendering/vulkan/renderer/vk_renderpass.cpp @@ -23,6 +23,7 @@ #include "vk_renderpass.h" #include "vk_renderbuffers.h" #include "vk_renderstate.h" +#include "vk_descriptorset.h" #include "vk_raytrace.h" #include "vulkan/textures/vk_samplers.h" #include "vulkan/shaders/vk_shader.h" @@ -39,15 +40,6 @@ VkRenderPassManager::VkRenderPassManager() VkRenderPassManager::~VkRenderPassManager() { - DynamicSet.reset(); // Needed since it must come before destruction of DynamicDescriptorPool -} - -void VkRenderPassManager::Init() -{ - CreateDynamicSetLayout(); - CreateDescriptorPool(); - CreateDynamicSet(); - CreateNullTexture(); } void VkRenderPassManager::RenderBuffersReset() @@ -55,23 +47,6 @@ void VkRenderPassManager::RenderBuffersReset() RenderPassSetup.clear(); } -void VkRenderPassManager::TextureSetPoolReset() -{ - if (auto fb = GetVulkanFrameBuffer()) - { - auto &deleteList = fb->FrameDeleteList; - - for (auto &desc : TextureDescriptorPools) - { - deleteList.DescriptorPools.push_back(std::move(desc)); - } - } - NullTextureDescriptorSet.reset(); - TextureDescriptorPools.clear(); - TextureDescriptorSetsLeft = 0; - TextureDescriptorsLeft = 0; -} - VkRenderPassSetup *VkRenderPassManager::GetRenderPass(const VkRenderPassKey &key) { auto &item = RenderPassSetup[key]; @@ -123,40 +98,6 @@ VkVertexFormat *VkRenderPassManager::GetVertexFormat(int index) return &VertexFormats[index]; } -void VkRenderPassManager::CreateDynamicSetLayout() -{ - DescriptorSetLayoutBuilder builder; - builder.addBinding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT); - builder.addBinding(1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT); - builder.addBinding(2, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT); - builder.addBinding(3, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT); - builder.addBinding(4, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT); - builder.addBinding(5, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT); - if (GetVulkanFrameBuffer()->device->SupportsDeviceExtension(VK_KHR_RAY_QUERY_EXTENSION_NAME)) - builder.addBinding(6, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1, VK_SHADER_STAGE_FRAGMENT_BIT); - DynamicSetLayout = builder.create(GetVulkanFrameBuffer()->device); - DynamicSetLayout->SetDebugName("VkRenderPassManager.DynamicSetLayout"); -} - -VulkanDescriptorSetLayout *VkRenderPassManager::GetTextureSetLayout(int numLayers) -{ - if (TextureSetLayouts.size() < (size_t)numLayers) - TextureSetLayouts.resize(numLayers); - - auto &layout = TextureSetLayouts[numLayers - 1]; - if (layout) - return layout.get(); - - DescriptorSetLayoutBuilder builder; - for (int i = 0; i < numLayers; i++) - { - builder.addBinding(i, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT); - } - layout = builder.create(GetVulkanFrameBuffer()->device); - layout->SetDebugName("VkRenderPassManager.TextureSetLayout"); - return layout.get(); -} - VulkanPipelineLayout* VkRenderPassManager::GetPipelineLayout(int numLayers) { if (PipelineLayouts.size() <= (size_t)numLayers) @@ -166,120 +107,19 @@ VulkanPipelineLayout* VkRenderPassManager::GetPipelineLayout(int numLayers) if (layout) return layout.get(); + auto descriptors = GetVulkanFrameBuffer()->GetDescriptorSetManager(); + PipelineLayoutBuilder builder; - builder.addSetLayout(DynamicSetLayout.get()); + builder.addSetLayout(descriptors->GetFixedSetLayout()); + builder.addSetLayout(descriptors->GetDynamicSetLayout()); if (numLayers != 0) - builder.addSetLayout(GetTextureSetLayout(numLayers)); + builder.addSetLayout(descriptors->GetTextureSetLayout(numLayers)); builder.addPushConstantRange(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstants)); layout = builder.create(GetVulkanFrameBuffer()->device); layout->SetDebugName("VkRenderPassManager.PipelineLayout"); return layout.get(); } -void VkRenderPassManager::CreateDescriptorPool() -{ - DescriptorPoolBuilder builder; - builder.addPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 3); - builder.addPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1); - builder.addPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2); - if (GetVulkanFrameBuffer()->device->SupportsDeviceExtension(VK_KHR_RAY_QUERY_EXTENSION_NAME)) - builder.addPoolSize(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1); - builder.setMaxSets(1); - DynamicDescriptorPool = builder.create(GetVulkanFrameBuffer()->device); - DynamicDescriptorPool->SetDebugName("VkRenderPassManager.DynamicDescriptorPool"); -} - -void VkRenderPassManager::CreateDynamicSet() -{ - DynamicSet = DynamicDescriptorPool->allocate(DynamicSetLayout.get()); - if (!DynamicSet) - I_FatalError("CreateDynamicSet failed.\n"); -} - -void VkRenderPassManager::CreateNullTexture() -{ - auto fb = GetVulkanFrameBuffer(); - - ImageBuilder imgbuilder; - imgbuilder.setFormat(VK_FORMAT_R8G8B8A8_UNORM); - imgbuilder.setSize(1, 1); - imgbuilder.setUsage(VK_IMAGE_USAGE_SAMPLED_BIT); - NullTexture = imgbuilder.create(fb->device); - NullTexture->SetDebugName("VkRenderPassManager.NullTexture"); - - ImageViewBuilder viewbuilder; - viewbuilder.setImage(NullTexture.get(), VK_FORMAT_R8G8B8A8_UNORM); - NullTextureView = viewbuilder.create(fb->device); - NullTextureView->SetDebugName("VkRenderPassManager.NullTextureView"); - - PipelineBarrier barrier; - barrier.addImage(NullTexture.get(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 0, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT); - barrier.execute(fb->GetTransferCommands(), VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); -} - -VulkanDescriptorSet* VkRenderPassManager::GetNullTextureDescriptorSet() -{ - if (!NullTextureDescriptorSet) - { - NullTextureDescriptorSet = AllocateTextureDescriptorSet(SHADER_MIN_REQUIRED_TEXTURE_LAYERS); - - auto fb = GetVulkanFrameBuffer(); - WriteDescriptors update; - for (int i = 0; i < SHADER_MIN_REQUIRED_TEXTURE_LAYERS; i++) - { - update.addCombinedImageSampler(NullTextureDescriptorSet.get(), i, NullTextureView.get(), fb->GetSamplerManager()->Get(CLAMP_XY_NOMIP), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - } - update.updateSets(fb->device); - } - - return NullTextureDescriptorSet.get(); -} - -VulkanImageView* VkRenderPassManager::GetNullTextureView() -{ - return NullTextureView.get(); -} - -void VkRenderPassManager::UpdateDynamicSet() -{ - auto fb = GetVulkanFrameBuffer(); - - // In some rare cases drawing commands may already have been created before VulkanFrameBuffer::BeginFrame is called. - // Make sure there there are no active command buffers using DynamicSet when we update it: - fb->GetRenderState()->EndRenderPass(); - fb->WaitForCommands(false); - - WriteDescriptors update; - update.addBuffer(DynamicSet.get(), 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, fb->ViewpointUBO->mBuffer.get(), 0, sizeof(HWViewpointUniforms)); - update.addBuffer(DynamicSet.get(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->LightBufferSSO->mBuffer.get()); - update.addBuffer(DynamicSet.get(), 2, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, fb->MatrixBuffer->UniformBuffer->mBuffer.get(), 0, sizeof(MatricesUBO)); - update.addBuffer(DynamicSet.get(), 3, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, fb->StreamBuffer->UniformBuffer->mBuffer.get(), 0, sizeof(StreamUBO)); - update.addCombinedImageSampler(DynamicSet.get(), 4, fb->GetBuffers()->Shadowmap.View.get(), fb->GetBuffers()->ShadowmapSampler.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - update.addCombinedImageSampler(DynamicSet.get(), 5, fb->GetBuffers()->Lightmap.View.get(), fb->GetBuffers()->LightmapSampler.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - if (GetVulkanFrameBuffer()->device->SupportsDeviceExtension(VK_KHR_RAY_QUERY_EXTENSION_NAME)) - update.addAccelerationStructure(DynamicSet.get(), 6, fb->GetRaytrace()->GetAccelStruct()); - update.updateSets(fb->device); -} - -std::unique_ptr VkRenderPassManager::AllocateTextureDescriptorSet(int numLayers) -{ - if (TextureDescriptorSetsLeft == 0 || TextureDescriptorsLeft < numLayers) - { - TextureDescriptorSetsLeft = 1000; - TextureDescriptorsLeft = 2000; - - DescriptorPoolBuilder builder; - builder.addPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, TextureDescriptorsLeft); - builder.setMaxSets(TextureDescriptorSetsLeft); - TextureDescriptorPools.push_back(builder.create(GetVulkanFrameBuffer()->device)); - TextureDescriptorPools.back()->SetDebugName("VkRenderPassManager.TextureDescriptorPool"); - } - - TextureDescriptorSetsLeft--; - TextureDescriptorsLeft -= numLayers; - return TextureDescriptorPools.back()->allocate(GetTextureSetLayout(numLayers)); -} - ///////////////////////////////////////////////////////////////////////////// VkRenderPassSetup::VkRenderPassSetup(const VkRenderPassKey &key) : PassKey(key) @@ -403,12 +243,7 @@ std::unique_ptr VkRenderPassSetup::CreatePipeline(const VkPipeli builder.addDynamicState(VK_DYNAMIC_STATE_VIEWPORT); builder.addDynamicState(VK_DYNAMIC_STATE_SCISSOR); - // builder.addDynamicState(VK_DYNAMIC_STATE_LINE_WIDTH); builder.addDynamicState(VK_DYNAMIC_STATE_DEPTH_BIAS); - // builder.addDynamicState(VK_DYNAMIC_STATE_BLEND_CONSTANTS); - // builder.addDynamicState(VK_DYNAMIC_STATE_DEPTH_BOUNDS); - // builder.addDynamicState(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK); - // builder.addDynamicState(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK); builder.addDynamicState(VK_DYNAMIC_STATE_STENCIL_REFERENCE); // Note: the actual values are ignored since we use dynamic viewport+scissor states diff --git a/src/common/rendering/vulkan/renderer/vk_renderpass.h b/src/common/rendering/vulkan/renderer/vk_renderpass.h index f192525225..ba5867b6af 100644 --- a/src/common/rendering/vulkan/renderer/vk_renderpass.h +++ b/src/common/rendering/vulkan/renderer/vk_renderpass.h @@ -80,44 +80,15 @@ public: VkRenderPassManager(); ~VkRenderPassManager(); - void Init(); void RenderBuffersReset(); - void UpdateDynamicSet(); - void TextureSetPoolReset(); VkRenderPassSetup *GetRenderPass(const VkRenderPassKey &key); int GetVertexFormat(int numBindingPoints, int numAttributes, size_t stride, const FVertexBufferAttribute *attrs); - VkVertexFormat *GetVertexFormat(int index); - - std::unique_ptr AllocateTextureDescriptorSet(int numLayers); VulkanPipelineLayout* GetPipelineLayout(int numLayers); - VulkanDescriptorSet* GetNullTextureDescriptorSet(); - VulkanImageView* GetNullTextureView(); - - std::unique_ptr DynamicSetLayout; - std::map> RenderPassSetup; - - std::unique_ptr DynamicSet; - private: - void CreateDynamicSetLayout(); - void CreateDescriptorPool(); - void CreateDynamicSet(); - void CreateNullTexture(); - - VulkanDescriptorSetLayout *GetTextureSetLayout(int numLayers); - - int TextureDescriptorSetsLeft = 0; - int TextureDescriptorsLeft = 0; - std::vector> TextureDescriptorPools; - std::unique_ptr DynamicDescriptorPool; - std::vector> TextureSetLayouts; + std::map> RenderPassSetup; std::vector> PipelineLayouts; std::vector VertexFormats; - - std::unique_ptr NullTexture; - std::unique_ptr NullTextureView; - std::unique_ptr NullTextureDescriptorSet; }; diff --git a/src/common/rendering/vulkan/renderer/vk_renderstate.cpp b/src/common/rendering/vulkan/renderer/vk_renderstate.cpp index 95560a870b..bac7e41d0c 100644 --- a/src/common/rendering/vulkan/renderer/vk_renderstate.cpp +++ b/src/common/rendering/vulkan/renderer/vk_renderstate.cpp @@ -25,6 +25,7 @@ #include "vulkan/system/vk_builders.h" #include "vulkan/renderer/vk_renderpass.h" #include "vulkan/renderer/vk_renderbuffers.h" +#include "vulkan/renderer/vk_descriptorset.h" #include "vulkan/textures/vk_hwtexture.h" #include "hw_skydome.h" @@ -265,6 +266,11 @@ void VkRenderState::ApplyRenderPass(int dt) mCommandBuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, mPassSetup->GetPipeline(pipelineKey)); mPipelineKey = pipelineKey; } + + if (!inRenderPass) + { + mCommandBuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, GetVulkanFrameBuffer()->GetRenderPassManager()->GetPipelineLayout(mPipelineKey.NumTextureLayers), 0, GetVulkanFrameBuffer()->GetDescriptorSetManager()->GetFixedDescriptorSet()); + } } void VkRenderState::ApplyStencilRef() @@ -432,12 +438,13 @@ void VkRenderState::ApplyMaterial() { auto fb = GetVulkanFrameBuffer(); auto passManager = fb->GetRenderPassManager(); + auto descriptors = fb->GetDescriptorSetManager(); if (mMaterial.mMaterial && mMaterial.mMaterial->Source()->isHardwareCanvas()) static_cast(mMaterial.mMaterial->Source()->GetTexture())->NeedUpdate(); - VulkanDescriptorSet* descriptorset = mMaterial.mMaterial ? static_cast(mMaterial.mMaterial)->GetDescriptorSet(mMaterial) : passManager->GetNullTextureDescriptorSet(); + VulkanDescriptorSet* descriptorset = mMaterial.mMaterial ? static_cast(mMaterial.mMaterial)->GetDescriptorSet(mMaterial) : descriptors->GetNullTextureDescriptorSet(); - mCommandBuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, passManager->GetPipelineLayout(mPipelineKey.NumTextureLayers), 1, descriptorset); + mCommandBuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, passManager->GetPipelineLayout(mPipelineKey.NumTextureLayers), 2, descriptorset); mMaterial.mChanged = false; } } @@ -450,9 +457,10 @@ void VkRenderState::ApplyDynamicSet() if (mViewpointOffset != mLastViewpointOffset || matrixOffset != mLastMatricesOffset || streamDataOffset != mLastStreamDataOffset) { auto passManager = fb->GetRenderPassManager(); + auto descriptors = fb->GetDescriptorSetManager(); uint32_t offsets[3] = { mViewpointOffset, matrixOffset, streamDataOffset }; - mCommandBuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, passManager->GetPipelineLayout(mPipelineKey.NumTextureLayers), 0, passManager->DynamicSet.get(), 3, offsets); + mCommandBuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, passManager->GetPipelineLayout(mPipelineKey.NumTextureLayers), 1, descriptors->GetDynamicDescriptorSet(), 3, offsets); mLastViewpointOffset = mViewpointOffset; mLastMatricesOffset = matrixOffset; diff --git a/src/common/rendering/vulkan/shaders/vk_shader.cpp b/src/common/rendering/vulkan/shaders/vk_shader.cpp index 544fe2a248..ff8348a74c 100644 --- a/src/common/rendering/vulkan/shaders/vk_shader.cpp +++ b/src/common/rendering/vulkan/shaders/vk_shader.cpp @@ -148,8 +148,21 @@ VkShaderProgram *VkShaderManager::Get(unsigned int eff, bool alphateston, EPassT static const char *shaderBindings = R"( + layout(set = 0, binding = 0) uniform sampler2D ShadowMap; + layout(set = 0, binding = 1) uniform sampler2DArray LightMap; + + // light buffers + layout(set = 0, binding = 2, std430) buffer LightBufferSSO + { + vec4 lights[]; + }; + + #ifdef SUPPORTS_RAYTRACING + layout(set = 0, binding = 3) uniform accelerationStructureEXT TopLevelAS; + #endif + // This must match the HWViewpointUniforms struct - layout(set = 0, binding = 0, std140) uniform ViewpointUBO { + layout(set = 1, binding = 0, std140) uniform ViewpointUBO { mat4 ProjectionMatrix; mat4 ViewMatrix; mat4 NormalViewMatrix; @@ -165,13 +178,7 @@ static const char *shaderBindings = R"( int uShadowmapFilter; }; - // light buffers - layout(set = 0, binding = 1, std430) buffer LightBufferSSO - { - vec4 lights[]; - }; - - layout(set = 0, binding = 2, std140) uniform MatricesUBO { + layout(set = 1, binding = 1, std140) uniform MatricesUBO { mat4 ModelMatrix; mat4 NormalModelMatrix; mat4 TextureMatrix; @@ -210,26 +217,22 @@ static const char *shaderBindings = R"( vec4 padding1, padding2, padding3; }; - layout(set = 0, binding = 3, std140) uniform StreamUBO { + layout(set = 1, binding = 2, std140) uniform StreamUBO { StreamData data[MAX_STREAM_DATA]; }; - layout(set = 0, binding = 4) uniform sampler2D ShadowMap; - layout(set = 0, binding = 5) uniform sampler2DArray LightMap; - layout(set = 0, binding = 6) uniform accelerationStructureEXT TopLevelAS; - // textures - layout(set = 1, binding = 0) uniform sampler2D tex; - layout(set = 1, binding = 1) uniform sampler2D texture2; - layout(set = 1, binding = 2) uniform sampler2D texture3; - layout(set = 1, binding = 3) uniform sampler2D texture4; - layout(set = 1, binding = 4) uniform sampler2D texture5; - layout(set = 1, binding = 5) uniform sampler2D texture6; - layout(set = 1, binding = 6) uniform sampler2D texture7; - layout(set = 1, binding = 7) uniform sampler2D texture8; - layout(set = 1, binding = 8) uniform sampler2D texture9; - layout(set = 1, binding = 9) uniform sampler2D texture10; - layout(set = 1, binding = 10) uniform sampler2D texture11; + layout(set = 2, binding = 0) uniform sampler2D tex; + layout(set = 2, binding = 1) uniform sampler2D texture2; + layout(set = 2, binding = 2) uniform sampler2D texture3; + layout(set = 2, binding = 3) uniform sampler2D texture4; + layout(set = 2, binding = 4) uniform sampler2D texture5; + layout(set = 2, binding = 5) uniform sampler2D texture6; + layout(set = 2, binding = 6) uniform sampler2D texture7; + layout(set = 2, binding = 7) uniform sampler2D texture8; + layout(set = 2, binding = 8) uniform sampler2D texture9; + layout(set = 2, binding = 9) uniform sampler2D texture10; + layout(set = 2, binding = 10) uniform sampler2D texture11; // This must match the PushConstants struct layout(push_constant) uniform PushConstants diff --git a/src/common/rendering/vulkan/system/vk_buffers.cpp b/src/common/rendering/vulkan/system/vk_buffers.cpp index 420b19f878..1a1227c569 100644 --- a/src/common/rendering/vulkan/system/vk_buffers.cpp +++ b/src/common/rendering/vulkan/system/vk_buffers.cpp @@ -24,7 +24,7 @@ #include "vk_builders.h" #include "vk_framebuffer.h" #include "vulkan/renderer/vk_renderstate.h" -#include "vulkan/renderer/vk_renderpass.h" +#include "vulkan/renderer/vk_descriptorset.h" #include "engineerrors.h" VKBuffer *VKBuffer::First = nullptr; @@ -197,12 +197,10 @@ void VKBuffer::Resize(size_t newsize) // Transfer data from old to new fb->GetTransferCommands()->copyBuffer(oldBuffer.get(), mBuffer.get(), 0, 0, oldsize); fb->WaitForCommands(false); + fb->GetDescriptorSetManager()->UpdateDynamicSet(); // Old buffer may be part of the dynamic set // Fetch pointer to new buffer map = mBuffer->Map(0, newsize); - - // Old buffer may be part of the dynamic set - fb->GetRenderPassManager()->UpdateDynamicSet(); } void VKBuffer::Map() diff --git a/src/common/rendering/vulkan/system/vk_device.cpp b/src/common/rendering/vulkan/system/vk_device.cpp index c61f913373..ac9d4aada7 100644 --- a/src/common/rendering/vulkan/system/vk_device.cpp +++ b/src/common/rendering/vulkan/system/vk_device.cpp @@ -406,7 +406,7 @@ VkBool32 VulkanDevice::DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT mess FString msg = callbackData->pMessage; // Attempt to parse the string because the default formatting is totally unreadable and half of what it writes is totally useless! - auto parts = msg.Split("|"); + auto parts = msg.Split(" | "); if (parts.Size() == 3) { msg = parts[2]; diff --git a/src/common/rendering/vulkan/system/vk_framebuffer.cpp b/src/common/rendering/vulkan/system/vk_framebuffer.cpp index da2ac1736c..626a7b807a 100644 --- a/src/common/rendering/vulkan/system/vk_framebuffer.cpp +++ b/src/common/rendering/vulkan/system/vk_framebuffer.cpp @@ -46,6 +46,7 @@ #include "vk_buffers.h" #include "vulkan/renderer/vk_renderstate.h" #include "vulkan/renderer/vk_renderpass.h" +#include "vulkan/renderer/vk_descriptorset.h" #include "vulkan/renderer/vk_streambuffer.h" #include "vulkan/renderer/vk_postprocess.h" #include "vulkan/renderer/vk_renderbuffers.h" @@ -153,6 +154,7 @@ void VulkanFrameBuffer::InitializeState() mActiveRenderBuffers = mScreenBuffers.get(); mPostprocess.reset(new VkPostprocess()); + mDescriptorSetManager.reset(new VkDescriptorSetManager()); mRenderPassManager.reset(new VkRenderPassManager()); mRaytrace.reset(new VkRaytrace()); @@ -169,7 +171,7 @@ void VulkanFrameBuffer::InitializeState() mShaderManager.reset(new VkShaderManager(device)); mSamplerManager.reset(new VkSamplerManager(device)); - mRenderPassManager->Init(); + mDescriptorSetManager->Init(); #ifdef __APPLE__ mRenderState.reset(new VkRenderStateMolten()); #else @@ -221,6 +223,7 @@ void VulkanFrameBuffer::DeleteFrameObjects(bool uploadOnly) if (!uploadOnly) { + FrameDeleteList.AccelStructs.clear(); FrameDeleteList.Images.clear(); FrameDeleteList.ImageViews.clear(); FrameDeleteList.Framebuffers.clear(); @@ -577,7 +580,10 @@ void VulkanFrameBuffer::BeginFrame() mScreenBuffers->BeginFrame(screen->mScreenViewport.width, screen->mScreenViewport.height, screen->mSceneViewport.width, screen->mSceneViewport.height); mSaveBuffers->BeginFrame(SAVEPICWIDTH, SAVEPICHEIGHT, SAVEPICWIDTH, SAVEPICHEIGHT); mRenderState->BeginFrame(); - mRenderPassManager->UpdateDynamicSet(); + + WaitForCommands(false); + mDescriptorSetManager->UpdateFixedSet(); + mDescriptorSetManager->UpdateDynamicSet(); if (mNextTimestampQuery > 0) { diff --git a/src/common/rendering/vulkan/system/vk_framebuffer.h b/src/common/rendering/vulkan/system/vk_framebuffer.h index 059af8eedb..2463ff8c21 100644 --- a/src/common/rendering/vulkan/system/vk_framebuffer.h +++ b/src/common/rendering/vulkan/system/vk_framebuffer.h @@ -7,6 +7,7 @@ struct FRenderViewpoint; class VkSamplerManager; class VkShaderManager; +class VkDescriptorSetManager; class VkRenderPassManager; class VkRaytrace; class VkRenderState; @@ -32,6 +33,7 @@ public: VulkanCommandBuffer *GetDrawCommands(); VkShaderManager *GetShaderManager() { return mShaderManager.get(); } VkSamplerManager *GetSamplerManager() { return mSamplerManager.get(); } + VkDescriptorSetManager* GetDescriptorSetManager() { return mDescriptorSetManager.get(); } VkRenderPassManager *GetRenderPassManager() { return mRenderPassManager.get(); } VkRaytrace* GetRaytrace() { return mRaytrace.get(); } VkRenderState *GetRenderState() { return mRenderState.get(); } @@ -61,6 +63,7 @@ public: std::vector> ImageViews; std::vector> Framebuffers; std::vector> Buffers; + std::vector> AccelStructs; std::vector> Descriptors; std::vector> DescriptorPools; std::vector> CommandBuffers; @@ -135,6 +138,7 @@ private: std::unique_ptr mScreenBuffers; std::unique_ptr mSaveBuffers; std::unique_ptr mPostprocess; + std::unique_ptr mDescriptorSetManager; std::unique_ptr mRenderPassManager; std::unique_ptr mRaytrace; std::unique_ptr mCommandPool; diff --git a/src/common/rendering/vulkan/textures/vk_hwtexture.cpp b/src/common/rendering/vulkan/textures/vk_hwtexture.cpp index 15b2f7f26c..49d3f37af1 100644 --- a/src/common/rendering/vulkan/textures/vk_hwtexture.cpp +++ b/src/common/rendering/vulkan/textures/vk_hwtexture.cpp @@ -29,7 +29,7 @@ #include "vulkan/system/vk_builders.h" #include "vulkan/system/vk_framebuffer.h" #include "vulkan/textures/vk_samplers.h" -#include "vulkan/renderer/vk_renderpass.h" +#include "vulkan/renderer/vk_descriptorset.h" #include "vulkan/renderer/vk_postprocess.h" #include "vulkan/renderer/vk_renderbuffers.h" #include "vulkan/shaders/vk_shader.h" @@ -372,7 +372,7 @@ void VkMaterial::ResetAllDescriptors() auto fb = GetVulkanFrameBuffer(); if (fb) - fb->GetRenderPassManager()->TextureSetPoolReset(); + fb->GetDescriptorSetManager()->TextureSetPoolReset(); } VulkanDescriptorSet* VkMaterial::GetDescriptorSet(const FMaterialState& state) @@ -392,7 +392,7 @@ VulkanDescriptorSet* VkMaterial::GetDescriptorSet(const FMaterialState& state) int numLayers = NumLayers(); auto fb = GetVulkanFrameBuffer(); - auto descriptor = fb->GetRenderPassManager()->AllocateTextureDescriptorSet(max(numLayers, SHADER_MIN_REQUIRED_TEXTURE_LAYERS)); + auto descriptor = fb->GetDescriptorSetManager()->AllocateTextureDescriptorSet(max(numLayers, SHADER_MIN_REQUIRED_TEXTURE_LAYERS)); descriptor->SetDebugName("VkHardwareTexture.mDescriptorSets"); @@ -424,7 +424,7 @@ VulkanDescriptorSet* VkMaterial::GetDescriptorSet(const FMaterialState& state) numLayers = 3; } - auto dummyImage = fb->GetRenderPassManager()->GetNullTextureView(); + auto dummyImage = fb->GetDescriptorSetManager()->GetNullTextureView(); for (int i = numLayers; i < SHADER_MIN_REQUIRED_TEXTURE_LAYERS; i++) { update.addCombinedImageSampler(descriptor.get(), i, dummyImage, sampler, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);