From 514790edf0e804f6d31b20af89edac6581ca6aee Mon Sep 17 00:00:00 2001 From: joe chu Date: Thu, 5 Nov 2026 02:12:58 -0800 Subject: [PATCH] arch(vulkan): reuse descriptor slots when rebinding resources --- tests/vulkan/DescriptorRebinding.comp | 13 + tests/vulkan/Roofline/ComputeCopy.cpp | 21 +- tests/vulkan/Roofline/ComputeCopy.h | 1 + .../vulkan/VulkanDescriptorRebindingTest.cpp | 298 ++++++++++++++++++ vulkan/include/VulkanBase.h | 59 +--- vulkan/src/VulkanBase.cpp | 102 +++--- 6 files changed, 391 insertions(+), 103 deletions(-) create mode 100644 tests/vulkan/DescriptorRebinding.comp create mode 100644 tests/vulkan/VulkanDescriptorRebindingTest.cpp diff --git a/tests/vulkan/DescriptorRebinding.comp b/tests/vulkan/DescriptorRebinding.comp new file mode 100644 index 0000000..1f7e5d6 --- /dev/null +++ b/tests/vulkan/DescriptorRebinding.comp @@ -0,0 +1,13 @@ +#version 450 + +layout(local_size_x = 8) in; +layout(binding = 7) uniform Parameters { uint addend; } parameters; +layout(binding = 3) uniform sampler2D image_in; +layout(std430, binding = 11) readonly buffer Input { uint values[]; } input_data; +layout(std430, binding = 21) writeonly buffer Output { uint values[]; } output_data; + +void main() { + uint index = gl_LocalInvocationID.x; + uint texel = uint(round(textureLod(image_in, vec2(0.5), 0.0).r * 255.0)); + output_data.values[index] = input_data.values[index] + parameters.addend + texel; +} diff --git a/tests/vulkan/Roofline/ComputeCopy.cpp b/tests/vulkan/Roofline/ComputeCopy.cpp index 458a18f..69c6b28 100644 --- a/tests/vulkan/Roofline/ComputeCopy.cpp +++ b/tests/vulkan/Roofline/ComputeCopy.cpp @@ -1,6 +1,7 @@ #include "ComputeCopy.h" #include +#include namespace core { namespace vulkan { @@ -18,10 +19,24 @@ void ComputeCopy::Init() { VulkanCompute::Init(); CreateUniformBufferDescriptorSet(0, uniform_buffer_); - CreateStorageBufferDescriptorSet(1, src_buffer_); - CreateStorageBufferDescriptorSet(2, dst_buffer_); + BindBuffers(src_buffer_, dst_buffer_); +} + +void ComputeCopy::BindBuffers(const VulkanBuffer& src, + const VulkanBuffer& dst) { + if (uniform_data_.count <= 0 || + src.Size() < static_cast(uniform_data_.count) * 16 || + dst.Size() < static_cast(uniform_data_.count) * 16 || + src.buffer == dst.buffer) { + throw std::invalid_argument( + "Compute copy requires distinct buffers with sufficient capacity"); + } + CreateStorageBufferDescriptorSet(1, src); + CreateStorageBufferDescriptorSet(2, dst); - vkUpdateDescriptorSets(context_->logical_device, writes_.size(), writes_.data(), 0, nullptr); + vkUpdateDescriptorSets(context_->logical_device, + static_cast(writes_.size()), writes_.data(), + 0, nullptr); } void ComputeCopy::Run(const VkCommandBuffer command_buffer) { diff --git a/tests/vulkan/Roofline/ComputeCopy.h b/tests/vulkan/Roofline/ComputeCopy.h index bab14a9..4279721 100644 --- a/tests/vulkan/Roofline/ComputeCopy.h +++ b/tests/vulkan/Roofline/ComputeCopy.h @@ -19,6 +19,7 @@ class ComputeCopy : public VulkanCompute { void Init() override; void Run(const VkCommandBuffer command_buffer); + void BindBuffers(const VulkanBuffer& src, const VulkanBuffer& dst); protected: std::vector GetBindingInfo() const override; diff --git a/tests/vulkan/VulkanDescriptorRebindingTest.cpp b/tests/vulkan/VulkanDescriptorRebindingTest.cpp new file mode 100644 index 0000000..365768a --- /dev/null +++ b/tests/vulkan/VulkanDescriptorRebindingTest.cpp @@ -0,0 +1,298 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ComputeCopy.h" +#include "VulkanCompute.h" +#include "VulkanSampler.h" +#include "VulkanSync.h" + +namespace core { +namespace test { +namespace { + +static_assert(!std::is_copy_constructible_v); +static_assert(!std::is_copy_assignable_v); +static_assert(!std::is_copy_constructible_v); +static_assert(!std::is_copy_assignable_v); + +constexpr VkMemoryPropertyFlags kHostMemory = + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + +void SubmitAndWait(vulkan::VulkanContext& context, + vulkan::VulkanCommandBuffer& command, + vulkan::VulkanFence& fence) { + VkMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; + barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_HOST_READ_BIT; + vkCmdPipelineBarrier(command.buffer(), VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_HOST_BIT, 0, 1, &barrier, 0, nullptr, + 0, nullptr); + fence.Reset(); + command.Submit(fence.fence, vulkan::SubmitSyncInfo{}); + VK_CHECK(vkWaitForFences(context.logical_device, 1, &fence.fence, VK_TRUE, + UINT64_MAX)); +} + +class DescriptorProbe : public vulkan::VulkanCompute { + public: + DescriptorProbe(vulkan::VulkanContext* context, + std::vector bindings) + : VulkanCompute(context), bindings_(std::move(bindings)) {} + + using VulkanBase::CreateCombinedImageSamplerDescriptorSet; + using VulkanBase::CreateStorageBufferDescriptorSet; + using VulkanBase::CreateUniformBufferDescriptorSet; + + const std::vector& writes() const { return writes_; } + + void Update() { + vkUpdateDescriptorSets(context_->logical_device, + static_cast(writes_.size()), + writes_.data(), 0, nullptr); + } + + void Run(VkCommandBuffer command) { + vkCmdBindPipeline(command, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); + vkCmdBindDescriptorSets(command, VK_PIPELINE_BIND_POINT_COMPUTE, + pipeline_layout, 0, 1, &descriptor_set_, 0, + nullptr); + vkCmdDispatch(command, 1, 1, 1); + } + + private: + std::vector GetBindingInfo() const override { + return bindings_; + } + + const std::vector& LoadShaderCode() const override { + static const std::vector code = +#include "DescriptorRebinding.comp.spv" + ; + return code; + } + + std::vector bindings_; +}; + +TEST(VulkanDescriptorRebindingTest, + ReusesComputeCopyAcrossCompletedBufferPairs) { + vulkan::VulkanContext context(true); + context.Init(); + constexpr int kCount = 513; + constexpr size_t kWords = static_cast(kCount) * 4; + constexpr size_t kGuard = 7; + constexpr VkDeviceSize kBytes = (kWords + kGuard) * sizeof(uint32_t); + std::array buffers; + for (vulkan::VulkanBuffer& buffer : buffers) { + buffer = vulkan::VulkanBuffer( + &context, kBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, kHostMemory); + } + vulkan::ComputeCopy copy(&context, buffers[0], buffers[1], kCount); + copy.Init(); + const VkPipeline pipeline = copy.pipeline; + const VkPipelineLayout layout = copy.pipeline_layout; + vulkan::VulkanCommandBuffer command(&context); + vulkan::VulkanFence fence(&context); + vulkan::VulkanBuffer short_buffer( + &context, 16, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, kHostMemory); + std::vector input(kWords + kGuard); + for (size_t round = 0; round < 6; ++round) { + SCOPED_TRACE(round); + vulkan::VulkanBuffer& source = buffers[round % buffers.size()]; + vulkan::VulkanBuffer& destination = buffers[(round + 1) % buffers.size()]; + for (size_t index = 0; index < input.size(); ++index) { + input[index] = + std::bit_cast(static_cast(round * 10000 + index)); + } + source.MapData( + [&input](void* data) { std::memcpy(data, input.data(), kBytes); }); + destination.MapData([](void* data) { std::memset(data, 0xff, kBytes); }); + command.Reset(); + if (round > 0) { + copy.BindBuffers(source, destination); + } + if (round == 5) { + EXPECT_THROW(copy.BindBuffers(short_buffer, destination), + std::invalid_argument); + EXPECT_THROW(copy.BindBuffers(source, short_buffer), + std::invalid_argument); + EXPECT_THROW(copy.BindBuffers(source, source), std::invalid_argument); + } + command.BeginCommandBuffer(); + copy.Run(command.buffer()); + SubmitAndWait(context, command, fence); + destination.MapData([&input](void* data) { + const uint32_t* actual = static_cast(data); + for (size_t index = 0; index < input.size(); ++index) { + ASSERT_EQ(actual[index], index < kWords ? input[index] : UINT32_MAX) + << index; + } + }); + source.MapData([&input](void* data) { + EXPECT_EQ(std::memcmp(data, input.data(), kBytes), 0); + }); + EXPECT_EQ(copy.pipeline, pipeline); + EXPECT_EQ(copy.pipeline_layout, layout); + } +} + +TEST(VulkanDescriptorRebindingTest, ReplacesSparseMixedBindingsInAnyOrder) { + vulkan::VulkanContext context(true); + context.Init(); + DescriptorProbe probe( + &context, {{.binding = 21, + .descriptor_type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .shader_stage = VK_SHADER_STAGE_COMPUTE_BIT}, + {.binding = 7, + .descriptor_type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .shader_stage = VK_SHADER_STAGE_COMPUTE_BIT}, + {.binding = 3, + .descriptor_type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .shader_stage = VK_SHADER_STAGE_COMPUTE_BIT}, + {.binding = 11, + .descriptor_type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .shader_stage = VK_SHADER_STAGE_COMPUTE_BIT}}); + std::array inputs; + std::array uniforms; + std::array outputs; + std::array images; + vulkan::VulkanSampler sampler(&context); + for (size_t index = 0; index < inputs.size(); ++index) { + inputs[index] = vulkan::VulkanBuffer( + &context, 32, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, kHostMemory); + uniforms[index] = vulkan::VulkanBuffer( + &context, 16, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, kHostMemory); + outputs[index] = vulkan::VulkanBuffer( + &context, 48, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, kHostMemory); + inputs[index].MapData([index](void* data) { + uint32_t* values = static_cast(data); + for (uint32_t element = 0; element < 8; ++element) { + values[element] = static_cast(index) * 100 + element; + } + }); + uniforms[index].MapData([index](void* data) { + const uint32_t addend = static_cast(index) * 1000 + 17; + std::memcpy(data, &addend, sizeof(addend)); + }); + images[index] = vulkan::VulkanImage( + &context, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + VK_IMAGE_ASPECT_COLOR_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + vulkan::VulkanBuffer staging(&context, 4, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + kHostMemory); + staging.MapData([index](void* data) { + const std::array pixel = { + static_cast(index * 100 + 31), 0, 0, 255}; + std::memcpy(data, pixel.data(), pixel.size()); + }); + vulkan::VulkanCommandBuffer upload = + vulkan::VulkanCommandBuffer::BeginOneTimeCommands(&context); + images[index].TransitionImageLayout( + upload.buffer(), VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_FORMAT_R8G8B8A8_UNORM); + staging.CopyToImage(upload.buffer(), images[index], 1, 1); + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = images[index].image; + barrier.subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1}; + vkCmdPipelineBarrier(upload.buffer(), VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, + nullptr, 1, &barrier); + vulkan::VulkanFence fence(&context); + fence.Reset(); + upload.Submit(fence.fence, vulkan::SubmitSyncInfo{}); + VK_CHECK(vkWaitForFences(context.logical_device, 1, &fence.fence, VK_TRUE, + UINT64_MAX)); + } + EXPECT_THROW(probe.CreateStorageBufferDescriptorSet(11, inputs[0]), + std::logic_error); + probe.Init(); + const VkPipeline pipeline = probe.pipeline; + const VkWriteDescriptorSet* writes = probe.writes().data(); + vulkan::VulkanCommandBuffer command(&context); + vulkan::VulkanFence fence(&context); + for (size_t round = 0; round < 4; ++round) { + SCOPED_TRACE(round); + command.Reset(); + for (vulkan::VulkanBuffer& output : outputs) { + output.MapData([](void* data) { std::memset(data, 0xff, 48); }); + } + for (size_t replacement = 0; replacement < 20; ++replacement) { + const size_t index = (replacement + round) % 2; + probe.CreateCombinedImageSamplerDescriptorSet(3, images[index].image_view, + sampler.sampler); + probe.CreateStorageBufferDescriptorSet(11, inputs[index]); + probe.CreateUniformBufferDescriptorSet(7, uniforms[index]); + probe.CreateStorageBufferDescriptorSet(21, outputs[index]); + } + EXPECT_THROW(probe.CreateStorageBufferDescriptorSet(7, inputs[0]), + std::invalid_argument); + EXPECT_THROW(probe.CreateUniformBufferDescriptorSet(11, uniforms[0]), + std::invalid_argument); + EXPECT_THROW(probe.CreateCombinedImageSamplerDescriptorSet( + 21, images[0].image_view, sampler.sampler), + std::invalid_argument); + EXPECT_THROW(probe.CreateStorageBufferDescriptorSet(0, inputs[0]), + std::invalid_argument); + ASSERT_EQ(probe.writes().size(), 4U); + EXPECT_EQ(probe.writes().data(), writes); + probe.Update(); + command.BeginCommandBuffer(); + probe.Run(command.buffer()); + SubmitAndWait(context, command, fence); + const size_t selected = (19 + round) % 2; + for (size_t index = 0; index < outputs.size(); ++index) { + outputs[index].MapData([index, selected](void* data) { + const uint32_t* values = static_cast(data); + for (uint32_t element = 0; element < 12; ++element) { + const uint32_t expected = + index == selected && element < 8 + ? static_cast(index) * 1200 + element + 48 + : UINT32_MAX; + ASSERT_EQ(values[element], expected) << index << ":" << element; + } + }); + } + EXPECT_EQ(probe.pipeline, pipeline); + } +} + +TEST(VulkanDescriptorRebindingTest, + RejectsDuplicateLayoutBindingsBeforeAllocation) { + vulkan::VulkanContext context(true); + context.Init(); + DescriptorProbe probe(&context, + {{.binding = 11, + .descriptor_type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .shader_stage = VK_SHADER_STAGE_COMPUTE_BIT}, + {.binding = 11, + .descriptor_type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .shader_stage = VK_SHADER_STAGE_COMPUTE_BIT}}); + EXPECT_THROW(probe.Init(), std::invalid_argument); + EXPECT_EQ(probe.pipeline, VK_NULL_HANDLE); + EXPECT_EQ(probe.pipeline_layout, VK_NULL_HANDLE); + EXPECT_TRUE(probe.writes().empty()); +} + +} +} +} diff --git a/vulkan/include/VulkanBase.h b/vulkan/include/VulkanBase.h index f7a5dee..725605a 100644 --- a/vulkan/include/VulkanBase.h +++ b/vulkan/include/VulkanBase.h @@ -1,7 +1,6 @@ #pragma once -#include -#include +#include #include #include "VulkanBuffer.h" @@ -26,6 +25,8 @@ class VulkanBase { public: explicit VulkanBase(VulkanContext* context); virtual ~VulkanBase(); + VulkanBase(const VulkanBase&) = delete; + VulkanBase& operator=(const VulkanBase&) = delete; virtual void Init(); @@ -58,59 +59,7 @@ class VulkanBase { std::vector writes_; private: - std::mutex write_mutex_; - - int buffer_idx_ = 0; - int image_idx_ = 0; - - int GetBufferSize(const std::vector&& bindings) const { - int size = 0; - // TODO: consider other types of buffer - for (const auto& info : bindings) { - if (info.descriptor_type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || - info.descriptor_type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) { - size++; - } - } - return size; - } - - int GetImageSize(const std::vector&& bindings) const { - int size = 0; - // TODO: consider other types of image - for (const auto& info : bindings) { - if (info.descriptor_type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || - info.descriptor_type == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || - info.descriptor_type == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) { - size++; - } - } - return size; - } - - void CheckBufferInfoSize() { - const auto size = GetBufferSize(GetBindingInfo()); - const auto total_size = GetBindingInfo().size(); - if (buffer_infos_.size() == 0) { - buffer_infos_.resize(size); - } - std::lock_guard lock(write_mutex_); - if (writes_.size() == 0) { - writes_.resize(total_size); - } - } - - void CheckImageInfoSize() { - const auto size = GetImageSize(GetBindingInfo()); - const auto total_size = GetBindingInfo().size(); - if (image_infos_.size() == 0) { - image_infos_.resize(size); - } - std::lock_guard lock(write_mutex_); - if (writes_.size() == 0) { - writes_.resize(total_size); - } - } + size_t GetDescriptorIndex(uint32_t binding, VkDescriptorType type) const; }; } // namespace vulkan diff --git a/vulkan/src/VulkanBase.cpp b/vulkan/src/VulkanBase.cpp index c250774..84ce770 100644 --- a/vulkan/src/VulkanBase.cpp +++ b/vulkan/src/VulkanBase.cpp @@ -1,6 +1,8 @@ #include "VulkanBase.h" +#include #include +#include namespace core { namespace vulkan { @@ -38,6 +40,12 @@ void VulkanBase::Init() { VkDescriptorSetLayoutCreateInfo layout_info{}; layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; const auto binding_info = CreateDescriptorLayoutBindings(); + std::unordered_set declared_bindings; + for (const VkDescriptorSetLayoutBinding& binding : binding_info) { + if (!declared_bindings.insert(binding.binding).second) { + throw std::invalid_argument("Duplicate descriptor binding"); + } + } layout_info.bindingCount = static_cast(binding_info.size()); layout_info.pBindings = binding_info.data(); VK_CHECK(vkCreateDescriptorSetLayout(context_->logical_device, &layout_info, nullptr, @@ -67,6 +75,18 @@ void VulkanBase::Init() { alloc_info.pSetLayouts = &descriptor_set_layout_; VK_CHECK(vkAllocateDescriptorSets(context_->logical_device, &alloc_info, &descriptor_set_)); + buffer_infos_.resize(binding_info.size()); + image_infos_.resize(binding_info.size()); + writes_.resize(binding_info.size()); + for (size_t index = 0; index < binding_info.size(); ++index) { + VkWriteDescriptorSet& write = writes_[index]; + write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write.dstSet = descriptor_set_; + write.dstBinding = binding_info[index].binding; + write.descriptorType = binding_info[index].descriptorType; + write.descriptorCount = 1; + } + // 3. Create pipeline layout VkPipelineLayoutCreateInfo pipeline_layout_info{}; pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; @@ -126,59 +146,51 @@ VkShaderModule VulkanBase::CreateShaderModule(const std::vector& shade return shader_module; } +size_t VulkanBase::GetDescriptorIndex(uint32_t binding, VkDescriptorType type) const { + if (descriptor_set_ == VK_NULL_HANDLE) { + throw std::logic_error( + "Initialize the pipeline before preparing descriptors"); + } + const auto write = + std::find_if(writes_.begin(), writes_.end(), + [binding](const VkWriteDescriptorSet& candidate) { + return candidate.dstBinding == binding; + }); + if (write == writes_.end() || write->descriptorType != type) { + throw std::invalid_argument( + "Descriptor binding is undeclared or has a different type"); + } + return static_cast(write - writes_.begin()); +} + void VulkanBase::CreateUniformBufferDescriptorSet(const uint32_t binding, const VulkanBuffer& buffer) { - CheckBufferInfoSize(); - auto& bi = buffer_infos_[static_cast(buffer_idx_++)]; - bi.buffer = buffer.buffer; - bi.offset = 0; - bi.range = buffer.Size(); - - VkWriteDescriptorSet w{}; - w.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - w.dstSet = descriptor_set_; - w.dstBinding = binding; - w.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - w.descriptorCount = 1; - w.pBufferInfo = &bi; - writes_[static_cast(binding)] = w; + const size_t index = + GetDescriptorIndex(binding, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); + buffer_infos_[index] = { + .buffer = buffer.buffer, .offset = 0, .range = buffer.Size()}; + writes_[index].pBufferInfo = &buffer_infos_[index]; } void VulkanBase::CreateStorageBufferDescriptorSet(const uint32_t binding, const VulkanBuffer& buffer) { - CheckBufferInfoSize(); - auto& bi = buffer_infos_[static_cast(buffer_idx_++)]; - bi.buffer = buffer.buffer; - bi.offset = 0; - bi.range = buffer.Size(); - - VkWriteDescriptorSet w{}; - w.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - w.dstSet = descriptor_set_; - w.dstBinding = binding; - w.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; - w.descriptorCount = 1; - w.pBufferInfo = &bi; - writes_[static_cast(binding)] = w; + const size_t index = + GetDescriptorIndex(binding, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + buffer_infos_[index] = { + .buffer = buffer.buffer, .offset = 0, .range = buffer.Size()}; + writes_[index].pBufferInfo = &buffer_infos_[index]; } -void VulkanBase::CreateCombinedImageSamplerDescriptorSet(const uint32_t binding, - const VkImageView& image_view, - const VkSampler& sampler) { - CheckImageInfoSize(); - auto& ii = image_infos_[static_cast(image_idx_++)]; - ii.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - ii.imageView = image_view; - ii.sampler = sampler; - - VkWriteDescriptorSet w{}; - w.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - w.dstSet = descriptor_set_; - w.dstBinding = binding; - w.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - w.descriptorCount = 1; - w.pImageInfo = ⅈ - writes_[static_cast(binding)] = w; +void VulkanBase::CreateCombinedImageSamplerDescriptorSet( + const uint32_t binding, const VkImageView& image_view, + const VkSampler& sampler) { + const size_t index = + GetDescriptorIndex(binding, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); + image_infos_[index] = { + .sampler = sampler, + .imageView = image_view, + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL}; + writes_[index].pImageInfo = &image_infos_[index]; } } // namespace vulkan