MayaFlux 0.3.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
DescriptorBindingsProcessor.cpp
Go to the documentation of this file.
2
4
6
9
12
14
15namespace MayaFlux::Buffers {
16
18 : ShaderProcessor(shader_path)
19{
20}
21
26
27//==============================================================================
28// Binding Methods
29//==============================================================================
30
32 const std::string& name,
33 const std::shared_ptr<Nodes::Node>& node,
34 const std::string& descriptor_name,
35 uint32_t set,
37 ProcessingMode mode)
38{
39 if (!node) {
41 "Cannot bind null node '{}'", name);
42 return;
43 }
44
45 if (m_config.bindings.find(descriptor_name) == m_config.bindings.end()) {
47 "Descriptor '{}' not found in shader config", descriptor_name);
48 return;
49 }
50
51 const auto& binding_config = m_config.bindings[descriptor_name];
52
53 auto gpu_buffer = create_descriptor_buffer(sizeof(float), role);
54
55 auto [it, inserted] = m_bindings.try_emplace(name);
56 auto& binding = it->second;
57
58 binding.node = node;
59 binding.descriptor_name = descriptor_name;
60 binding.set_index = set;
61 binding.binding_index = binding_config.binding;
62 binding.role = role;
63 binding.binding_type = BindingType::SCALAR;
64 binding.gpu_buffer = gpu_buffer;
65 binding.buffer_offset = 0;
66 binding.buffer_size = sizeof(float);
67 binding.processing_mode.store(mode, std::memory_order_release);
68
69 bind_buffer(descriptor_name, gpu_buffer);
70
72
74 "Bound scalar node '{}' to descriptor '{}' (mode: {})",
75 name, descriptor_name, mode == ProcessingMode::INTERNAL ? "INTERNAL" : "EXTERNAL");
76}
77
79 const std::string& name,
80 const std::shared_ptr<Nodes::Node>& node,
81 const std::string& descriptor_name,
82 uint32_t set,
84 ProcessingMode mode)
85{
86 if (!node) {
88 "Cannot bind null node '{}'", name);
89 return;
90 }
91
92 if (m_config.bindings.find(descriptor_name) == m_config.bindings.end()) {
94 "Descriptor '{}' not found in shader config", descriptor_name);
95 return;
96 }
97
98 const auto& binding_config = m_config.bindings[descriptor_name];
99
100 size_t initial_size = 4096 * sizeof(float);
101 auto gpu_buffer = create_descriptor_buffer(initial_size, role);
102
103 auto [it, inserted] = m_bindings.try_emplace(name);
104 auto& binding = it->second;
105
106 binding.node = node;
107 binding.descriptor_name = descriptor_name;
108 binding.set_index = set;
109 binding.binding_index = binding_config.binding;
110 binding.role = role;
111 binding.binding_type = BindingType::VECTOR;
112 binding.gpu_buffer = gpu_buffer;
113 binding.buffer_offset = 0;
114 binding.buffer_size = initial_size;
115 binding.processing_mode.store(mode, std::memory_order_release);
116
117 bind_buffer(descriptor_name, gpu_buffer);
118
120
122 "Bound vector node '{}' to descriptor '{}' (mode: {})",
123 name, descriptor_name, mode == ProcessingMode::INTERNAL ? "INTERNAL" : "EXTERNAL");
124}
125
127 const std::string& name,
128 const std::shared_ptr<Nodes::Node>& node,
129 const std::string& descriptor_name,
130 uint32_t set,
132 ProcessingMode mode)
133{
134 if (!node) {
136 "Cannot bind null node '{}'", name);
137 return;
138 }
139
140 if (m_config.bindings.find(descriptor_name) == m_config.bindings.end()) {
142 "Descriptor '{}' not found in shader config", descriptor_name);
143 return;
144 }
145
146 const auto& binding_config = m_config.bindings[descriptor_name];
147
148 size_t initial_size = static_cast<long>(1024) * 1024 * sizeof(float);
149 auto gpu_buffer = create_descriptor_buffer(initial_size, role);
150
151 auto [it, inserted] = m_bindings.try_emplace(name);
152 auto& binding = it->second;
153
154 binding.node = node;
155 binding.descriptor_name = descriptor_name;
156 binding.set_index = set;
157 binding.binding_index = binding_config.binding;
158 binding.role = role;
159 binding.binding_type = BindingType::MATRIX;
160 binding.gpu_buffer = gpu_buffer;
161 binding.buffer_offset = 0;
162 binding.buffer_size = initial_size;
163 binding.processing_mode.store(mode, std::memory_order_release);
164
165 bind_buffer(descriptor_name, gpu_buffer);
166
168
170 "Bound matrix node '{}' to descriptor '{}' (mode: {})",
171 name, descriptor_name, mode == ProcessingMode::INTERNAL ? "INTERNAL" : "EXTERNAL");
172}
173
175 const std::string& name,
176 const std::shared_ptr<Nodes::Node>& node,
177 const std::string& descriptor_name,
178 uint32_t set,
180 ProcessingMode mode)
181{
182 if (!node) {
184 "Cannot bind null node '{}'", name);
185 return;
186 }
187
188 if (m_config.bindings.find(descriptor_name) == m_config.bindings.end()) {
190 "Descriptor '{}' not found in shader config", descriptor_name);
191 return;
192 }
193
194 const auto& binding_config = m_config.bindings[descriptor_name];
195
196 size_t initial_size = static_cast<long>(1024) * 64;
197 auto gpu_buffer = create_descriptor_buffer(initial_size, role);
198
199 auto [it, inserted] = m_bindings.try_emplace(name);
200 auto& binding = it->second;
201
202 binding.node = node;
203 binding.descriptor_name = descriptor_name;
204 binding.set_index = set;
205 binding.binding_index = binding_config.binding;
206 binding.role = role;
207 binding.binding_type = BindingType::STRUCTURED;
208 binding.gpu_buffer = gpu_buffer;
209 binding.buffer_offset = 0;
210 binding.buffer_size = initial_size;
211 binding.processing_mode.store(mode, std::memory_order_release);
212
213 bind_buffer(descriptor_name, gpu_buffer);
214
216
218 "Bound structured node '{}' to descriptor '{}'", name, descriptor_name);
219}
220
222 const std::string& name,
223 const std::shared_ptr<AudioBuffer>& buffer,
224 const std::string& descriptor_name,
225 uint32_t set,
227{
228 if (!buffer) {
230 "Cannot bind null AudioBuffer '{}'", name);
231 return;
232 }
233
234 if (m_config.bindings.find(descriptor_name) == m_config.bindings.end()) {
236 "Descriptor '{}' not found in shader config", descriptor_name);
237 return;
238 }
239
240 const auto& binding_config = m_config.bindings[descriptor_name];
241 size_t initial_size = buffer->get_num_samples() * sizeof(float);
242 auto gpu_buffer = create_descriptor_buffer(initial_size, role);
243
244 auto [it, inserted] = m_bindings.try_emplace(name);
245 auto& binding = it->second;
246
247 binding.buffer = buffer;
248 binding.descriptor_name = descriptor_name;
249 binding.set_index = set;
250 binding.binding_index = binding_config.binding;
251 binding.role = role;
252 binding.binding_type = BindingType::VECTOR;
253 binding.source_type = SourceType::AUDIO_BUFFER;
254 binding.gpu_buffer = gpu_buffer;
255 binding.buffer_offset = 0;
256 binding.buffer_size = initial_size;
257
258 bind_buffer(descriptor_name, gpu_buffer);
260
262 "Bound AudioBuffer '{}' to descriptor '{}' ({} samples)",
263 name, descriptor_name, buffer->get_num_samples());
264}
265
267 const std::string& name,
268 const std::shared_ptr<VKBuffer>& buffer,
269 const std::string& descriptor_name,
270 uint32_t set,
272{
273 if (!buffer) {
275 "Cannot bind null VKBuffer '{}'", name);
276 return;
277 }
278
279 if (!buffer->is_host_visible()) {
281 "VKBuffer '{}' is device-local. Binding device-local buffers as descriptor "
282 "sources requires a GPU-side copy via compute shader, not CPU readback. "
283 "Use a host-visible buffer or restructure the pipeline.",
284 name);
285 return;
286 }
287
288 if (m_config.bindings.find(descriptor_name) == m_config.bindings.end()) {
290 "Descriptor '{}' not found in shader config", descriptor_name);
291 return;
292 }
293
294 const auto& binding_config = m_config.bindings[descriptor_name];
295 auto gpu_buffer = create_descriptor_buffer(buffer->get_size_bytes(), role);
296
297 auto [it, inserted] = m_bindings.try_emplace(name);
298 auto& binding = it->second;
299
300 binding.buffer = buffer;
301 binding.descriptor_name = descriptor_name;
302 binding.set_index = set;
303 binding.binding_index = binding_config.binding;
304 binding.role = role;
305 binding.binding_type = BindingType::VECTOR;
306 binding.source_type = SourceType::HOST_VK_BUFFER;
307 binding.gpu_buffer = gpu_buffer;
308 binding.buffer_offset = 0;
309 binding.buffer_size = buffer->get_size_bytes();
310
311 bind_buffer(descriptor_name, gpu_buffer);
313
315 "Bound host VKBuffer '{}' to descriptor '{}' ({} bytes)",
316 name, descriptor_name, buffer->get_size_bytes());
317}
318
320 const std::string& name,
321 const std::shared_ptr<Nodes::Network::NodeNetwork>& network,
322 const std::string& descriptor_name,
323 uint32_t set,
325{
326 if (!network) {
328 "Cannot bind null network '{}'", name);
329 return;
330 }
331
332 if (m_config.bindings.find(descriptor_name) == m_config.bindings.end()) {
334 "Descriptor '{}' not found in shader config", descriptor_name);
335 return;
336 }
337
338 SourceType resolved {};
339 BindingType binding_type {};
340 size_t initial_size {};
341
342 auto network_mode = network->get_output_mode();
344 resolved = SourceType::NETWORK_AUDIO;
345 binding_type = BindingType::VECTOR;
346 auto probe = network->get_audio_buffer();
347 if (probe) {
348 initial_size = probe->size() * sizeof(float);
349 } else {
351 "Network '{}' is configured for audio output but has no audio buffer. "
352 "Binding will be created with default size of 4096 samples (16 KB).",
353 name);
354 initial_size = 4096 * sizeof(float);
355 }
356 } else if (network->has_operator() && dynamic_cast<Nodes::Network::GraphicsOperator*>(network->get_operator())) {
357 resolved = SourceType::NETWORK_GPU;
358 binding_type = BindingType::STRUCTURED;
359 initial_size = static_cast<size_t>(1024) * 64;
360 } else {
362 "Network '{}' produces neither audio nor GPU geometry. "
363 "Bind is only valid for networks with audio output or a GraphicsOperator.",
364 name);
365 return;
366 }
367
368 const auto& binding_config = m_config.bindings[descriptor_name];
369 auto gpu_buffer = create_descriptor_buffer(initial_size, role);
370
371 auto [it, inserted] = m_bindings.try_emplace(name);
372 auto& binding = it->second;
373
374 binding.network = network;
375 binding.descriptor_name = descriptor_name;
376 binding.set_index = set;
377 binding.binding_index = binding_config.binding;
378 binding.role = role;
379 binding.binding_type = binding_type;
380 binding.source_type = resolved;
381 binding.gpu_buffer = gpu_buffer;
382 binding.buffer_offset = 0;
383 binding.buffer_size = initial_size;
384
385 bind_buffer(descriptor_name, gpu_buffer);
387
389 "Bound network '{}' to descriptor '{}' (source: {})",
390 name, descriptor_name,
391 resolved == SourceType::NETWORK_AUDIO ? "NETWORK_AUDIO" : "NETWORK_GPU");
392}
393
394void DescriptorBindingsProcessor::unbind(const std::string& name)
395{
396 auto it = m_bindings.find(name);
397 if (it != m_bindings.end()) {
398 unbind_buffer(it->second.descriptor_name);
399 m_bindings.erase(it);
400
402
404 "Unbound '{}'", name);
405 }
406}
407
408bool DescriptorBindingsProcessor::has_binding(const std::string& name) const
409{
410 return m_bindings.find(name) != m_bindings.end();
411}
412
414{
415 std::vector<std::string> names;
416 names.reserve(m_bindings.size());
417 for (const auto& [name, _] : m_bindings) {
418 names.push_back(name);
419 }
420 return names;
421}
422
424{
425 auto it = m_bindings.find(name);
426 if (it != m_bindings.end()) {
427 it->second.processing_mode.store(mode, std::memory_order_release);
428 }
429}
430
432{
433 for (auto& [name, binding] : m_bindings) {
434 binding.processing_mode.store(mode, std::memory_order_release);
435 }
436}
437
439{
440 auto it = m_bindings.find(name);
441 if (it != m_bindings.end()) {
442 return it->second.processing_mode.load(std::memory_order_acquire);
443 }
445}
446
447//==============================================================================
448// Protected Hooks
449//==============================================================================
450
451void DescriptorBindingsProcessor::execute_shader(const std::shared_ptr<VKBuffer>& buffer)
452{
453 auto& bindings_list = buffer->get_pipeline_context().descriptor_buffer_bindings;
454
455 for (auto& [name, binding] : m_bindings) {
457
458 bool found = false;
459 for (auto& existing_binding : bindings_list) {
460 if (existing_binding.set == binding.set_index && existing_binding.binding == binding.binding_index) {
461 existing_binding.buffer_info.buffer = binding.gpu_buffer->get_buffer();
462 existing_binding.buffer_info.offset = binding.buffer_offset;
463 existing_binding.buffer_info.range = binding.buffer_size;
464 found = true;
465 break;
466 }
467 }
468
469 if (!found) {
470 bindings_list.push_back({ .set = binding.set_index,
471 .binding = binding.binding_index,
472 .type = to_vk_descriptor_type(binding.role),
473 .buffer_info = vk::DescriptorBufferInfo {
474 binding.gpu_buffer->get_buffer(),
475 binding.buffer_offset,
476 binding.buffer_size } });
477 }
478 }
479}
480
482{
484 "Pipeline created for DescriptorBindingsProcessor (ID: {}, {} node bindings)",
485 pipeline_id, m_bindings.size());
486
488}
489
490//==============================================================================
491// Private Implementation
492//==============================================================================
493
495{
496 switch (binding.source_type) {
497
498 case SourceType::NODE: {
499 if (!binding.node) {
501 "Binding has null node");
502 return;
503 }
504
505 float value {};
506
507 if (binding.processing_mode.load(std::memory_order_acquire) == ProcessingMode::INTERNAL) {
508 value = static_cast<float>(Buffers::extract_single_sample(binding.node));
509 } else {
510 value = static_cast<float>(binding.node->get_last_output());
511 }
512 Nodes::NodeContext& ctx = binding.node->get_last_context();
513
514 switch (binding.binding_type) {
515
516 case BindingType::SCALAR: {
517 ensure_buffer_capacity(binding, sizeof(float));
518 upload_to_gpu(&value, sizeof(float), binding.gpu_buffer, nullptr);
519 break;
520 }
521
522 case BindingType::VECTOR: {
523 auto* gpu_vec = dynamic_cast<Nodes::GpuVectorData*>(&ctx);
524 if (!gpu_vec || !gpu_vec->has_gpu_data()) {
526 "Node context does not provide GpuVectorData");
527 return;
528 }
529
530 auto data = gpu_vec->gpu_data();
531
532 ensure_buffer_capacity(binding, data.size_bytes());
533 upload_to_gpu(data.data(), data.size_bytes(), binding.gpu_buffer, nullptr);
534 break;
535 }
536
537 case BindingType::MATRIX: {
538 auto* gpu_mat = dynamic_cast<Nodes::GpuMatrixData*>(&ctx);
539 if (!gpu_mat || !gpu_mat->has_gpu_data()) {
541 "Node context does not provide GpuMatrixData");
542 return;
543 }
544
545 auto data = gpu_mat->gpu_data();
546 ensure_buffer_capacity(binding, data.size_bytes());
547 upload_to_gpu(data.data(), data.size_bytes(), binding.gpu_buffer, nullptr);
548 break;
549 }
550
552 auto* gpu_struct = dynamic_cast<Nodes::GpuStructuredData*>(&ctx);
553 if (!gpu_struct || !gpu_struct->has_gpu_data()) {
555 "Node context does not provide GpuStructuredData");
556 return;
557 }
558
559 auto data = gpu_struct->gpu_data();
560 ensure_buffer_capacity(binding, data.size_bytes());
561 upload_to_gpu(data.data(), data.size_bytes(), binding.gpu_buffer, nullptr);
562 break;
563 }
564 }
565 }
566
568 auto audio = std::dynamic_pointer_cast<AudioBuffer>(binding.buffer);
569 const auto& samples = audio->get_data();
570 size_t required = samples.size() * sizeof(float);
571 ensure_buffer_capacity(binding, required);
572
573 thread_local std::vector<float> conv;
574 conv.resize(samples.size());
575 std::ranges::transform(samples, conv.begin(),
576 [](double d) { return static_cast<float>(d); });
577
578 upload_to_gpu(conv.data(), required, binding.gpu_buffer, nullptr);
579 break;
580 }
581
583 auto vk = std::dynamic_pointer_cast<VKBuffer>(binding.buffer);
584 auto data = vk->get_data();
585 if (data.empty()) {
587 "Host VKBuffer binding '{}' returned no data", binding.descriptor_name);
588 return;
589 }
590 auto& raw = std::get<std::vector<uint8_t>>(data[0]);
591 ensure_buffer_capacity(binding, raw.size());
592 upload_to_gpu(raw.data(), raw.size(), binding.gpu_buffer, nullptr);
593 break;
594 }
595
597
598 auto data = binding.network->get_audio_buffer();
599 if (!data) {
601 "Network audio binding '{}' has no audio buffer", binding.descriptor_name);
602 return;
603 }
604 const auto& samples = *data;
605 size_t required = samples.size() * sizeof(float);
606
607 thread_local std::vector<float> conv;
608 conv.resize(samples.size());
609
610 std::ranges::transform(samples, conv.begin(),
611 [](double d) { return static_cast<float>(d); });
612
613 upload_to_gpu(conv.data(), required, binding.gpu_buffer, nullptr);
614 break;
615 }
616
618 auto gpu_data = extract_network_gpu_data(binding.network, binding.descriptor_name);
619 if (gpu_data.vertex_data.empty()) {
620 return;
621 }
622 ensure_buffer_capacity(binding, gpu_data.vertex_data.size_bytes());
624 gpu_data.vertex_data.data(),
625 gpu_data.vertex_data.size_bytes(),
626 binding.gpu_buffer,
627 nullptr);
628 break;
629 }
630 }
631}
632
634 DescriptorBinding& binding,
635 size_t required_size)
636{
637 if (required_size <= binding.gpu_buffer->get_size_bytes()) {
638 return;
639 }
640
641 size_t new_size = required_size * 3 / 2;
642
644 "Resizing descriptor buffer '{}': {} → {} bytes",
645 binding.descriptor_name, binding.buffer_size, new_size);
646
647 binding.gpu_buffer->resize(new_size, false);
648
649 binding.buffer_size = new_size;
650
652}
653
655 size_t size,
657{
658 VKBuffer::Usage usage {};
659
662 } else { // SSBO
664 }
665
666 auto buffer = std::make_shared<VKBuffer>(
667 size,
668 usage,
670
671 auto buffer_service = Registry::BackendRegistry::instance()
673
674 if (!buffer_service) {
675 error<std::runtime_error>(
678 std::source_location::current(),
679 "create_descriptor_buffer requires a valid BufferService");
680 }
681
682 buffer_service->initialize_buffer(buffer);
683
684 return buffer;
685}
686
687} // namespace MayaFlux::Buffers
#define MF_ERROR(comp, ctx,...)
#define MF_RT_WARN(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
ProcessingMode get_processing_mode(const std::string &name) const
Get processing mode for a specific binding.
void bind_matrix_node(const std::string &name, const std::shared_ptr< Nodes::Node > &node, const std::string &descriptor_name, uint32_t set, Portal::Graphics::DescriptorRole role=Portal::Graphics::DescriptorRole::STORAGE, ProcessingMode mode=ProcessingMode::INTERNAL)
Bind matrix node (MatrixContext) to descriptor.
void update_descriptor_from_node(DescriptorBinding &binding)
Update descriptor from node context.
void bind_scalar_node(const std::string &name, const std::shared_ptr< Nodes::Node > &node, const std::string &descriptor_name, uint32_t set, Portal::Graphics::DescriptorRole role=Portal::Graphics::DescriptorRole::UNIFORM, ProcessingMode mode=ProcessingMode::INTERNAL)
Bind scalar node output to descriptor.
void set_processing_mode(const std::string &name, ProcessingMode mode)
Set processing mode for a specific binding.
void execute_shader(const std::shared_ptr< VKBuffer > &buffer) override
void on_pipeline_created(Portal::Graphics::ComputePipelineID pipeline_id) override
Called after pipeline creation - allocates GPU buffers for descriptors.
void ensure_buffer_capacity(DescriptorBinding &binding, size_t required_size)
Ensure descriptor buffer has sufficient capacity.
bool has_binding(const std::string &name) const
Check if binding exists.
void bind_vector_node(const std::string &name, const std::shared_ptr< Nodes::Node > &node, const std::string &descriptor_name, uint32_t set, Portal::Graphics::DescriptorRole role=Portal::Graphics::DescriptorRole::STORAGE, ProcessingMode mode=ProcessingMode::INTERNAL)
Bind vector node (VectorContext) to descriptor.
void bind_network(const std::string &name, const std::shared_ptr< Nodes::Network::NodeNetwork > &network, const std::string &descriptor_name, uint32_t set, Portal::Graphics::DescriptorRole role=Portal::Graphics::DescriptorRole::STORAGE)
Bind a NodeNetwork to a descriptor.
@ INTERNAL
Processor calls extract_single_sample() or processes node context.
std::unordered_map< std::string, DescriptorBinding > m_bindings
std::shared_ptr< VKBuffer > create_descriptor_buffer(size_t size, Portal::Graphics::DescriptorRole role)
Create GPU buffer for a descriptor binding.
@ STRUCTURED
Array of structs from StructuredContext.
DescriptorBindingsProcessor(const std::string &shader_path)
Create DescriptorBindingsProcessor with shader path.
void bind_host_vk_buffer(const std::string &name, const std::shared_ptr< VKBuffer > &buffer, const std::string &descriptor_name, uint32_t set, Portal::Graphics::DescriptorRole role=Portal::Graphics::DescriptorRole::STORAGE)
Bind a host-visible VKBuffer as a descriptor source.
void bind_audio_buffer(const std::string &name, const std::shared_ptr< AudioBuffer > &buffer, const std::string &descriptor_name, uint32_t set, Portal::Graphics::DescriptorRole role=Portal::Graphics::DescriptorRole::STORAGE)
Bind an AudioBuffer as a descriptor source.
std::vector< std::string > get_binding_names() const
Get all binding names.
void bind_structured_node(const std::string &name, const std::shared_ptr< Nodes::Node > &node, const std::string &descriptor_name, uint32_t set, Portal::Graphics::DescriptorRole role=Portal::Graphics::DescriptorRole::STORAGE, ProcessingMode mode=ProcessingMode::INTERNAL)
Bind structured node (arrays of POD structs) to descriptor.
void unbind(const std::string &name)
Remove a binding.
void unbind_buffer(const std::string &descriptor_name)
Unbind a buffer from a descriptor.
virtual void on_pipeline_created(Portal::Graphics::ComputePipelineID pipeline_id)
Called after pipeline is created.
void bind_buffer(const std::string &descriptor_name, const std::shared_ptr< VKBuffer > &buffer)
Bind a VKBuffer to a named shader descriptor.
Abstract base class for shader-based buffer processing.
@ UNIFORM
Uniform buffer (host-visible when requested)
@ COMPUTE
Storage buffer for compute shaders.
GPU-uploadable 2D grid data interface.
GPU-uploadable structured data (arrays of POD structs)
GPU-uploadable 1D array data interface.
Operator that produces GPU-renderable geometry.
Base context class for node callbacks.
Definition Node.hpp:30
Interface * get_service()
Query for a backend service.
static BackendRegistry & instance()
Get the global registry instance.
double extract_single_sample(const std::shared_ptr< Nodes::Node > &node)
Extract a single sample from a node with proper snapshot management.
NetworkGpuData extract_network_gpu_data(const std::shared_ptr< Nodes::Network::NodeNetwork > &network, std::string_view name)
Extract GPU geometry data from a NodeNetwork via its GraphicsOperator.
void upload_to_gpu(const void *data, size_t size, const std::shared_ptr< VKBuffer > &target, const std::shared_ptr< VKBuffer > &staging)
Upload raw data to GPU buffer (auto-detects host-visible vs device-local)
@ BufferProcessing
Buffer processing (Buffers::BufferManager, processing chains)
@ Buffers
Buffers, Managers, processors and processing chains.
@ UNKNOWN
Unknown or undefined modality.
@ AUDIO_COMPUTE
processed each cycle but not sent to output
@ AUDIO_SINK
Aggregated audio samples sent to output.
DescriptorRole
Semantic descriptor type — maps to Vulkan descriptor types internally.
@ UNIFORM
Small, read-only, frequently updated (scalars, param structs)
std::shared_ptr< VKBuffer > gpu_buffer
UBO/SSBO backing storage.
std::unordered_map< std::string, ShaderBinding > bindings
Backend buffer management service interface.