MayaFlux 0.1.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VKDescriptorManager.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <vulkan/vulkan.hpp>
4
5namespace MayaFlux::Core {
6
7/**
8 * @struct DescriptorBinding
9 * @brief Describes a single descriptor binding in a set
10 *
11 * Matches the binding declaration in shaders:
12 * layout(set = X, binding = Y) buffer Data { ... };
13 */
15 uint32_t binding; ///< Binding index within set
16 vk::DescriptorType type; ///< Type (storage buffer, uniform, etc.)
17 uint32_t count; ///< Array size (1 for non-arrays)
18 vk::ShaderStageFlags stage_flags; ///< Which shader stages access this
19
21 uint32_t binding_,
22 vk::DescriptorType type_,
23 vk::ShaderStageFlags stages_,
24 uint32_t count_ = 1)
25 : binding(binding_)
26 , type(type_)
27 , count(count_)
28 , stage_flags(stages_)
29 {
30 }
31};
32
33/**
34 * @struct DescriptorSetLayoutConfig
35 * @brief Configuration for creating a descriptor set layout
36 *
37 * Defines all bindings in a descriptor set. Multiple sets can exist
38 * per pipeline (set=0, set=1, etc.), each with its own layout.
39 */
41 std::vector<DescriptorBinding> bindings;
42
43 void add_binding(uint32_t binding, vk::DescriptorType type,
44 vk::ShaderStageFlags stages, uint32_t count = 1)
45 {
46 bindings.emplace_back(binding, type, stages, count);
47 }
48
49 void add_storage_buffer(uint32_t binding, vk::ShaderStageFlags stages = vk::ShaderStageFlagBits::eCompute)
50 {
51 add_binding(binding, vk::DescriptorType::eStorageBuffer, stages);
52 }
53
54 void add_uniform_buffer(uint32_t binding, vk::ShaderStageFlags stages = vk::ShaderStageFlagBits::eCompute)
55 {
56 add_binding(binding, vk::DescriptorType::eUniformBuffer, stages);
57 }
58
59 void add_storage_image(uint32_t binding, vk::ShaderStageFlags stages = vk::ShaderStageFlagBits::eCompute)
60 {
61 add_binding(binding, vk::DescriptorType::eStorageImage, stages);
62 }
63
64 void add_sampled_image(uint32_t binding, vk::ShaderStageFlags stages = vk::ShaderStageFlagBits::eCompute)
65 {
66 add_binding(binding, vk::DescriptorType::eCombinedImageSampler, stages);
67 }
68};
69
70/**
71 * @class DescriptorUpdateBatch
72 * @brief Fluent interface for batching descriptor updates
73 *
74 * Usage:
75 * manager.begin_batch(device, descriptor_set)
76 * .buffer(0, vk_buffer)
77 * .image(1, image_view, sampler)
78 * .buffer(2, another_buffer)
79 * .submit();
80 */
82public:
83 DescriptorUpdateBatch(vk::Device device, vk::DescriptorSet set);
84
85 DescriptorUpdateBatch& buffer(uint32_t binding, vk::Buffer buffer,
86 vk::DeviceSize offset = 0,
87 vk::DeviceSize range = VK_WHOLE_SIZE);
88
89 DescriptorUpdateBatch& storage_image(uint32_t binding,
90 vk::ImageView image_view,
91 vk::ImageLayout layout = vk::ImageLayout::eGeneral);
92
94 vk::ImageView image_view,
95 vk::Sampler sampler,
96 vk::ImageLayout layout = vk::ImageLayout::eShaderReadOnlyOptimal);
97
98 DescriptorUpdateBatch& sampler(uint32_t binding, vk::Sampler sampler);
99
100 void submit(); // Actually performs the update
101
102private:
103 vk::Device m_device;
104 vk::DescriptorSet m_set;
105 std::vector<vk::WriteDescriptorSet> m_writes;
106 std::vector<vk::DescriptorBufferInfo> m_buffer_infos;
107 std::vector<vk::DescriptorImageInfo> m_image_infos;
108};
109
110/**
111 * @class VKDescriptorManager
112 * @brief Manages descriptor pools, layouts, and set allocation
113 *
114 * Responsibilities:
115 * - Create descriptor set layouts from bindings
116 * - Allocate descriptor pools
117 * - Allocate descriptor sets from pools
118 * - Update descriptor sets (bind buffers/images)
119 * - Handle pool exhaustion and growth
120 *
121 * Does NOT handle:
122 * - Pipeline creation (that's VKComputePipeline)
123 * - Command buffer recording (that's VKCommandManager)
124 * - Buffer/image creation (that's VKBuffer/VKImage)
125 *
126 * Design:
127 * - One manager per logical context (e.g., per VulkanBackend)
128 * - Pools grow automatically when exhausted
129 * - Layouts are cached by configuration
130 */
132public:
135
139 VKDescriptorManager& operator=(VKDescriptorManager&&) noexcept;
140
141 /**
142 * @brief Initialize descriptor manager
143 * @param device Logical device
144 * @param initial_pool_size Number of descriptor sets per pool
145 * @return true if initialization succeeded
146 *
147 * Creates initial descriptor pool. More pools are allocated on-demand
148 * when the current pool is exhausted.
149 */
150 bool initialize(vk::Device device, uint32_t initial_pool_size = 1024);
151
152 /**
153 * @brief Cleanup all descriptor resources
154 * @param device Logical device (must match initialization)
155 *
156 * Destroys all pools and layouts. Safe to call multiple times.
157 */
158 void cleanup(vk::Device device);
159
160 /**
161 * @brief Create descriptor set layout from configuration
162 * @param device Logical device
163 * @param config Layout configuration (bindings)
164 * @return Descriptor set layout handle, or null on failure
165 *
166 * Layouts are cached - subsequent calls with identical configs
167 * return the same layout without recreation.
168 *
169 * Example:
170 * DescriptorSetLayoutConfig config;
171 * config.add_storage_buffer(0); // layout(binding=0) buffer
172 * config.add_storage_buffer(1); // layout(binding=1) buffer
173 * auto layout = manager.create_layout(device, config);
174 */
175 vk::DescriptorSetLayout create_layout(
176 vk::Device device,
177 const DescriptorSetLayoutConfig& config);
178
179 /**
180 * @brief Allocate a descriptor set from the pool
181 * @param device Logical device
182 * @param layout Descriptor set layout
183 * @return Allocated descriptor set, or null on failure
184 *
185 * Allocates from current pool. If pool is exhausted, creates a new
186 * pool automatically and retries allocation.
187 *
188 * Sets are freed when their pool is destroyed (on cleanup()).
189 */
190 vk::DescriptorSet allocate_set(
191 vk::Device device,
192 vk::DescriptorSetLayout layout);
193
194 /**
195 * @brief Update descriptor set with buffer binding
196 * @param device Logical device
197 * @param set Descriptor set to update
198 * @param binding Binding index
199 * @param buffer Buffer to bind
200 * @param offset Offset in buffer (bytes)
201 * @param range Size to bind (VK_WHOLE_SIZE for entire buffer)
202 *
203 * Binds a buffer to the specified binding point in the descriptor set.
204 * Must match the type declared in the layout (storage/uniform buffer).
205 *
206 * Example:
207 * manager.update_buffer(device, set, 0, vk_buffer, 0, VK_WHOLE_SIZE);
208 * // Now shader can access: layout(binding=0) buffer Data { ... };
209 */
210 void update_buffer(
211 vk::Device device,
212 vk::DescriptorSet set,
213 uint32_t binding,
214 vk::Buffer buffer,
215 vk::DeviceSize offset = 0,
216 vk::DeviceSize range = VK_WHOLE_SIZE);
217
218 /**
219 * @brief Update descriptor set with image binding
220 * @param device Logical device
221 * @param set Descriptor set to update
222 * @param binding Binding index
223 * @param image_view Image view to bind
224 * @param sampler Sampler (for combined image samplers, null for storage images)
225 * @param layout Image layout (typically eGeneral or eShaderReadOnlyOptimal)
226 *
227 * Binds an image to the specified binding point in the descriptor set.
228 * For storage images: sampler = null, layout = eGeneral
229 * For sampled images: provide sampler, layout = eShaderReadOnlyOptimal
230 */
231 void update_image(
232 vk::Device device,
233 vk::DescriptorSet set,
234 uint32_t binding,
235 vk::ImageView image_view,
236 vk::Sampler sampler = nullptr,
237 vk::ImageLayout layout = vk::ImageLayout::eGeneral);
238
239 /**
240 * @brief Update descriptor set with sampler binding
241 * @param device Logical device
242 * @param set Descriptor set to update
243 * @param binding Binding index
244 * @param sampler Sampler to bind
245 *
246 * For samplers used independently of images (immutable samplers in layout).
247 */
248 void update_sampler(
249 vk::Device device,
250 vk::DescriptorSet set,
251 uint32_t binding,
252 vk::Sampler sampler);
253
254 /**
255 * @brief Update descriptor set with combined image+sampler
256 * @param device Logical device
257 * @param set Descriptor set to update
258 * @param binding Binding index
259 * @param image_view Image view to bind
260 * @param sampler Sampler to bind
261 * @param layout Image layout (typically eShaderReadOnlyOptimal)
262 *
263 * This is the standard way to bind textures in graphics shaders.
264 */
266 vk::Device device,
267 vk::DescriptorSet set,
268 uint32_t binding,
269 vk::ImageView image_view,
270 vk::Sampler sampler,
271 vk::ImageLayout layout = vk::ImageLayout::eShaderReadOnlyOptimal);
272
273 /**
274 * @brief Update descriptor set with input attachment
275 * @param device Logical device
276 * @param set Descriptor set to update
277 * @param binding Binding index
278 * @param image_view Image view to bind
279 * @param layout Image layout (must be eShaderReadOnlyOptimal or eGeneral)
280 *
281 * For reading from attachments in the same render pass (subpass inputs).
282 */
284 vk::Device device,
285 vk::DescriptorSet set,
286 uint32_t binding,
287 vk::ImageView image_view,
288 vk::ImageLayout layout = vk::ImageLayout::eShaderReadOnlyOptimal);
289
290 /**
291 * @brief Begin a batch descriptor update
292 * @return Batch builder for fluent interface
293 */
294 DescriptorUpdateBatch begin_batch(vk::Device device, vk::DescriptorSet set)
295 {
296 return { device, set };
297 }
298
299 /**
300 * @brief Copy descriptor set contents
301 * @param device Logical device
302 * @param src Source descriptor set
303 * @param dst Destination descriptor set
304 * @param copy_count Number of bindings to copy (0 = all)
305 */
307 vk::Device device,
308 vk::DescriptorSet src,
309 vk::DescriptorSet dst,
310 uint32_t copy_count = 0);
311
312 /**
313 * @brief Allocate multiple descriptor sets at once
314 * @param device Logical device
315 * @param layouts Vector of layouts (one per set)
316 * @return Vector of allocated descriptor sets
317 *
318 * More efficient than multiple allocate_set() calls.
319 */
320 std::vector<vk::DescriptorSet> allocate_sets(
321 vk::Device device,
322 const std::vector<vk::DescriptorSetLayout>& layouts);
323
324 /**
325 * @brief Batch update multiple bindings at once
326 * @param device Logical device
327 * @param writes Vector of descriptor write operations
328 *
329 * More efficient than multiple individual updates when updating
330 * many bindings in the same set.
331 */
332 void batch_update(
333 vk::Device device,
334 const std::vector<vk::WriteDescriptorSet>& writes);
335
336 /**
337 * @brief Reset all descriptor pools
338 * @param device Logical device
339 *
340 * Frees all allocated descriptor sets. Useful for clearing temporary
341 * descriptors between frames or processing cycles.
342 * Does NOT destroy pools or layouts.
343 */
344 void reset_pools(vk::Device device);
345
346 /**
347 * @brief Get current pool utilization
348 * @return Pair of (allocated_sets, total_capacity)
349 */
350 std::pair<uint32_t, uint32_t> get_pool_stats() const
351 {
353 }
354
355private:
356 vk::Device m_device = nullptr;
357
358 std::vector<vk::DescriptorPool> m_pools;
360 uint32_t m_pool_size = 1024; ///< Sets per pool
361 uint32_t m_allocated_count = 0; ///< Total allocated sets
362 uint32_t m_pool_capacity = 0; ///< Total capacity across all pools
363
364 std::vector<vk::DescriptorSetLayout> m_layouts;
365
366 std::unordered_map<size_t, size_t> m_layout_cache;
367
368 /**
369 * @brief Create a new descriptor pool
370 * @param device Logical device
371 * @param max_sets Maximum descriptor sets this pool can allocate
372 * @return Pool handle, or null on failure
373 *
374 * Pool sizes are calculated to handle common descriptor types:
375 * - Storage buffers (common in compute)
376 * - Uniform buffers (common in graphics)
377 * - Storage images (less common)
378 * - Combined image samplers (less common)
379 */
380 vk::DescriptorPool create_pool(vk::Device device, uint32_t max_sets);
381
382 /**
383 * @brief Grow pool capacity by allocating new pool
384 * @param device Logical device
385 * @return true if new pool created successfully
386 */
387 bool grow_pools(vk::Device device);
388
389 /**
390 * @brief Compute hash of descriptor set layout config
391 * @param config Layout configuration
392 * @return Hash value for cache lookup
393 */
394 size_t hash_layout_config(const DescriptorSetLayoutConfig& config) const;
395};
396
397} // namespace MayaFlux::Core
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 & 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.
DescriptorUpdateBatch begin_batch(vk::Device device, vk::DescriptorSet set)
Begin a batch descriptor update.
std::pair< uint32_t, uint32_t > get_pool_stats() const
Get current pool utilization.
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(const VKDescriptorManager &)=delete
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
Manages descriptor pools, layouts, and set allocation.
void initialize()
Definition main.cpp:11
vk::DescriptorType type
Type (storage buffer, uniform, etc.)
vk::ShaderStageFlags stage_flags
Which shader stages access this.
uint32_t count
Array size (1 for non-arrays)
uint32_t binding
Binding index within set.
DescriptorBinding(uint32_t binding_, vk::DescriptorType type_, vk::ShaderStageFlags stages_, uint32_t count_=1)
Describes a single descriptor binding in a set.
std::vector< DescriptorBinding > bindings
void add_sampled_image(uint32_t binding, vk::ShaderStageFlags stages=vk::ShaderStageFlagBits::eCompute)
void add_storage_image(uint32_t binding, vk::ShaderStageFlags stages=vk::ShaderStageFlagBits::eCompute)
void add_storage_buffer(uint32_t binding, vk::ShaderStageFlags stages=vk::ShaderStageFlagBits::eCompute)
void add_uniform_buffer(uint32_t binding, vk::ShaderStageFlags stages=vk::ShaderStageFlagBits::eCompute)
void add_binding(uint32_t binding, vk::DescriptorType type, vk::ShaderStageFlags stages, uint32_t count=1)
Configuration for creating a descriptor set layout.