5using namespace VisionInternal;
7using namespace Portal::Graphics;
8using namespace Kinesis::Vision;
22 constexpr std::array<FlowBufferSpec, 2> k_flow_export {
32 enum class FlowArgs : uint8_t {
46 bool frame_is_duplicate(
const VisionGpuContexts& contexts,
const std::array<uint32_t, 4>& meta)
58 std::array<uint32_t, 4> read_flow_meta(VisionGpuContexts& contexts)
60 std::array<uint32_t, 4> meta {};
61 if (contexts.flow_state.have_prev)
62 contexts.flow.download_shared(1, 3, meta.data(),
sizeof(meta));
67 IndirectGroupsSource flow_args_source(FlowArgs slot)
69 return { .set = k_flow_args.set, .binding = k_flow_args.binding, .offset_bytes =
static_cast<uint64_t
>(slot) *
k_flow_args_stride };
76 std::vector<Portal::Graphics::HazardResource> flow_hazards(GpuDispatchCore& ctx,
const std::vector<FlowBufferSpec>& specs)
78 std::vector<Portal::Graphics::HazardResource> hazards;
79 hazards.reserve(specs.size());
80 for (
const auto& s : specs) {
81 hazards.push_back(ctx.shared_buffer_hazard({
84 .direction = GpuBufferBinding::Direction::INPUT_OUTPUT,
85 .element_type = s.type,
97 std::vector<Kinesis::Vision::TrackResult> decode_track_records(
const glm::vec4* records, uint32_t
count)
99 std::vector<Kinesis::Vision::TrackResult> tracks;
100 tracks.reserve(
count);
101 for (uint32_t i = 0; i <
count; ++i) {
102 const auto& pose = records[
static_cast<size_t>(i) * 2U];
103 const auto& status = records[
static_cast<size_t>(i) * 2U + 1U];
105 .position = { pose.x, pose.y },
107 .tracked = status.y > 0.5F,
108 .previous = { pose.z, pose.w },
109 .id = std::bit_cast<uint32_t>(status.z),
110 .age = std::bit_cast<uint32_t>(status.w),
123 bool flow_step_ahead(
const VisionGpuContexts& contexts)
125 const auto& steps = contexts.pass.sequence->steps;
126 for (
size_t i = contexts.pass.index + 1; i < steps.size(); ++i) {
127 if (steps[i].op == VisionOp::TrackKeypoints || steps[i].op == VisionOp::OpticalFlowDense)
138 void commit_flow_frame(VisionGpuContexts& contexts)
140 if (flow_step_ahead(contexts))
143 auto& state = contexts.flow_state;
144 state.have_prev =
true;
146 state.curr_ready =
false;
162 void finish_track(VisionGpuContexts& contexts)
164 auto& state = contexts.flow_state;
165 auto& flow = contexts.flow;
166 std::vector<Kinesis::Vision::TrackResult> tracks;
167 const auto meta = read_flow_meta(contexts);
168 const bool duplicate = frame_is_duplicate(contexts, meta) && state.last_track_count > 0;
171 if (state.host_pending) {
172 if (duplicate && !state.last_tracks.empty()) {
173 tracks = state.last_tracks;
176 std::vector<glm::vec4> records(
static_cast<size_t>(
count) * 2U);
177 flow.download_shared(1, 2, records.data(), records.size() *
sizeof(glm::vec4));
178 tracks = decode_track_records(records.data(),
count);
180 state.last_tracks = tracks;
185 state.last_track_count =
count;
187 if (state.export_pending) {
188 if (!duplicate || !state.last_export) {
189 const auto& slot = k_flow_export[state.export_slot];
190 auto& view = state.export_view[state.export_slot];
192 view = std::make_shared<Portal::Graphics::GpuBufferHandle>(flow.shared_buffer_handle(slot.set, slot.binding));
193 state.last_export = view;
194 state.export_slot ^= 1U;
196 contexts.pass.result.buffers.tracks = state.last_export;
197 state.export_pending =
false;
199 commit_flow_frame(contexts);
201 contexts.pass.result.structured = std::move(tracks);
202 contexts.pass.result.w = 0;
203 contexts.pass.result.h = 0;
214 bool ensure_dense_images(VisionGpuContexts& contexts)
216 auto& state = contexts.flow_state;
217 const auto& layout = state.layout;
219 if (state.flow_lvl && state.dense_layout == layout)
223 const auto make = [&loom](uint32_t w, uint32_t
h) {
224 return loom.create_storage_image(w,
h, ImageFormat::RGBA32F);
227 state.flow_out[0] = make(layout.level[0].w, layout.level[0].h);
228 state.flow_out[1] = make(layout.level[0].w, layout.level[0].h);
229 state.flow_lvl = make(layout.atlas_w, layout.atlas_h);
230 state.dense_a = make(layout.atlas_w, layout.atlas_h);
231 state.dense_b = make(layout.atlas_w, layout.atlas_h);
232 state.dense_tensor = make(layout.atlas_w, layout.atlas_h);
233 state.flow_vis = make(layout.level[0].w, layout.level[0].h);
234 state.dense_layout = layout;
236 return state.flow_out[0] && state.flow_out[1] && state.flow_lvl
237 && state.dense_a && state.dense_b && state.dense_tensor && state.flow_vis;
247 void finish_dense(VisionGpuContexts& contexts,
bool visualized)
249 auto& state = contexts.flow_state;
250 const auto meta = read_flow_meta(contexts);
251 const bool duplicate = frame_is_duplicate(contexts, meta) && state.last_flow;
253 state.last_flow = state.flow_out[state.curr];
255 contexts.pass.result.flow = state.last_flow;
256 const auto pixel_count =
static_cast<float>(contexts.pass.w) *
static_cast<float>(contexts.pass.h);
257 contexts.pass.result.motion_energy = pixel_count > 0.0F
258 ?
static_cast<float>(meta[1]) / 1024.0F / pixel_count
260 if (visualized && state.last_flow)
261 contexts.pass.result.images.flow_visualization = state.flow_vis;
262 commit_flow_frame(contexts);
276 void ensure_flow_resources(VisionGpuContexts& contexts)
278 auto& state = contexts.flow_state;
279 if (state.buffers_ready)
282 auto& flow = contexts.flow;
295 for (
const auto& spec : k_flow_export)
299 flow.swap_shader({ .shader_path =
"flow_reset.comp.spv", .workgroup_size = { 64, 1, 1 } });
300 flow.set_output_dimensions(1, 1);
301 const auto fence = flow.dispatch_async({});
302 flow.clear_output_dimensions();
303 foundry.wait_for_fence(
fence);
304 foundry.release_fence(
fence);
306 state.buffers_ready =
true;
314 auto& flow = contexts.
flow;
322 if (!peaks || peaks->op != VisionOp::ExtractPeaks) {
324 "run_gpu: TrackKeypoints requires ExtractPeaks as the immediately preceding step");
328 if (!state.curr_ready || !state.atlas[0]) {
330 "run_gpu: TrackKeypoints requires an earlier RgbaToGray step to supply the gray frame");
334 const auto& p = std::get<TrackKeypointsParams>(step.
params);
335 const auto& pk = std::get<ExtractPeaksParams>(peaks->params);
336 const auto& layout = state.layout;
337 const uint32_t w = contexts.
pass.
w;
338 const uint32_t
h = contexts.
pass.
h;
341 if (w != layout.level[0].w ||
h != layout.level[0].h) {
343 "run_gpu: TrackKeypoints image is {}x{} but the gray frame was captured at {}x{}",
344 w,
h, layout.level[0].w, layout.level[0].h);
348 const bool export_tracks = p.export_tracks;
349 const uint32_t duplicate_cutoff = (state.last_track_count > 0 && state.last_export)
354 const auto atlas_a = state.atlas[0];
355 const auto atlas_b = state.atlas[1];
359 .nms_radius = pk.nms_radius,
366 std::vector<DependencyStage> stages;
367 stages.reserve(layout.levels * (p.forward_backward_threshold > 0.0F ? 2U : 1U) + 8U);
370 .config = { .shader_path =
"extract_peaks.comp.spv", .workgroup_size =
k_wg2d, .push_constant_size =
sizeof(
ExtractPeaksPC) },
373 ctx.set_push_constants(peaks_pc); },
374 .hazard_fn = [](
GpuDispatchCore& ctx) {
return flow_hazards(ctx, { k_flow_det_count, k_flow_det_points }); },
378 const auto base_w =
static_cast<float>(layout.level[0].w);
379 const auto base_h =
static_cast<float>(layout.level[0].h);
380 float cell = std::max(p.min_distance, 1.0F);
381 auto grid_w =
static_cast<uint32_t
>(std::ceil(base_w / cell));
382 auto grid_h =
static_cast<uint32_t
>(std::ceil(base_h / cell));
385 grid_w =
static_cast<uint32_t
>(std::ceil(base_w / cell));
386 grid_h =
static_cast<uint32_t
>(std::ceil(base_h / cell));
388 const uint32_t grid_cells = grid_w * grid_h;
391 const uint32_t have_prev = state.have_prev ? 1U : 0U;
393 const auto add_select = [&](
FlowSelectPhase phase, uint32_t groups, std::vector<FlowBufferSpec> touched, std::optional<FlowArgs> args = std::nullopt) {
395 .
phase =
static_cast<uint32_t
>(phase),
396 .max_points = max_points,
397 .have_prev = have_prev,
400 .grid_cells = grid_cells,
404 .publish_slot = state.export_slot,
405 .duplicate_cutoff = duplicate_cutoff,
409 .stage_fn = [pc](
GpuDispatchCore& ctx) { ctx.set_push_constants(pc); },
410 .hazard_fn = [touched = std::move(touched)](
GpuDispatchCore& ctx) {
return flow_hazards(ctx, touched); },
411 .explicit_groups = std::array<uint32_t, 3> { groups, 1U, 1U },
412 .indirect_groups = args ? std::optional<IndirectGroupsSource> { flow_args_source(*args) } : std::nullopt,
416 add_select(FlowSelectPhase::ARGS, 1U, {});
418 const auto add_lk = [&](
bool backward) {
419 for (uint32_t level = layout.levels; level-- > 0;) {
421 const bool finest = level == 0;
426 .coarsest = level + 1U == layout.levels ? 1U : 0U,
432 .base_w = layout.level[0].w,
433 .base_h = layout.level[0].h,
435 .max_iterations = p.max_iterations,
436 .eigen_threshold = p.eigen_threshold,
437 .error_threshold = p.error_threshold,
438 .max_points = max_points,
440 .backward = backward ? 1U : 0U,
441 .forward_backward_threshold = p.forward_backward_threshold,
445 .config = { .shader_path =
"flow_lk.comp.spv", .workgroup_size = { 64, 1, 1 }, .push_constant_size =
sizeof(
FlowLkPC) },
449 ctx.set_push_constants(pc); },
451 if (finest || backward)
452 return flow_hazards(ctx, { k_flow_tracks, k_flow_meta, k_flow_prev_count, k_flow_prev_points });
453 return flow_hazards(ctx, { k_flow_tracks, k_flow_meta }); },
455 .indirect_groups = flow_args_source(FlowArgs::LK),
459 if (state.have_prev) {
461 if (p.forward_backward_threshold > 0.0F)
465 add_select(FlowSelectPhase::CLEAR, clear_groups, { k_flow_grid, k_flow_histogram, k_flow_counters });
466 add_select(FlowSelectPhase::SURVIVORS, candidate_groups, { k_flow_next_points, k_flow_counters, k_flow_grid }, FlowArgs::RETAINED);
467 add_select(FlowSelectPhase::HISTOGRAM, candidate_groups, { k_flow_histogram }, FlowArgs::DETECTED);
468 add_select(FlowSelectPhase::THRESHOLD, 1U, { k_flow_counters });
469 add_select(FlowSelectPhase::ADD_STRONG, candidate_groups, { k_flow_next_points, k_flow_counters, k_flow_grid }, FlowArgs::DETECTED);
470 add_select(FlowSelectPhase::ADD_MARGINAL, candidate_groups, { k_flow_next_points, k_flow_counters, k_flow_grid }, FlowArgs::DETECTED);
471 add_select(FlowSelectPhase::COMMIT, 1U, {});
473 add_select(FlowSelectPhase::PUBLISH, candidate_groups, { k_flow_export[0], k_flow_export[1] }, FlowArgs::PUBLISH);
478 track_params.
stages = stages;
479 track_params.
async =
true;
480 track_ctx.parameters = track_params;
481 const auto track_dispatch_result = flow.execute(
Datum<> {}, track_ctx);
485 "run_gpu: TrackKeypoints failed to submit its dispatch sequence");
488 state.export_pending = export_tracks;
489 state.host_pending = p.host_tracks;
497 foundry.wait_for_fence(
fence);
498 foundry.release_fence(
fence);
499 finish_track(contexts);
506 auto& flow = contexts.
flow;
509 if (!state.curr_ready || !state.atlas[0]) {
511 "run_gpu: OpticalFlowDense requires an earlier RgbaToGray step to supply the gray frame");
515 const auto& p = std::get<OpticalFlowDenseParams>(step.
params);
516 const auto& layout = state.layout;
517 const uint32_t w = contexts.
pass.
w;
518 const uint32_t
h = contexts.
pass.
h;
520 if (w != layout.level[0].w ||
h != layout.level[0].h) {
522 "run_gpu: OpticalFlowDense image is {}x{} but the gray frame was captured at {}x{}",
523 w,
h, layout.level[0].w, layout.level[0].h);
527 if (!state.have_prev) {
528 commit_flow_frame(contexts);
532 if (!ensure_dense_images(contexts)) {
534 "run_gpu: OpticalFlowDense failed to allocate its flow images");
539 std::shared_ptr<Core::VKImage> atlas_a;
540 std::shared_ptr<Core::VKImage> atlas_b;
541 std::shared_ptr<Core::VKImage> flow_out_a;
542 std::shared_ptr<Core::VKImage> flow_out_b;
543 std::shared_ptr<Core::VKImage> flow_lvl;
544 std::shared_ptr<Core::VKImage> scratch_a;
545 std::shared_ptr<Core::VKImage> scratch_b;
546 std::shared_ptr<Core::VKImage> tensor;
547 std::shared_ptr<Core::VKImage> vis;
550 const auto images = std::make_shared<const DenseImages>(DenseImages {
551 .atlas_a = state.atlas[0],
552 .atlas_b = state.atlas[1],
553 .flow_out_a = state.flow_out[0],
554 .flow_out_b = state.flow_out[1],
555 .flow_lvl = state.flow_lvl,
556 .scratch_a = state.dense_a,
557 .scratch_b = state.dense_b,
558 .tensor = state.dense_tensor,
559 .vis = state.flow_vis,
562 const uint32_t out_parity = state.curr;
564 const uint32_t iterations = std::max(p.iterations, 1U);
566 const auto image_hazard = [](
const std::shared_ptr<Core::VKImage>&
image, uint32_t binding) {
569 .image =
image->get_image(),
573 const auto scratch_a_hazard = image_hazard(images->scratch_a, 8U);
574 const auto scratch_b_hazard = image_hazard(images->scratch_b, 9U);
575 const auto tensor_hazard = image_hazard(images->tensor, 10U);
576 const auto coarse_hazard = image_hazard(images->flow_lvl, 7U);
577 const auto out_hazard = image_hazard(out_parity == 0 ? images->flow_out_a : images->flow_out_b, 5U + out_parity);
579 std::vector<DependencyStage> stages;
580 stages.reserve(
static_cast<size_t>(layout.levels) * (2U + 3U * iterations) + 1U);
582 const auto push_stage = [&](
const FlowDensePC& stage_pc,
const PyramidLevel& region, std::vector<HazardResource> hazards) {
584 .config = { .shader_path =
"flow_dense.comp.spv", .workgroup_size =
k_wg2d, .push_constant_size =
sizeof(
FlowDensePC) },
587 ctx.stage_image_at(3, images->atlas_a, Kind::IMAGE_STORAGE);
588 ctx.stage_image_at(4, images->atlas_b, Kind::IMAGE_STORAGE);
589 ctx.stage_image_at(5, images->flow_out_a, Kind::IMAGE_STORAGE);
590 ctx.stage_image_at(6, images->flow_out_b, Kind::IMAGE_STORAGE);
591 ctx.stage_image_at(7, images->flow_lvl, Kind::IMAGE_STORAGE);
592 ctx.stage_image_at(8, images->scratch_a, Kind::IMAGE_STORAGE);
593 ctx.stage_image_at(9, images->scratch_b, Kind::IMAGE_STORAGE);
594 ctx.stage_image_at(10, images->tensor, Kind::IMAGE_STORAGE);
595 ctx.stage_image_at(11, images->vis, Kind::IMAGE_STORAGE);
596 ctx.set_push_constants(stage_pc); },
597 .hazard_fn = [hazards = std::move(hazards)](
GpuDispatchCore&) {
return hazards; },
598 .explicit_groups = std::array<uint32_t, 3> { (region.w +
k_wg2d[0] - 1U) /
k_wg2d[0], (region.h +
k_wg2d[1] - 1U) /
k_wg2d[1], 1U },
602 for (uint32_t level = layout.levels; level-- > 0;) {
604 const bool coarsest = level + 1U == layout.levels;
606 const auto& flow_hazard = level == 0 ? out_hazard : coarse_hazard;
610 .curr_atlas = state.curr,
611 .out_parity = out_parity,
621 .has_coarse = coarsest ? 0U : 1U,
623 .eigen_threshold = p.eigen_threshold,
624 .max_step = p.max_step,
625 .visual_range = p.visual_range,
626 .visual_min_motion = p.visual_min_motion,
630 const auto add = [&](
FlowDensePhase phase, std::vector<HazardResource> hazards) {
631 pc.phase =
static_cast<uint32_t
>(phase);
632 push_stage(pc, lv, std::move(hazards));
635 add(FlowDensePhase::TENSOR_H, { scratch_a_hazard });
636 add(FlowDensePhase::TENSOR_V, { tensor_hazard, scratch_a_hazard });
637 for (uint32_t iteration = 0; iteration < iterations; ++iteration) {
638 add(iteration == 0 ? FlowDensePhase::WARP_INIT : FlowDensePhase::WARP, { scratch_a_hazard, flow_hazard });
639 add(FlowDensePhase::BOX_H, { scratch_b_hazard, scratch_a_hazard });
640 pc.last_iteration = iteration + 1U == iterations ? 1U : 0U;
641 add(FlowDensePhase::SOLVE, { flow_hazard, scratch_b_hazard });
648 .
phase =
static_cast<uint32_t
>(FlowDensePhase::VISUALIZE),
649 .curr_atlas = state.curr,
650 .out_parity = out_parity,
662 .eigen_threshold = p.eigen_threshold,
663 .max_step = p.max_step,
664 .visual_range = p.visual_range,
665 .visual_min_motion = p.visual_min_motion,
668 push_stage(vis_pc, finest, { out_hazard, image_hazard(images->vis, 11U) });
674 dense_params.
stages = stages;
675 dense_params.
async =
true;
677 const auto dense_dispatch_result = flow.execute(
Datum<> {}, dense_ctx);
681 "run_gpu: OpticalFlowDense failed to submit its dispatch sequence");
691 foundry.wait_for_fence(
fence);
692 foundry.release_fence(
fence);
693 finish_dense(contexts, p.visualize);
705 const auto* header =
static_cast<const glm::vec4*
>(handle->mapped_ptr);
706 return decode_track_records(header + 1, *
count);
711 auto& flow = contexts.
flow;
715 if (!gray || !gray->is_initialized())
718 const uint32_t w = contexts.
pass.
w;
719 const uint32_t
h = contexts.
pass.
h;
722 ensure_flow_resources(contexts);
724 if (!state.atlas[0] || !(state.layout == layout)) {
726 state.atlas[0] = loom.create_storage_image(layout.atlas_w, layout.atlas_h, ImageFormat::RGBA16F);
727 state.atlas[1] = loom.create_storage_image(layout.atlas_w, layout.atlas_h, ImageFormat::RGBA16F);
728 state.layout = layout;
729 state.have_prev =
false;
733 const auto atlas_a = state.atlas[0];
734 const auto atlas_b = state.atlas[1];
735 const auto target = state.atlas[state.curr];
737 std::vector<DependencyStage> stages;
738 stages.reserve(layout.levels);
740 for (uint32_t level = 0; level < layout.levels; ++level) {
755 .src_channel = src_channel,
759 .config = { .shader_path =
"flow_pyramid.comp.spv", .workgroup_size =
k_wg2d, .push_constant_size =
sizeof(
FlowPyramidPC) },
764 ctx.set_push_constants(pc); },
766 std::vector<HazardResource> hazards;
769 .image = target->get_image(),
774 .image = gray->get_image(),
784 foundry.wait_for_fence(state.build_fence);
785 foundry.release_fence(state.build_fence);
789 const std::array<uint32_t, 4> cleared {};
790 flow.upload_shared_raw(1, 3,
reinterpret_cast<const uint8_t*
>(cleared.data()),
sizeof(cleared));
795 pyramid_params.
stages = stages;
796 pyramid_params.
async =
true;
798 const auto pyramid_dispatch_result = flow.execute(
Datum<> {}, pyramid_ctx);
#define MF_ERROR(comp, ctx,...)
vk::PhysicalDeviceType type
static TextureLoom & instance()
Non-template base that owns all type-independent GPU dispatch logic.
static void build_flow_pyramid(VisionGpuContexts &contexts, uint32_t requested_levels, uint32_t src_channel)
Build the current frame's pyramid atlas from the working image.
bool op_dense_flow(VisionGpuContexts &contexts, const Kinesis::Vision::VisionStep &step)
Dense optical flow from the previous frame to the current one.
bool op_track_keypoints(VisionGpuContexts &contexts, const Kinesis::Vision::VisionStep &step)
Track the previous frame's keypoints into the current frame.
static std::vector< Kinesis::Vision::TrackResult > read_exported_tracks(const Kinesis::Vision::VisionResult &result)
Host tracks decoded from result.buffers.tracks, or empty.
@ ComputeMatrix
Compute operations (Yantra - algorithms, matrices, DSP)
@ Yantra
DSP algorithms, computational units, matrix operations, Grammar.
@ Portal
High-level user-facing API layer.
PyramidLayout pyramid_layout(uint32_t w, uint32_t h, uint32_t requested, uint32_t min_extent=16)
Place up to requested pyramid levels for a w x h base image.
constexpr FenceID INVALID_FENCE
MAYAFLUX_API ShaderFoundry & get_shader_foundry()
Get the global shader compiler instance.
BufferUsageHint
Semantic usage hint for buffer allocation and memory properties.
@ INDIRECT
Indirect draw/dispatch buffer (device-local)
@ COMPUTE
Storage buffer for compute shaders (device-local)
constexpr size_t k_flow_export_vec4
Export buffer size in vec4: one header plus two per track.
constexpr uint32_t k_flow_min_level_extent
Smallest pyramid level edge worth building.
constexpr uint32_t k_flow_duplicate_energy
Frame change, in 1/1024 intensity summed over pixels, at or below which a frame counts as a repeat.
constexpr uint32_t k_flow_max_radius
Largest window radius the tracker's shared template cache holds.
constexpr uint32_t k_flow_select_local
constexpr uint64_t k_flow_args_stride
std::optional< uint32_t > exported_record_count(const std::shared_ptr< Portal::Graphics::GpuBufferHandle > &handle, size_t stride_vec4)
Clamped record count from an exported buffer's vec4 header, or nullopt when the buffer isn't readable...
constexpr size_t k_flow_args_count
constexpr uint32_t k_flow_grid_capacity
Occupancy grid capacity in cells; the cell size grows to fit large frames.
PixelRect region_rect(const std::optional< Kinesis::Vision::BoundingBox > ®ion, uint32_t w, uint32_t h)
Pixel rectangle of a normalised region over a w x h image, clamped to the image.
constexpr uint32_t k_flow_max_points
Upper bound on tracked keypoints, matching extract_peaks' buffer capacity.
constexpr std::array< uint32_t, 3 > k_wg2d
Standard 2D workgroup used by all pixel-to-pixel vision shaders.
constexpr uint32_t k_flow_dense_max_radius
Largest summation window radius of the dense solver.
@ DEPENDENCY
Part of dependency graph.
static constexpr DomainSpec Graphics
Domain constant for Graphics domain.
std::shared_ptr< Portal::Graphics::GpuBufferHandle > tracks
Extent and position of one pyramid level inside an atlas.
const VisionStep * behind(size_t offset=1) const noexcept
Op at offset steps back, or nullptr before the start.
BufferOutputs buffers
GPU-resident export buffers, one named slot per exporting op.
StructuredOutput structured
Result of executing a VisionSequence on one frame.
One step in a VisionSequence: an op and its parameters.
uint32_t set
Descriptor set index.
ElementType
Element type the shader expects in this binding.
enum MayaFlux::Portal::Graphics::GpuBufferBinding::ElementType FLOAT32
GpuBufferBinding binding
Direction/element_type describing this resource.
One resource this stage's dispatch reads/writes that a later stage in the sequence depends on,...
Input/Output container for computation pipeline data flow with structure preservation.
bool async
Submit the sequence without waiting for it to complete.
std::vector< DependencyStage > stages
Parameters for ExecutionMode::DEPENDENCY.
ExecutionMode mode
Execution mode controlling scheduling behavior.
ExecutionParams parameters
Optional parameters specific to the execution mode.
Context information controlling how a compute operation executes.
std::function< void(VisionGpuContexts &)> finalize
Work to run once the fence has signaled, before the walk resumes.
Portal::Graphics::FenceID fence
Kinesis::Vision::GpuVisionPass pass
Walk state for the current run: sequence position, geometry, working image, and the result under cons...
TextureExecutionContext flow
Pyramidal optical flow.
Fixed set of TextureExecutionContexts covering every GPU-implemented VisionOp shape.