MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Routine.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "Promise.hpp"
4
5namespace MayaFlux::Vruta {
6
7/**
8 * @class Routine
9 * @brief Base class for all coroutine types in the MayaFlux engine
10 *
11 * This abstract base class provides the common interface and functionality
12 * for all types of coroutines, regardless of their processing domain.
13 * It enables polymorphic handling of different routine types while maintaining
14 * type safety through the template-based promise system.
15 */
16class MAYAFLUX_API Routine {
17public:
18 /**
19 * @brief Destructor
20 *
21 * Destroys the Routine and releases its reference to the
22 * underlying coroutine. If this is the last reference, the
23 * coroutine frame is destroyed.
24 */
25 virtual ~Routine() = default;
26
27 /**
28 * @brief Get the processing token that determines how this routine should be scheduled
29 * @return The processing token indicating the scheduling domain
30 */
31 [[nodiscard]] virtual ProcessingToken get_processing_token() const = 0;
32
33 /**
34 * @brief Initializes the coroutine's state for execution
35 * @param current_sample Current sample position in the timeline
36 * @return True if initialization succeeded, false otherwise
37 *
38 * Prepares the coroutine for execution by setting its initial
39 * sample position and other state. This should be called before
40 * the first attempt to resume the coroutine.
41 */
42 virtual bool initialize_state(uint64_t current_context = 0U) = 0;
43
44 /**
45 * @brief Checks if the coroutine is still active
46 * @return True if the coroutine is active, false if it's completed or invalid
47 *
48 * An active coroutine has not yet completed its execution and can
49 * be resumed. Inactive coroutines have either completed or were
50 * never properly initialized.
51 */
52 [[nodiscard]] virtual bool is_active() const = 0;
53
54 /**
55 * @brief Gets the sample position when this routine should next execute
56 * @return Sample position for next execution, or uint64_MAX if inactive
57 *
58 * This method is crucial for sample-accurate scheduling. It returns
59 * the exact sample position when the coroutine should be resumed next.
60 * The scheduler uses this to determine when to call try_resume().
61 */
62 [[nodiscard]] virtual inline uint64_t next_execution() const = 0;
63
64 /**
65 * @brief Attempts to resume the coroutine if it's ready to execute
66 * @param current_sample Current sample position in the timeline
67 * @return True if the coroutine was resumed, false otherwise
68 *
69 * This method checks if the current sample position has reached or
70 * passed the coroutine's next execution time. If so, it resumes
71 * the coroutine, allowing it to execute until its next suspension
72 * point or completion.
73 */
74 virtual bool try_resume(uint64_t current_context) = 0;
75
76 /**
77 * @brief Attempts to resume the coroutine with explicit temporal context
78 * @param current_value Current position in the timeline (samples, frames, cycles, etc.)
79 * @param context The temporal context being processed
80 * @return True if the coroutine was resumed, false otherwise
81 *
82 * This context-aware resume method allows different temporal mechanisms
83 * to coexist within the same processing token. For example, both sample-based
84 * and buffer-cycle-based delays can use SAMPLE_ACCURATE token without
85 * interfering with each other.
86 *
87 * The default implementation delegates to try_resume(uint64_t) for backward
88 * compatibility. Derived classes can override to implement context-specific
89 * resumption logic.
90 */
91 virtual bool try_resume_with_context(uint64_t current_value, DelayContext /*context*/)
92 {
93 return try_resume(current_value);
94 }
95
96 /**
97 * @brief Force resume the coroutine, bypassing all checks
98 * Used only during shutdown to push coroutines to final_suspend
99 * @return True if coroutine was resumed, false if already done
100 */
101 virtual bool force_resume() = 0;
102
103 /**
104 * @brief Check if the routine should synchronize with a clock
105 * @return True if the routine requires clock synchronization
106 */
107 [[nodiscard]] virtual bool requires_clock_sync() const = 0;
108
109 /**
110 * @brief Restarts the coroutine from the beginning
111 * @return True if restart succeeded, false otherwise
112 *
113 * Resets the coroutine to its initial state and prepares it for
114 * execution from the beginning. This allows reusing the same
115 * coroutine logic multiple times without creating a new coroutine.
116 */
117 virtual bool restart() = 0;
118
119 /**
120 * @brief Get auto_resume flag from promise
121 * @return True if coroutine should be automatically resumed
122 */
123 [[nodiscard]] virtual bool get_auto_resume() const = 0;
124
125 /**
126 * @brief Set auto_resume flag in promise
127 * @param auto_resume Whether the coroutine should be automatically resumed
128 */
129 virtual void set_auto_resume(bool auto_resume) = 0;
130
131 /**
132 * @brief Get should_terminate flag from promise
133 * @return True if coroutine should be terminated
134 */
135 [[nodiscard]] virtual bool get_should_terminate() const = 0;
136
137 /**
138 * @brief Set should_terminate flag in promise
139 * @param should_terminate Whether the coroutine should be terminated
140 */
141 virtual void set_should_terminate(bool should_terminate) = 0;
142
143 /**
144 * @brief Register a callback invoked once at final suspend or a termination request, whichever occurs first.
145 *
146 * A termination request may invoke it while is_active() still returns true.
147 * It runs on the thread that reaches final suspend or requests termination.
148 * Register before scheduling; replacing a callback while the routine is live is unsupported and racy.
149 * A callback first registered after either event runs immediately on the caller's thread.
150 * @param callback Function to invoke on completion
151 */
152 virtual void on_complete(std::function<void()> callback) = 0;
153
154 /**
155 * @brief Get sync_to_clock flag from promise
156 * @return True if coroutine should synchronize with clock
157 */
158 [[nodiscard]] virtual bool get_sync_to_clock() const = 0;
159
160 // Domain-specific timing methods (return 0/false for unsupported domains)
161 /**
162 * @brief Get next sample execution time (audio domain)
163 * @return Sample position for next execution, or 0 if not audio domain
164 */
165 [[nodiscard]] virtual uint64_t get_next_sample() const = 0;
166
167 /**
168 * @brief Set next sample execution time (audio domain)
169 * @param next_sample Sample position for next execution
170 */
171 virtual void set_next_sample(uint64_t next_sample) = 0;
172
173 /**
174 * @brief Get next frame execution time (graphics domain)
175 * @return Frame position for next execution, or 0 if not graphics domain
176 */
177 [[nodiscard]] virtual uint64_t get_next_frame() const = 0;
178
179 /**
180 * @brief Set next frame execution time (graphics domain)
181 * @param next_frame Frame position for next execution
182 */
183 virtual void set_next_frame(uint64_t next_frame) = 0;
184
185 /**
186 * @brief Get the active delay context for this routine
187 * @return Current delay context, or NONE if not waiting
188 */
189 [[nodiscard]] virtual DelayContext get_delay_context() const { return DelayContext::NONE; }
190
191 /**
192 * @brief Set the active delay context for this routine
193 * @param context New delay context
194 */
195 virtual void set_delay_context(DelayContext /* Context */) { /* no-op in base */ }
196
197 /**
198 * @brief Updates multiple named parameters in the coroutine's state
199 * @param args Variable number of key-value pairs to update
200 *
201 * Provides a convenient way to update multiple state values at once.
202 * This is useful for configuring a coroutine before or during execution.
203 *
204 * Example:
205 * ```cpp
206 * routine.update_params("duration", 2.0f, "amplitude", 0.8f);
207 * ```
208 */
209 template <typename... Args>
210 inline void update_params(Args... args)
211 {
212 update_params_impl(std::forward<Args>(args)...);
213 }
214
215 /**
216 * @brief Sets a named state value in the coroutine
217 * @param key Name of the state value
218 * @param value Value to store
219 *
220 * Stores a value in the coroutine's state dictionary under the given key.
221 * This allows the coroutine to maintain state between suspensions and
222 * enables external code to influence the coroutine's behavior.
223 */
224 template <typename T>
225 inline void set_state(const std::string& key, T value)
226 {
227 set_state_impl(key, std::move(value));
228 }
229
230 /**
231 * @brief Gets a named state value from the coroutine
232 * @param key Name of the state value to retrieve
233 * @return Pointer to the stored value, or nullptr if not found
234 *
235 * Retrieves a previously stored value from the coroutine's state
236 * dictionary. Returns nullptr if the key doesn't exist or the
237 * stored type doesn't match the requested type.
238 */
239 template <typename T>
240 inline T* get_state(const std::string& key)
241 {
242 return get_state_impl<T>(key);
243 }
244
245protected:
246 virtual void set_state_impl(const std::string& key, std::any value) = 0;
247 virtual void* get_state_impl_raw(const std::string& key) = 0;
248
249 /**
250 * @brief Implementation helper for get_state
251 * @param key Name of the state value to retrieve
252 * @return Pointer to the stored value, or nullptr if not found or type mismatch
253 */
254 template <typename T>
255 T* get_state_impl(const std::string& key)
256 {
257 void* raw_ptr = get_state_impl_raw(key);
258 if (!raw_ptr)
259 return nullptr;
260
261 try {
262 return std::any_cast<T>(static_cast<std::any*>(raw_ptr));
263 } catch (const std::bad_any_cast&) {
264 return nullptr;
265 }
266 }
267
268 /**
269 * brief Implementation helper for update_params
270 */
271 virtual void update_params_impl() { }
272
273 /**
274 * @brief Implementation helper for update_params
275 * @param promise The promise object to update
276 * @param key Name of the state value
277 * @param value Value to store
278 * @param args Remaining key-value pairs to process
279 *
280 * Recursive template function that processes each key-value pair
281 * in the update_params variadic argument list.
282 */
283 template <typename T, typename... Args>
284 void update_params_impl(const std::string& key, T value, Args... args)
285 {
286 set_state(key, std::move(value));
287 if constexpr (sizeof...(args) > 0) {
288 update_params_impl(std::forward<Args>(args)...);
289 }
290 }
291};
292
293/**
294 * @class SoundRoutine
295 * @brief A C++20 coroutine-based audio processing task with sample-accurate timing
296 *
297 * SoundRoutine encapsulates a coroutine that can execute audio processing logic
298 * with sample-accurate timing. It provides a powerful abstraction for writing
299 * time-based audio code that appears sequential but executes asynchronously
300 * in perfect sync with the audio timeline.
301 *
302 * Key features:
303 * - Sample-accurate timing for precise audio scheduling
304 * - State persistence between suspensions and resumptions
305 * - Automatic management of coroutine lifetime
306 * - Ability to restart and reschedule tasks
307 * - Dynamic parameter updates during execution
308 * - Named state storage for flexible data management
309 *
310 * This implementation leverages C++20 coroutines to create a cooperative
311 * multitasking system specifically designed for audio processing. Each routine
312 * can suspend itself at precise sample positions and be resumed exactly when needed,
313 * enabling complex temporal behaviors without blocking the audio thread.
314 *
315 * Example usage:
316 * ```cpp
317 * auto fade_in = [](TaskScheduler& scheduler) -> SoundRoutine {
318 * float gain = 0.0f;
319 * for (int i = 0; i < 100; i++) {
320 * gain += 0.01f;
321 * set_volume(gain);
322 * co_await SampleDelay{441}; // Wait 10ms at 44.1kHz
323 * }
324 * };
325 * ```
326 */
327class MAYAFLUX_API SoundRoutine : public Routine {
328public:
329 /**
330 * @brief Promise type used by this coroutine
331 *
332 * This is the promise type that manages the coroutine state and
333 * provides the co_await, co_yield, and co_return behaviors.
334 */
336
337 /**
338 * @brief Get the processing token that determines how this routine should be scheduled
339 * @return The processing token indicating the scheduling domain
340 */
341 [[nodiscard]] ProcessingToken get_processing_token() const override;
342
343 /**
344 * @brief Constructs a SoundRoutine from a coroutine handle
345 * @param h Handle to the coroutine
346 *
347 * Creates a SoundRoutine that wraps and manages the given coroutine.
348 * This is typically called by the compiler-generated code when a
349 * coroutine function returns a SoundRoutine.
350 */
351 SoundRoutine(std::coroutine_handle<promise_type> h);
352
353 SoundRoutine(const SoundRoutine& other) = delete;
354 SoundRoutine& operator=(const SoundRoutine& other) = delete;
355
356 /**
357 * @brief Move constructor
358 * @param other SoundRoutine to move from
359 *
360 * Transfers ownership of the coroutine from other to this SoundRoutine.
361 * After the move, other no longer references any coroutine.
362 */
363 SoundRoutine(SoundRoutine&& other) noexcept;
364
365 /**
366 * @brief Move assignment operator
367 * @param other SoundRoutine to move from
368 * @return Reference to this SoundRoutine
369 *
370 * Transfers ownership of the coroutine from other to this SoundRoutine.
371 * After the move, other no longer references any coroutine.
372 */
373 SoundRoutine& operator=(SoundRoutine&& other) noexcept;
374
375 ~SoundRoutine() override;
376
377 [[nodiscard]] bool is_active() const override;
378
379 bool initialize_state(uint64_t current_sample = 0U) override;
380
381 bool try_resume(uint64_t current_context) override;
382
383 bool try_resume_with_context(uint64_t current_value, DelayContext context) override;
384
385 bool force_resume() override;
386
387 [[nodiscard]] DelayContext get_delay_context() const override
388 {
389 return m_handle.promise().active_delay_context;
390 }
391
392 void set_delay_context(DelayContext context) override
393 {
394 m_handle.promise().active_delay_context = context;
395 }
396
397 bool restart() override;
398
399 [[nodiscard]] uint64_t next_execution() const override;
400
401 [[nodiscard]] bool requires_clock_sync() const override;
402
403 [[nodiscard]] bool get_auto_resume() const override
404 {
405 return m_handle.promise().auto_resume;
406 }
407
408 void set_auto_resume(bool auto_resume) override
409 {
410 m_handle.promise().auto_resume = auto_resume;
411 }
412
413 [[nodiscard]] bool get_should_terminate() const override
414 {
415 return m_handle.promise().should_terminate;
416 }
417
418 void set_should_terminate(bool should_terminate) override
419 {
420 m_handle.promise().set_should_terminate(should_terminate);
421 }
422
423 void on_complete(std::function<void()> callback) override
424 {
425 m_handle.promise().on_complete(std::move(callback));
426 }
427
428 [[nodiscard]] bool get_sync_to_clock() const override
429 {
430 return m_handle.promise().sync_to_clock;
431 }
432
433 [[nodiscard]] uint64_t get_next_sample() const override
434 {
435 return m_handle.promise().next_sample;
436 }
437
438 void set_next_sample(uint64_t next_sample) override
439 {
440 m_handle.promise().next_sample = next_sample;
441 }
442
443 [[nodiscard]] uint64_t get_next_frame() const override
444 {
445 return m_handle.promise().next_buffer_cycle;
446 }
447
448 void set_next_frame(uint64_t next_cycle) override
449 {
450 m_handle.promise().next_buffer_cycle = next_cycle;
451 }
452
453 // Non-audio domain methods (return defaults for audio routines)
454 // uint64_t get_next_frame() const override { return 0; }
455 // void set_next_frame(uint64_t /*next_frame*/) override { /* no-op for audio */ }
456
457protected:
458 void set_state_impl(const std::string& key, std::any value) override;
459 void* get_state_impl_raw(const std::string& key) override;
460
461private:
462 /**
463 * @brief Handle to the underlying coroutine
464 *
465 * This handle provides access to the coroutine frame, allowing
466 * the SoundRoutine to resume, suspend, and destroy the coroutine.
467 */
468 std::coroutine_handle<promise_type> m_handle;
469};
470
471/**
472 * @class GraphicsRoutine
473 * @brief A C++20 coroutine-based graphics processing task with frame-accurate timing
474 *
475 * GraphicsRoutine encapsulates a coroutine that can execute visual processing logic
476 * with frame-accurate timing. It provides the graphics-domain equivalent to SoundRoutine,
477 * enabling time-based visual code that appears sequential but executes asynchronously
478 * in perfect sync with the frame timeline.
479 *
480 * Key architectural differences from SoundRoutine:
481 * - Timing source: FrameClock (self-driven) vs SampleClock (hardware-driven)
482 * - The routine doesn't care HOW the clock advances, only that it gets tick updates
483 * - GraphicsRoutine synchronized to frame positions, not sample positions
484 * - Works with FrameDelay awaiters instead of SampleDelay
485 *
486 * Key features:
487 * - Frame-accurate timing for precise visual scheduling
488 * - State persistence between suspensions and resumptions
489 * - Automatic management of coroutine lifetime
490 * - Ability to restart and reschedule tasks
491 * - Dynamic parameter updates during execution
492 * - Named state storage for flexible data management
493 *
494 * This implementation leverages C++20 coroutines to create a cooperative
495 * multitasking system specifically designed for visual processing. Each routine
496 * can suspend itself at precise frame positions and be resumed exactly when needed,
497 * enabling complex temporal behaviors for animations, visual effects, and
498 * data-driven visuals without blocking the graphics thread.
499 *
500 * Example usage:
501 * ```cpp
502 * auto fade_animation = [](TaskScheduler& scheduler) -> GraphicsRoutine {
503 * float opacity = 0.0f;
504 * for (int i = 0; i < 60; i++) { // 60 frames at 60fps = 1 second
505 * opacity += 1.0f / 60.0f;
506 * set_shader_opacity(opacity);
507 * co_await FrameDelay{1}; // Wait exactly 1 frame
508 * }
509 * };
510 * ```
511 */
512class MAYAFLUX_API GraphicsRoutine : public Routine {
513public:
514 /**
515 * @brief Promise type used by this coroutine
516 *
517 * This is the promise type that manages the coroutine state and
518 * provides the co_await, co_yield, and co_return behaviors for
519 * frame-based timing.
520 */
522
523 /**
524 * @brief Get the processing token that determines how this routine should be scheduled
525 * @return FRAME_ACCURATE token indicating frame-based scheduling
526 */
527 [[nodiscard]] ProcessingToken get_processing_token() const override;
528
529 /**
530 * @brief Constructs a GraphicsRoutine from a coroutine handle
531 * @param h Handle to the coroutine
532 *
533 * Creates a GraphicsRoutine that wraps and manages the given coroutine.
534 * This is typically called by the compiler-generated code when a
535 * coroutine function returns a GraphicsRoutine.
536 */
537 GraphicsRoutine(std::coroutine_handle<promise_type> h);
538
539 GraphicsRoutine(const GraphicsRoutine& other) = delete;
541
542 /**
543 * @brief Move constructor
544 * @param other GraphicsRoutine to move from
545 *
546 * Transfers ownership of the coroutine from other to this GraphicsRoutine.
547 * After the move, other no longer references any coroutine.
548 */
549 GraphicsRoutine(GraphicsRoutine&& other) noexcept;
550
551 /**
552 * @brief Move assignment operator
553 * @param other GraphicsRoutine to move from
554 * @return Reference to this GraphicsRoutine
555 *
556 * Transfers ownership of the coroutine from other to this GraphicsRoutine.
557 * After the move, other no longer references any coroutine.
558 */
559 GraphicsRoutine& operator=(GraphicsRoutine&& other) noexcept;
560
561 ~GraphicsRoutine() override;
562
563 [[nodiscard]] bool is_active() const override;
564
565 bool initialize_state(uint64_t current_frame = 0U) override;
566
567 bool try_resume(uint64_t current_context) override;
568
569 bool try_resume_with_context(uint64_t current_value, DelayContext context) override;
570
571 bool force_resume() override;
572
573 [[nodiscard]] DelayContext get_delay_context() const override
574 {
575 return m_handle.promise().active_delay_context;
576 }
577
578 void set_delay_context(DelayContext context) override
579 {
580 m_handle.promise().active_delay_context = context;
581 }
582
583 bool restart() override;
584
585 [[nodiscard]] uint64_t next_execution() const override;
586
587 [[nodiscard]] bool requires_clock_sync() const override;
588
589 [[nodiscard]] bool get_auto_resume() const override
590 {
591 return m_handle.promise().auto_resume;
592 }
593
594 void set_auto_resume(bool auto_resume) override
595 {
596 m_handle.promise().auto_resume = auto_resume;
597 }
598
599 [[nodiscard]] bool get_should_terminate() const override
600 {
601 return m_handle.promise().should_terminate;
602 }
603
604 void set_should_terminate(bool should_terminate) override
605 {
606 m_handle.promise().set_should_terminate(should_terminate);
607 }
608
609 void on_complete(std::function<void()> callback) override
610 {
611 m_handle.promise().on_complete(std::move(callback));
612 }
613
614 [[nodiscard]] bool get_sync_to_clock() const override
615 {
616 return m_handle.promise().sync_to_clock;
617 }
618
619 [[nodiscard]] uint64_t get_next_frame() const override
620 {
621 return m_handle.promise().next_frame;
622 }
623
624 void set_next_frame(uint64_t next_frame) override
625 {
626 m_handle.promise().next_frame = next_frame;
627 }
628
629 // Non-graphics domain methods (return defaults for graphics routines)
630 [[nodiscard]] uint64_t get_next_sample() const override { return 0; }
631 void set_next_sample(uint64_t /*next_sample*/) override { /* no-op for graphics */ }
632
633protected:
634 void set_state_impl(const std::string& key, std::any value) override;
635 void* get_state_impl_raw(const std::string& key) override;
636
637private:
638 /**
639 * @brief Handle to the underlying coroutine
640 *
641 * This handle provides access to the coroutine frame, allowing
642 * the GraphicsRoutine to resume, suspend, and destroy the coroutine.
643 */
644 std::coroutine_handle<promise_type> m_handle;
645};
646
647/**
648 * @class CrossRoutine
649 * @brief Coroutine resumed by more than one clock.
650 *
651 * Sibling to SoundRoutine and GraphicsRoutine. Where those bind to a single
652 * clock on a single thread, CrossRoutine lives in the MULTI_RATE task list,
653 * which both the sample-clock pump (audio thread) and the frame-clock pump
654 * (graphics thread) scan. It suspends on MultiRateDelay, which arms both
655 * clocks; the first clock to reach its target resumes the coroutine. A
656 * zero count on one clock disarms that clock for the suspension.
657 *
658 * Both pumps may reach the gate concurrently. The gate claims the resume with
659 * a compare-exchange on the promise's active_delay_context so exactly one
660 * thread resumes the handle; the other observes the changed context and backs
661 * off. The coroutine body re-establishes the context on its next co_await.
662 */
663class MAYAFLUX_API CrossRoutine : public Routine {
664public:
666
667 [[nodiscard]] ProcessingToken get_processing_token() const override;
668
669 CrossRoutine(std::coroutine_handle<promise_type> h);
670
671 CrossRoutine(const CrossRoutine& other);
672 CrossRoutine& operator=(const CrossRoutine& other);
673 CrossRoutine(CrossRoutine&& other) noexcept;
674 CrossRoutine& operator=(CrossRoutine&& other) noexcept;
675 ~CrossRoutine() override;
676
677 [[nodiscard]] bool is_active() const override;
678 bool initialize_state(uint64_t current_context = 0U) override;
679 bool try_resume(uint64_t current_context) override;
680 bool try_resume_with_context(uint64_t current_value, DelayContext context) override;
681 bool force_resume() override;
682 bool restart() override;
683
684 [[nodiscard]] uint64_t next_execution() const override;
685 [[nodiscard]] bool requires_clock_sync() const override;
686
687 [[nodiscard]] DelayContext get_delay_context() const override
688 {
689 return m_handle.promise().active_delay_context.load(std::memory_order_acquire);
690 }
691
692 void set_delay_context(DelayContext context) override
693 {
694 m_handle.promise().active_delay_context.store(context, std::memory_order_release);
695 }
696
697 [[nodiscard]] bool get_auto_resume() const override
698 {
699 return m_handle.promise().auto_resume;
700 }
701
702 void set_auto_resume(bool auto_resume) override
703 {
704 m_handle.promise().auto_resume = auto_resume;
705 }
706
707 [[nodiscard]] bool get_should_terminate() const override
708 {
709 return m_handle.promise().should_terminate;
710 }
711
712 void set_should_terminate(bool should_terminate) override
713 {
714 m_handle.promise().set_should_terminate(should_terminate);
715 }
716
717 void on_complete(std::function<void()> callback) override
718 {
719 m_handle.promise().on_complete(std::move(callback));
720 }
721
722 [[nodiscard]] bool get_sync_to_clock() const override
723 {
724 return m_handle.promise().sync_to_clock;
725 }
726
727 [[nodiscard]] uint64_t get_next_sample() const override
728 {
729 return m_handle.promise().next_sample.load(std::memory_order_acquire);
730 }
731
732 void set_next_sample(uint64_t next_sample) override
733 {
734 m_handle.promise().next_sample.store(next_sample, std::memory_order_release);
735 }
736
737 [[nodiscard]] uint64_t get_next_frame() const override
738 {
739 return m_handle.promise().next_frame.load(std::memory_order_acquire);
740 }
741
742 void set_next_frame(uint64_t next_frame) override
743 {
744 m_handle.promise().next_frame.store(next_frame, std::memory_order_release);
745 }
746
747protected:
748 void set_state_impl(const std::string& key, std::any value) override;
749 void* get_state_impl_raw(const std::string& key) override;
750
751private:
752 std::coroutine_handle<promise_type> m_handle;
753};
754
755/**
756 * @class FreeRoutine
757 * @brief Coroutine resumed when a caller-supplied condition becomes true.
758 *
759 * FreeRoutine has no clock domain. It suspends on ConditionAwaiter, which
760 * stores a std::function<bool()> in the promise. The scheduler's dedicated
761 * CONDITIONAL thread evaluates that condition on every pass; the coroutine
762 * resumes on that thread the moment the condition returns true.
763 *
764 * Intended for compute loops that must run independently of both the audio
765 * sample clock and the graphics frame clock - cellular automata, physics
766 * integration, ML inference, any free-running iterative process.
767 *
768 * Usage:
769 * @code
770 * auto my_routine = [&state]() -> Vruta::FreeRoutine {
771 * while (true) {
772 * co_await ConditionAwaiter{ [&]{ return state.ready.load(); } };
773 * state.evolve();
774 * }
775 * };
776 * scheduler->add_task(std::make_shared<FreeRoutine>(my_routine()), "ca_evolve");
777 * @endcode
778 */
779class MAYAFLUX_API FreeRoutine : public Routine {
780public:
782
783 [[nodiscard]] ProcessingToken get_processing_token() const override;
784
785 explicit FreeRoutine(std::coroutine_handle<promise_type> h);
786
787 FreeRoutine(const FreeRoutine& other) = delete;
788 FreeRoutine& operator=(const FreeRoutine& other) = delete;
789 FreeRoutine(FreeRoutine&& other) noexcept;
790 FreeRoutine& operator=(FreeRoutine&& other) noexcept;
791 ~FreeRoutine() override;
792
793 [[nodiscard]] bool is_active() const override;
794 bool initialize_state(uint64_t current_context = 0U) override;
795 bool try_resume(uint64_t current_context) override;
796 bool try_resume_with_context(uint64_t current_value, DelayContext context) override;
797 bool force_resume() override;
798 bool restart() override;
799
800 [[nodiscard]] uint64_t next_execution() const override { return 0; }
801 [[nodiscard]] bool requires_clock_sync() const override { return false; }
802
803 [[nodiscard]] bool get_auto_resume() const override
804 {
805 return m_handle ? m_handle.promise().auto_resume : false;
806 }
807
808 void set_auto_resume(bool v) override
809 {
810 if (m_handle)
811 m_handle.promise().auto_resume = v;
812 }
813
814 [[nodiscard]] bool get_should_terminate() const override
815 {
816 return m_handle ? m_handle.promise().should_terminate.load() : true;
817 }
818
819 void set_should_terminate(bool v) override
820 {
821 if (m_handle)
822 m_handle.promise().set_should_terminate(v);
823 }
824
825 void on_complete(std::function<void()> callback) override
826 {
827 if (m_handle)
828 m_handle.promise().on_complete(std::move(callback));
829 }
830
831 [[nodiscard]] bool get_sync_to_clock() const override { return false; }
832
833 [[nodiscard]] uint64_t get_next_sample() const override { return 0; }
834 void set_next_sample(uint64_t) override { }
835 [[nodiscard]] uint64_t get_next_frame() const override { return 0; }
836 void set_next_frame(uint64_t) override { }
837
838protected:
839 void set_state_impl(const std::string& key, std::any value) override;
840 void* get_state_impl_raw(const std::string& key) override;
841
842private:
843 std::coroutine_handle<promise_type> m_handle;
844};
845}
uint32_t h
Definition InkPress.cpp:29
PickCallback callback
Definition Picker.cpp:22
glm::vec3 value
bool get_auto_resume() const override
Get auto_resume flag from promise.
Definition Routine.hpp:697
std::coroutine_handle< promise_type > m_handle
Definition Routine.hpp:752
void set_auto_resume(bool auto_resume) override
Set auto_resume flag in promise.
Definition Routine.hpp:702
uint64_t get_next_sample() const override
Get next sample execution time (audio domain)
Definition Routine.hpp:727
bool get_sync_to_clock() const override
Get sync_to_clock flag from promise.
Definition Routine.hpp:722
void set_delay_context(DelayContext context) override
Set the active delay context for this routine.
Definition Routine.hpp:692
void set_next_frame(uint64_t next_frame) override
Set next frame execution time (graphics domain)
Definition Routine.hpp:742
uint64_t get_next_frame() const override
Get next frame execution time (graphics domain)
Definition Routine.hpp:737
void on_complete(std::function< void()> callback) override
Register a callback invoked once at final suspend or a termination request, whichever occurs first.
Definition Routine.hpp:717
void set_next_sample(uint64_t next_sample) override
Set next sample execution time (audio domain)
Definition Routine.hpp:732
bool get_should_terminate() const override
Get should_terminate flag from promise.
Definition Routine.hpp:707
DelayContext get_delay_context() const override
Get the active delay context for this routine.
Definition Routine.hpp:687
void set_should_terminate(bool should_terminate) override
Set should_terminate flag in promise.
Definition Routine.hpp:712
Coroutine resumed by more than one clock.
Definition Routine.hpp:663
FreeRoutine(const FreeRoutine &other)=delete
std::coroutine_handle< promise_type > m_handle
Definition Routine.hpp:843
void on_complete(std::function< void()> callback) override
Register a callback invoked once at final suspend or a termination request, whichever occurs first.
Definition Routine.hpp:825
FreeRoutine & operator=(const FreeRoutine &other)=delete
uint64_t get_next_sample() const override
Get next sample execution time (audio domain)
Definition Routine.hpp:833
bool get_auto_resume() const override
Get auto_resume flag from promise.
Definition Routine.hpp:803
uint64_t next_execution() const override
Gets the sample position when this routine should next execute.
Definition Routine.hpp:800
void set_next_frame(uint64_t) override
Set next frame execution time (graphics domain)
Definition Routine.hpp:836
void set_should_terminate(bool v) override
Set should_terminate flag in promise.
Definition Routine.hpp:819
bool requires_clock_sync() const override
Check if the routine should synchronize with a clock.
Definition Routine.hpp:801
uint64_t get_next_frame() const override
Get next frame execution time (graphics domain)
Definition Routine.hpp:835
void set_auto_resume(bool v) override
Set auto_resume flag in promise.
Definition Routine.hpp:808
bool get_sync_to_clock() const override
Get sync_to_clock flag from promise.
Definition Routine.hpp:831
void set_next_sample(uint64_t) override
Set next sample execution time (audio domain)
Definition Routine.hpp:834
bool get_should_terminate() const override
Get should_terminate flag from promise.
Definition Routine.hpp:814
Coroutine resumed when a caller-supplied condition becomes true.
Definition Routine.hpp:779
uint64_t get_next_sample() const override
Get next sample execution time (audio domain)
Definition Routine.hpp:630
void set_auto_resume(bool auto_resume) override
Set auto_resume flag in promise.
Definition Routine.hpp:594
void set_should_terminate(bool should_terminate) override
Set should_terminate flag in promise.
Definition Routine.hpp:604
bool get_should_terminate() const override
Get should_terminate flag from promise.
Definition Routine.hpp:599
void set_next_frame(uint64_t next_frame) override
Set next frame execution time (graphics domain)
Definition Routine.hpp:624
GraphicsRoutine(const GraphicsRoutine &other)=delete
void set_next_sample(uint64_t) override
Set next sample execution time (audio domain)
Definition Routine.hpp:631
bool get_auto_resume() const override
Get auto_resume flag from promise.
Definition Routine.hpp:589
GraphicsRoutine & operator=(const GraphicsRoutine &other)=delete
void on_complete(std::function< void()> callback) override
Register a callback invoked once at final suspend or a termination request, whichever occurs first.
Definition Routine.hpp:609
std::coroutine_handle< promise_type > m_handle
Handle to the underlying coroutine.
Definition Routine.hpp:644
uint64_t get_next_frame() const override
Get next frame execution time (graphics domain)
Definition Routine.hpp:619
DelayContext get_delay_context() const override
Get the active delay context for this routine.
Definition Routine.hpp:573
void set_delay_context(DelayContext context) override
Set the active delay context for this routine.
Definition Routine.hpp:578
bool get_sync_to_clock() const override
Get sync_to_clock flag from promise.
Definition Routine.hpp:614
A C++20 coroutine-based graphics processing task with frame-accurate timing.
Definition Routine.hpp:512
void set_state(const std::string &key, T value)
Sets a named state value in the coroutine.
Definition Routine.hpp:225
virtual void set_next_frame(uint64_t next_frame)=0
Set next frame execution time (graphics domain)
virtual bool force_resume()=0
Force resume the coroutine, bypassing all checks Used only during shutdown to push coroutines to fina...
virtual void on_complete(std::function< void()> callback)=0
Register a callback invoked once at final suspend or a termination request, whichever occurs first.
virtual uint64_t get_next_sample() const =0
Get next sample execution time (audio domain)
virtual bool try_resume_with_context(uint64_t current_value, DelayContext)
Attempts to resume the coroutine with explicit temporal context.
Definition Routine.hpp:91
virtual bool restart()=0
Restarts the coroutine from the beginning.
virtual void set_delay_context(DelayContext)
Set the active delay context for this routine.
Definition Routine.hpp:195
void update_params(Args... args)
Updates multiple named parameters in the coroutine's state.
Definition Routine.hpp:210
virtual bool is_active() const =0
Checks if the coroutine is still active.
virtual ProcessingToken get_processing_token() const =0
Get the processing token that determines how this routine should be scheduled.
virtual void set_state_impl(const std::string &key, std::any value)=0
virtual ~Routine()=default
Destructor.
virtual bool get_sync_to_clock() const =0
Get sync_to_clock flag from promise.
virtual uint64_t next_execution() const =0
Gets the sample position when this routine should next execute.
virtual void set_should_terminate(bool should_terminate)=0
Set should_terminate flag in promise.
virtual bool requires_clock_sync() const =0
Check if the routine should synchronize with a clock.
virtual void set_next_sample(uint64_t next_sample)=0
Set next sample execution time (audio domain)
virtual void set_auto_resume(bool auto_resume)=0
Set auto_resume flag in promise.
virtual void update_params_impl()
brief Implementation helper for update_params
Definition Routine.hpp:271
virtual bool get_should_terminate() const =0
Get should_terminate flag from promise.
virtual void * get_state_impl_raw(const std::string &key)=0
virtual uint64_t get_next_frame() const =0
Get next frame execution time (graphics domain)
T * get_state(const std::string &key)
Gets a named state value from the coroutine.
Definition Routine.hpp:240
void update_params_impl(const std::string &key, T value, Args... args)
Implementation helper for update_params.
Definition Routine.hpp:284
virtual DelayContext get_delay_context() const
Get the active delay context for this routine.
Definition Routine.hpp:189
virtual bool initialize_state(uint64_t current_context=0U)=0
Initializes the coroutine's state for execution.
virtual bool try_resume(uint64_t current_context)=0
Attempts to resume the coroutine if it's ready to execute.
T * get_state_impl(const std::string &key)
Implementation helper for get_state.
Definition Routine.hpp:255
virtual bool get_auto_resume() const =0
Get auto_resume flag from promise.
Base class for all coroutine types in the MayaFlux engine.
Definition Routine.hpp:16
std::coroutine_handle< promise_type > m_handle
Handle to the underlying coroutine.
Definition Routine.hpp:468
SoundRoutine(const SoundRoutine &other)=delete
bool get_sync_to_clock() const override
Get sync_to_clock flag from promise.
Definition Routine.hpp:428
void set_auto_resume(bool auto_resume) override
Set auto_resume flag in promise.
Definition Routine.hpp:408
uint64_t get_next_sample() const override
Get next sample execution time (audio domain)
Definition Routine.hpp:433
bool get_auto_resume() const override
Get auto_resume flag from promise.
Definition Routine.hpp:403
uint64_t get_next_frame() const override
Get next frame execution time (graphics domain)
Definition Routine.hpp:443
void set_next_sample(uint64_t next_sample) override
Set next sample execution time (audio domain)
Definition Routine.hpp:438
SoundRoutine & operator=(const SoundRoutine &other)=delete
bool get_should_terminate() const override
Get should_terminate flag from promise.
Definition Routine.hpp:413
void on_complete(std::function< void()> callback) override
Register a callback invoked once at final suspend or a termination request, whichever occurs first.
Definition Routine.hpp:423
void set_next_frame(uint64_t next_cycle) override
Set next frame execution time (graphics domain)
Definition Routine.hpp:448
void set_delay_context(DelayContext context) override
Set the active delay context for this routine.
Definition Routine.hpp:392
DelayContext get_delay_context() const override
Get the active delay context for this routine.
Definition Routine.hpp:387
void set_should_terminate(bool should_terminate) override
Set should_terminate flag in promise.
Definition Routine.hpp:418
A C++20 coroutine-based audio processing task with sample-accurate timing.
Definition Routine.hpp:327
DelayContext
Discriminator for different temporal delay mechanisms.
Coroutine promise type for audio processing tasks with sample-accurate timing.
Definition Promise.hpp:240
Coroutine promise for routines suspended on an arbitrary boolean condition.
Definition Promise.hpp:484
Coroutine promise for routines resumed by more than one clock.
Definition Promise.hpp:393
Coroutine promise type for graphics processing tasks with frame-accurate timing.
Definition Promise.hpp:303