MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
ImageCVProcessor.hpp
Go to the documentation of this file.
1#pragma once
2
6
8
10
12
13namespace MayaFlux::Buffers {
14
15/**
16 * @class ImageCVProcessor
17 * @brief BufferProcessor executing a Kinesis::Vision pipeline on a GpuImageSource buffer.
18 *
19 * Renamed from ImageCVProcessor. ImageCVProcessor makes the domain
20 * (computer vision on image data) explicit and avoids collision with
21 * VisionProcessor (the Kakshya DataProcessor over pixel containers).
22 *
23 * m_gpu_staging is a persistent host-visible VKBuffer allocated once in
24 * on_attach, sized to the image footprint. Passed to download_and_normalise
25 * on every call so TextureLoom::download_data uses the fenced path rather
26 * than waitIdle, avoiding graphics queue stalls on each frame download.
27 *
28 * @tparam T A VKBuffer subclass satisfying GpuImageSource.
29 */
30template <GpuImageSource T>
32public:
33 /**
34 * @brief Construct with the vision pipeline to execute each processing_function call.
35 * @param sequence Ordered VisionSteps describing the pipeline.
36 */
37 explicit ImageCVProcessor(Kinesis::Vision::VisionSequence sequence, bool force_cpu = false)
38 : m_sequence(std::move(sequence))
39 , m_force_cpu(force_cpu)
40 {
42 if (!m_force_cpu) {
43 m_executor = std::make_unique<Yantra::VisionGpuExecutor>();
44 }
45 }
46
47 ~ImageCVProcessor() override = default;
48
49 /**
50 * @brief Validate the buffer type and reset executor state.
51 *
52 * Throws std::invalid_argument if the buffer cannot be cast to T.
53 * Called automatically by BufferProcessingChain::add_processor.
54 *
55 * @param buffer The Buffer to attach to.
56 */
57 void on_attach(const std::shared_ptr<Buffer>& buffer) override
58 {
59 auto typed = std::dynamic_pointer_cast<T>(buffer);
60 if (!typed) {
61 error<std::invalid_argument>(
64 std::source_location::current(),
65 "ImageCVProcessor<T>: buffer is not the expected type");
66 }
67 m_buffer = typed;
68
69 if (m_force_cpu) {
71 } else {
72 if (!m_executor) {
73 m_executor = std::make_unique<Yantra::VisionGpuExecutor>();
74 }
75 }
76
77 if (!m_gpu_staging) {
78 constexpr size_t k_max_frame_bytes = 3840 * 2160 * 4;
79 m_gpu_staging = create_image_staging_buffer(k_max_frame_bytes);
80 }
81
83 "ImageCVProcessor attached");
84 }
85
86 /**
87 * @brief Clear state and reset executor.
88 * @param buffer The Buffer being detached.
89 */
90 void on_detach(const std::shared_ptr<Buffer>& /*buffer*/) override
91 {
92 m_buffer.reset();
93
94 if (m_force_cpu) {
96 }
97
98 m_gpu_staging.reset();
99 }
100
101 /**
102 * @brief Download the current GPU image, run the VisionSequence, store the result.
103 *
104 * No-op if the buffer has expired or the image is unavailable.
105 *
106 * @param buffer The GpuImageSource buffer to read from.
107 */
108 void processing_function(const std::shared_ptr<Buffer>& /*buffer*/) override
109 {
110 if (m_skipped_frames < m_eval_delta - 1) {
112 return;
113 }
115
116 auto typed = m_buffer.lock();
117 if (!typed)
118 return;
119
120 auto image = resolve_gpu_image(*typed);
121 if (!image || !image->is_initialized())
122 return;
123
124 m_is_processing.store(true, std::memory_order_release);
125
126 if (m_force_cpu) {
128 if (!frame.empty()) {
130 m_sequence, frame,
131 image->get_width(), image->get_height());
132 }
133 } else {
134 m_result = m_executor->run(
136 image->get_width(), image->get_height());
137 }
138
139 if (m_result_source)
140 m_result_source->signal(m_result);
141
142 m_is_processing.store(false, std::memory_order_release);
143 }
144
145 /**
146 * @brief Replace the pipeline and reset inter-frame executor state.
147 *
148 * Not thread-safe relative to processing_function. Call only when idle.
149 *
150 * @param sequence Replacement VisionSequence.
151 */
153 {
154 m_sequence = std::move(sequence);
155 m_executor.reset();
156 }
157
158 /**
159 * @brief The result of the last successful processing_function call.
160 *
161 * Default-initialised until the first successful call completes.
162 *
163 * @return Most recent VisionResult.
164 */
165 [[nodiscard]] const Kinesis::Vision::VisionResult& get_result() const { return m_result; }
166
167 /**
168 * @brief Shared BroadcastSource signalled with each VisionResult after a
169 * successful processing_function call.
170 *
171 * Created on first call. Wire with Kriya::on_signal to consume results
172 * from a coroutine without polling get_result().
173 *
174 * @return Shared pointer to the BroadcastSource, never null after first call.
175 */
176 [[nodiscard]] std::shared_ptr<Vruta::BroadcastSource<Kinesis::Vision::VisionResult>>
178 {
179 if (!m_result_source) {
180 m_result_source = std::make_shared<
182 }
183 return m_result_source;
184 }
185
186 /**
187 * @brief Set how often this processor actually evaluates, relative to the
188 * engine's preferred frame rate.
189 *
190 * ImageCVProcessor is a deliberate exception to the engine's normal
191 * scheduling model. Every other processor registered with the engine is
192 * scheduled by string-keyed rate registration at the point of registration,
193 * and runs on that schedule without needing its own internal throttle.
194 * ImageCVProcessor throttles itself internally instead, because its result
195 * is not fenceable for the next evaluation cycle (a CV pass in flight
196 * cannot be safely interrupted or reissued for the following frame) and
197 * because the GPU vision dispatch is heavy enough that running it on every
198 * render frame is often wasted work. This self-throttle exists only for
199 * that reason and is not a pattern to copy into ordinary processors; a
200 * normal processor should be scheduled through the engine's registration
201 * mechanism, not by skipping frames internally.
202 *
203 * fps below the preferred frame rate throttles down: m_eval_delta becomes
204 * ceil(s_preferred_frame_rate / fps), rounded down here to match integer
205 * frame counting, so the processor runs roughly every m_eval_delta frames.
206 * fps at or above the preferred frame rate runs every frame (m_eval_delta = 1).
207 *
208 * @param fps Desired evaluation rate in frames per second.
209 */
210 void set_eval_rate(uint32_t fps)
211 {
213 ? s_preferred_frame_rate / std::max(fps, 1U)
214 : 1;
215 }
216
217 /**
218 * @brief Get the currently achieved evaluation rate in frames per second.
219 *
220 * Derived from m_eval_delta against s_preferred_frame_rate, not measured.
221 * @return Approximate evaluation rate in frames per second.
222 */
223 uint32_t get_eval_rate() const
224 {
226 }
227
228private:
230 std::unique_ptr<Yantra::VisionGpuExecutor> m_executor;
233
234 std::vector<uint8_t> m_raw_staging;
235 std::vector<float> m_float_work;
236 std::shared_ptr<VKBuffer> m_gpu_staging;
237
238 std::weak_ptr<T> m_buffer;
239 std::shared_ptr<Vruta::BroadcastSource<Kinesis::Vision::VisionResult>> m_result_source;
240 std::atomic<bool> m_is_processing { false };
241
242 bool m_force_cpu {};
243 uint32_t m_eval_delta { 1 };
244 uint32_t m_skipped_frames { 0 };
245};
246
247} // namespace MayaFlux::Buffers
#define MF_INFO(comp, ctx,...)
IO::ImageData image
Definition Decoder.cpp:64
GPU execution layer for Kinesis::Vision::VisionSequence.
Central computational transformation interface for continuous buffer processing.
void processing_function(const std::shared_ptr< Buffer > &) override
Download the current GPU image, run the VisionSequence, store the result.
~ImageCVProcessor() override=default
Kinesis::Vision::VisionSequence m_sequence
std::shared_ptr< Vruta::BroadcastSource< Kinesis::Vision::VisionResult > > m_result_source
std::shared_ptr< Vruta::BroadcastSource< Kinesis::Vision::VisionResult > > get_result_source()
Shared BroadcastSource signalled with each VisionResult after a successful processing_function call.
std::unique_ptr< Yantra::VisionGpuExecutor > m_executor
void set_eval_rate(uint32_t fps)
Set how often this processor actually evaluates, relative to the engine's preferred frame rate.
void on_detach(const std::shared_ptr< Buffer > &) override
Clear state and reset executor.
uint32_t get_eval_rate() const
Get the currently achieved evaluation rate in frames per second.
ImageCVProcessor(Kinesis::Vision::VisionSequence sequence, bool force_cpu=false)
Construct with the vision pipeline to execute each processing_function call.
Kinesis::Vision::VisionResult m_result
std::shared_ptr< VKBuffer > m_gpu_staging
const Kinesis::Vision::VisionResult & get_result() const
The result of the last successful processing_function call.
void set_sequence(Kinesis::Vision::VisionSequence sequence)
Replace the pipeline and reset inter-frame executor state.
Kinesis::Vision::VisionExecutor m_cpu_executor
void on_attach(const std::shared_ptr< Buffer > &buffer) override
Validate the buffer type and reset executor state.
BufferProcessor executing a Kinesis::Vision pipeline on a GpuImageSource buffer.
void reset()
Clear stored inter-frame state.
VisionResult run(const VisionSequence &sequence, std::span< const float > frame, uint32_t w, uint32_t h)
Execute a VisionSequence on one frame.
Stateful executor for a VisionSequence.
Awaitable single-value broadcast channel for cross-thread signal delivery.
std::shared_ptr< VKBuffer > create_image_staging_buffer(size_t size)
Allocate a persistent host-visible staging buffer sized for repeated streaming uploads to an image of...
std::shared_ptr< Core::VKImage > resolve_gpu_image(const T &buffer)
Resolve the GPU-resident image from any GpuImageSource buffer.
@ GRAPHICS_BACKEND
Standard graphics processing backend configuration.
std::span< const float > download_and_normalise(const std::shared_ptr< Core::VKImage > &image, std::vector< uint8_t > &raw_staging, std::vector< float > &work, const std::shared_ptr< VKBuffer > &gpu_staging)
Download a VKImage to CPU and return a normalised float span.
uint32_t s_preferred_frame_rate
Global default frame rate.
@ BufferProcessing
Buffer processing (Buffers::BufferManager, processing chains)
@ Buffers
Buffers, Managers, processors and processing chains.
Result of executing a VisionSequence on one frame.
Ordered sequence of VisionSteps describing a complete vision pipeline.
Definition VisionOp.hpp:163