MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
TextureExecutionContext.hpp
Go to the documentation of this file.
1#pragma once
2
4
8
10
12
13namespace MayaFlux::Yantra {
14
15/**
16 * @class TextureExecutionContext
17 * @brief GpuExecutionContext specialisation for image compute shaders.
18 *
19 * Accepts DataIO (Datum<vector<DataVariant>>) as both input and output,
20 * matching the standard ComputeOperation contract. Image staging is driven
21 * by the optional container field on the input Datum:
22 *
23 * - When datum.container holds a TextureContainer, extract_inputs() stashes
24 * it and on_before_gpu_dispatch() uploads it to a VKImage staged at the
25 * declared image input binding. This is the preferred path: the container
26 * carries format, layer count, mip metadata, and sampler config.
27 *
28 * - When datum.container is absent or not a TextureContainer, no image
29 * staging occurs. The shader receives whatever bindings the caller
30 * declared via set_binding_data / stage_passthrough / stage_image_sampled
31 * directly. dispatch_async still works; it is a no-op on the image side.
32 *
33 * Output options (select one at construction):
34 *
35 * OutputMode::CONTAINER — the storage image at binding 0 is downloaded into
36 * a new TextureContainer and placed in the output Datum's container field.
37 * Primary float readback is empty. Mirrors the original behaviour for
38 * texture-transform pipelines (blur, colour grade, format convert).
39 *
40 * OutputMode::SCALAR — no image download. collect_result() returns the
41 * primary float readback and aux SSBOs from GpuChannelResult, exactly as
42 * ShaderExecutionContext does. Use for reduction shaders that write a small
43 * SSBO (spatial average, histogram, region sample) and feed the node graph.
44 *
45 * dispatch_async / collect_result mirror ShaderExecutionContext so
46 * GpuComputeNode can own this class without modification.
47 *
48 * calculate_dispatch_size() uses image dimensions when a TextureContainer is
49 * present, otherwise falls through to the standard element-count path.
50 */
51class MAYAFLUX_API TextureExecutionContext
52 : public GpuExecutionContext<
53 std::vector<Kakshya::DataVariant>,
54 std::vector<Kakshya::DataVariant>> {
55public:
57 std::vector<Kakshya::DataVariant>,
58 std::vector<Kakshya::DataVariant>>;
59
60 enum class OutputMode : uint8_t {
61 CONTAINER, ///< Download storage image at binding 0 into a TextureContainer.
62 SCALAR, ///< Return SSBO readback via collect_result(); no image download.
63 IMAGE, ///< Transition output image to shader-read layout; no CPU download.
64 ///< Retrieve via get_output_image(0). Zero readback cost.
65 };
66
67 /**
68 * @param config Shader path, workgroup size, push constant size.
69 * @param output_format Pixel format of the storage image created per dispatch.
70 * Ignored when mode is SCALAR.
71 * @param mode Controls what collect_gpu_outputs / collect_result return.
72 * @param image_binding Binding index at which the input image is staged.
73 * Default 1.
74 * @param aux_bindings Additional buffer bindings to declare.
75 * @param image_access Access mode for the input image binding: IMAGE_SAMPLED
76 * (default) or IMAGE_STORAGE.
77 * @param output_binding Binding index for the output storage image.
78 * Ignored when mode is SCALAR. Default 0.
79 */
81 GpuComputeConfig config,
82 Portal::Graphics::ImageFormat output_format = Portal::Graphics::ImageFormat::RGBA8,
83 OutputMode mode = OutputMode::CONTAINER,
84 uint32_t image_binding = 1,
85 std::vector<GpuBufferBinding> aux_bindings = {},
86 GpuBufferBinding::ElementType image_access = GpuBufferBinding::ElementType::IMAGE_SAMPLED,
87 uint32_t output_binding = 0)
88 : Base(std::move(config))
89 , m_output_format(output_format)
90 , m_output_mode(mode)
91 , m_aux_bindings(std::move(aux_bindings))
92 {
93 m_image_slots.push_back({ .binding = { .set = 0,
94 .binding = image_binding,
95 .direction = GpuBufferBinding::Direction::INPUT,
96 .element_type = image_access } });
97 if (m_output_mode != OutputMode::SCALAR) {
98 m_image_slots.push_back({ .binding = { .set = 0,
99 .binding = output_binding,
100 .direction = GpuBufferBinding::Direction::OUTPUT,
101 .element_type = GpuBufferBinding::ElementType::IMAGE_STORAGE } });
102 }
103 }
104
105 /**
106 * @brief Construct from an explicit, fully-specified binding list.
107 *
108 * For dispatch shapes the positional constructor's image/aux split does
109 * not fit cleanly: any combination of input image, output image, and
110 * SSBOs declared directly as GpuBufferBinding entries. mode still governs
111 * collect_gpu_outputs / collect_result; the caller is responsible for the
112 * binding list matching that mode (e.g. an OUTPUT IMAGE_STORAGE entry
113 * present whenever mode != SCALAR).
114 *
115 * @param config Shader path, workgroup size, push constant size.
116 * @param bindings Full binding list, image and buffer entries mixed freely.
117 * @param mode Controls what collect_gpu_outputs / collect_result return.
118 */
120 GpuComputeConfig config,
121 const std::vector<GpuBufferBinding>& bindings,
122 OutputMode mode = OutputMode::SCALAR)
123 : Base(std::move(config))
124 , m_output_format(Portal::Graphics::ImageFormat::RGBA8)
125 , m_output_mode(mode)
126 {
127 for (const auto& b : bindings) {
128 const bool is_image = b.element_type == GpuBufferBinding::ElementType::IMAGE_STORAGE
129 || b.element_type == GpuBufferBinding::ElementType::IMAGE_SAMPLED;
130 if (is_image) {
131 m_image_slots.push_back({ .binding = b });
132 } else {
133 m_aux_bindings.push_back(b);
134 }
135 }
136 }
137
138 /**
139 * @brief Set the array layer staged from the input TextureContainer on the
140 * next dispatch_async call.
141 *
142 * Defaults to 0. Call before dispatch_async when iterating video frames,
143 * animation layers, or any multi-layer container where the layer index
144 * changes per dispatch. Has no effect when no TextureContainer is present
145 * on the input Datum.
146 *
147 * @param layer Array layer index. Must be < TextureContainer::get_layer_count().
148 */
149 void set_input_layer(uint32_t layer) { m_pending_layer = layer; }
150
151 /**
152 * @brief Override the output storage image dimensions for the next dispatch.
153 *
154 * When set, on_before_gpu_dispatch allocates the output image at these
155 * dimensions rather than the input container dimensions, and
156 * calculate_dispatch_size dispatches workgroups to cover this extent.
157 * Callers are responsible for supplying matching push constants so the
158 * shader maps output pixels to the correct source coordinates.
159 *
160 * Pass std::nullopt to restore container-derived sizing.
161 *
162 * @param w Output width in pixels.
163 * @param h Output height in pixels.
164 */
165 void set_output_dimensions(uint32_t w, uint32_t h)
166 {
167 m_output_dim_override = { w, h };
168 }
169
171 {
172 m_output_dim_override = std::nullopt;
173 }
174
175 /**
176 * @brief Direct access to an image slot's binding descriptor by index.
177 *
178 * Mutating .direction is used by callers driving dispatch_core_chained
179 * directly, where the batched-dispatch cross-pass barrier only fires for
180 * INPUT_OUTPUT. Caller is responsible for restoring OUTPUT afterward if
181 * the slot's normal dispatch_async path expects that.
182 */
183 GpuBufferBinding& slot_binding(uint32_t binding_index)
184 {
185 for (auto& s : m_image_slots) {
186 if (s.binding.binding == binding_index)
187 return s.binding;
188 }
189 error<std::runtime_error>(Journal::Component::Yantra, Journal::Context::BufferProcessing,
190 std::source_location::current(),
191 "TextureExecutionContext: no image slot at binding {}", binding_index);
192 }
193
194 // =========================================================================
195 // Async dispatch — mirrors ShaderExecutionContext
196 // =========================================================================
197
198 /**
199 * @brief Non-blocking dispatch.
200 *
201 * If datum.container holds a TextureContainer it is uploaded and staged
202 * before submission. Fence becomes signaled when GPU work completes.
203 * Call collect_result() (SCALAR mode) or retrieve the output Datum via
204 * execute() (CONTAINER mode) once signaled.
205 *
206 * @param input DataIO whose container field drives optional image staging.
207 * @return FenceID to poll with ShaderFoundry::is_fence_signaled.
208 */
210 {
211 if (!ensure_gpu_ready())
212 return Portal::Graphics::INVALID_FENCE;
213
214 auto [channels, structure_info] = extract_inputs(input);
215 return dispatch_core_async(channels, structure_info);
216 }
217
218 /**
219 * @brief Collect SSBO readback after a signaled async dispatch.
220 *
221 * Valid only in SCALAR mode. Mirrors ShaderExecutionContext::collect_result().
222 *
223 * @return GpuChannelResult with primary float data and aux buffers.
224 */
226 {
227 GpuChannelResult result;
228 result.primary = readback_primary(last_effective_element_count());
229 readback_aux(result);
230 return result;
231 }
232
233 /**
234 * @brief Collect the output TextureContainer after a signaled async dispatch.
235 *
236 * Valid only in CONTAINER mode. Mirrors collect_result() for SCALAR mode.
237 * Must be called only after ShaderFoundry::is_fence_signaled returns true
238 * for the FenceID returned by dispatch_async.
239 *
240 * @return DataIO with container field holding the output TextureContainer,
241 * or empty DataIO on failure.
242 */
244 {
246 readback_aux(raw);
247 return collect_gpu_outputs(raw, {}, {});
248 }
249
250 /**
251 * @brief Override for CHAINED mode: skips collect_gpu_outputs().
252 *
253 * Callers driving multi-pass image dispatch (e.g. ConnectedComponents
254 * merge loop) may flip an image slot to INPUT_OUTPUT for the cross-pass
255 * barrier in dispatch_batched, which leaves no OUTPUT-direction slot for
256 * collect_gpu_outputs' output_slot() lookup to find. Non-CHAINED modes
257 * are unaffected and fall through to the base implementation.
258 *
259 * @param input Ignored for CHAINED mode (image already staged manually).
260 * @param ctx ExecutionContext; CHAINED mode requires pass_count and pc_updater.
261 */
263 {
264 if (ctx.mode != ExecutionMode::CHAINED && ctx.mode != ExecutionMode::CHAINED_INDIRECT)
265 return Base::execute(input, ctx);
266
267 if (!ensure_gpu_ready()) {
268 error<std::runtime_error>(
269 Journal::Component::Yantra,
270 Journal::Context::BufferProcessing,
271 std::source_location::current(),
272 "TextureExecutionContext: GPU initialisation failed");
273 }
274
275 auto [ch_copies, structure_info] = extract_inputs(input);
276
277 if (ctx.mode == ExecutionMode::CHAINED_INDIRECT) {
278 dispatch_core_chained_indirect(ch_copies, structure_info, ctx);
279 } else {
280 dispatch_core_chained(ch_copies, structure_info, ctx);
281 }
282
283 return output_type {};
284 }
285
286 // =========================================================================
287 // Manual image staging — for callers that already hold a VKImage
288 // =========================================================================
289
290 /**
291 * @brief Stage an arbitrary VKImage at the declared input image binding.
292 *
293 * Bypasses container resolution. The image must be in
294 * eShaderReadOnlyOptimal layout for IMAGE_SAMPLED access, or eGeneral
295 * for IMAGE_STORAGE access. Use before dispatch_async when the source
296 * is a render-pass output or camera frame rather than a container.
297 *
298 * @param image Initialized VKImage.
299 * @param sampler Vulkan sampler. Defaults to the TextureLoom linear sampler
300 * when nullptr. Ignored for IMAGE_STORAGE access.
301 */
303 const std::shared_ptr<Core::VKImage>& image,
304 vk::Sampler sampler = nullptr)
305 {
306 auto& slot = input_slot();
307 if (slot.binding.element_type == GpuBufferBinding::ElementType::IMAGE_STORAGE) {
308 stage_image_at(slot.binding.binding, image, GpuBufferBinding::ElementType::IMAGE_STORAGE);
309 } else {
310 auto s = sampler
311 ? sampler
312 : Portal::Graphics::SamplerForge::instance().get_default_linear();
313 stage_image_at(slot.binding.binding, image, GpuBufferBinding::ElementType::IMAGE_SAMPLED, s);
314 }
315 slot.image = image;
316 m_pending_container = nullptr;
317 }
318
319 /**
320 * @brief Stage a TextureContainer layer at the declared input image binding.
321 *
322 * Convenience for callers that hold a container and want explicit control
323 * over layer and sampler rather than relying on Datum.container resolution.
324 * Always stages as IMAGE_SAMPLED regardless of the input slot's declared
325 * access mode.
326 *
327 * @param container Source TextureContainer.
328 * @param layer Array layer index (default 0).
329 * @param sampler Vulkan sampler. Defaults to linear when nullptr.
330 */
332 const Kakshya::TextureContainer& container,
333 uint32_t layer = 0,
334 vk::Sampler sampler = nullptr)
335 {
336 auto& slot = input_slot();
337 auto img = container.to_image(layer);
338 auto s = sampler
339 ? sampler
340 : Portal::Graphics::SamplerForge::instance().get_default_linear();
341 stage_image_at(slot.binding.binding, img, GpuBufferBinding::ElementType::IMAGE_SAMPLED, s);
342 slot.image = img;
343 m_pending_container = nullptr;
344 }
345
346 /**
347 * @brief Allocates the output storage image and stages it at the
348 * declared output binding.
349 *
350 * Only called in CONTAINER or IMAGE mode, from on_before_gpu_dispatch
351 * or per-step by callers chaining multi-pass sequences. Caches by
352 * dimension: reallocates only when width or height change from the
353 * previously staged output.
354 */
355 void prepare_output_image(uint32_t width, uint32_t height)
356 {
357 auto& slot = output_slot();
358 if (!slot.image || slot.width != width || slot.height != height) {
359 slot.image = Portal::Graphics::TextureLoom::instance()
360 .create_storage_image(width, height, m_output_format);
361 slot.width = width;
362 slot.height = height;
363 }
364 stage_image_at(slot.binding.binding, slot.image, GpuBufferBinding::ElementType::IMAGE_STORAGE);
365 }
366
367protected:
368 // =========================================================================
369 // GpuExecutionContext overrides
370 // =========================================================================
371
372 /**
373 * @brief Declare the image bindings from m_image_slots, plus any
374 * aux SSBO bindings provided at construction.
375 */
376 [[nodiscard]] std::vector<GpuBufferBinding> declare_buffer_bindings() const override
377 {
378 std::vector<GpuBufferBinding> bindings;
379 for (const auto& slot : m_image_slots)
380 bindings.push_back(slot.binding);
381 bindings.insert(bindings.end(), m_aux_bindings.begin(), m_aux_bindings.end());
382 return bindings;
383 }
384
385 /**
386 * @brief Stashes the TextureContainer from datum.container for use in
387 * on_before_gpu_dispatch; returns empty channels (image shaders
388 * do not use the numeric channel path).
389 */
390 std::pair<std::vector<std::vector<double>>, DataStructureInfo>
392 {
393 m_pending_container = resolve_texture_container(input);
394 return { {}, {} };
395 }
396
397 /**
398 * @brief Stages the pending TextureContainer at the declared input image
399 * binding and, in CONTAINER or IMAGE mode, allocates the output
400 * storage image at the declared output binding.
401 */
403 const std::vector<std::vector<double>>& /*channels*/,
404 const DataStructureInfo& /*structure_info*/) override
405 {
406 if (m_pending_container) {
407 const auto sampler = Portal::Graphics::SamplerForge::instance().get_default_linear();
408 std::shared_ptr<Core::VKImage> img;
409 if (m_pending_container->get_layer_count() > 1) {
410 if (!m_upload_staging) {
411 m_upload_staging = Buffers::create_image_staging_buffer(
412 m_pending_container->byte_size() * m_pending_container->get_layer_count());
413 }
414 img = m_pending_container->to_image_array(m_upload_staging);
415 } else {
416 if (!m_upload_staging) {
417 m_upload_staging = Buffers::create_image_staging_buffer(
418 m_pending_container->byte_size());
419 }
420 img = m_pending_container->to_image(m_pending_layer, m_upload_staging);
421 }
422
423 auto& in_slot = input_slot();
424 if (in_slot.binding.element_type == GpuBufferBinding::ElementType::IMAGE_STORAGE) {
425 stage_image_at(in_slot.binding.binding, img, GpuBufferBinding::ElementType::IMAGE_STORAGE);
426 } else {
427 stage_image_at(in_slot.binding.binding, img, GpuBufferBinding::ElementType::IMAGE_SAMPLED, sampler);
428 }
429 in_slot.image = img;
430
431 if (m_output_mode == OutputMode::CONTAINER
432 || m_output_mode == OutputMode::IMAGE) {
433 const uint32_t ow = m_output_dim_override
434 ? m_output_dim_override->first
435 : m_pending_container->get_width();
436 const uint32_t oh = m_output_dim_override
437 ? m_output_dim_override->second
438 : m_pending_container->get_height();
439 prepare_output_image(ow, oh);
440 }
441 }
442 }
443
444 /**
445 * @brief Derives dispatch size from the staged container dimensions when
446 * available; falls back to element-count dispatch otherwise.
447 */
448 [[nodiscard]] std::array<uint32_t, 3> calculate_dispatch_size(
449 size_t total_elements,
450 const DataStructureInfo& structure_info) const override
451 {
452 const auto& ws = gpu_config().workgroup_size;
453 if (m_output_dim_override) {
454 const uint32_t w = m_output_dim_override->first;
455 const uint32_t h = m_output_dim_override->second;
456 return {
457 (w + ws[0] - 1) / ws[0],
458 (h + ws[1] - 1) / ws[1],
459 1U
460 };
461 }
462 if (m_pending_container) {
463 const uint32_t w = m_pending_container->get_width();
464 const uint32_t h = m_pending_container->get_height();
465 return {
466 (w + ws[0] - 1) / ws[0],
467 (h + ws[1] - 1) / ws[1],
468 1U
469 };
470 }
471 return Base::calculate_dispatch_size(total_elements, structure_info);
472 }
473
474 /**
475 * @brief In CONTAINER mode: downloads the storage image at the declared
476 * output binding into a new TextureContainer placed in output.container.
477 * In SCALAR mode: returns an empty DataIO (use collect_result() instead).
478 */
480 const GpuChannelResult& /*raw*/,
481 const std::vector<std::vector<double>>& /*channels*/,
482 const DataStructureInfo& /*structure_info*/) override
483 {
484 if (m_output_mode == OutputMode::SCALAR)
485 return output_type {};
486
487 auto img = get_output_image(output_slot().binding.binding);
488 if (!img) {
489 error<std::runtime_error>(
490 Journal::Component::Yantra,
491 Journal::Context::BufferProcessing,
492 std::source_location::current(),
493 "TextureExecutionContext: no output image at declared output binding after dispatch");
494 }
495
496 Portal::Graphics::TextureLoom::instance().transition_layout(
497 img, vk::ImageLayout::eGeneral, vk::ImageLayout::eShaderReadOnlyOptimal);
498
499 if (m_output_mode == OutputMode::IMAGE)
500 return output_type {};
501
502 const uint32_t w = m_output_dim_override
503 ? m_output_dim_override->first
504 : (m_pending_container ? m_pending_container->get_width() : img->get_width());
505 const uint32_t h = m_output_dim_override
506 ? m_output_dim_override->second
507 : (m_pending_container ? m_pending_container->get_height() : img->get_height());
508
509 if (!m_output_container) {
510 m_output_container = std::make_shared<Kakshya::TextureContainer>(
511 w, h, m_output_format);
512 }
513
514 if (!m_download_staging) {
515 m_download_staging = Buffers::create_image_staging_buffer(
516 m_output_container->byte_size());
517 }
518
519 m_output_container->from_image(img, m_download_staging, 0);
520
521 output_type result;
522 result.container = std::static_pointer_cast<Kakshya::SignalSourceContainer>(
523 m_output_container);
524 return result;
525 }
526
527private:
528 /**
529 * @struct ImageSlot
530 * @brief One declared image binding: its GpuBufferBinding descriptor,
531 * the currently staged VKImage, and cached dimensions.
532 *
533 * direction on the stored GpuBufferBinding distinguishes the input slot
534 * from the output slot; element_type distinguishes IMAGE_STORAGE from
535 * IMAGE_SAMPLED. Binding index is caller-configurable at construction,
536 * never hardcoded elsewhere in this class.
537 */
538 struct ImageSlot {
540 std::shared_ptr<Core::VKImage> image;
541 uint32_t width {};
542 uint32_t height {};
543 };
544
547 uint32_t m_pending_layer {};
548 std::vector<ImageSlot> m_image_slots;
549
550 std::shared_ptr<Kakshya::TextureContainer> m_pending_container;
551 std::shared_ptr<Kakshya::TextureContainer> m_output_container;
552 std::shared_ptr<Buffers::VKBuffer> m_download_staging;
553 std::shared_ptr<Buffers::VKBuffer> m_upload_staging;
554 std::vector<GpuBufferBinding> m_aux_bindings;
555
556 std::optional<std::pair<uint32_t, uint32_t>> m_output_dim_override;
557
558 // =========================================================================
559 // Helpers
560 // =========================================================================
561
562 /**
563 * @brief Find the declared input image slot.
564 * @return Reference to the ImageSlot with Direction::INPUT.
565 */
566 [[nodiscard]] ImageSlot& input_slot()
567 {
568 for (auto& s : m_image_slots) {
569 if (s.binding.direction == GpuBufferBinding::Direction::INPUT)
570 return s;
571 }
572
573 error<std::runtime_error>(
574 Journal::Component::Yantra,
575 Journal::Context::BufferProcessing,
576 std::source_location::current(),
577 "TextureExecutionContext: no input image slot declared");
578 }
579
580 /**
581 * @brief Find the declared output image slot.
582 * @return Reference to the ImageSlot with Direction::OUTPUT.
583 * @note Only present when constructed with mode != SCALAR.
584 */
585 [[nodiscard]] ImageSlot& output_slot()
586 {
587 for (auto& s : m_image_slots) {
588 if (s.binding.direction == GpuBufferBinding::Direction::OUTPUT)
589 return s;
590 }
591
592 error<std::runtime_error>(
593 Journal::Component::Yantra,
594 Journal::Context::BufferProcessing,
595 std::source_location::current(),
596 "TextureExecutionContext: no output image slot declared (SCALAR mode?)");
597 }
598
599 /**
600 * @brief Extracts a TextureContainer pointer from datum.container if present.
601 *
602 * Returns nullptr when the container field is absent or holds a different
603 * SignalSourceContainer subtype.
604 */
605 static std::shared_ptr<Kakshya::TextureContainer> resolve_texture_container(const input_type& input)
606 {
607 if (!input.container || !*input.container)
608 return nullptr;
609 return std::dynamic_pointer_cast<Kakshya::TextureContainer>(input.container.value());
610 }
611};
612
613} // namespace MayaFlux::Yantra
Core::GlobalInputConfig input
Definition Config.cpp:38
IO::ImageData image
Definition Decoder.cpp:64
uint32_t width
Definition Decoder.cpp:66
uint32_t h
Definition InkPress.cpp:28
size_t b
uint32_t height
std::shared_ptr< Core::VKImage > to_image(uint32_t layer=0) const
Upload the pixel buffer to a new VKImage via TextureLoom.
SignalSourceContainer wrapping GPU texture data as addressable pixel bytes.
Type-parameterised shell over GpuDispatchCore.
std::shared_ptr< Kakshya::TextureContainer > m_output_container
ImageSlot & output_slot()
Find the declared output image slot.
std::shared_ptr< Buffers::VKBuffer > m_download_staging
output_type execute(const input_type &input, const ExecutionContext &ctx) override
Override for CHAINED mode: skips collect_gpu_outputs().
TextureExecutionContext(GpuComputeConfig config, Portal::Graphics::ImageFormat output_format=Portal::Graphics::ImageFormat::RGBA8, OutputMode mode=OutputMode::CONTAINER, uint32_t image_binding=1, std::vector< GpuBufferBinding > aux_bindings={}, GpuBufferBinding::ElementType image_access=GpuBufferBinding::ElementType::IMAGE_SAMPLED, uint32_t output_binding=0)
output_type collect_container_result()
Collect the output TextureContainer after a signaled async dispatch.
std::shared_ptr< Buffers::VKBuffer > m_upload_staging
void on_before_gpu_dispatch(const std::vector< std::vector< double > > &, const DataStructureInfo &) override
Stages the pending TextureContainer at the declared input image binding and, in CONTAINER or IMAGE mo...
void set_output_dimensions(uint32_t w, uint32_t h)
Override the output storage image dimensions for the next dispatch.
void set_input_layer(uint32_t layer)
Set the array layer staged from the input TextureContainer on the next dispatch_async call.
std::shared_ptr< Kakshya::TextureContainer > m_pending_container
output_type collect_gpu_outputs(const GpuChannelResult &, const std::vector< std::vector< double > > &, const DataStructureInfo &) override
In CONTAINER mode: downloads the storage image at the declared output binding into a new TextureConta...
static std::shared_ptr< Kakshya::TextureContainer > resolve_texture_container(const input_type &input)
Extracts a TextureContainer pointer from datum.container if present.
void prepare_output_image(uint32_t width, uint32_t height)
Allocates the output storage image and stages it at the declared output binding.
Portal::Graphics::FenceID dispatch_async(const input_type &input)
Non-blocking dispatch.
ImageSlot & input_slot()
Find the declared input image slot.
void stage_container(const Kakshya::TextureContainer &container, uint32_t layer=0, vk::Sampler sampler=nullptr)
Stage a TextureContainer layer at the declared input image binding.
std::optional< std::pair< uint32_t, uint32_t > > m_output_dim_override
GpuChannelResult collect_result()
Collect SSBO readback after a signaled async dispatch.
std::array< uint32_t, 3 > calculate_dispatch_size(size_t total_elements, const DataStructureInfo &structure_info) const override
Derives dispatch size from the staged container dimensions when available; falls back to element-coun...
void stage_image(const std::shared_ptr< Core::VKImage > &image, vk::Sampler sampler=nullptr)
Stage an arbitrary VKImage at the declared input image binding.
std::pair< std::vector< std::vector< double > >, DataStructureInfo > extract_inputs(const input_type &input) override
Stashes the TextureContainer from datum.container for use in on_before_gpu_dispatch; returns empty ch...
GpuBufferBinding & slot_binding(uint32_t binding_index)
Direct access to an image slot's binding descriptor by index.
TextureExecutionContext(GpuComputeConfig config, const std::vector< GpuBufferBinding > &bindings, OutputMode mode=OutputMode::SCALAR)
Construct from an explicit, fully-specified binding list.
std::vector< GpuBufferBinding > declare_buffer_bindings() const override
Declare the image bindings from m_image_slots, plus any aux SSBO bindings provided at construction.
GpuExecutionContext specialisation for image compute shaders.
ImageFormat
User-friendly image format enum.
bool is_image(const fs::path &filepath)
Definition Depot.cpp:108
ElementType
Element type the shader expects in this binding.
uint32_t binding
Binding index within the set.
Declares a single storage buffer or image binding a compute shader expects.
Plain-data description of the compute shader to dispatch.
Metadata about data structure for reconstruction.
std::optional< std::shared_ptr< Kakshya::SignalSourceContainer > > container
Optional reference to container, required for regions.
Definition DataIO.hpp:31
Input/Output container for computation pipeline data flow with structure preservation.
Definition DataIO.hpp:24
ExecutionMode mode
Execution mode controlling scheduling behavior.
Context information controlling how a compute operation executes.
Erased output of a GPU dispatch: reconstructed float data plus any raw auxiliary outputs keyed by bin...
One declared image binding: its GpuBufferBinding descriptor, the currently staged VKImage,...