MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
BufferProcessingChain.cpp
Go to the documentation of this file.
2
3#include "Buffer.hpp"
4
6
7namespace MayaFlux::Buffers {
8
9bool BufferProcessingChain::add_processor(const std::shared_ptr<BufferProcessor>& processor, const std::shared_ptr<Buffer>& buffer, std::string* rejection_reason)
10{
11 if (m_is_processing.load(std::memory_order_acquire) || processor->m_active_processing.load(std::memory_order_acquire) > 0) {
12 return queue_pending_processor_op(processor, buffer, true, false, rejection_reason);
13 }
14
15 return add_processor_direct(processor, buffer, rejection_reason);
16}
17
18bool BufferProcessingChain::add_processor_direct(const std::shared_ptr<BufferProcessor>& processor, const std::shared_ptr<Buffer>& buffer, std::string* rejection_reason)
19{
20 auto processor_token = processor->get_processing_token();
21
22 switch (m_enforcement_strategy) {
24 if (processor_token != m_token_filter_mask) {
25 if (rejection_reason) {
26 *rejection_reason = "Processor token (" + std::to_string(static_cast<uint32_t>(processor_token)) + ") does not exactly match chain's preferred token (" + std::to_string(static_cast<uint32_t>(m_token_filter_mask)) + ") in STRICT mode";
27 }
28 return false;
29 }
30 break;
31
33 if (!are_tokens_compatible(m_token_filter_mask, processor_token)) {
34 if (rejection_reason) {
35 *rejection_reason = "Processor token (" + std::to_string(static_cast<uint32_t>(processor_token)) + ") is not compatible with chain's preferred token (" + std::to_string(static_cast<uint32_t>(m_token_filter_mask)) + ") in FILTERED mode";
36 }
37 return false;
38 }
39 break;
40
42 if (!are_tokens_compatible(m_token_filter_mask, processor_token)) {
43 m_conditional_processors[buffer].insert(processor);
44 }
45 break;
46
48 if (!are_tokens_compatible(m_token_filter_mask, processor_token)) {
49 m_pending_removal[buffer].insert(processor);
50 }
51 break;
52
54 break;
55 }
56
57 auto& processors = m_buffer_processors[buffer];
58
59 auto it = std::ranges::find(processors, processor);
60
61 if (it != processors.end()) {
62 if (rejection_reason) {
63 *rejection_reason = "Processor already exists in chain for this buffer";
64 }
65 return false;
66 }
67
68 processors.push_back(processor);
69 processor->on_attach(buffer);
70
71 return true;
72}
73
74void BufferProcessingChain::remove_processor(const std::shared_ptr<BufferProcessor>& processor, const std::shared_ptr<Buffer>& buffer)
75{
76 remove_processor_internal(processor, buffer, false);
77}
78
79void BufferProcessingChain::remove_processor_anywhere(const std::shared_ptr<BufferProcessor>& processor, const std::shared_ptr<Buffer>& buffer)
80{
81 remove_processor_internal(processor, buffer, true);
82}
83
85 const std::shared_ptr<BufferProcessor>& processor,
86 const std::shared_ptr<Buffer>& buffer,
87 bool search_all_slots)
88{
89 if (m_is_processing.load(std::memory_order_acquire)) {
90 queue_pending_processor_op(processor, buffer, false, search_all_slots);
91 return;
92 }
93
94 if (search_all_slots) {
96 } else {
98 }
99}
100
101void BufferProcessingChain::remove_processor_direct(const std::shared_ptr<BufferProcessor>& processor, const std::shared_ptr<Buffer>& buffer)
102{
103 auto& processors = m_buffer_processors[buffer];
104 auto it = std::ranges::find(processors, processor);
105
106 if (it != processors.end()) {
107 processor->on_detach(buffer);
108 processors.erase(it);
109
110 m_conditional_processors[buffer].erase(processor);
111 m_pending_removal[buffer].erase(processor);
112 }
113}
114
115void BufferProcessingChain::remove_processor_anywhere_direct(const std::shared_ptr<BufferProcessor>& processor, const std::shared_ptr<Buffer>& buffer)
116{
117 auto regular_it = m_buffer_processors.find(buffer);
118 if (regular_it != m_buffer_processors.end()) {
119 auto processor_it = std::ranges::find(regular_it->second, processor);
120 if (processor_it != regular_it->second.end()) {
121 processor->on_detach(buffer);
122 regular_it->second.erase(processor_it);
123 m_conditional_processors[buffer].erase(processor);
124 m_pending_removal[buffer].erase(processor);
125 return;
126 }
127 }
128
129 auto remove_slot = [&processor, &buffer](auto& slots, const auto& key) {
130 auto it = slots.find(key);
131 if (it == slots.end() || it->second != processor)
132 return false;
133
134 it->second->on_detach(buffer);
135 slots.erase(it);
136 return true;
137 };
138
139 if (remove_slot(m_preprocessors, buffer)
140 || remove_slot(m_postprocessors, buffer)
141 || remove_slot(m_final_processors, buffer)) {
142 return;
143 }
144}
145
146void BufferProcessingChain::process_non_owning(const std::shared_ptr<Buffer>& buffer)
147{
148 if (m_pending_count.load(std::memory_order_relaxed) > 0) {
150 }
151
152 auto it = m_buffer_processors.find(buffer);
153 if (it == m_buffer_processors.end() || it->second.empty()) {
154 return;
155 }
156
157 for (auto& processor : it->second) {
158 bool should_process = true;
159
161 auto processor_token = processor->get_processing_token();
162 should_process = are_tokens_compatible(m_token_filter_mask, processor_token);
163 }
164
165 if (should_process) {
166 processor->process_non_owning(buffer);
167 }
168 }
169
172 }
173}
174
175void BufferProcessingChain::process(const std::shared_ptr<Buffer>& buffer)
176{
177 bool expected = false;
178 if (!m_is_processing.compare_exchange_strong(expected, true,
179 std::memory_order_acquire, std::memory_order_relaxed)) {
180 return;
181 }
182
183 if (m_pending_count.load(std::memory_order_relaxed) > 0) {
185 }
186
187 auto it = m_buffer_processors.find(buffer);
188 if (it == m_buffer_processors.end() || it->second.empty()) {
189 m_is_processing.store(false, std::memory_order_release);
190 return;
191 }
192
193 for (auto& processor : it->second) {
194 bool should_process = true;
195
197 auto processor_token = processor->get_processing_token();
198 should_process = are_tokens_compatible(m_token_filter_mask, processor_token);
199 }
200
201 if (should_process) {
202 processor->process(buffer);
203 }
204 }
205
208 }
209
210 m_is_processing.store(false, std::memory_order_release);
211}
212
213void BufferProcessingChain::process_complete(const std::shared_ptr<Buffer>& buffer)
214{
219}
220
222{
223 for (auto& m_pending_op : m_pending_ops) {
224 if (m_pending_op.active.load(std::memory_order_acquire)) {
225 auto& op = m_pending_op;
226
227 if (op.is_addition) {
228 add_processor_direct(op.processor, op.buffer);
229 } else if (op.search_all_slots) {
230 remove_processor_anywhere_direct(op.processor, op.buffer);
231 } else {
232 remove_processor_direct(op.processor, op.buffer);
233 }
234
235 op.processor.reset();
236 op.buffer.reset();
237 op.active.store(false, std::memory_order_release);
238 m_pending_count.fetch_sub(1, std::memory_order_relaxed);
239 }
240 }
241}
242
243void BufferProcessingChain::add_preprocessor(const std::shared_ptr<BufferProcessor>& processor, const std::shared_ptr<Buffer>& buffer)
244{
245 processor->on_attach(buffer);
246 m_preprocessors[buffer] = processor;
247}
248
249void BufferProcessingChain::add_postprocessor(const std::shared_ptr<BufferProcessor>& processor, const std::shared_ptr<Buffer>& buffer)
250{
251 processor->on_attach(buffer);
252 m_postprocessors[buffer] = processor;
253}
254
255void BufferProcessingChain::add_final_processor(const std::shared_ptr<BufferProcessor>& processor, const std::shared_ptr<Buffer>& buffer)
256{
257 processor->on_attach(buffer);
258 m_final_processors[buffer] = processor;
259}
260
261bool BufferProcessingChain::has_processors(const std::shared_ptr<Buffer>& buffer) const
262{
263 auto it = m_buffer_processors.find(buffer);
264 return it != m_buffer_processors.end() && !it->second.empty();
265}
266
267const std::vector<std::shared_ptr<BufferProcessor>>& BufferProcessingChain::get_processors(const std::shared_ptr<Buffer>& buffer) const
268{
269 static const std::vector<std::shared_ptr<BufferProcessor>> empty_vector;
270
271 auto it = m_buffer_processors.find(buffer);
272 if (it != m_buffer_processors.end()) {
273 return it->second;
274 }
275
276 return empty_vector;
277}
278
279std::shared_ptr<BufferProcessor>
280BufferProcessingChain::get_preprocessor(const std::shared_ptr<Buffer>& buffer) const
281{
282 auto it = m_preprocessors.find(buffer);
283 return it != m_preprocessors.end() ? it->second : nullptr;
284}
285
286std::shared_ptr<BufferProcessor>
287BufferProcessingChain::get_postprocessor(const std::shared_ptr<Buffer>& buffer) const
288{
289 auto it = m_postprocessors.find(buffer);
290 return it != m_postprocessors.end() ? it->second : nullptr;
291}
292
293std::shared_ptr<BufferProcessor>
294BufferProcessingChain::get_final_processor(const std::shared_ptr<Buffer>& buffer) const
295{
296 auto it = m_final_processors.find(buffer);
297 return it != m_final_processors.end() ? it->second : nullptr;
298}
299
300void BufferProcessingChain::preprocess(const std::shared_ptr<Buffer>& buffer)
301{
302 auto pre_it = m_preprocessors.find(buffer);
303 if (pre_it != m_preprocessors.end()) {
304 pre_it->second->process(buffer);
305 }
306}
307
308void BufferProcessingChain::postprocess(const std::shared_ptr<Buffer>& buffer)
309{
310 auto post_it = m_postprocessors.find(buffer);
311 if (post_it != m_postprocessors.end()) {
312 post_it->second->process(buffer);
313 }
314}
315
316void BufferProcessingChain::process_final(const std::shared_ptr<Buffer>& buffer)
317{
318 auto final_it = m_final_processors.find(buffer);
319 if (final_it != m_final_processors.end()) {
320 final_it->second->process(buffer);
321 }
322}
323
324void BufferProcessingChain::merge_chain(const std::shared_ptr<BufferProcessingChain>& other)
325{
326 for (const auto& [buffer, processors] : other->get_chain()) {
327 if (!m_buffer_processors.count(buffer)) {
328 m_buffer_processors.try_emplace(buffer, processors);
329 } else {
330 auto& target_processors = m_buffer_processors[buffer];
331 target_processors.reserve(target_processors.size() + processors.size());
332
333 for (const auto& processor : processors) {
334 if (std::ranges::find(target_processors, processor) == target_processors.end()) {
335 target_processors.push_back(processor);
336 }
337 }
338 }
339 }
340}
341
342void BufferProcessingChain::optimize_for_tokens(const std::shared_ptr<Buffer>& buffer)
343{
344 auto& processors = m_buffer_processors[buffer];
345 if (processors.empty()) {
346 return;
347 }
348
349 std::vector<std::shared_ptr<BufferProcessor>> compatible_processors;
350 std::vector<std::shared_ptr<BufferProcessor>> incompatible_processors;
351
352 for (auto& processor : processors) {
353 auto processor_token = processor->get_processing_token();
354 if (are_tokens_compatible(m_token_filter_mask, processor_token)) {
355 compatible_processors.push_back(processor);
356 } else {
357 incompatible_processors.push_back(processor);
358 }
359 }
360
361 processors.clear();
362
363 processors.insert(processors.end(), compatible_processors.begin(), compatible_processors.end());
364
366 processors.insert(processors.end(), incompatible_processors.begin(), incompatible_processors.end());
367 }
368}
369
371{
372 auto& processors = m_buffer_processors[buffer];
373
374 std::erase_if(processors, [this](const std::shared_ptr<BufferProcessor>& processor) {
375 auto processor_token = processor->get_processing_token();
376 return !are_tokens_compatible(m_token_filter_mask, processor_token);
377 });
378
379 m_pending_removal[buffer].clear();
380}
381
382std::vector<TokenCompatibilityReport> BufferProcessingChain::analyze_token_compatibility() const
383{
384 std::vector<TokenCompatibilityReport> reports;
385
386 for (const auto& [buffer, processors] : m_buffer_processors) {
388 report.buffer = buffer;
391
392 for (const auto& processor : processors) {
394 info.processor = processor;
395 info.processor_token = processor->get_processing_token();
399
400 report.processor_infos.push_back(info);
401 }
402
403 reports.push_back(report);
404 }
405
406 return reports;
407}
408
409bool BufferProcessingChain::validate_all_processors(std::vector<std::string>* incompatibility_reasons) const
410{
411 bool all_compatible = true;
412
413 for (const auto& [buffer, processors] : m_buffer_processors) {
414 for (const auto& processor : processors) {
415 auto processor_token = processor->get_processing_token();
416 if (!are_tokens_compatible(m_token_filter_mask, processor_token)) {
417 all_compatible = false;
418 if (incompatibility_reasons) {
419 incompatibility_reasons->push_back(
420 "Processor with token " + std::to_string(static_cast<uint32_t>(processor_token)) + " incompatible with chain preferred token " + std::to_string(static_cast<uint32_t>(m_token_filter_mask)));
421 }
422 }
423 }
424 }
425
426 return all_compatible;
427}
428
430{
431 for (const auto& [buffer, processors] : m_buffer_processors) {
432 for (auto& processor : processors) {
433 auto processor_token = processor->get_processing_token();
434 if (processor_token != m_token_filter_mask && are_tokens_compatible(m_token_filter_mask, processor_token)) {
435
436 try {
437 processor->set_processing_token(m_token_filter_mask);
438 } catch (const std::exception& e) {
440 std::source_location::current(),
441 "Failed to enforce chain token on processor: " + std::string(e.what()));
442 }
443 }
444 }
445 }
446}
447
449 const std::shared_ptr<BufferProcessor>& processor,
450 const std::shared_ptr<Buffer>& buffer,
451 bool is_addition,
452 bool search_all_slots,
453 std::string* rejection_reason)
454{
455 for (auto& m_pending_op : m_pending_ops) {
456 bool expected = false;
457 if (m_pending_op.active.compare_exchange_strong(
458 expected, true,
459 std::memory_order_acquire,
460 std::memory_order_relaxed)) {
461
462 m_pending_op.processor = processor;
463 m_pending_op.buffer = buffer;
464 m_pending_op.is_addition = is_addition;
465 m_pending_op.search_all_slots = search_all_slots;
466 m_pending_count.fetch_add(1, std::memory_order_relaxed);
467 return true;
468 }
469 }
470
471 if (rejection_reason && is_addition) {
472 *rejection_reason = "Processor operation queue full";
473 }
474 return false;
475}
476
477}
std::shared_ptr< NetworkGeometryBuffer > buffer
std::shared_ptr< BufferProcessor > get_preprocessor(const std::shared_ptr< Buffer > &buffer) const
Returns the preprocessor for a buffer, or nullptr if none is set.
void process_complete(const std::shared_ptr< Buffer > &buffer)
Applies preprocessors, processing chain, post processors and final processors sequentially to a buffe...
void add_final_processor(const std::shared_ptr< BufferProcessor > &processor, const std::shared_ptr< Buffer > &buffer)
Sets a special processor to be applied after the main pipeline.
void remove_processor_anywhere(const std::shared_ptr< BufferProcessor > &processor, const std::shared_ptr< Buffer > &buffer)
Removes a processor from any slot associated with a buffer.
void process(const std::shared_ptr< Buffer > &buffer)
Applies the transformation pipeline to a buffer with intelligent execution.
std::unordered_map< std::shared_ptr< Buffer >, std::shared_ptr< BufferProcessor > > m_final_processors
Map of buffers to their final processors.
void add_preprocessor(const std::shared_ptr< BufferProcessor > &processor, const std::shared_ptr< Buffer > &buffer)
Sets a preprocessor to be applied before the main pipeline.
void add_postprocessor(const std::shared_ptr< BufferProcessor > &processor, const std::shared_ptr< Buffer > &buffer)
Sets a postprocessor to be applied after the main pipeline.
std::unordered_map< std::shared_ptr< Buffer >, std::unordered_set< std::shared_ptr< BufferProcessor > > > m_pending_removal
Map of buffers to processors pending removal.
void remove_processor_internal(const std::shared_ptr< BufferProcessor > &processor, const std::shared_ptr< Buffer > &buffer, bool search_all_slots)
std::vector< TokenCompatibilityReport > analyze_token_compatibility() const
Analyzes token compatibility across all processors in the chain.
void remove_processor_anywhere_direct(const std::shared_ptr< BufferProcessor > &processor, const std::shared_ptr< Buffer > &buffer)
ProcessingToken m_token_filter_mask
Preferred processing token for this chain.
std::unordered_map< std::shared_ptr< Buffer >, std::vector< std::shared_ptr< BufferProcessor > > > m_buffer_processors
Map of buffers to their processor sequences.
void process_non_owning(const std::shared_ptr< Buffer > &buffer)
Internal processing method for non-owning buffer contexts.
void preprocess(const std::shared_ptr< Buffer > &buffer)
Applies the preprocessor to a buffer.
std::shared_ptr< BufferProcessor > get_postprocessor(const std::shared_ptr< Buffer > &buffer) const
Returns the postprocessor for a buffer, or nullptr if none is set.
bool validate_all_processors(std::vector< std::string > *incompatibility_reasons=nullptr) const
Validates all processors in the chain against the preferred processing token.
std::unordered_map< std::shared_ptr< Buffer >, std::shared_ptr< BufferProcessor > > m_preprocessors
Map of buffers to their preprocessors.
const std::vector< std::shared_ptr< BufferProcessor > > & get_processors(const std::shared_ptr< Buffer > &buffer) const
Gets all processors in a buffer's transformation pipeline.
bool add_processor(const std::shared_ptr< BufferProcessor > &processor, const std::shared_ptr< Buffer > &buffer, std::string *rejection_reason=nullptr)
Adds a processor to the transformation pipeline for a specific buffer.
void remove_processor(const std::shared_ptr< BufferProcessor > &processor, const std::shared_ptr< Buffer > &buffer)
Removes a processor from the pipeline for a specific buffer.
void cleanup_rejected_processors(const std::shared_ptr< Buffer > &buffer)
Validates the processing token against the chain's preferred token.
void merge_chain(const std::shared_ptr< BufferProcessingChain > &other)
Combines another processing pipeline into this one with optimization.
void process_pending_processor_operations()
Process pending processor operations.
void enforce_chain_token_on_processors()
Enforces the chain's preferred processing token on all processors.
bool queue_pending_processor_op(const std::shared_ptr< BufferProcessor > &processor, const std::shared_ptr< Buffer > &buffer, bool is_addition, bool search_all_slots=false, std::string *rejection_reason=nullptr)
std::shared_ptr< BufferProcessor > get_final_processor(const std::shared_ptr< Buffer > &buffer) const
Returns the final processor for a buffer, or nullptr if none is set.
TokenEnforcementStrategy m_enforcement_strategy
Token enforcement strategy for this chain.
void remove_processor_direct(const std::shared_ptr< BufferProcessor > &processor, const std::shared_ptr< Buffer > &buffer)
void postprocess(const std::shared_ptr< Buffer > &buffer)
Applies the postprocessor to a buffer.
void optimize_for_tokens(const std::shared_ptr< Buffer > &buffer)
Optimizes the processing pipeline for improved performance.
PendingProcessorOp m_pending_ops[MAX_PENDING_PROCESSORS]
bool has_processors(const std::shared_ptr< Buffer > &buffer) const
Checks if a buffer has any processors in its pipeline.
void process_final(const std::shared_ptr< Buffer > &buffer)
Applies the final processor to a buffer with guaranteed execution.
bool add_processor_direct(const std::shared_ptr< BufferProcessor > &processor, const std::shared_ptr< Buffer > &buffer, std::string *rejection_reason=nullptr)
std::unordered_map< std::shared_ptr< Buffer >, std::unordered_set< std::shared_ptr< BufferProcessor > > > m_conditional_processors
Map of buffers to processors that are conditionally applied.
std::unordered_map< std::shared_ptr< Buffer >, std::shared_ptr< BufferProcessor > > m_postprocessors
Map of buffers to their postprocessors.
bool are_tokens_compatible(ProcessingToken preferred, ProcessingToken current)
Determines if two processing tokens are compatible for joint execution.
@ OVERRIDE_SKIP
Allows token overrides but skips processing for incompatible operations.
@ STRICT
Strictly enforces token assignment with no cross-token sharing.
@ OVERRIDE_REJECT
Allows token overrides but rejects incompatible processors from chains.
@ FILTERED
Filters processors through token enumeration, allowing compatible combinations.
@ IGNORE
Ignores token assignments completely, allowing any processing combination.
@ BufferProcessing
Buffer processing (Buffers::BufferManager, processing chains)
@ Buffers
Buffers, Managers, processors and processing chains.
std::shared_ptr< BufferProcessor > processor
Holds information about a processor's compatibility with a buffer's processing token.
std::vector< ProcessorTokenInfo > processor_infos
Holds the results of token compatibility analysis for a buffer processing chain.