MayaFlux 0.4.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 Get sync_to_clock flag from promise
145 * @return True if coroutine should synchronize with clock
146 */
147 [[nodiscard]] virtual bool get_sync_to_clock() const = 0;
148
149 // Domain-specific timing methods (return 0/false for unsupported domains)
150 /**
151 * @brief Get next sample execution time (audio domain)
152 * @return Sample position for next execution, or 0 if not audio domain
153 */
154 [[nodiscard]] virtual uint64_t get_next_sample() const = 0;
155
156 /**
157 * @brief Set next sample execution time (audio domain)
158 * @param next_sample Sample position for next execution
159 */
160 virtual void set_next_sample(uint64_t next_sample) = 0;
161
162 /**
163 * @brief Get next frame execution time (graphics domain)
164 * @return Frame position for next execution, or 0 if not graphics domain
165 */
166 [[nodiscard]] virtual uint64_t get_next_frame() const = 0;
167
168 /**
169 * @brief Set next frame execution time (graphics domain)
170 * @param next_frame Frame position for next execution
171 */
172 virtual void set_next_frame(uint64_t next_frame) = 0;
173
174 /**
175 * @brief Get the active delay context for this routine
176 * @return Current delay context, or NONE if not waiting
177 */
178 [[nodiscard]] virtual DelayContext get_delay_context() const { return DelayContext::NONE; }
179
180 /**
181 * @brief Set the active delay context for this routine
182 * @param context New delay context
183 */
184 virtual void set_delay_context(DelayContext /* Context */) { /* no-op in base */ }
185
186 /**
187 * @brief Updates multiple named parameters in the coroutine's state
188 * @param args Variable number of key-value pairs to update
189 *
190 * Provides a convenient way to update multiple state values at once.
191 * This is useful for configuring a coroutine before or during execution.
192 *
193 * Example:
194 * ```cpp
195 * routine.update_params("duration", 2.0f, "amplitude", 0.8f);
196 * ```
197 */
198 template <typename... Args>
199 inline void update_params(Args... args)
200 {
201 update_params_impl(std::forward<Args>(args)...);
202 }
203
204 /**
205 * @brief Sets a named state value in the coroutine
206 * @param key Name of the state value
207 * @param value Value to store
208 *
209 * Stores a value in the coroutine's state dictionary under the given key.
210 * This allows the coroutine to maintain state between suspensions and
211 * enables external code to influence the coroutine's behavior.
212 */
213 template <typename T>
214 inline void set_state(const std::string& key, T value)
215 {
216 set_state_impl(key, std::move(value));
217 }
218
219 /**
220 * @brief Gets a named state value from the coroutine
221 * @param key Name of the state value to retrieve
222 * @return Pointer to the stored value, or nullptr if not found
223 *
224 * Retrieves a previously stored value from the coroutine's state
225 * dictionary. Returns nullptr if the key doesn't exist or the
226 * stored type doesn't match the requested type.
227 */
228 template <typename T>
229 inline T* get_state(const std::string& key)
230 {
231 return get_state_impl<T>(key);
232 }
233
234protected:
235 virtual void set_state_impl(const std::string& key, std::any value) = 0;
236 virtual void* get_state_impl_raw(const std::string& key) = 0;
237
238 /**
239 * @brief Implementation helper for get_state
240 * @param key Name of the state value to retrieve
241 * @return Pointer to the stored value, or nullptr if not found or type mismatch
242 */
243 template <typename T>
244 T* get_state_impl(const std::string& key)
245 {
246 void* raw_ptr = get_state_impl_raw(key);
247 if (!raw_ptr)
248 return nullptr;
249
250 try {
251 return std::any_cast<T>(static_cast<std::any*>(raw_ptr));
252 } catch (const std::bad_any_cast&) {
253 return nullptr;
254 }
255 }
256
257 /**
258 * brief Implementation helper for update_params
259 */
260 virtual void update_params_impl() { }
261
262 /**
263 * @brief Implementation helper for update_params
264 * @param promise The promise object to update
265 * @param key Name of the state value
266 * @param value Value to store
267 * @param args Remaining key-value pairs to process
268 *
269 * Recursive template function that processes each key-value pair
270 * in the update_params variadic argument list.
271 */
272 template <typename T, typename... Args>
273 void update_params_impl(const std::string& key, T value, Args... args)
274 {
275 set_state(key, std::move(value));
276 if constexpr (sizeof...(args) > 0) {
277 update_params_impl(std::forward<Args>(args)...);
278 }
279 }
280};
281
282/**
283 * @class SoundRoutine
284 * @brief A C++20 coroutine-based audio processing task with sample-accurate timing
285 *
286 * SoundRoutine encapsulates a coroutine that can execute audio processing logic
287 * with sample-accurate timing. It provides a powerful abstraction for writing
288 * time-based audio code that appears sequential but executes asynchronously
289 * in perfect sync with the audio timeline.
290 *
291 * Key features:
292 * - Sample-accurate timing for precise audio scheduling
293 * - State persistence between suspensions and resumptions
294 * - Automatic management of coroutine lifetime
295 * - Ability to restart and reschedule tasks
296 * - Dynamic parameter updates during execution
297 * - Named state storage for flexible data management
298 *
299 * This implementation leverages C++20 coroutines to create a cooperative
300 * multitasking system specifically designed for audio processing. Each routine
301 * can suspend itself at precise sample positions and be resumed exactly when needed,
302 * enabling complex temporal behaviors without blocking the audio thread.
303 *
304 * Example usage:
305 * ```cpp
306 * auto fade_in = [](TaskScheduler& scheduler) -> SoundRoutine {
307 * float gain = 0.0f;
308 * for (int i = 0; i < 100; i++) {
309 * gain += 0.01f;
310 * set_volume(gain);
311 * co_await SampleDelay{441}; // Wait 10ms at 44.1kHz
312 * }
313 * };
314 * ```
315 */
316class MAYAFLUX_API SoundRoutine : public Routine {
317public:
318 /**
319 * @brief Promise type used by this coroutine
320 *
321 * This is the promise type that manages the coroutine state and
322 * provides the co_await, co_yield, and co_return behaviors.
323 */
325
326 /**
327 * @brief Get the processing token that determines how this routine should be scheduled
328 * @return The processing token indicating the scheduling domain
329 */
330 [[nodiscard]] ProcessingToken get_processing_token() const override;
331
332 /**
333 * @brief Constructs a SoundRoutine from a coroutine handle
334 * @param h Handle to the coroutine
335 *
336 * Creates a SoundRoutine that wraps and manages the given coroutine.
337 * This is typically called by the compiler-generated code when a
338 * coroutine function returns a SoundRoutine.
339 */
340 SoundRoutine(std::coroutine_handle<promise_type> h);
341
342 SoundRoutine(const SoundRoutine& other) = delete;
343 SoundRoutine& operator=(const SoundRoutine& other) = delete;
344
345 /**
346 * @brief Move constructor
347 * @param other SoundRoutine to move from
348 *
349 * Transfers ownership of the coroutine from other to this SoundRoutine.
350 * After the move, other no longer references any coroutine.
351 */
352 SoundRoutine(SoundRoutine&& other) noexcept;
353
354 /**
355 * @brief Move assignment operator
356 * @param other SoundRoutine to move from
357 * @return Reference to this SoundRoutine
358 *
359 * Transfers ownership of the coroutine from other to this SoundRoutine.
360 * After the move, other no longer references any coroutine.
361 */
362 SoundRoutine& operator=(SoundRoutine&& other) noexcept;
363
364 ~SoundRoutine() override;
365
366 [[nodiscard]] bool is_active() const override;
367
368 bool initialize_state(uint64_t current_sample = 0U) override;
369
370 bool try_resume(uint64_t current_context) override;
371
372 bool try_resume_with_context(uint64_t current_value, DelayContext context) override;
373
374 bool force_resume() override;
375
376 [[nodiscard]] DelayContext get_delay_context() const override
377 {
378 return m_handle.promise().active_delay_context;
379 }
380
381 void set_delay_context(DelayContext context) override
382 {
383 m_handle.promise().active_delay_context = context;
384 }
385
386 bool restart() override;
387
388 [[nodiscard]] uint64_t next_execution() const override;
389
390 [[nodiscard]] bool requires_clock_sync() const override;
391
392 [[nodiscard]] bool get_auto_resume() const override
393 {
394 return m_handle.promise().auto_resume;
395 }
396
397 void set_auto_resume(bool auto_resume) override
398 {
399 m_handle.promise().auto_resume = auto_resume;
400 }
401
402 [[nodiscard]] bool get_should_terminate() const override
403 {
404 return m_handle.promise().should_terminate;
405 }
406
407 void set_should_terminate(bool should_terminate) override
408 {
409 m_handle.promise().should_terminate = should_terminate;
410 }
411
412 [[nodiscard]] bool get_sync_to_clock() const override
413 {
414 return m_handle.promise().sync_to_clock;
415 }
416
417 [[nodiscard]] uint64_t get_next_sample() const override
418 {
419 return m_handle.promise().next_sample;
420 }
421
422 void set_next_sample(uint64_t next_sample) override
423 {
424 m_handle.promise().next_sample = next_sample;
425 }
426
427 [[nodiscard]] uint64_t get_next_frame() const override
428 {
429 return m_handle.promise().next_buffer_cycle;
430 }
431
432 void set_next_frame(uint64_t next_cycle) override
433 {
434 m_handle.promise().next_buffer_cycle = next_cycle;
435 }
436
437 // Non-audio domain methods (return defaults for audio routines)
438 // uint64_t get_next_frame() const override { return 0; }
439 // void set_next_frame(uint64_t /*next_frame*/) override { /* no-op for audio */ }
440
441protected:
442 void set_state_impl(const std::string& key, std::any value) override;
443 void* get_state_impl_raw(const std::string& key) override;
444
445private:
446 /**
447 * @brief Handle to the underlying coroutine
448 *
449 * This handle provides access to the coroutine frame, allowing
450 * the SoundRoutine to resume, suspend, and destroy the coroutine.
451 */
452 std::coroutine_handle<promise_type> m_handle;
453};
454
455/**
456 * @class GraphicsRoutine
457 * @brief A C++20 coroutine-based graphics processing task with frame-accurate timing
458 *
459 * GraphicsRoutine encapsulates a coroutine that can execute visual processing logic
460 * with frame-accurate timing. It provides the graphics-domain equivalent to SoundRoutine,
461 * enabling time-based visual code that appears sequential but executes asynchronously
462 * in perfect sync with the frame timeline.
463 *
464 * Key architectural differences from SoundRoutine:
465 * - Timing source: FrameClock (self-driven) vs SampleClock (hardware-driven)
466 * - The routine doesn't care HOW the clock advances, only that it gets tick updates
467 * - GraphicsRoutine synchronized to frame positions, not sample positions
468 * - Works with FrameDelay awaiters instead of SampleDelay
469 *
470 * Key features:
471 * - Frame-accurate timing for precise visual scheduling
472 * - State persistence between suspensions and resumptions
473 * - Automatic management of coroutine lifetime
474 * - Ability to restart and reschedule tasks
475 * - Dynamic parameter updates during execution
476 * - Named state storage for flexible data management
477 *
478 * This implementation leverages C++20 coroutines to create a cooperative
479 * multitasking system specifically designed for visual processing. Each routine
480 * can suspend itself at precise frame positions and be resumed exactly when needed,
481 * enabling complex temporal behaviors for animations, visual effects, and
482 * data-driven visuals without blocking the graphics thread.
483 *
484 * Example usage:
485 * ```cpp
486 * auto fade_animation = [](TaskScheduler& scheduler) -> GraphicsRoutine {
487 * float opacity = 0.0f;
488 * for (int i = 0; i < 60; i++) { // 60 frames at 60fps = 1 second
489 * opacity += 1.0f / 60.0f;
490 * set_shader_opacity(opacity);
491 * co_await FrameDelay{1}; // Wait exactly 1 frame
492 * }
493 * };
494 * ```
495 */
496class MAYAFLUX_API GraphicsRoutine : public Routine {
497public:
498 /**
499 * @brief Promise type used by this coroutine
500 *
501 * This is the promise type that manages the coroutine state and
502 * provides the co_await, co_yield, and co_return behaviors for
503 * frame-based timing.
504 */
506
507 /**
508 * @brief Get the processing token that determines how this routine should be scheduled
509 * @return FRAME_ACCURATE token indicating frame-based scheduling
510 */
511 [[nodiscard]] ProcessingToken get_processing_token() const override;
512
513 /**
514 * @brief Constructs a GraphicsRoutine from a coroutine handle
515 * @param h Handle to the coroutine
516 *
517 * Creates a GraphicsRoutine that wraps and manages the given coroutine.
518 * This is typically called by the compiler-generated code when a
519 * coroutine function returns a GraphicsRoutine.
520 */
521 GraphicsRoutine(std::coroutine_handle<promise_type> h);
522
523 GraphicsRoutine(const GraphicsRoutine& other) = delete;
525
526 /**
527 * @brief Move constructor
528 * @param other GraphicsRoutine to move from
529 *
530 * Transfers ownership of the coroutine from other to this GraphicsRoutine.
531 * After the move, other no longer references any coroutine.
532 */
533 GraphicsRoutine(GraphicsRoutine&& other) noexcept;
534
535 /**
536 * @brief Move assignment operator
537 * @param other GraphicsRoutine to move from
538 * @return Reference to this GraphicsRoutine
539 *
540 * Transfers ownership of the coroutine from other to this GraphicsRoutine.
541 * After the move, other no longer references any coroutine.
542 */
543 GraphicsRoutine& operator=(GraphicsRoutine&& other) noexcept;
544
545 ~GraphicsRoutine() override;
546
547 [[nodiscard]] bool is_active() const override;
548
549 bool initialize_state(uint64_t current_frame = 0U) override;
550
551 bool try_resume(uint64_t current_context) override;
552
553 bool try_resume_with_context(uint64_t current_value, DelayContext context) override;
554
555 bool force_resume() override;
556
557 [[nodiscard]] DelayContext get_delay_context() const override
558 {
559 return m_handle.promise().active_delay_context;
560 }
561
562 void set_delay_context(DelayContext context) override
563 {
564 m_handle.promise().active_delay_context = context;
565 }
566
567 bool restart() override;
568
569 [[nodiscard]] uint64_t next_execution() const override;
570
571 [[nodiscard]] bool requires_clock_sync() const override;
572
573 [[nodiscard]] bool get_auto_resume() const override
574 {
575 return m_handle.promise().auto_resume;
576 }
577
578 void set_auto_resume(bool auto_resume) override
579 {
580 m_handle.promise().auto_resume = auto_resume;
581 }
582
583 [[nodiscard]] bool get_should_terminate() const override
584 {
585 return m_handle.promise().should_terminate;
586 }
587
588 void set_should_terminate(bool should_terminate) override
589 {
590 m_handle.promise().should_terminate = should_terminate;
591 }
592
593 [[nodiscard]] bool get_sync_to_clock() const override
594 {
595 return m_handle.promise().sync_to_clock;
596 }
597
598 [[nodiscard]] uint64_t get_next_frame() const override
599 {
600 return m_handle.promise().next_frame;
601 }
602
603 void set_next_frame(uint64_t next_frame) override
604 {
605 m_handle.promise().next_frame = next_frame;
606 }
607
608 // Non-graphics domain methods (return defaults for graphics routines)
609 [[nodiscard]] uint64_t get_next_sample() const override { return 0; }
610 void set_next_sample(uint64_t /*next_sample*/) override { /* no-op for graphics */ }
611
612protected:
613 void set_state_impl(const std::string& key, std::any value) override;
614 void* get_state_impl_raw(const std::string& key) override;
615
616private:
617 /**
618 * @brief Handle to the underlying coroutine
619 *
620 * This handle provides access to the coroutine frame, allowing
621 * the GraphicsRoutine to resume, suspend, and destroy the coroutine.
622 */
623 std::coroutine_handle<promise_type> m_handle;
624};
625
626/**
627 * @class CrossRoutine
628 * @brief Coroutine resumed by more than one clock.
629 *
630 * Sibling to SoundRoutine and GraphicsRoutine. Where those bind to a single
631 * clock on a single thread, CrossRoutine lives in the MULTI_RATE task list,
632 * which both the sample-clock pump (audio thread) and the frame-clock pump
633 * (graphics thread) scan. It suspends on MultiRateDelay, which arms both
634 * clocks; the first clock to reach its target resumes the coroutine. A
635 * zero count on one clock disarms that clock for the suspension.
636 *
637 * Both pumps may reach the gate concurrently. The gate claims the resume with
638 * a compare-exchange on the promise's active_delay_context so exactly one
639 * thread resumes the handle; the other observes the changed context and backs
640 * off. The coroutine body re-establishes the context on its next co_await.
641 */
642class MAYAFLUX_API CrossRoutine : public Routine {
643public:
645
646 [[nodiscard]] ProcessingToken get_processing_token() const override;
647
648 CrossRoutine(std::coroutine_handle<promise_type> h);
649
650 CrossRoutine(const CrossRoutine& other);
651 CrossRoutine& operator=(const CrossRoutine& other);
652 CrossRoutine(CrossRoutine&& other) noexcept;
653 CrossRoutine& operator=(CrossRoutine&& other) noexcept;
654 ~CrossRoutine() override;
655
656 [[nodiscard]] bool is_active() const override;
657 bool initialize_state(uint64_t current_context = 0U) override;
658 bool try_resume(uint64_t current_context) override;
659 bool try_resume_with_context(uint64_t current_value, DelayContext context) override;
660 bool force_resume() override;
661 bool restart() override;
662
663 [[nodiscard]] uint64_t next_execution() const override;
664 [[nodiscard]] bool requires_clock_sync() const override;
665
666 [[nodiscard]] DelayContext get_delay_context() const override
667 {
668 return m_handle.promise().active_delay_context.load(std::memory_order_acquire);
669 }
670
671 void set_delay_context(DelayContext context) override
672 {
673 m_handle.promise().active_delay_context.store(context, std::memory_order_release);
674 }
675
676 [[nodiscard]] bool get_auto_resume() const override
677 {
678 return m_handle.promise().auto_resume;
679 }
680
681 void set_auto_resume(bool auto_resume) override
682 {
683 m_handle.promise().auto_resume = auto_resume;
684 }
685
686 [[nodiscard]] bool get_should_terminate() const override
687 {
688 return m_handle.promise().should_terminate;
689 }
690
691 void set_should_terminate(bool should_terminate) override
692 {
693 m_handle.promise().should_terminate = should_terminate;
694 }
695
696 [[nodiscard]] bool get_sync_to_clock() const override
697 {
698 return m_handle.promise().sync_to_clock;
699 }
700
701 [[nodiscard]] uint64_t get_next_sample() const override
702 {
703 return m_handle.promise().next_sample.load(std::memory_order_acquire);
704 }
705
706 void set_next_sample(uint64_t next_sample) override
707 {
708 m_handle.promise().next_sample.store(next_sample, std::memory_order_release);
709 }
710
711 [[nodiscard]] uint64_t get_next_frame() const override
712 {
713 return m_handle.promise().next_frame.load(std::memory_order_acquire);
714 }
715
716 void set_next_frame(uint64_t next_frame) override
717 {
718 m_handle.promise().next_frame.store(next_frame, std::memory_order_release);
719 }
720
721protected:
722 void set_state_impl(const std::string& key, std::any value) override;
723 void* get_state_impl_raw(const std::string& key) override;
724
725private:
726 std::coroutine_handle<promise_type> m_handle;
727};
728
729/**
730 * @class FreeRoutine
731 * @brief Coroutine resumed when a caller-supplied condition becomes true.
732 *
733 * FreeRoutine has no clock domain. It suspends on ConditionAwaiter, which
734 * stores a std::function<bool()> in the promise. The scheduler's dedicated
735 * CONDITIONAL thread evaluates that condition on every pass; the coroutine
736 * resumes on that thread the moment the condition returns true.
737 *
738 * Intended for compute loops that must run independently of both the audio
739 * sample clock and the graphics frame clock - cellular automata, physics
740 * integration, ML inference, any free-running iterative process.
741 *
742 * Usage:
743 * @code
744 * auto my_routine = [&state]() -> Vruta::FreeRoutine {
745 * while (true) {
746 * co_await ConditionAwaiter{ [&]{ return state.ready.load(); } };
747 * state.evolve();
748 * }
749 * };
750 * scheduler->add_task(std::make_shared<FreeRoutine>(my_routine()), "ca_evolve");
751 * @endcode
752 */
753class MAYAFLUX_API FreeRoutine : public Routine {
754public:
756
757 [[nodiscard]] ProcessingToken get_processing_token() const override;
758
759 explicit FreeRoutine(std::coroutine_handle<promise_type> h);
760
761 FreeRoutine(const FreeRoutine& other) = delete;
762 FreeRoutine& operator=(const FreeRoutine& other) = delete;
763 FreeRoutine(FreeRoutine&& other) noexcept;
764 FreeRoutine& operator=(FreeRoutine&& other) noexcept;
765 ~FreeRoutine() override;
766
767 [[nodiscard]] bool is_active() const override;
768 bool initialize_state(uint64_t current_context = 0U) override;
769 bool try_resume(uint64_t current_context) override;
770 bool try_resume_with_context(uint64_t current_value, DelayContext context) override;
771 bool force_resume() override;
772 bool restart() override;
773
774 [[nodiscard]] uint64_t next_execution() const override { return 0; }
775 [[nodiscard]] bool requires_clock_sync() const override { return false; }
776
777 [[nodiscard]] bool get_auto_resume() const override
778 {
779 return m_handle ? m_handle.promise().auto_resume : false;
780 }
781
782 void set_auto_resume(bool v) override
783 {
784 if (m_handle)
785 m_handle.promise().auto_resume = v;
786 }
787
788 [[nodiscard]] bool get_should_terminate() const override
789 {
790 return m_handle ? m_handle.promise().should_terminate : true;
791 }
792
793 void set_should_terminate(bool v) override
794 {
795 if (m_handle)
796 m_handle.promise().should_terminate = v;
797 }
798
799 [[nodiscard]] bool get_sync_to_clock() const override { return false; }
800
801 [[nodiscard]] uint64_t get_next_sample() const override { return 0; }
802 void set_next_sample(uint64_t) override { }
803 [[nodiscard]] uint64_t get_next_frame() const override { return 0; }
804 void set_next_frame(uint64_t) override { }
805
806protected:
807 void set_state_impl(const std::string& key, std::any value) override;
808 void* get_state_impl_raw(const std::string& key) override;
809
810private:
811 std::coroutine_handle<promise_type> m_handle;
812};
813}
uint32_t h
Definition InkPress.cpp:28
bool get_auto_resume() const override
Get auto_resume flag from promise.
Definition Routine.hpp:676
std::coroutine_handle< promise_type > m_handle
Definition Routine.hpp:726
void set_auto_resume(bool auto_resume) override
Set auto_resume flag in promise.
Definition Routine.hpp:681
uint64_t get_next_sample() const override
Get next sample execution time (audio domain)
Definition Routine.hpp:701
bool get_sync_to_clock() const override
Get sync_to_clock flag from promise.
Definition Routine.hpp:696
void set_delay_context(DelayContext context) override
Set the active delay context for this routine.
Definition Routine.hpp:671
void set_next_frame(uint64_t next_frame) override
Set next frame execution time (graphics domain)
Definition Routine.hpp:716
uint64_t get_next_frame() const override
Get next frame execution time (graphics domain)
Definition Routine.hpp:711
void set_next_sample(uint64_t next_sample) override
Set next sample execution time (audio domain)
Definition Routine.hpp:706
bool get_should_terminate() const override
Get should_terminate flag from promise.
Definition Routine.hpp:686
DelayContext get_delay_context() const override
Get the active delay context for this routine.
Definition Routine.hpp:666
void set_should_terminate(bool should_terminate) override
Set should_terminate flag in promise.
Definition Routine.hpp:691
Coroutine resumed by more than one clock.
Definition Routine.hpp:642
FreeRoutine(const FreeRoutine &other)=delete
std::coroutine_handle< promise_type > m_handle
Definition Routine.hpp:811
FreeRoutine & operator=(const FreeRoutine &other)=delete
uint64_t get_next_sample() const override
Get next sample execution time (audio domain)
Definition Routine.hpp:801
bool get_auto_resume() const override
Get auto_resume flag from promise.
Definition Routine.hpp:777
uint64_t next_execution() const override
Gets the sample position when this routine should next execute.
Definition Routine.hpp:774
void set_next_frame(uint64_t) override
Set next frame execution time (graphics domain)
Definition Routine.hpp:804
void set_should_terminate(bool v) override
Set should_terminate flag in promise.
Definition Routine.hpp:793
bool requires_clock_sync() const override
Check if the routine should synchronize with a clock.
Definition Routine.hpp:775
uint64_t get_next_frame() const override
Get next frame execution time (graphics domain)
Definition Routine.hpp:803
void set_auto_resume(bool v) override
Set auto_resume flag in promise.
Definition Routine.hpp:782
bool get_sync_to_clock() const override
Get sync_to_clock flag from promise.
Definition Routine.hpp:799
void set_next_sample(uint64_t) override
Set next sample execution time (audio domain)
Definition Routine.hpp:802
bool get_should_terminate() const override
Get should_terminate flag from promise.
Definition Routine.hpp:788
Coroutine resumed when a caller-supplied condition becomes true.
Definition Routine.hpp:753
uint64_t get_next_sample() const override
Get next sample execution time (audio domain)
Definition Routine.hpp:609
void set_auto_resume(bool auto_resume) override
Set auto_resume flag in promise.
Definition Routine.hpp:578
void set_should_terminate(bool should_terminate) override
Set should_terminate flag in promise.
Definition Routine.hpp:588
bool get_should_terminate() const override
Get should_terminate flag from promise.
Definition Routine.hpp:583
void set_next_frame(uint64_t next_frame) override
Set next frame execution time (graphics domain)
Definition Routine.hpp:603
GraphicsRoutine(const GraphicsRoutine &other)=delete
void set_next_sample(uint64_t) override
Set next sample execution time (audio domain)
Definition Routine.hpp:610
bool get_auto_resume() const override
Get auto_resume flag from promise.
Definition Routine.hpp:573
GraphicsRoutine & operator=(const GraphicsRoutine &other)=delete
std::coroutine_handle< promise_type > m_handle
Handle to the underlying coroutine.
Definition Routine.hpp:623
uint64_t get_next_frame() const override
Get next frame execution time (graphics domain)
Definition Routine.hpp:598
DelayContext get_delay_context() const override
Get the active delay context for this routine.
Definition Routine.hpp:557
void set_delay_context(DelayContext context) override
Set the active delay context for this routine.
Definition Routine.hpp:562
bool get_sync_to_clock() const override
Get sync_to_clock flag from promise.
Definition Routine.hpp:593
A C++20 coroutine-based graphics processing task with frame-accurate timing.
Definition Routine.hpp:496
void set_state(const std::string &key, T value)
Sets a named state value in the coroutine.
Definition Routine.hpp:214
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 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:184
void update_params(Args... args)
Updates multiple named parameters in the coroutine's state.
Definition Routine.hpp:199
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:260
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:229
void update_params_impl(const std::string &key, T value, Args... args)
Implementation helper for update_params.
Definition Routine.hpp:273
virtual DelayContext get_delay_context() const
Get the active delay context for this routine.
Definition Routine.hpp:178
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:244
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:452
SoundRoutine(const SoundRoutine &other)=delete
bool get_sync_to_clock() const override
Get sync_to_clock flag from promise.
Definition Routine.hpp:412
void set_auto_resume(bool auto_resume) override
Set auto_resume flag in promise.
Definition Routine.hpp:397
uint64_t get_next_sample() const override
Get next sample execution time (audio domain)
Definition Routine.hpp:417
bool get_auto_resume() const override
Get auto_resume flag from promise.
Definition Routine.hpp:392
uint64_t get_next_frame() const override
Get next frame execution time (graphics domain)
Definition Routine.hpp:427
void set_next_sample(uint64_t next_sample) override
Set next sample execution time (audio domain)
Definition Routine.hpp:422
SoundRoutine & operator=(const SoundRoutine &other)=delete
bool get_should_terminate() const override
Get should_terminate flag from promise.
Definition Routine.hpp:402
void set_next_frame(uint64_t next_cycle) override
Set next frame execution time (graphics domain)
Definition Routine.hpp:432
void set_delay_context(DelayContext context) override
Set the active delay context for this routine.
Definition Routine.hpp:381
DelayContext get_delay_context() const override
Get the active delay context for this routine.
Definition Routine.hpp:376
void set_should_terminate(bool should_terminate) override
Set should_terminate flag in promise.
Definition Routine.hpp:407
A C++20 coroutine-based audio processing task with sample-accurate timing.
Definition Routine.hpp:316
DelayContext
Discriminator for different temporal delay mechanisms.
Coroutine promise type for audio processing tasks with sample-accurate timing.
Definition Promise.hpp:198
Coroutine promise for routines suspended on an arbitrary boolean condition.
Definition Promise.hpp:442
Coroutine promise for routines resumed by more than one clock.
Definition Promise.hpp:351
Coroutine promise type for graphics processing tasks with frame-accurate timing.
Definition Promise.hpp:261