MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
BufferPipelineCapture.cpp
Go to the documentation of this file.
2
5
11
12namespace MayaFlux::Kriya {
13
14namespace {
15
16 template <typename Fn>
17 void visit_matching(Kakshya::DataVariant& existing, const Kakshya::DataVariant& incoming, const Fn& fn)
18 {
19 if (existing.index() != incoming.index()) {
20 throw std::bad_variant_access();
21 }
22
23 std::visit([&](auto& stored) {
24 fn(stored, std::get<std::decay_t<decltype(stored)>>(incoming));
25 },
26 existing);
27 }
28
29 template <typename Vec>
30 void drop_front(Vec& values, size_t count)
31 {
32 values.erase(values.begin(), values.begin() + static_cast<std::ptrdiff_t>(count));
33 }
34
35 void append_capture_data(Kakshya::DataVariant& existing, const Kakshya::DataVariant& incoming)
36 {
37 visit_matching(existing, incoming, [](auto& stored, const auto& added) {
38 stored.insert(stored.end(), added.begin(), added.end());
39 });
40 }
41
42 void update_circular_capture(Kakshya::DataVariant& existing, const Kakshya::DataVariant& incoming, uint32_t circular_size)
43 {
44 visit_matching(existing, incoming, [circular_size](auto& circular, const auto& added) {
45 circular.insert(circular.end(), added.begin(), added.end());
46
47 if (circular.size() > circular_size) {
48 drop_front(circular, circular.size() - circular_size);
49 }
50 });
51 }
52
53 void update_windowed_capture(Kakshya::DataVariant& existing, const Kakshya::DataVariant& incoming,
54 uint32_t window_size, uint32_t hop_size)
55 {
56 visit_matching(existing, incoming, [window_size, hop_size](auto& windowed, const auto& added) {
57 if (windowed.size() >= window_size) {
58 if (hop_size >= windowed.size()) {
59 windowed = added;
60 return;
61 }
62 drop_front(windowed, hop_size);
63 }
64
65 windowed.insert(windowed.end(), added.begin(), added.end());
66
67 if (windowed.size() > window_size) {
68 drop_front(windowed, windowed.size() - window_size);
69 }
70 });
71 }
72
73}
74
75std::optional<Kakshya::DataVariant> BufferPipeline::read_graphics_buffer(
76 BufferOperation& op, const std::shared_ptr<Buffers::VKBuffer>& buffer)
77{
78 auto& entry = m_readers[{ &op, buffer.get() }];
79 if (!entry.processor) {
80 entry.buffer = buffer;
81 entry.processor = std::make_shared<Buffers::DataReadProcessor>();
82 buffer->get_processing_chain()->add_processor(entry.processor, buffer);
83 entry.processor->set_skip_while_pending(true);
84 }
85
86 auto data = detail::resolve_graphics_read(*entry.processor);
87 detail::arm_graphics_read(*entry.processor, buffer);
88 return data;
89}
90
91bool BufferPipeline::route_image(BufferOperation& op)
92{
93 const auto source = op.m_capture.get_graphics_buffer();
94 if (!source || !has_immediate_routing(op)) {
95 return false;
96 }
97
98 const auto current = std::ranges::find_if(m_operations,
99 [&op](const BufferOperation& o) { return &o == &op; });
100 auto route = std::next(current);
101
102 if (!route->m_target_graphics_buffer || !detail::accepts_layer_write(route->m_target_graphics_buffer)) {
103 return false;
104 }
105
106 const auto image = detail::source_image(source);
107 if (!image) {
108 return false;
109 }
110
111 detail::write_layer(route->m_target_graphics_buffer, route->m_target_layer, image);
112
113 const auto route_index = static_cast<size_t>(std::distance(m_operations.begin(), route));
114 if (route_index < m_data_states.size()) {
115 m_data_states[route_index] = DataState::CONSUMED;
116 }
117
118 return true;
119}
120
121void BufferPipeline::capture_operation(BufferOperation& op, uint64_t cycle)
122{
123 if (route_image(op)) {
124 return;
125 }
126
127 Kakshya::DataVariant buffer_data;
128
129 if (op.m_capture.get_audio_buffer()) {
130 bool should_process = op.m_capture.get_processing_control() == BufferCapture::ProcessingControl::ON_CAPTURE;
131 buffer_data = detail::extract_buffer_data(op.m_capture.get_audio_buffer(), should_process);
132 } else if (op.m_capture.get_graphics_buffer()) {
133 auto graphics_data = read_graphics_buffer(op, op.m_capture.get_graphics_buffer());
134 if (!graphics_data) {
135 return;
136 }
137 buffer_data = std::move(*graphics_data);
138 }
139
141 op.m_capture.m_data_ready_callback(buffer_data, cycle);
142 }
143
144 auto capture_mode = op.m_capture.get_mode();
145
146 switch (capture_mode) {
147 case BufferCapture::CaptureMode::TRANSIENT:
148 m_operation_data[&op] = buffer_data;
149 break;
150
151 case BufferCapture::CaptureMode::ACCUMULATE: {
152 auto it = m_operation_data.find(&op);
153 if (it == m_operation_data.end()) {
154 m_operation_data[&op] = buffer_data;
155 } else {
156 try {
157 append_capture_data(it->second, buffer_data);
158
159 } catch (const std::bad_variant_access& e) {
160 MF_ERROR(Journal::Component::Kriya,
161 Journal::Context::CoroutineScheduling,
162 "Data type mismatch during ACCUMULATE capture: {}",
163 e.what());
164 m_operation_data[&op] = buffer_data;
165 }
166 }
167 break;
168 }
169 case BufferCapture::CaptureMode::CIRCULAR: {
170 uint32_t circular_size = op.m_capture.get_circular_size();
171 if (circular_size == 0) {
172 circular_size = 4096;
173 }
174
175 auto it = m_operation_data.find(&op);
176 if (it == m_operation_data.end()) {
177 m_operation_data[&op] = buffer_data;
178 } else {
179 try {
180 update_circular_capture(it->second, buffer_data, circular_size);
181
182 } catch (const std::bad_variant_access& e) {
183 MF_ERROR(Journal::Component::Kriya,
184 Journal::Context::CoroutineScheduling,
185 "Data type mismatch during CIRCULAR capture: {}",
186 e.what());
187 m_operation_data[&op] = buffer_data;
188 } catch (std::exception& e) {
189 error_rethrow(Journal::Component::Kriya,
190 Journal::Context::CoroutineScheduling,
191 std::source_location::current(),
192 "Error during CIRCULAR capture: {}",
193 e.what());
194 m_operation_data[&op] = buffer_data;
195 }
196 }
197 break;
198 }
199 case BufferCapture::CaptureMode::WINDOWED: {
200 uint32_t window_size = op.m_capture.get_window_size();
201 float overlap_ratio = op.m_capture.get_overlap_ratio();
202
203 if (window_size == 0) {
204 window_size = 512;
205 }
206
207 auto hop_size = static_cast<uint32_t>((float)window_size * (1.0F - overlap_ratio));
208 if (hop_size == 0)
209 hop_size = 1;
210
211 auto it = m_operation_data.find(&op);
212 if (it == m_operation_data.end()) {
213 m_operation_data[&op] = buffer_data;
214 } else {
215 try {
216 update_windowed_capture(it->second, buffer_data, window_size, hop_size);
217
218 } catch (const std::bad_variant_access& e) {
219 MF_ERROR(Journal::Component::Kriya, Journal::Context::CoroutineScheduling,
220 "Data type mismatch during WINDOWED capture: {}", e.what());
221 m_operation_data[&op] = buffer_data;
222 }
223 }
224 break;
225 }
226
227 case BufferCapture::CaptureMode::TRIGGERED: {
229 m_operation_data[&op] = buffer_data;
230 }
231 break;
232 }
233
234 default:
235 m_operation_data[&op] = buffer_data;
236 break;
237 }
238
239 if (has_immediate_routing(op)) {
240 auto current_it = std::ranges::find_if(m_operations,
241 [&op](const BufferOperation& o) { return &o == &op; });
242
243 if (current_it != m_operations.end()) {
244 auto next_it = std::next(current_it);
245 if (next_it != m_operations.end() && next_it->get_type() == BufferOperation::OpType::ROUTE) {
246
247 if (next_it->m_target_audio_buffer) {
248 if (!m_buffer_manager) {
249 error<std::invalid_argument>(Journal::Component::Kriya,
250 Journal::Context::CoroutineScheduling,
251 std::source_location::current(),
252 "BufferPipeline has no BufferManager for immediate ROUTE-to-buffer");
253 }
254
255 if (!next_it->m_attached_processor) {
256 auto writer = std::make_shared<Buffers::AudioWriteProcessor>();
257 m_buffer_manager->add_processor(writer, next_it->m_target_audio_buffer,
258 Buffers::ProcessingToken::AUDIO_BACKEND);
259 next_it->m_attached_processor = writer;
260 }
261
262 std::static_pointer_cast<Buffers::AudioWriteProcessor>(next_it->m_attached_processor)
263 ->set_data(buffer_data);
264 } else if (next_it->m_target_graphics_buffer) {
265 queue_graphics_write(*next_it, next_it->m_target_graphics_buffer, buffer_data);
266 } else if (next_it->m_target_audio_stream) {
267 detail::write_to_container(next_it->m_target_audio_stream, buffer_data, next_it->m_target_audio_channel);
268 } else if (next_it->m_target_graphics_stream) {
269 detail::write_to_container(next_it->m_target_graphics_stream, buffer_data);
270 }
271
272 size_t route_index = std::distance(m_operations.begin(), next_it);
273 if (route_index < m_data_states.size()) {
274 m_data_states[route_index] = DataState::CONSUMED;
275 }
276 }
277 }
278 }
279}
280
281void BufferPipeline::reset_accumulated_data()
282{
283 for (auto& op : m_operations) {
284 if (op.get_type() == BufferOperation::OpType::CAPTURE) {
285 auto mode = op.m_capture.get_mode();
286 if (mode == BufferCapture::CaptureMode::ACCUMULATE || mode == BufferCapture::CaptureMode::CIRCULAR || mode == BufferCapture::CaptureMode::WINDOWED) {
287 m_operation_data.erase(&op);
288 }
289 }
290 }
291}
292
293bool BufferPipeline::has_immediate_routing(const BufferOperation& op) const
294{
295 auto it = std::ranges::find_if(m_operations,
296 [&op](const BufferOperation& o) { return &o == &op; });
297
298 if (it == m_operations.end() || std::next(it) == m_operations.end()) {
299 return false;
300 }
301
302 auto next_op = std::next(it);
303 return next_op->get_type() == BufferOperation::OpType::ROUTE;
304}
305
306void BufferPipeline::cleanup_expired_data()
307{
308 for (size_t i = 0; i < m_data_states.size(); ++i) {
309 if (m_data_states[i] == DataState::READY) {
310 if (i < m_operations.size() && m_operations[i].get_type() == BufferOperation::OpType::CAPTURE && m_operations[i].m_capture.get_mode() == BufferCapture::CaptureMode::TRANSIENT) {
311
312 if (m_operations[i].m_capture.m_data_expired_callback) {
313 auto it = m_operation_data.find(&m_operations[i]);
314 if (it != m_operation_data.end()) {
315 m_operations[i].m_capture.m_data_expired_callback(it->second, m_current_cycle);
316 }
317 }
318
319 m_data_states[i] = DataState::EXPIRED;
320 } else {
321 m_data_states[i] = DataState::CONSUMED;
322 }
323 }
324 }
325
326 auto it = m_operation_data.begin();
327 while (it != m_operation_data.end()) {
328 if (m_current_cycle > 2) {
329 it = m_operation_data.erase(it);
330 } else {
331 ++it;
332 }
333 }
334}
335
336}
#define MF_ERROR(comp, ctx,...)
IO::ImageData image
Definition Decoder.cpp:47
glm::vec2 current
std::shared_ptr< NetworkGeometryBuffer > buffer
uint32_t mode
std::vector< std::byte > values
Definition VDBWriter.cpp:26
size_t count
Cycle Behavior: The for_cycles(N) configuration controls how many times the capture operation execute...
OperationFunction m_data_ready_callback
Definition Capture.hpp:205
std::shared_ptr< Buffers::AudioBuffer > get_audio_buffer() const
Return the configured audio source.
Definition Capture.hpp:180
ProcessingControl get_processing_control() const
Definition Capture.hpp:186
std::shared_ptr< Buffers::VKBuffer > get_graphics_buffer() const
Return the configured graphics source.
Definition Capture.hpp:182
std::function< bool()> m_stop_condition
Definition Capture.hpp:204
CaptureMode get_mode() const
Definition Capture.hpp:185
uint32_t get_window_size() const
Definition Capture.hpp:190
uint32_t get_circular_size() const
Definition Capture.hpp:189
Fundamental unit of operation in buffer processing pipelines.
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