MayaFlux 0.1.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VKDescriptorManager.cpp
Go to the documentation of this file.
2
4
5namespace MayaFlux::Core {
6
7// ============================================================================
8// Lifecycle
9// ============================================================================
10
12{
13 if (!m_pools.empty() || !m_layouts.empty()) {
15 "VKDescriptorManager destroyed without cleanup() - potential leak");
16 }
17}
18
20 : m_device(other.m_device)
21 , m_pools(std::move(other.m_pools))
22 , m_current_pool_index(other.m_current_pool_index)
23 , m_pool_size(other.m_pool_size)
24 , m_allocated_count(other.m_allocated_count)
25 , m_pool_capacity(other.m_pool_capacity)
26 , m_layouts(std::move(other.m_layouts))
27 , m_layout_cache(std::move(other.m_layout_cache))
28{
29 other.m_device = nullptr;
30 other.m_pools.clear();
31 other.m_layouts.clear();
32}
33
35{
36 if (this != &other) {
37 if (!m_pools.empty() || !m_layouts.empty()) {
39 "VKDescriptorManager move-assigned without cleanup() - potential leak");
40 }
41
42 m_device = other.m_device;
43 m_pools = std::move(other.m_pools);
44 m_current_pool_index = other.m_current_pool_index;
45 m_pool_size = other.m_pool_size;
46 m_allocated_count = other.m_allocated_count;
47 m_pool_capacity = other.m_pool_capacity;
48 m_layouts = std::move(other.m_layouts);
49 m_layout_cache = std::move(other.m_layout_cache);
50
51 other.m_device = nullptr;
52 other.m_pools.clear();
53 other.m_layouts.clear();
54 }
55 return *this;
56}
57
58bool VKDescriptorManager::initialize(vk::Device device, uint32_t initial_pool_size)
59{
60 if (!device) {
62 "Cannot initialize descriptor manager with null device");
63 return false;
64 }
65
66 m_device = device;
67 m_pool_size = initial_pool_size;
68
69 auto pool = create_pool(device, m_pool_size);
70 if (!pool) {
72 "Failed to create initial descriptor pool");
73 return false;
74 }
75
76 m_pools.push_back(pool);
80
82 "Descriptor manager initialized (pool size: {} sets)", m_pool_size);
83
84 return true;
85}
86
87void VKDescriptorManager::cleanup(vk::Device device)
88{
89 if (!device) {
91 "cleanup() called with null device");
92 return;
93 }
94
95 for (auto layout : m_layouts) {
96 if (layout) {
97 device.destroyDescriptorSetLayout(layout);
98 }
99 }
100 m_layouts.clear();
101 m_layout_cache.clear();
102
103 for (auto pool : m_pools) {
104 if (pool) {
105 device.destroyDescriptorPool(pool);
106 }
107 }
108 m_pools.clear();
109
112 m_pool_capacity = 0;
113 m_device = nullptr;
114
116 "Descriptor manager cleaned up");
117}
118
119// ============================================================================
120// Descriptor Set Layout
121// ============================================================================
122
124 vk::Device device,
125 const DescriptorSetLayoutConfig& config)
126{
127 if (config.bindings.empty()) {
129 "Creating descriptor set layout with no bindings");
130 }
131
132 size_t config_hash = hash_layout_config(config);
133 auto it = m_layout_cache.find(config_hash);
134 if (it != m_layout_cache.end()) {
136 "Reusing cached descriptor set layout (hash: 0x{:X})", config_hash);
137 return m_layouts[it->second];
138 }
139
140 std::vector<vk::DescriptorSetLayoutBinding> vk_bindings;
141 vk_bindings.reserve(config.bindings.size());
142
143 for (const auto& binding : config.bindings) {
144 vk::DescriptorSetLayoutBinding vk_binding;
145 vk_binding.binding = binding.binding;
146 vk_binding.descriptorType = binding.type;
147 vk_binding.descriptorCount = binding.count;
148 vk_binding.stageFlags = binding.stage_flags;
149 vk_binding.pImmutableSamplers = nullptr;
150
151 vk_bindings.push_back(vk_binding);
152 }
153
154 vk::DescriptorSetLayoutCreateInfo layout_info;
155 layout_info.bindingCount = static_cast<uint32_t>(vk_bindings.size());
156 layout_info.pBindings = vk_bindings.data();
157
158 vk::DescriptorSetLayout layout;
159 try {
160 layout = device.createDescriptorSetLayout(layout_info);
161 } catch (const vk::SystemError& e) {
163 "Failed to create descriptor set layout: {}", e.what());
164 return nullptr;
165 }
166
167 size_t layout_index = m_layouts.size();
168 m_layouts.push_back(layout);
169 m_layout_cache[config_hash] = layout_index;
170
172 "Created descriptor set layout ({} bindings, hash: 0x{:X})",
173 config.bindings.size(), config_hash);
174
175 return layout;
176}
177
179{
180 size_t hash = 0;
181
182 auto hash_combine = [](size_t& seed, size_t value) {
183 seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
184 };
185
186 for (const auto& binding : config.bindings) {
187 hash_combine(hash, binding.binding);
188 hash_combine(hash, static_cast<size_t>(binding.type));
189 hash_combine(hash, binding.count);
190 hash_combine(hash, static_cast<uint32_t>(binding.stage_flags));
191 }
192
193 return hash;
194}
195
196// ============================================================================
197// Descriptor Pool Management
198// ============================================================================
199
200vk::DescriptorPool VKDescriptorManager::create_pool(vk::Device device, uint32_t max_sets)
201{
202 std::vector<vk::DescriptorPoolSize> pool_sizes = {
203 { vk::DescriptorType::eStorageBuffer, max_sets * 4 },
204
205 { vk::DescriptorType::eUniformBuffer, max_sets * 2 },
206
207 { vk::DescriptorType::eStorageImage, max_sets * 2 },
208
209 { vk::DescriptorType::eCombinedImageSampler, max_sets * 2 },
210 };
211
212 vk::DescriptorPoolCreateInfo pool_info;
213 pool_info.flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet;
214 pool_info.maxSets = max_sets;
215 pool_info.poolSizeCount = static_cast<uint32_t>(pool_sizes.size());
216 pool_info.pPoolSizes = pool_sizes.data();
217
218 vk::DescriptorPool pool;
219 try {
220 pool = device.createDescriptorPool(pool_info);
221 } catch (const vk::SystemError& e) {
223 "Failed to create descriptor pool: {}", e.what());
224 return nullptr;
225 }
226
228 "Created descriptor pool (max sets: {})", max_sets);
229
230 return pool;
231}
232
233bool VKDescriptorManager::grow_pools(vk::Device device)
234{
235 auto new_pool = create_pool(device, m_pool_size);
236 if (!new_pool) {
238 "Failed to grow descriptor pool");
239 return false;
240 }
241
242 m_pools.push_back(new_pool);
244 m_current_pool_index = m_pools.size() - 1;
245
247 "Grew descriptor pools (new capacity: {} sets)", m_pool_capacity);
248
249 return true;
250}
251
252// ============================================================================
253// Descriptor Set Allocation
254// ============================================================================
255
257 vk::Device device,
258 vk::DescriptorSetLayout layout)
259{
260 if (!layout) {
262 "Cannot allocate descriptor set with null layout");
263 return nullptr;
264 }
265
266 if (m_pools.empty()) {
268 "No descriptor pools available - call initialize() first");
269 return nullptr;
270 }
271
272 vk::DescriptorSetAllocateInfo alloc_info;
273 alloc_info.descriptorPool = m_pools[m_current_pool_index];
274 alloc_info.descriptorSetCount = 1;
275 alloc_info.pSetLayouts = &layout;
276
277 vk::DescriptorSet set;
278 try {
279 auto sets = device.allocateDescriptorSets(alloc_info);
280 set = sets[0];
282 } catch (const vk::OutOfPoolMemoryError&) {
284 "Descriptor pool {} exhausted, growing...", m_current_pool_index);
285
286 if (!grow_pools(device)) {
287 return nullptr;
288 }
289
290 alloc_info.descriptorPool = m_pools[m_current_pool_index];
291 try {
292 auto sets = device.allocateDescriptorSets(alloc_info);
293 set = sets[0];
295 } catch (const vk::SystemError& e) {
297 "Failed to allocate descriptor set after pool growth: {}", e.what());
298 return nullptr;
299 }
300 } catch (const vk::SystemError& e) {
302 "Failed to allocate descriptor set: {}", e.what());
303 return nullptr;
304 }
305
307 "Allocated descriptor set (total: {}/{})", m_allocated_count, m_pool_capacity);
308
309 return set;
310}
311
312// ============================================================================
313// Descriptor Set Updates
314// ============================================================================
315
317 vk::Device device,
318 vk::DescriptorSet set,
319 uint32_t binding,
320 vk::Buffer buffer,
321 vk::DeviceSize offset,
322 vk::DeviceSize range)
323{
324 if (!set) {
326 "Cannot update null descriptor set");
327 return;
328 }
329
330 if (!buffer) {
332 "Cannot bind null buffer to descriptor set");
333 return;
334 }
335
336 vk::DescriptorBufferInfo buffer_info;
337 buffer_info.buffer = buffer;
338 buffer_info.offset = offset;
339 buffer_info.range = range;
340
341 vk::WriteDescriptorSet write;
342 write.dstSet = set;
343 write.dstBinding = binding;
344 write.dstArrayElement = 0;
345 write.descriptorCount = 1;
346 // Note: type is inferred from layout, but we assume storage buffer
347 // For flexibility, could add type parameter if needed
348 write.descriptorType = vk::DescriptorType::eStorageBuffer;
349 write.pBufferInfo = &buffer_info;
350
351 device.updateDescriptorSets(1, &write, 0, nullptr);
352
354 "Updated descriptor set binding {} with buffer (offset: {}, range: {})",
355 binding, offset, range);
356}
357
359 vk::Device device,
360 vk::DescriptorSet set,
361 uint32_t binding,
362 vk::ImageView image_view,
363 vk::Sampler sampler,
364 vk::ImageLayout layout)
365{
366 if (!set) {
368 "Cannot update null descriptor set");
369 return;
370 }
371
372 if (!image_view) {
374 "Cannot bind null image view to descriptor set");
375 return;
376 }
377
378 vk::DescriptorImageInfo image_info;
379 image_info.imageView = image_view;
380 image_info.sampler = sampler;
381 image_info.imageLayout = layout;
382
383 vk::WriteDescriptorSet write;
384 write.dstSet = set;
385 write.dstBinding = binding;
386 write.dstArrayElement = 0;
387 write.descriptorCount = 1;
388 write.descriptorType = sampler ? vk::DescriptorType::eCombinedImageSampler : vk::DescriptorType::eStorageImage;
389 write.pImageInfo = &image_info;
390
391 device.updateDescriptorSets(1, &write, 0, nullptr);
392
394 "Updated descriptor set binding {} with image (layout: {})",
395 binding, vk::to_string(layout));
396}
397
399 vk::Device device,
400 vk::DescriptorSet set,
401 uint32_t binding,
402 vk::Sampler sampler)
403{
404 if (!set) {
406 "Cannot update null descriptor set");
407 return;
408 }
409
410 if (!sampler) {
412 "Cannot bind null sampler to descriptor set");
413 return;
414 }
415
416 vk::DescriptorImageInfo image_info;
417 image_info.sampler = sampler;
418 image_info.imageView = nullptr;
419 image_info.imageLayout = vk::ImageLayout::eUndefined;
420
421 vk::WriteDescriptorSet write;
422 write.dstSet = set;
423 write.dstBinding = binding;
424 write.dstArrayElement = 0;
425 write.descriptorCount = 1;
426 write.descriptorType = vk::DescriptorType::eSampler;
427 write.pImageInfo = &image_info;
428
429 device.updateDescriptorSets(1, &write, 0, nullptr);
430
432 "Updated descriptor set binding {} with sampler", binding);
433}
434
436 vk::Device device,
437 vk::DescriptorSet set,
438 uint32_t binding,
439 vk::ImageView image_view,
440 vk::Sampler sampler,
441 vk::ImageLayout layout)
442{
443 if (!set) {
445 "Cannot update null descriptor set");
446 return;
447 }
448
449 if (!image_view || !sampler) {
451 "Cannot bind null image view or sampler");
452 return;
453 }
454
455 vk::DescriptorImageInfo image_info;
456 image_info.imageView = image_view;
457 image_info.sampler = sampler;
458 image_info.imageLayout = layout;
459
460 vk::WriteDescriptorSet write;
461 write.dstSet = set;
462 write.dstBinding = binding;
463 write.dstArrayElement = 0;
464 write.descriptorCount = 1;
465 write.descriptorType = vk::DescriptorType::eCombinedImageSampler;
466 write.pImageInfo = &image_info;
467
468 device.updateDescriptorSets(1, &write, 0, nullptr);
469
471 "Updated descriptor set binding {} with combined image sampler", binding);
472}
473
475 vk::Device device,
476 vk::DescriptorSet set,
477 uint32_t binding,
478 vk::ImageView image_view,
479 vk::ImageLayout layout)
480{
481 if (!set) {
483 "Cannot update null descriptor set");
484 return;
485 }
486
487 if (!image_view) {
489 "Cannot bind null image view to input attachment");
490 return;
491 }
492
493 vk::DescriptorImageInfo image_info;
494 image_info.imageView = image_view;
495 image_info.sampler = nullptr;
496 image_info.imageLayout = layout;
497
498 vk::WriteDescriptorSet write;
499 write.dstSet = set;
500 write.dstBinding = binding;
501 write.dstArrayElement = 0;
502 write.descriptorCount = 1;
503 write.descriptorType = vk::DescriptorType::eInputAttachment;
504 write.pImageInfo = &image_info;
505
506 device.updateDescriptorSets(1, &write, 0, nullptr);
507
509 "Updated descriptor set binding {} with input attachment", binding);
510}
511
513 vk::Device device,
514 vk::DescriptorSet src,
515 vk::DescriptorSet dst,
516 uint32_t copy_count)
517{
518 if (!src || !dst) {
520 "Cannot copy null descriptor sets");
521 return;
522 }
523
524 vk::CopyDescriptorSet copy;
525 copy.srcSet = src;
526 copy.srcBinding = 0;
527 copy.srcArrayElement = 0;
528 copy.dstSet = dst;
529 copy.dstBinding = 0;
530 copy.dstArrayElement = 0;
531 copy.descriptorCount = copy_count;
532
533 device.updateDescriptorSets(0, nullptr, 1, &copy);
534
536 "Copied descriptor set ({} descriptors)", copy_count);
537}
538
539std::vector<vk::DescriptorSet> VKDescriptorManager::allocate_sets(
540 vk::Device device,
541 const std::vector<vk::DescriptorSetLayout>& layouts)
542{
543 if (layouts.empty()) {
545 "Allocating zero descriptor sets");
546 return {};
547 }
548
549 if (m_pools.empty()) {
551 "No descriptor pools available - call initialize() first");
552 return {};
553 }
554
555 vk::DescriptorSetAllocateInfo alloc_info;
556 alloc_info.descriptorPool = m_pools[m_current_pool_index];
557 alloc_info.descriptorSetCount = static_cast<uint32_t>(layouts.size());
558 alloc_info.pSetLayouts = layouts.data();
559
560 std::vector<vk::DescriptorSet> sets;
561 try {
562 sets = device.allocateDescriptorSets(alloc_info);
563 m_allocated_count += static_cast<uint32_t>(layouts.size());
564 } catch (const vk::OutOfPoolMemoryError&) {
566 "Descriptor pool {} exhausted, growing...", m_current_pool_index);
567
568 if (!grow_pools(device)) {
569 return {};
570 }
571
572 alloc_info.descriptorPool = m_pools[m_current_pool_index];
573 try {
574 sets = device.allocateDescriptorSets(alloc_info);
575 m_allocated_count += static_cast<uint32_t>(layouts.size());
576 } catch (const vk::SystemError& e) {
578 "Failed to allocate descriptor sets after pool growth: {}", e.what());
579 return {};
580 }
581 } catch (const vk::SystemError& e) {
583 "Failed to allocate descriptor sets: {}", e.what());
584 return {};
585 }
586
588 "Allocated {} descriptor sets (total: {}/{})",
589 layouts.size(), m_allocated_count, m_pool_capacity);
590
591 return sets;
592}
593
595 vk::Device device,
596 const std::vector<vk::WriteDescriptorSet>& writes)
597{
598 if (writes.empty()) {
600 "Batch update called with no writes");
601 return;
602 }
603
604 device.updateDescriptorSets(
605 static_cast<uint32_t>(writes.size()),
606 writes.data(),
607 0, nullptr);
608
610 "Batch updated {} descriptor bindings", writes.size());
611}
612
613void VKDescriptorManager::reset_pools(vk::Device device)
614{
615 if (m_pools.empty()) {
616 return;
617 }
618
619 for (auto pool : m_pools) {
620 if (pool) {
621 try {
622 device.resetDescriptorPool(pool);
623 } catch (const vk::SystemError& e) {
625 "Failed to reset descriptor pool: {}", e.what());
626 }
627 }
628 }
629
632
634 "Reset all descriptor pools");
635}
636
637// ============================================================================
638// DescriptorUpdateBatch Implementation
639// ============================================================================
640
641DescriptorUpdateBatch::DescriptorUpdateBatch(vk::Device device, vk::DescriptorSet set)
642 : m_device(device)
643 , m_set(set)
644{
645}
646
648 uint32_t binding,
649 vk::Buffer buffer,
650 vk::DeviceSize offset,
651 vk::DeviceSize range)
652{
653 vk::DescriptorBufferInfo buffer_info;
654 buffer_info.buffer = buffer;
655 buffer_info.offset = offset;
656 buffer_info.range = range;
657 m_buffer_infos.push_back(buffer_info);
658
659 vk::WriteDescriptorSet write;
660 write.dstSet = m_set;
661 write.dstBinding = binding;
662 write.dstArrayElement = 0;
663 write.descriptorCount = 1;
664 write.descriptorType = vk::DescriptorType::eStorageBuffer;
665 write.pBufferInfo = &m_buffer_infos.back();
666 m_writes.push_back(write);
667
668 return *this;
669}
670
672 uint32_t binding,
673 vk::ImageView image_view,
674 vk::ImageLayout layout)
675{
676 vk::DescriptorImageInfo image_info;
677 image_info.imageView = image_view;
678 image_info.sampler = nullptr;
679 image_info.imageLayout = layout;
680 m_image_infos.push_back(image_info);
681
682 vk::WriteDescriptorSet write;
683 write.dstSet = m_set;
684 write.dstBinding = binding;
685 write.dstArrayElement = 0;
686 write.descriptorCount = 1;
687 write.descriptorType = vk::DescriptorType::eStorageImage;
688 write.pImageInfo = &m_image_infos.back();
689 m_writes.push_back(write);
690
691 return *this;
692}
693
695 uint32_t binding,
696 vk::ImageView image_view,
697 vk::Sampler sampler,
698 vk::ImageLayout layout)
699{
700 vk::DescriptorImageInfo image_info;
701 image_info.imageView = image_view;
702 image_info.sampler = sampler;
703 image_info.imageLayout = layout;
704 m_image_infos.push_back(image_info);
705
706 vk::WriteDescriptorSet write;
707 write.dstSet = m_set;
708 write.dstBinding = binding;
709 write.dstArrayElement = 0;
710 write.descriptorCount = 1;
711 write.descriptorType = vk::DescriptorType::eCombinedImageSampler;
712 write.pImageInfo = &m_image_infos.back();
713 m_writes.push_back(write);
714
715 return *this;
716}
717
719 uint32_t binding,
720 vk::Sampler sampler)
721{
722 vk::DescriptorImageInfo image_info;
723 image_info.sampler = sampler;
724 image_info.imageView = nullptr;
725 image_info.imageLayout = vk::ImageLayout::eUndefined;
726 m_image_infos.push_back(image_info);
727
728 vk::WriteDescriptorSet write;
729 write.dstSet = m_set;
730 write.dstBinding = binding;
731 write.dstArrayElement = 0;
732 write.descriptorCount = 1;
733 write.descriptorType = vk::DescriptorType::eSampler;
734 write.pImageInfo = &m_image_infos.back();
735 m_writes.push_back(write);
736
737 return *this;
738}
739
741{
742 if (m_writes.empty()) {
743 return;
744 }
745
746 m_device.updateDescriptorSets(
747 static_cast<uint32_t>(m_writes.size()),
748 m_writes.data(),
749 0, nullptr);
750}
751
752} // namespace MayaFlux::Core
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
DescriptorUpdateBatch & sampler(uint32_t binding, vk::Sampler sampler)
std::vector< vk::DescriptorBufferInfo > m_buffer_infos
std::vector< vk::WriteDescriptorSet > m_writes
DescriptorUpdateBatch & combined_image_sampler(uint32_t binding, vk::ImageView image_view, vk::Sampler sampler, vk::ImageLayout layout=vk::ImageLayout::eShaderReadOnlyOptimal)
DescriptorUpdateBatch(vk::Device device, vk::DescriptorSet set)
DescriptorUpdateBatch & buffer(uint32_t binding, vk::Buffer buffer, vk::DeviceSize offset=0, vk::DeviceSize range=VK_WHOLE_SIZE)
DescriptorUpdateBatch & storage_image(uint32_t binding, vk::ImageView image_view, vk::ImageLayout layout=vk::ImageLayout::eGeneral)
std::vector< vk::DescriptorImageInfo > m_image_infos
Fluent interface for batching descriptor updates.
void reset_pools(vk::Device device)
Reset all descriptor pools.
uint32_t m_allocated_count
Total allocated sets.
vk::DescriptorSet allocate_set(vk::Device device, vk::DescriptorSetLayout layout)
Allocate a descriptor set from the pool.
void batch_update(vk::Device device, const std::vector< vk::WriteDescriptorSet > &writes)
Batch update multiple bindings at once.
std::unordered_map< size_t, size_t > m_layout_cache
vk::DescriptorSetLayout create_layout(vk::Device device, const DescriptorSetLayoutConfig &config)
Create descriptor set layout from configuration.
uint32_t m_pool_capacity
Total capacity across all pools.
void update_image(vk::Device device, vk::DescriptorSet set, uint32_t binding, vk::ImageView image_view, vk::Sampler sampler=nullptr, vk::ImageLayout layout=vk::ImageLayout::eGeneral)
Update descriptor set with image binding.
void update_combined_image_sampler(vk::Device device, vk::DescriptorSet set, uint32_t binding, vk::ImageView image_view, vk::Sampler sampler, vk::ImageLayout layout=vk::ImageLayout::eShaderReadOnlyOptimal)
Update descriptor set with combined image+sampler.
bool grow_pools(vk::Device device)
Grow pool capacity by allocating new pool.
void cleanup(vk::Device device)
Cleanup all descriptor resources.
std::vector< vk::DescriptorSet > allocate_sets(vk::Device device, const std::vector< vk::DescriptorSetLayout > &layouts)
Allocate multiple descriptor sets at once.
void update_buffer(vk::Device device, vk::DescriptorSet set, uint32_t binding, vk::Buffer buffer, vk::DeviceSize offset=0, vk::DeviceSize range=VK_WHOLE_SIZE)
Update descriptor set with buffer binding.
vk::DescriptorPool create_pool(vk::Device device, uint32_t max_sets)
Create a new descriptor pool.
void update_input_attachment(vk::Device device, vk::DescriptorSet set, uint32_t binding, vk::ImageView image_view, vk::ImageLayout layout=vk::ImageLayout::eShaderReadOnlyOptimal)
Update descriptor set with input attachment.
VKDescriptorManager & operator=(const VKDescriptorManager &)=delete
void copy_descriptor_set(vk::Device device, vk::DescriptorSet src, vk::DescriptorSet dst, uint32_t copy_count=0)
Copy descriptor set contents.
void update_sampler(vk::Device device, vk::DescriptorSet set, uint32_t binding, vk::Sampler sampler)
Update descriptor set with sampler binding.
std::vector< vk::DescriptorPool > m_pools
size_t hash_layout_config(const DescriptorSetLayoutConfig &config) const
Compute hash of descriptor set layout config.
std::vector< vk::DescriptorSetLayout > m_layouts
bool initialize(vk::Device device, uint32_t initial_pool_size=1024)
Initialize descriptor manager.
Manages descriptor pools, layouts, and set allocation.
@ GraphicsBackend
Graphics/visual rendering backend (Vulkan, OpenGL)
@ Core
Core engine, backend, subsystems.
std::vector< DescriptorBinding > bindings
Configuration for creating a descriptor set layout.