MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VisionGpuDispatch.hpp
Go to the documentation of this file.
1#pragma once
2
4
6
7namespace MayaFlux::Yantra {
8
9/**
10 * @file VisionGpuDispatch.hpp
11 * @brief GPU execution layer for Kinesis::Vision::VisionSequence.
12 *
13 * VisionGpuExecutor::run() mirrors VisionExecutor::run() in contract: same
14 * input types, same VisionResult output. Internally it drives a
15 * VisionGpuContexts through the sequence via swap_shader() + stage_image()
16 * + dispatch_core() per step, keeping the working image GPU-resident
17 * between steps via OutputMode::IMAGE.
18 *
19 * VisionGpuExecutor::config() is the inspectable shader config table. It
20 * returns the GpuComputeConfig for any given VisionOp. Ops that are
21 * expressible via ShaderSpec are assembled at call time (no .comp file);
22 * ops requiring neighbourhood access or structured output name a .comp
23 * file. Ops with no GPU implementation return a config with INVALID_SHADER.
24 *
25 * The caller is responsible for only passing sequences composed of ops
26 * whose config is valid. run() logs an error and returns a default
27 * VisionResult on encountering INVALID_SHADER mid-sequence.
28 */
29
30/**
31 * @brief Fixed set of TextureExecutionContexts covering every GPU-implemented
32 * VisionOp shape.
33 *
34 * Owned exclusively by VisionGpuExecutor, which lazily constructs and holds
35 * one instance per executor via m_contexts. Never rebuilt inside run() or
36 * per-step; the bindings each member declares are fixed at construction and
37 * dictated entirely by the shaders they drive, not by caller preference.
38 *
39 * There is exactly one correct binding layout per member, so this struct
40 * carries no configuration surface.
41 */
42struct MAYAFLUX_API VisionGpuContexts {
43 TextureExecutionContext pixel; ///< Image pipeline. IMAGE mode. Drives
44 ///< every op that reads/writes the
45 ///< working image (Threshold, Sobel,
46 ///< HarrisResponse, Downsample2x, etc).
47 TextureExecutionContext structured; ///< Buffer-only readback. SCALAR mode.
48 ///< Drives ops with no image output
49 ///< of their own (ExtractPeaks).
50 TextureExecutionContext labels; ///< Image + aux SSBO. IMAGE mode.
51 ///< Drives ops needing both a resident
52 ///< image output and structured aux
53 ///< data (ConnectedComponents,
54 ///< FindContours).
56 TextureExecutionContext ingest; ///< Sampled-in, rgba32f-storage-out.
57 ///< IMAGE mode. Runs vision_ingest.comp
58 ///< at the top of a fresh run to convert
59 ///< a non-storage seed frame before any
60 ///< rgba32f op reads it.
61
62 /**
63 * @brief Walk state for the current run: sequence position, geometry,
64 * working image, and the result under construction.
65 *
66 * Lives here rather than as a run local so a deferred step's yield
67 * preserves it without copying. Reset by begin() at the top of a fresh
68 * run; left intact across a suspension.
69 */
71
72 /**
73 * @brief Input image the current walk started from.
74 *
75 * Ops needing the original frame rather than the working image read this
76 * (contour_march stages it at binding 1). Set on a fresh run and untouched
77 * across suspensions, so a polling call uses the frame the sequence began
78 * on rather than whatever argument it was passed.
79 *
80 * GPU-only. The CPU pass has no equivalent: its handle is an index into a
81 * reused slot pool, so retaining the input handle would retain a slot whose
82 * contents the ping-pong overwrites.
83 */
84 std::shared_ptr<Core::VKImage> source;
85
86 /**
87 * @brief Shader currently bound on pixel, and the image staged into it.
88 *
89 * TextureExecutionContext does not report its own state, so run tracks
90 * it. Held here rather than as a run local: a swap_shader or stage_image
91 * elided on one call stays elided on the next. The allocated output
92 * dimensions live in pass.storage_w / pass.storage_h.
93 */
95 std::shared_ptr<Core::VKImage> bound_staged;
96
97 /**
98 * @brief Outstanding work at a deferred step, and the point to resume.
99 *
100 * fence is INVALID_FENCE when nothing is outstanding. While live, run
101 * polls it and does nothing else. The working image, geometry, and
102 * partial result the resumed sequence needs are already in pass, which
103 * is not reset while a suspension is active.
104 */
105 struct Suspension {
106 Portal::Graphics::FenceID fence { Portal::Graphics::INVALID_FENCE };
107
108 [[nodiscard]] bool is_active() const
109 {
110 return fence != Portal::Graphics::INVALID_FENCE;
111 }
112 };
113
115
116 /// op_ingest's dispatch + trailing-barrier fences. Not awaited by run();
117 /// reaped by reap_ingest_fences on the next fresh run and in reset().
118 Portal::Graphics::FenceID ingest_fence { Portal::Graphics::INVALID_FENCE };
119 Portal::Graphics::FenceID ingest_barrier_fence { Portal::Graphics::INVALID_FENCE };
120
121 /**
122 * @brief Construct all three contexts in place with the one correct
123 * binding layout for every currently GPU-implemented VisionOp.
124 *
125 * TextureExecutionContext has no copy or move constructor (it owns GPU
126 * resource handles), so each member is built directly in this
127 * constructor's initializer list rather than assigned from a temporary.
128 * Constructed lazily by VisionGpuExecutor on first run(); never
129 * constructed directly by callers.
130 */
132};
133
134/**
135 * @class VisionGpuExecutor
136 * @brief Stateful GPU dispatch engine for VisionSequence execution.
137 *
138 * Owns a lazily-constructed VisionGpuContexts (m_contexts) plus any
139 * op-specific persistent GPU state that doesn't belong on a shared
140 * context (e.g. ConnectedComponents' ping-pong output image). Mirrors
141 * VisionExecutor's ownership model on the CPU side: construct one
142 * instance per independent pipeline, hold it, call run() every frame.
143 *
144 * Immovable: TextureExecutionContext owns GPU resource handles with no
145 * copy or move constructor, so VisionGpuContexts and therefore
146 * VisionGpuExecutor cannot be copied or moved either. Construct once,
147 * hold by reference, pointer, or shared_ptr, reuse indefinitely.
148 *
149 * Not safe to call run() concurrently from multiple threads on the same
150 * instance. Independent pipelines running concurrently should each own
151 * a separate VisionGpuExecutor instance.
152 */
153class MAYAFLUX_API VisionGpuExecutor {
154public:
155 /**
156 * @brief GpuComputeConfig for a given VisionOp and its parameters.
157 *
158 * Assembled ops (Threshold, NormalizeInplace, NormalizeRange, RgbaToGray,
159 * GrayToRgba) produce a config via ShaderSpec::Assemble + config_from_spec
160 * with no .comp file. All other implemented ops reference a .comp path
161 * under Portal/Shaders/Vision/. Unimplemented ops return a config with
162 * shader_id == INVALID_SHADER.
163 *
164 * Stateless; does not depend on or affect m_contexts.
165 *
166 * @param op VisionOp to look up.
167 * @param params Parameters for that op; used to derive push constant size
168 * for assembled ops.
169 */
170 [[nodiscard]] static GpuComputeConfig config(
172 const Kinesis::Vision::VisionParams& params);
173
174 /**
175 * @brief Execute a VisionSequence on the GPU through an explicit context set.
176 *
177 * contexts is caller-supplied rather than the instance's own lazily-built
178 * m_contexts. Reused across calls with no reset needed; construction is
179 * the caller's responsibility and never happens inside this function.
180 *
181 * @param contexts Long-lived context set. Never constructed internally.
182 * @param sequence Ordered steps to execute.
183 * @param image Input frame in eShaderReadOnlyOptimal layout.
184 * @param w Frame width in pixels.
185 * @param h Frame height in pixels.
186 * @return VisionResult matching the VisionExecutor::run() contract.
187 */
189 VisionGpuContexts& contexts,
190 const Kinesis::Vision::VisionSequence& sequence,
191 const std::shared_ptr<Core::VKImage>& image,
192 uint32_t w, uint32_t h);
193
194 /**
195 * @brief Execute a VisionSequence on the GPU using this instance's own
196 * lazily-constructed context set.
197 *
198 * m_contexts is built on first call and reused for every subsequent
199 * call to this overload on the same VisionGpuExecutor instance. The
200 * primary entry point; prefer this over the explicit-contexts overload
201 * unless a caller specifically needs to inspect or share a
202 * VisionGpuContexts across multiple calls outside this class.
203 *
204 * @param sequence Ordered steps to execute.
205 * @param image Input frame in eShaderReadOnlyOptimal layout.
206 * @param w Frame width in pixels.
207 * @param h Frame height in pixels.
208 * @return VisionResult matching the VisionExecutor::run() contract.
209 */
211 const Kinesis::Vision::VisionSequence& sequence,
212 const std::shared_ptr<Core::VKImage>& image,
213 uint32_t w, uint32_t h);
214
215 /**
216 * @brief Abandon outstanding work and clear the resume point.
217 *
218 * Waits on the fence before releasing it, then discards the retained
219 * working image and partial result. Call when the pixel source changes
220 * so the next run starts a fresh sequence. Safe with nothing outstanding.
221 */
222 void reset();
223
224 /**
225 * @brief True when a deferred step has work outstanding and the next run
226 * will poll rather than start a fresh sequence.
227 */
228 [[nodiscard]] bool is_suspended() const
229 {
230 return m_contexts && m_contexts->suspended.is_active();
231 }
232
233 VisionGpuExecutor() = default;
239
240private:
241 std::unique_ptr<VisionGpuContexts> m_contexts;
242};
243
244} // namespace MayaFlux::Yantra
vk::Fence fence
IO::ImageData image
Definition Decoder.cpp:64
uint32_t h
Definition InkPress.cpp:28
GpuExecutionContext specialisation for image compute shaders.
VisionGpuExecutor & operator=(const VisionGpuExecutor &)=delete
VisionGpuExecutor(const VisionGpuExecutor &)=delete
VisionGpuExecutor & operator=(VisionGpuExecutor &&)=delete
VisionGpuExecutor(VisionGpuExecutor &&)=delete
std::unique_ptr< VisionGpuContexts > m_contexts
bool is_suspended() const
True when a deferred step has work outstanding and the next run will poll rather than start a fresh s...
Stateful GPU dispatch engine for VisionSequence execution.
void run()
Definition main.cpp:22
std::variant< std::monostate, ThresholdParams, ThresholdAdaptiveParams, NormalizeRangeParams, GaussianBlurParams, FilterSeparableParams, CannyParams, MorphParams, HarrisParams, ExtractPeaksParams, TrackKeypointsParams, ConnectedComponentsParams, FindContoursParams > VisionParams
Parameter variant covering all ops that carry parameters.
Definition VisionOp.hpp:148
VisionOp
Named operations available in a VisionSequence.
Definition VisionOp.hpp:29
Result of executing a VisionSequence on one frame.
Ordered sequence of VisionSteps describing a complete vision pipeline.
Definition VisionOp.hpp:169
Plain-data description of the compute shader to dispatch.
Outstanding work at a deferred step, and the point to resume.
GpuComputeConfig bound_config
Shader currently bound on pixel, and the image staged into it.
TextureExecutionContext ingest
Sampled-in, rgba32f-storage-out.
TextureExecutionContext labels
Image + aux SSBO.
Kinesis::Vision::GpuVisionPass pass
Walk state for the current run: sequence position, geometry, working image, and the result under cons...
std::shared_ptr< Core::VKImage > source
Input image the current walk started from.
std::shared_ptr< Core::VKImage > bound_staged
TextureExecutionContext pixel
Image pipeline.
TextureExecutionContext structured
Buffer-only readback.
Fixed set of TextureExecutionContexts covering every GPU-implemented VisionOp shape.