32 const std::string& name,
33 const std::shared_ptr<Nodes::Node>& node,
34 const std::string& descriptor_name,
41 "Cannot bind null node '{}'", name);
47 "Descriptor '{}' not found in shader config", descriptor_name);
55 auto [it, inserted] =
m_bindings.try_emplace(name);
56 auto& binding = it->second;
59 binding.descriptor_name = descriptor_name;
60 binding.set_index = set;
61 binding.binding_index = binding_config.binding;
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);
74 "Bound scalar node '{}' to descriptor '{}' (mode: {})",
79 const std::string& name,
80 const std::shared_ptr<Nodes::Node>& node,
81 const std::string& descriptor_name,
88 "Cannot bind null node '{}'", name);
94 "Descriptor '{}' not found in shader config", descriptor_name);
100 size_t initial_size = 4096 *
sizeof(float);
103 auto [it, inserted] =
m_bindings.try_emplace(name);
104 auto& binding = it->second;
107 binding.descriptor_name = descriptor_name;
108 binding.set_index = set;
109 binding.binding_index = binding_config.binding;
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);
122 "Bound vector node '{}' to descriptor '{}' (mode: {})",
127 const std::string& name,
128 const std::shared_ptr<Nodes::Node>& node,
129 const std::string& descriptor_name,
136 "Cannot bind null node '{}'", name);
142 "Descriptor '{}' not found in shader config", descriptor_name);
148 size_t initial_size =
static_cast<long>(1024) * 1024 *
sizeof(
float);
151 auto [it, inserted] =
m_bindings.try_emplace(name);
152 auto& binding = it->second;
155 binding.descriptor_name = descriptor_name;
156 binding.set_index = set;
157 binding.binding_index = binding_config.binding;
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);
170 "Bound matrix node '{}' to descriptor '{}' (mode: {})",
175 const std::string& name,
176 const std::shared_ptr<Nodes::Node>& node,
177 const std::string& descriptor_name,
184 "Cannot bind null node '{}'", name);
190 "Descriptor '{}' not found in shader config", descriptor_name);
196 size_t initial_size =
static_cast<long>(1024) * 64;
199 auto [it, inserted] =
m_bindings.try_emplace(name);
200 auto& binding = it->second;
203 binding.descriptor_name = descriptor_name;
204 binding.set_index = set;
205 binding.binding_index = binding_config.binding;
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);
218 "Bound structured node '{}' to descriptor '{}'", name, descriptor_name);
222 const std::string& name,
223 const std::shared_ptr<AudioBuffer>& buffer,
224 const std::string& descriptor_name,
230 "Cannot bind null AudioBuffer '{}'", name);
236 "Descriptor '{}' not found in shader config", descriptor_name);
241 size_t initial_size = buffer->get_num_samples() *
sizeof(float);
244 auto [it, inserted] =
m_bindings.try_emplace(name);
245 auto& binding = it->second;
247 binding.buffer = buffer;
248 binding.descriptor_name = descriptor_name;
249 binding.set_index = set;
250 binding.binding_index = binding_config.binding;
254 binding.gpu_buffer = gpu_buffer;
255 binding.buffer_offset = 0;
256 binding.buffer_size = initial_size;
262 "Bound AudioBuffer '{}' to descriptor '{}' ({} samples)",
263 name, descriptor_name, buffer->get_num_samples());
267 const std::string& name,
268 const std::shared_ptr<VKBuffer>& buffer,
269 const std::string& descriptor_name,
275 "Cannot bind null VKBuffer '{}'", name);
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.",
290 "Descriptor '{}' not found in shader config", descriptor_name);
297 auto [it, inserted] =
m_bindings.try_emplace(name);
298 auto& binding = it->second;
300 binding.buffer = buffer;
301 binding.descriptor_name = descriptor_name;
302 binding.set_index = set;
303 binding.binding_index = binding_config.binding;
307 binding.gpu_buffer = gpu_buffer;
308 binding.buffer_offset = 0;
309 binding.buffer_size = buffer->get_size_bytes();
315 "Bound host VKBuffer '{}' to descriptor '{}' ({} bytes)",
316 name, descriptor_name, buffer->get_size_bytes());
320 const std::string& name,
321 const std::shared_ptr<Nodes::Network::NodeNetwork>& network,
322 const std::string& descriptor_name,
328 "Cannot bind null network '{}'", name);
334 "Descriptor '{}' not found in shader config", descriptor_name);
340 size_t initial_size {};
342 auto network_mode = network->get_output_mode();
346 auto probe = network->get_audio_buffer();
348 initial_size = probe->size() *
sizeof(float);
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).",
354 initial_size = 4096 *
sizeof(float);
359 initial_size =
static_cast<size_t>(1024) * 64;
362 "Network '{}' produces neither audio nor GPU geometry. "
363 "Bind is only valid for networks with audio output or a GraphicsOperator.",
371 auto [it, inserted] =
m_bindings.try_emplace(name);
372 auto& binding = it->second;
374 binding.network = network;
375 binding.descriptor_name = descriptor_name;
376 binding.set_index = set;
377 binding.binding_index = binding_config.binding;
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;
389 "Bound network '{}' to descriptor '{}' (source: {})",
390 name, descriptor_name,
404 "Unbound '{}'", name);
415 std::vector<std::string> names;
418 names.push_back(name);
427 it->second.processing_mode.store(mode, std::memory_order_release);
434 binding.processing_mode.store(mode, std::memory_order_release);
442 return it->second.processing_mode.load(std::memory_order_acquire);
453 auto& bindings_list = buffer->get_pipeline_context().descriptor_buffer_bindings;
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;
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 } });
484 "Pipeline created for DescriptorBindingsProcessor (ID: {}, {} node bindings)",
501 "Binding has null node");
510 value =
static_cast<float>(binding.
node->get_last_output());
524 if (!gpu_vec || !gpu_vec->has_gpu_data()) {
526 "Node context does not provide GpuVectorData");
530 auto data = gpu_vec->gpu_data();
539 if (!gpu_mat || !gpu_mat->has_gpu_data()) {
541 "Node context does not provide GpuMatrixData");
545 auto data = gpu_mat->gpu_data();
553 if (!gpu_struct || !gpu_struct->has_gpu_data()) {
555 "Node context does not provide GpuStructuredData");
559 auto data = gpu_struct->gpu_data();
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);
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); });
583 auto vk = std::dynamic_pointer_cast<VKBuffer>(binding.
buffer);
584 auto data = vk->get_data();
587 "Host VKBuffer binding '{}' returned no data", binding.
descriptor_name);
590 auto& raw = std::get<std::vector<uint8_t>>(data[0]);
598 auto data = binding.
network->get_audio_buffer();
601 "Network audio binding '{}' has no audio buffer", binding.
descriptor_name);
604 const auto& samples = *data;
605 size_t required = samples.size() *
sizeof(float);
607 thread_local std::vector<float> conv;
608 conv.resize(samples.size());
610 std::ranges::transform(samples, conv.begin(),
611 [](
double d) { return static_cast<float>(d); });
619 if (gpu_data.vertex_data.empty()) {
624 gpu_data.vertex_data.data(),
625 gpu_data.vertex_data.size_bytes(),
635 size_t required_size)
637 if (required_size <= binding.gpu_buffer->get_size_bytes()) {
641 size_t new_size = required_size * 3 / 2;
644 "Resizing descriptor buffer '{}': {} → {} bytes",
666 auto buffer = std::make_shared<VKBuffer>(
674 if (!buffer_service) {
675 error<std::runtime_error>(
678 std::source_location::current(),
679 "create_descriptor_buffer requires a valid BufferService");
682 buffer_service->initialize_buffer(buffer);
#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.
@ MATRIX
2D grid from MatrixContext
@ VECTOR
Array from VectorContext.
@ SCALAR
Single value from node output.
@ 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.
bool m_needs_descriptor_rebuild
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.
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.
uint64_t ComputePipelineID
DescriptorRole
Semantic descriptor type — maps to Vulkan descriptor types internally.
@ UNIFORM
Small, read-only, frequently updated (scalars, param structs)
size_t buffer_size
Size to write.
std::string descriptor_name
Matches ShaderProcessor binding name.
std::shared_ptr< Nodes::Network::NodeNetwork > network
std::shared_ptr< Nodes::Node > node
std::shared_ptr< Buffers::Buffer > buffer
std::atomic< ProcessingMode > processing_mode
std::shared_ptr< VKBuffer > gpu_buffer
UBO/SSBO backing storage.
std::unordered_map< std::string, ShaderBinding > bindings
Backend buffer management service interface.