MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Bridge.cpp
Go to the documentation of this file.
1#include "Bridge.hpp"
2
7
11
13
15
17
19 Vruta::TaskScheduler& scheduler,
20 Buffers::BufferManager& buffer_manager)
21 : m_scheduler(scheduler)
22 , m_buffer_manager(buffer_manager)
23{
24}
25
27{
28 for (auto& [layer, records] : m_records) {
29 for (auto& [id, rec] : records) {
30 cancel_inbound(rec);
31 cancel_outbound(rec);
32 }
33 }
34
35 for (auto& [layer, tasks] : m_layer_tasks) {
36 for (const auto& name : tasks)
38 }
39}
40
41// =============================================================================
42// Inbound
43// =============================================================================
44
46 const ElementRef& ref,
47 std::shared_ptr<Nodes::Node> node,
48 std::function<float(double)> project)
49{
50 auto reader = project
51 ? std::function<float()>([n = std::move(node), p = std::move(project)] {
52 return p(n->get_last_output());
53 })
54 : std::function<float()>([n = std::move(node)] {
55 return static_cast<float>(n->get_last_output());
56 });
57
58 spawn_inbound(ref, std::move(reader));
59}
60
61void Bridge::bind(const ElementRef& ref, std::function<float()> source)
62{
63 spawn_inbound(ref, std::move(source));
64}
65
66void Bridge::spawn_inbound(const ElementRef& ref, std::function<float()> source)
67{
68 auto* rec = find_record(ref);
69 if (!rec) {
71 "Bridge::bind: unknown element id {}", ref.second);
72 return;
73 }
74
75 cancel_inbound(*rec);
76
77 auto name = make_task_name(ref.second, "inbound");
78 rec->inbound_task = name;
79
80 auto writer = rec->writer;
81
82 auto routine = [](Vruta::TaskScheduler&,
83 std::function<float()> src,
84 std::function<void(float)> write) -> Vruta::GraphicsRoutine {
85 auto& p = co_await Kriya::GetGraphicsPromise {};
86 while (!p.should_terminate) {
87 write(src());
88 co_await Kriya::FrameDelay { .frames_to_wait = 1 };
89 }
90 };
91
93 std::make_shared<Vruta::GraphicsRoutine>(
94 routine(m_scheduler, std::move(source), std::move(writer))),
95 name, false);
96}
97
98// =============================================================================
99// Outbound
100// =============================================================================
101
103 const ElementRef& ref,
104 const std::shared_ptr<Buffers::VKBuffer>& target_buffer,
105 const std::string& shader_path,
106 uint32_t offset,
107 size_t size)
108{
109 auto* rec = find_record(ref);
110 if (!rec) {
112 "Bridge::write: unknown element id {}", ref.second);
113 return;
114 }
115
116 if (!rec->bindings) {
117 rec->bindings = std::make_shared<Buffers::FormaBindingsProcessor>(shader_path);
118 m_buffer_manager.add_processor(rec->bindings, target_buffer,
120 }
121
122 auto binding_name = std::to_string(ref.second) + "_pc_" + std::to_string(offset);
123 rec->bindings->bind_push_constant(binding_name, rec->reader, offset, size);
124 rec->binding_names.push_back(std::move(binding_name));
125}
126
128 const ElementRef& ref,
129 const std::shared_ptr<Buffers::VKBuffer>& target_buffer,
130 const std::string& shader_path,
131 uint32_t offset,
133{
134 auto* rec = find_record(ref);
135 if (!rec) {
137 "Bridge::write: unknown element id {}", ref.second);
138 return;
139 }
140
141 if (!rec->bindings) {
142 rec->bindings = std::make_shared<Buffers::FormaBindingsProcessor>(shader_path);
143 m_buffer_manager.add_processor(rec->bindings, target_buffer,
145 }
146
147 auto binding_name = std::to_string(ref.second) + "_pc_" + std::to_string(offset);
148 if (rec->typed_reader && rec->typed_format == format) {
149 rec->bindings->bind_typed_push_constant(binding_name, rec->typed_reader, offset);
150 } else {
151 rec->bindings->bind_push_constant(binding_name, rec->reader, offset, format);
152 }
153 rec->binding_names.push_back(std::move(binding_name));
154}
155
157 const ElementRef& ref,
158 const std::shared_ptr<Buffers::VKBuffer>& target_buffer,
159 const std::string& shader_path,
160 const std::string& descriptor_name,
161 uint32_t binding_index,
162 uint32_t set,
164{
165 auto* rec = find_record(ref);
166 if (!rec) {
168 "Bridge::write: unknown element id {}", ref.second);
169 return;
170 }
171
172 if (!rec->bindings) {
173 rec->bindings = std::make_shared<Buffers::FormaBindingsProcessor>(shader_path);
174 m_buffer_manager.add_processor(rec->bindings, target_buffer,
176 }
177
178 auto binding_name = descriptor_name + "_" + std::to_string(ref.second);
179 rec->bindings->bind_descriptor(binding_name, rec->reader, descriptor_name, binding_index, set, role);
180 rec->binding_names.push_back(std::move(binding_name));
181}
182
183void Bridge::write(const ElementRef& ref, std::shared_ptr<Buffers::AudioWriteProcessor> target)
184{
185 auto* rec = find_record(ref);
186 if (!rec) {
188 "Bridge::write: unknown element id {}", ref.second);
189 return;
190 }
191
192 auto name = make_task_name(ref.second, "audio");
193 rec->outbound_tasks.push_back(name);
194
195 auto bulk = rec->bulk_reader;
196 auto reader = rec->reader;
197
198 if (bulk) {
199 auto routine = [](Vruta::TaskScheduler&,
200 std::function<std::vector<float>()> b,
201 std::shared_ptr<Buffers::AudioWriteProcessor> proc)
203 auto& p = co_await Kriya::GetGraphicsPromise {};
204 while (!p.should_terminate) {
205 proc->set_data(std::span<const float>(b()));
206 co_await Kriya::FrameDelay { .frames_to_wait = 1 };
207 }
208 };
210 std::make_shared<Vruta::GraphicsRoutine>(
211 routine(m_scheduler, std::move(bulk), std::move(target))),
212 name, false);
213 } else {
214 auto routine = [](Vruta::TaskScheduler&,
215 std::function<float()> r,
216 std::shared_ptr<Buffers::AudioWriteProcessor> proc)
218 auto& p = co_await Kriya::GetGraphicsPromise {};
219 while (!p.should_terminate) {
220 proc->set_data(std::vector<double> { static_cast<double>(r()) });
221 co_await Kriya::FrameDelay { .frames_to_wait = 1 };
222 }
223 };
225 std::make_shared<Vruta::GraphicsRoutine>(
226 routine(m_scheduler, std::move(reader), std::move(target))),
227 name, false);
228 }
229}
230
231void Bridge::write(const ElementRef& ref, std::shared_ptr<Buffers::DataWriteProcessor> target)
232{
233 auto* rec = find_record(ref);
234 if (!rec) {
236 "Bridge::write: unknown element id {}", ref.second);
237 return;
238 }
239
240 auto name = make_task_name(ref.second, "geometry");
241 rec->outbound_tasks.push_back(name);
242
243 auto bulk = rec->bulk_reader;
244 auto reader = rec->reader;
245
246 if (bulk) {
247 auto routine = [](Vruta::TaskScheduler&,
248 std::function<std::vector<float>()> b,
249 std::shared_ptr<Buffers::DataWriteProcessor> proc)
251 auto& p = co_await Kriya::GetGraphicsPromise {};
252 while (!p.should_terminate) {
253 proc->set_data(Kakshya::DataVariant { b() });
254 co_await Kriya::FrameDelay { .frames_to_wait = 1 };
255 }
256 };
258 std::make_shared<Vruta::GraphicsRoutine>(
259 routine(m_scheduler, std::move(bulk), std::move(target))),
260 name, false);
261 } else {
262 auto routine = [](Vruta::TaskScheduler&,
263 std::function<float()> r,
264 std::shared_ptr<Buffers::DataWriteProcessor> proc)
266 auto& p = co_await Kriya::GetGraphicsPromise {};
267 while (!p.should_terminate) {
268 proc->set_data(std::vector<Kakshya::DataVariant> { Kakshya::DataVariant { std::vector<float> { r() } } });
269 co_await Kriya::FrameDelay { .frames_to_wait = 1 };
270 }
271 };
273 std::make_shared<Vruta::GraphicsRoutine>(
274 routine(m_scheduler, std::move(reader), std::move(target))),
275 name, false);
276 }
277}
278
279void Bridge::write(const ElementRef& ref, std::shared_ptr<Nodes::Constant> node)
280{
281 auto* rec = find_record(ref);
282 if (!rec) {
284 "Bridge::write: unknown element id {}", ref.second);
285 return;
286 }
287
288 auto name = make_task_name(ref.second, "constant");
289 rec->outbound_tasks.push_back(name);
290
291 auto reader = rec->reader;
292
293 auto routine = [](Vruta::TaskScheduler&,
294 std::function<float()> r,
295 std::shared_ptr<Nodes::Constant> n)
297 auto& p = co_await Kriya::GetGraphicsPromise {};
298 while (!p.should_terminate) {
299 n->set_constant(static_cast<double>(r()));
300 co_await Kriya::FrameDelay { .frames_to_wait = 1 };
301 }
302 };
303
305 std::make_shared<Vruta::GraphicsRoutine>(
306 routine(m_scheduler, std::move(reader), std::move(node))),
307 name, false);
308}
309
310void Bridge::write(const ElementRef& ref, std::function<void(std::span<const float>)> sink)
311{
312 auto* rec = find_record(ref);
313 if (!rec) {
315 "Bridge::write: unknown element id {}", ref.second);
316 return;
317 }
318
319 auto name = make_task_name(ref.second, "bulk_sink");
320 rec->outbound_tasks.push_back(name);
321
322 auto bulk = rec->bulk_reader;
323 auto reader = rec->reader;
324
325 if (bulk) {
326 auto routine = [](Vruta::TaskScheduler&,
327 std::function<std::vector<float>()> b,
328 std::function<void(std::span<const float>)> s)
330 auto& p = co_await Kriya::GetGraphicsPromise {};
331 while (!p.should_terminate) {
332 const auto v = b();
333 s(v);
334 co_await Kriya::FrameDelay { .frames_to_wait = 1 };
335 }
336 };
338 std::make_shared<Vruta::GraphicsRoutine>(
339 routine(m_scheduler, std::move(bulk), std::move(sink))),
340 name, false);
341 } else {
342 auto routine = [](Vruta::TaskScheduler&,
343 std::function<float()> r,
344 std::function<void(std::span<const float>)> s)
346 auto& p = co_await Kriya::GetGraphicsPromise {};
347 float val {};
348 while (!p.should_terminate) {
349 val = r();
350 s(std::span<const float> { &val, 1 });
351 co_await Kriya::FrameDelay { .frames_to_wait = 1 };
352 }
353 };
355 std::make_shared<Vruta::GraphicsRoutine>(
356 routine(m_scheduler, std::move(reader), std::move(sink))),
357 name, false);
358 }
359}
360
362 Layer& layer,
363 const std::vector<std::shared_ptr<MappedState<float>>>& states,
364 std::function<void(std::span<const float>)> sink)
365{
366 std::vector<std::function<float()>> readers;
367 readers.reserve(states.size());
368 for (const auto& state : states) {
369 readers.emplace_back([state] {
370 return state->value;
371 });
372 }
373 write_aggregate(layer, std::move(readers), std::move(sink));
374}
375
376// =============================================================================
377// Lifecycle
378// =============================================================================
379
381{
382 auto* rec = find_record(ref);
383 if (!rec)
384 return;
385
386 cancel_inbound(*rec);
387 cancel_outbound(*rec);
388}
389
391{
392 auto it = m_layer_tasks.find(layer.weak_from_this());
393 if (it == m_layer_tasks.end())
394 return;
395
396 for (const auto& name : it->second)
398
399 m_layer_tasks.erase(it);
400}
401
402// =============================================================================
403// Private
404// =============================================================================
405
407{
408 const auto layer_it = m_records.find(ref.first);
409 if (layer_it == m_records.end())
410 return nullptr;
411
412 const auto it = layer_it->second.find(ref.second);
413 return it == layer_it->second.end() ? nullptr : &it->second;
414}
415
416std::string Bridge::make_task_name(uint32_t id, const char* suffix) const
417{
418 return "forma_bridge_" + std::to_string(id) + "_" + suffix
419 + "_" + std::to_string(m_next_id++);
420}
421
422std::string Bridge::make_layer_task_name(const char* suffix) const
423{
424 return "forma_bridge_layer_" + std::string(suffix)
425 + "_" + std::to_string(m_next_id++);
426}
427
429{
430 if (!rec.inbound_task.empty()) {
432 rec.inbound_task.clear();
433 }
434}
435
437{
438 for (const auto& name : rec.outbound_tasks) {
440 }
441 rec.outbound_tasks.clear();
442
443 if (rec.bindings) {
444 for (const auto& name : rec.binding_names)
445 rec.bindings->unbind(name);
446 }
447 rec.binding_names.clear();
448}
449
450void Bridge::spawn_sync(Layer& layer, uint32_t id, std::function<void()> sync_fn)
451{
452 auto name = make_task_name(id, "sync");
453 const ElementRef ref { layer.weak_from_this(), id };
454 m_layer_tasks[ref.first].push_back(name);
455 if (auto* rec = find_record(ref))
456 rec->outbound_tasks.push_back(name);
457
458 auto routine = [](Vruta::TaskScheduler&,
459 std::function<void()> fn) -> Vruta::GraphicsRoutine {
460 auto& p = co_await Kriya::GetGraphicsPromise {};
461 while (!p.should_terminate) {
462 fn();
463 co_await Kriya::FrameDelay { .frames_to_wait = 1 };
464 }
465 };
466
468 std::make_shared<Vruta::GraphicsRoutine>(
469 routine(m_scheduler, std::move(sync_fn))),
470 name, false);
471}
472
474 Layer& layer,
475 std::vector<std::function<float()>> readers,
476 std::function<void(std::span<const float>)> sink)
477{
478 auto name = make_layer_task_name("bulk_sink");
479 m_layer_tasks[layer.weak_from_this()].push_back(name);
480
481 auto routine = [](Vruta::TaskScheduler&,
482 std::vector<std::function<float()>> sources,
483 std::function<void(std::span<const float>)> target)
485 auto& p = co_await Kriya::GetGraphicsPromise {};
486 std::vector<float> values(sources.size());
487 while (!p.should_terminate) {
488 for (size_t i = 0; i < sources.size(); ++i)
489 values[i] = sources[i]();
490 target(values);
491 co_await Kriya::FrameDelay { .frames_to_wait = 1 };
492 }
493 };
494
496 std::make_shared<Vruta::GraphicsRoutine>(
497 routine(m_scheduler, std::move(readers), std::move(sink))),
498 name, false);
499}
500
502 Layer& layer,
503 std::function<Kakshya::DataVariant()> assemble,
504 std::function<void(Kakshya::DataVariant)> write)
505{
506 auto name = make_layer_task_name("assembled_data");
507 m_layer_tasks[layer.weak_from_this()].push_back(name);
508
509 auto routine = [](Vruta::TaskScheduler&,
510 std::function<Kakshya::DataVariant()> make_data,
511 std::function<void(Kakshya::DataVariant)> target)
513 auto& p = co_await Kriya::GetGraphicsPromise {};
514 while (!p.should_terminate) {
515 target(make_data());
516 co_await Kriya::FrameDelay { .frames_to_wait = 1 };
517 }
518 };
519
521 std::make_shared<Vruta::GraphicsRoutine>(
522 routine(m_scheduler, std::move(assemble), std::move(write))),
523 name, false);
524}
525
526} // namespace MayaFlux::Portal::Forma
#define MF_ERROR(comp, ctx,...)
uint64_t offset
Definition Composite.cpp:10
bool format
bool size
const uint32_t * src
size_t b
std::vector< std::byte > values
Definition VDBWriter.cpp:26
std::string name
Definition VKDevice.cpp:123
void add_processor(const std::shared_ptr< BufferProcessor > &processor, const std::shared_ptr< Buffer > &buffer, ProcessingToken token=ProcessingToken::AUDIO_BACKEND)
Adds a processor to a buffer.
Token-based multimodal buffer management system for unified data stream processing.
std::map< std::weak_ptr< Layer >, std::vector< std::string >, std::owner_less<> > m_layer_tasks
Definition Bridge.hpp:699
std::map< std::weak_ptr< Layer >, std::unordered_map< uint32_t, ElementRecord >, std::owner_less<> > m_records
Definition Bridge.hpp:698
void write(std::shared_ptr< MappedState< T > > state, std::function< void(std::span< const float >)> sink)
Definition Bridge.hpp:138
void spawn_inbound(const ElementRef &ref, std::function< float()> source)
Definition Bridge.cpp:66
void write_assembled_data(Layer &layer, std::function< Kakshya::DataVariant()> assemble, std::function< void(Kakshya::DataVariant)> write)
Definition Bridge.cpp:501
void stop_sync(Layer &layer)
Stop Bridge tasks owned by a Layer.
Definition Bridge.cpp:390
void unbind(const ElementRef &ref)
Cancel all inbound and outbound bindings for an element.
Definition Bridge.cpp:380
Vruta::TaskScheduler & m_scheduler
Definition Bridge.hpp:693
void bind(const ElementRef &ref, std::shared_ptr< Nodes::Node > node, std::function< float(double)> project={})
Drive element value from a Node's output each frame.
Definition Bridge.cpp:45
void cancel_inbound(ElementRecord &rec)
Definition Bridge.cpp:428
void spawn_sync(Layer &layer, uint32_t id, std::function< void()> sync_fn)
Spawn a presentation sync task scoped to a Layer.
Definition Bridge.cpp:450
std::string make_task_name(uint32_t id, const char *suffix) const
Definition Bridge.cpp:416
Buffers::BufferManager & m_buffer_manager
Definition Bridge.hpp:694
void cancel_outbound(ElementRecord &rec)
Definition Bridge.cpp:436
void write_aggregate(Layer &layer, std::vector< std::function< float()> > readers, std::function< void(std::span< const float >)> sink)
Definition Bridge.cpp:473
ElementRecord * find_record(const ElementRef &ref)
Definition Bridge.cpp:406
Bridge(Vruta::TaskScheduler &scheduler, Buffers::BufferManager &buffer_manager)
Definition Bridge.cpp:18
std::string make_layer_task_name(const char *suffix) const
Definition Bridge.cpp:422
Flat registry of Elements on a surface.
Definition Layer.hpp:24
A C++20 coroutine-based graphics processing task with frame-accurate timing.
Definition Routine.hpp:512
void add_task(const std::shared_ptr< Routine > &routine, const std::string &name="", bool initialize=false)
Add a routine to the scheduler based on its processing token.
Definition Scheduler.cpp:94
bool cancel_task(const std::shared_ptr< Routine > &routine)
Cancels and removes a task from the scheduler.
Token-based multimodal task scheduling system for unified coroutine processing.
Definition Scheduler.hpp:51
@ GRAPHICS_BACKEND
Standard graphics processing backend configuration.
@ Init
Engine/subsystem initialization.
@ Portal
High-level user-facing API layer.
std::variant< std::vector< double >, std::vector< float >, std::vector< uint8_t >, std::vector< uint16_t >, std::vector< uint32_t >, std::vector< std::complex< float > >, std::vector< std::complex< double > >, std::vector< glm::vec2 >, std::vector< glm::vec3 >, std::vector< glm::vec4 >, std::vector< glm::mat4 > > DataVariant
Multi-type data storage for different precision needs.
Definition NDData.hpp:70
GpuDataFormat
GPU data formats with explicit precision levels.
std::pair< std::weak_ptr< Layer >, uint32_t > ElementRef
Layer-scoped element identity.
Definition Bridge.hpp:34
DescriptorRole
Semantic descriptor type — maps to Vulkan descriptor types internally.
graphics-domain awaiter for frame-accurate timing delays
Templated awaitable for accessing a coroutine's promise object.
std::vector< std::string > outbound_tasks
Definition Bridge.hpp:687
std::shared_ptr< Buffers::FormaBindingsProcessor > bindings
Definition Bridge.hpp:682
std::vector< std::string > binding_names
Definition Bridge.hpp:688
Value carrier for a Mapped primitive.
Definition Mapped.hpp:40