MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
BufferPipeline.cpp
Go to the documentation of this file.
1#include "BufferPipeline.hpp"
2
7
8namespace MayaFlux::Kriya {
9
11 Vruta::TaskScheduler& scheduler,
12 std::shared_ptr<Buffers::BufferManager> buffer_manager,
13 std::shared_ptr<IO::IOManager> io_manager)
14 : m_buffer_manager(std::move(buffer_manager))
15 , m_io_manager(std::move(io_manager))
16 , m_scheduler(&scheduler)
17{
18}
19
21{
22 for (auto& [key, entry] : m_readers) {
23 entry.buffer->get_processing_chain()->remove_processor(entry.processor, entry.buffer);
24 }
25
26 if (!m_buffer_manager) {
27 return;
28 }
29
30 for (auto& op : m_operations) {
31 if (op.m_attached_processor) {
32 const bool detachable = op.get_type() == BufferOperation::OpType::ROUTE
33 || op.get_type() == BufferOperation::OpType::LOAD
34 || op.get_type() == BufferOperation::OpType::FUSE
35 || op.get_type() == BufferOperation::OpType::MODIFY;
36
37 if (detachable && op.m_target_audio_buffer) {
38 m_buffer_manager->remove_processor(op.m_attached_processor, op.m_target_audio_buffer);
39 }
40 if (detachable && op.m_target_graphics_buffer) {
41 m_buffer_manager->remove_processor(op.m_attached_processor, op.m_target_graphics_buffer);
42 }
43 op.m_attached_processor = nullptr;
44 }
45 }
46}
47
48BufferPipeline& BufferPipeline::parallel(std::initializer_list<BufferOperation> operations)
49{
50 for (auto& op : operations) {
51 BufferOperation parallel_op = BufferOperation(op);
52 parallel_op.with_priority(255);
53 m_operations.emplace_back(std::move(parallel_op));
54 }
55 return *this;
56}
57
59 std::function<void(uint32_t)> on_cycle_start,
60 std::function<void(uint32_t)> on_cycle_end)
61{
62 m_cycle_start_callback = std::move(on_cycle_start);
63 m_cycle_end_callback = std::move(on_cycle_end);
64 return *this;
65}
66
67void BufferPipeline::execute_buffer_rate(uint64_t max_cycles)
68{
69 if (!m_scheduler) {
70 error<std::runtime_error>(Journal::Component::Kriya,
72 std::source_location::current(),
73 "Pipeline requires scheduler for execution");
74 }
75
76 auto self = shared_from_this();
77
78 if (max_cycles == 0) {
79 max_cycles = UINT64_MAX;
81 }
82 m_max_cycles = max_cycles;
83
84 auto routine = std::make_shared<Vruta::SoundRoutine>(
85 execute_internal(max_cycles, 0));
86
87 m_routine = routine;
88 m_scheduler->add_task(std::move(routine));
89
90 m_active_self = self;
91}
92
93void BufferPipeline::execute_frame_rate(uint64_t max_cycles, uint64_t frames_per_operation)
94{
95 if (!m_scheduler) {
96 error<std::runtime_error>(Journal::Component::Kriya,
98 std::source_location::current(),
99 "Pipeline requires scheduler for execution");
100 }
101
102 auto self = shared_from_this();
103
104 if (max_cycles == 0) {
105 max_cycles = UINT64_MAX;
107 }
108 m_max_cycles = max_cycles;
109
110 auto routine = std::make_shared<Vruta::GraphicsRoutine>(
111 execute_frame_internal(max_cycles, frames_per_operation));
112
113 m_routine = routine;
114 m_scheduler->add_task(std::move(routine));
115
116 m_active_self = self;
117}
118
120{
121 if (!m_scheduler) {
122 error<std::runtime_error>(Journal::Component::Kriya,
124 std::source_location::current(),
125 "Pipeline requires scheduler for execution");
126 }
127 auto self = shared_from_this();
128
129 m_max_cycles = 1;
130 auto routine = std::make_shared<Vruta::SoundRoutine>(
131 execute_internal(1, 0));
132 m_routine = routine;
133 m_scheduler->add_task(std::move(routine));
134 m_active_self = self;
135}
136
138{
139 if (!m_scheduler) {
140 error<std::runtime_error>(Journal::Component::Kriya,
142 std::source_location::current(),
143 "Pipeline requires scheduler for execution");
144 }
145
146 auto self = shared_from_this();
147
148 if (cycles == 0) {
149 cycles = UINT64_MAX;
151 }
152 m_max_cycles = cycles;
153 auto routine = std::make_shared<Vruta::SoundRoutine>(
154 execute_internal(cycles, 0));
155 m_routine = routine;
156 m_scheduler->add_task(std::move(routine));
157 m_active_self = self;
158}
159
165
167 uint64_t max_cycles,
168 uint64_t samples_per_operation)
169{
170 if (!m_scheduler) {
171 error<std::runtime_error>(Journal::Component::Kriya,
173 std::source_location::current(),
174 "Pipeline must have scheduler for scheduled execution");
175 }
176
177 auto self = shared_from_this();
178
179 if (max_cycles == 0) {
180 max_cycles = UINT64_MAX;
182 }
183
184 m_max_cycles = max_cycles;
185
186 auto routine = std::make_shared<Vruta::SoundRoutine>(
187 execute_internal(max_cycles, samples_per_operation));
188
189 m_routine = routine;
190 m_scheduler->add_task(std::move(routine));
191
192 m_active_self = self;
193}
194
196 uint32_t max_cycles,
197 double seconds_per_operation)
198{
199 if (!m_scheduler) {
200 error<std::runtime_error>(Journal::Component::Kriya,
202 std::source_location::current(),
203 "Pipeline must have scheduler for scheduled execution");
204 }
205
206 uint64_t samples = m_scheduler->seconds_to_samples(seconds_per_operation);
207 execute_scheduled(max_cycles, samples);
208}
209
211{
213
214 const auto routine = std::move(m_routine);
215 const auto self = std::move(m_active_self);
216
217 if (!routine) {
218 return;
219 }
220
221 const bool running = routine->is_active();
222
223 if (m_scheduler) {
224 m_scheduler->cancel_task(routine);
225 }
226
227 if (running && m_on_complete) {
229 }
230}
231
233{
234 return m_routine && m_routine->is_active();
235}
236
237void BufferPipeline::mark_data_consumed(uint32_t operation_index)
238{
239 if (operation_index < m_data_states.size()) {
240 m_data_states[operation_index] = DataState::CONSUMED;
241 }
242}
243
245{
246 return std::ranges::any_of(m_data_states,
247 [](DataState state) { return state == DataState::READY; });
248}
249
251{
252 m_on_complete = std::move(cb);
253 return *this;
254}
255
256Vruta::SoundRoutine BufferPipeline::execute_internal(uint64_t max_cycles, uint64_t samples_per_operation)
257{
259
260 switch (m_execution_strategy) {
262 return execute_phased(max_cycles, samples_per_operation);
263
265 return execute_streaming(max_cycles, samples_per_operation);
266
268 return execute_parallel(max_cycles, samples_per_operation);
269
271 return execute_reactive(max_cycles, samples_per_operation);
272
273 default:
274 error<std::runtime_error>(Journal::Component::Kriya,
276 std::source_location::current(),
277 "Unknown execution strategy in BufferPipeline");
278 }
279}
280
281}
BufferOperation & with_priority(uint8_t priority)
Set execution priority for scheduler ordering.
@ LOAD
Load data from container to buffer with position control.
@ FUSE
Fuse multiple sources using custom fusion functions.
@ ROUTE
Route data to destination (buffer or container)
@ MODIFY
Modify Buffer Data using custom quick process.
Fundamental unit of operation in buffer processing pipelines.
@ READY
Data ready for processing.
Vruta::SoundRoutine execute_reactive(uint64_t max_cycles, uint64_t samples_per_operation)
void end()
End a running pipeline now.
BufferPipeline & on_complete(std::function< void()> cb)
Register a callback fired once when pipeline execution ends.
Vruta::GraphicsRoutine execute_frame_internal(uint64_t max_cycles, uint64_t frames_per_operation)
void execute_for_cycles(uint64_t cycles=0)
Execute the pipeline for a specified number of cycles.
Vruta::SoundRoutine execute_internal(uint64_t max_cycles, uint64_t samples_per_operation)
void execute_continuous()
Start continuous execution of the pipeline.
std::map< std::pair< const BufferOperation *, const Buffers::VKBuffer * >, GraphicsReader > m_readers
void execute_scheduled_at_rate(uint32_t max_cycles=0, double seconds_per_operation=1)
Execute pipeline with real-time rate control.
std::shared_ptr< BufferPipeline > m_active_self
std::vector< BufferOperation > m_operations
void execute_once()
Execute the pipeline for a single cycle.
bool is_running() const
True from an execute call until the pipeline ends or runs out of cycles.
std::vector< DataState > m_data_states
void execute_frame_rate(uint64_t max_cycles=0, uint64_t frames_per_operation=0)
Execute the pipeline at graphics frame rate.
Vruta::SoundRoutine execute_streaming(uint64_t max_cycles, uint64_t samples_per_operation)
Vruta::SoundRoutine execute_parallel(uint64_t max_cycles, uint64_t samples_per_operation)
std::function< void(uint32_t)> m_cycle_end_callback
bool has_pending_data() const
Check if any operations have pending data ready for processing.
std::shared_ptr< Vruta::Routine > m_routine
void mark_data_consumed(uint32_t operation_index)
Execute pipeline synchronized to audio hardware cycle boundaries.
std::function< void(uint32_t)> m_cycle_start_callback
std::function< void()> m_on_complete
void execute_buffer_rate(uint64_t max_cycles=0)
Execute pipeline synchronized to audio hardware cycle boundaries.
std::shared_ptr< Buffers::BufferManager > m_buffer_manager
BufferPipeline & parallel(std::initializer_list< BufferOperation > operations)
Execute operations in parallel within the current cycle.
void execute_scheduled(uint64_t max_cycles=0, uint64_t samples_per_operation=1)
Execute pipeline with sample-accurate timing between operations.
Vruta::SoundRoutine execute_phased(uint64_t max_cycles, uint64_t samples_per_operation)
Vruta::TaskScheduler * m_scheduler
BufferPipeline & with_lifecycle(std::function< void(uint32_t)> on_cycle_start, std::function< void(uint32_t)> on_cycle_end)
Set lifecycle callbacks for cycle management.
Coroutine-based execution engine for composable, multi-strategy buffer processing.
A C++20 coroutine-based audio processing task with sample-accurate timing.
Definition Routine.hpp:327
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
uint64_t seconds_to_samples(double seconds) const
Converts a time in seconds to a number of samples.
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
@ CoroutineScheduling
Coroutine scheduling and temporal coordination (Vruta::TaskScheduler)
@ Kriya
Automatable tasks and fluent scheduling api for Nodes and Buffers.
@ PHASED
PHASED: Traditional phased execution (default)
@ STREAMING
STREAMING: Immediate flow-through execution.
@ PARALLEL
PARALLEL: Concurrent capture with synchronization.
@ REACTIVE
REACTIVE: Data-driven reactive execution.