MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches

◆ run() [2/2]

VisionResult MayaFlux::Yantra::VisionGpuExecutor::run ( VisionGpuContexts &  contexts,
const Kinesis::Vision::VisionSequence &  sequence,
const std::shared_ptr< Core::VKImage > &  image,
uint32_t  w,
uint32_t  h 
)

Execute a VisionSequence on the GPU through an explicit context set.

contexts is caller-supplied rather than the instance's own lazily-built m_contexts. Reused across calls with no reset needed; construction is the caller's responsibility and never happens inside this function.

Parameters
contextsLong-lived context set. Never constructed internally.
sequenceOrdered steps to execute.
imageInput frame in eShaderReadOnlyOptimal layout.
wFrame width in pixels.
hFrame height in pixels.
Returns
VisionResult matching the VisionExecutor::run() contract.

Definition at line 450 of file VisionGpuDispatch.cpp.

455{
456 auto& pixel_ctx = contexts.pixel;
457 auto& structured_ctx = contexts.structured;
458 auto& label_ctx = contexts.labels;
459 auto& cc_pipeline = contexts.component_contours;
460
461 auto& foundry = Portal::Graphics::get_shader_foundry();
462 std::unordered_map<size_t, CompletedOp> completed_ops;
463 size_t begin = 0;
464
465 if (contexts.suspended.is_active()) {
466 if (&sequence != contexts.pass.sequence) {
468 "run_gpu: polling a suspension with a different sequence; "
469 "the walk continues on the sequence the run started from");
470 }
471
472 if (!foundry.is_fence_signaled(contexts.suspended.fence)) {
473 VisionResult pending;
474 pending.status = VisionStatus::SUSPENDED;
475 pending.suspended_at = contexts.pass.index;
476 return pending;
477 }
478
479 foundry.release_fence(contexts.suspended.fence);
480 contexts.suspended.fence = Portal::Graphics::INVALID_FENCE;
481
482 if (auto finalize = std::move(contexts.suspended.finalize)) {
483 contexts.suspended.finalize = nullptr;
484 finalize(contexts);
485 }
486
487 begin = contexts.pass.index + 1;
488 } else {
489 reap_fences(contexts);
490 contexts.flow_state.curr_ready = false;
491 contexts.pass.begin(sequence, w, h);
492 contexts.pass.storage_w = 0;
493 contexts.pass.storage_h = 0;
494 contexts.bound_staged.reset();
495 const auto seed = op_ingest(contexts, image, w, h);
496 contexts.pass.current = seed;
497 contexts.source = seed;
498 }
499
500 for (contexts.pass.index = begin; contexts.pass.index < sequence.steps.size(); ++contexts.pass.index) {
501 const auto& step = sequence.steps[contexts.pass.index];
502
503 if (step.op == VisionOp::Snapshot) {
504 op_snapshot(contexts);
505 continue;
506 }
507
508 const uint32_t w = contexts.pass.w;
509 const uint32_t h = contexts.pass.h;
510
511 const auto cfg = config(step.op, step.params);
512
513 if (cfg.shader_id == Portal::Graphics::INVALID_SHADER && cfg.shader_path.empty()) {
515 "run_gpu: no GPU implementation for VisionOp {}",
516 static_cast<int>(step.op));
517 return VisionResult {};
518 }
519
520 if (cfg.shader_id != contexts.bound_config.shader_id
521 || cfg.shader_path != contexts.bound_config.shader_path) {
522 pixel_ctx.swap_shader(cfg);
523 contexts.bound_config = cfg;
524 }
525 if (contexts.pass.current != contexts.bound_staged) {
526 pixel_ctx.stage_image(contexts.pass.current);
527 contexts.bound_staged = contexts.pass.current;
528 }
529 if (w != contexts.pass.storage_w || h != contexts.pass.storage_h
530 || (contexts.pass.current && contexts.pass.current == pixel_ctx.get_output_image(0))) {
531 pixel_ctx.prepare_output_image(w, h);
532 contexts.pass.storage_w = w;
533 contexts.pass.storage_h = h;
534 }
535 pixel_ctx.set_output_dimensions(w, h);
536
537 switch (step.op) {
538 case VisionOp::Downsample2x: {
539 const uint32_t new_w = std::max(1U, w / 2);
540 const uint32_t new_h = std::max(1U, h / 2);
541
542 pixel_ctx.prepare_output_image(new_w, new_h);
543 pixel_ctx.set_output_dimensions(new_w, new_h);
544 {
545 const auto f = pixel_ctx.dispatch_async({});
546 foundry.wait_for_fence(f);
547 foundry.release_fence(f);
548 }
549 pixel_ctx.clear_output_dimensions();
550
551 auto downsampled = pixel_ctx.get_output_image(0);
552 completed_ops[Kinesis::Vision::hash_vision_step(step.op, step.params)] = { .output = downsampled, .input = contexts.pass.current };
553 contexts.pass.result.images.downsample_2x = downsampled;
554 contexts.pass.current = downsampled;
555 contexts.bound_staged.reset();
556
557 contexts.pass.set_geometry(new_w, new_h);
558 contexts.pass.storage_w = new_w;
559 contexts.pass.storage_h = new_h;
560
561 continue;
562 }
563 case VisionOp::Threshold: {
564 const auto& p = std::get<ThresholdParams>(step.params);
565 if (p.channels == ChannelMask::NONE) {
566 pixel_ctx.set_push_constants(ThresholdPC { .value = p.value });
567 } else {
568 pixel_ctx.set_push_constants(ThresholdBandsPC {
569 .lo0 = p.bands[0].lo, .lo1 = p.bands[1].lo, .lo2 = p.bands[2].lo,
570 .hi0 = p.bands[0].hi, .hi1 = p.bands[1].hi, .hi2 = p.bands[2].hi,
571 .channels = static_cast<uint32_t>(p.channels) });
572 }
573 break;
574 }
575 case VisionOp::NormalizeRange: {
576 const auto& p = std::get<NormalizeRangeParams>(step.params);
577 const float scale = (p.hi > p.lo) ? 1.0F / (p.hi - p.lo) : 1.0F;
578 const float off = (p.hi > p.lo) ? -p.lo / (p.hi - p.lo) : 0.0F;
579 pixel_ctx.set_push_constants(NormalizePC { .scale = scale, .offset = off });
580 break;
581 }
582 case VisionOp::RgbaToGray:
583 pixel_ctx.set_push_constants(RgbaToGrayPC {
584 .wr = 0.299F, .wg = 0.587F, .wb = 0.114F, .wa = 0.0F });
585 break;
586 case VisionOp::GaussianBlur: {
587 const auto& p = std::get<GaussianBlurParams>(step.params);
588 const auto radius = static_cast<uint32_t>(std::ceil(p.sigma * 3.0F));
589 const auto& weights = gaussian_kernel_2d(radius, p.sigma);
590 pixel_ctx.set_binding_data(2, std::span<const float>(weights));
591 pixel_ctx.set_push_constants(GaussianPC { .radius = radius, .width = w, .height = h });
592 break;
593 }
594 case VisionOp::Erode:
595 case VisionOp::Dilate:
596 case VisionOp::MorphGradient:
597 pixel_ctx.set_push_constants(MorphPC {
598 .radius = std::get<MorphParams>(step.params).radius });
599 break;
600 case VisionOp::ThresholdAdaptive: {
601 const auto& p = std::get<ThresholdAdaptiveParams>(step.params);
602 pixel_ctx.set_push_constants(ThresholdAdaptivePC { .block_size = p.block_size, .offset = p.offset, .channels = static_cast<uint32_t>(p.channels) });
603 break;
604 }
605 case VisionOp::ThresholdOtsu: {
606 const auto* otsu = std::get_if<OtsuParams>(&step.params);
607 contexts.pass.completed[Kinesis::Vision::hash_vision_step(step.op, step.params)] = op_threshold_otsu(contexts, otsu ? *otsu : OtsuParams {});
608 continue;
609 }
610 case VisionOp::Confine: {
611 contexts.pass.completed[Kinesis::Vision::hash_vision_step(step.op, step.params)] = op_confine(contexts, std::get<ConfineParams>(step.params));
612 continue;
613 }
614 case VisionOp::Open:
615 case VisionOp::Close: {
616 contexts.pass.completed[Kinesis::Vision::hash_vision_step(step.op, step.params)] = op_open_close(contexts, step.op, std::get<MorphParams>(step.params));
617 continue;
618 }
619 case VisionOp::Canny: {
620 auto done = op_canny(contexts, step.params, std::get<CannyParams>(step.params));
621 contexts.pass.completed[Kinesis::Vision::hash_vision_step(step.op, step.params)] = done;
622 continue;
623 }
624 case VisionOp::HarrisResponse: {
625 contexts.pass.completed[Kinesis::Vision::hash_vision_step(step.op, step.params)] = op_harris_response(contexts, std::get<HarrisParams>(step.params));
626 continue;
627 }
628 case VisionOp::ExtractPeaks: {
629 op_extract_peaks(contexts, std::get<ExtractPeaksParams>(step.params));
630 continue;
631 }
632 case VisionOp::ConnectedComponents: {
633 if (!contexts.pass.current) {
634 continue;
635 }
636 op_connected_components(contexts, std::get<Kinesis::Vision::ConnectedComponentsParams>(step.params));
637 continue;
638 }
639 case VisionOp::FindContours: {
640 if (!op_find_contours(contexts,
641 std::get<Kinesis::Vision::FindContoursParams>(step.params)))
642 return VisionResult {};
643 continue;
644 }
645 case VisionOp::TrackKeypoints: {
646 if (op_track_keypoints(contexts, step)) {
647 VisionResult pending;
648 pending.status = VisionStatus::SUSPENDED;
649 pending.suspended_at = contexts.pass.index;
650 return pending;
651 }
652 continue;
653 }
654 case VisionOp::OpticalFlowDense: {
655 if (op_dense_flow(contexts, step)) {
656 VisionResult pending;
657 pending.status = VisionStatus::SUSPENDED;
658 pending.suspended_at = contexts.pass.index;
659 return pending;
660 }
661 continue;
662 }
663 default:
664 break;
665 }
666
667 auto dispatch_input = contexts.pass.current;
668 const auto fence = pixel_ctx.dispatch_async({});
669
670 if (step.deferred) {
671 pixel_ctx.clear_output_dimensions();
672 contexts.pass.current = pixel_ctx.get_output_image(0);
673 contexts.bound_staged.reset();
674
675 const Kinesis::Vision::GpuVisionPass::Completed done {
676 .output = contexts.pass.current,
677 .input = dispatch_input
678 };
679 contexts.pass.completed[Kinesis::Vision::hash_vision_step(step.op, step.params)] = done;
680
681 contexts.suspended.fence = fence;
682 contexts.suspended.finalize = [](VisionGpuContexts& c) { after_step(c, c.pass.index); };
683
684 VisionResult pending;
685 pending.status = VisionStatus::SUSPENDED;
686 pending.suspended_at = contexts.pass.index;
687 return pending;
688 }
689
690 foundry.wait_for_fence(fence);
691 foundry.release_fence(fence);
692
693 pixel_ctx.clear_output_dimensions();
694 contexts.pass.current = pixel_ctx.get_output_image(0);
695 contexts.bound_staged.reset();
696 completed_ops[Kinesis::Vision::hash_vision_step(step.op, step.params)] = { .output = contexts.pass.current, .input = dispatch_input };
697
698 switch (step.op) {
699 case VisionOp::RgbaToGray:
700 contexts.pass.result.gray = contexts.pass.current;
701 break;
702 case VisionOp::RgbaToHsv:
703 contexts.pass.result.images.rgba_to_hsv = contexts.pass.current;
704 break;
705 case VisionOp::GrayToRgba:
706 contexts.pass.result.images.gray_to_rgba = contexts.pass.current;
707 break;
708 case VisionOp::Threshold:
709 contexts.pass.result.images.threshold = contexts.pass.current;
710 break;
711 case VisionOp::ThresholdAdaptive:
712 contexts.pass.result.images.threshold_adaptive = contexts.pass.current;
713 break;
714 case VisionOp::NormalizeInplace:
715 contexts.pass.result.images.normalize_inplace = contexts.pass.current;
716 break;
717 case VisionOp::NormalizeRange:
718 contexts.pass.result.images.normalize_range = contexts.pass.current;
719 break;
720 case VisionOp::GaussianBlur:
721 contexts.pass.result.images.gaussian_blur = contexts.pass.current;
722 break;
723 case VisionOp::FilterSeparable:
724 contexts.pass.result.images.filter_separable = contexts.pass.current;
725 break;
726 case VisionOp::Sobel:
727 contexts.pass.result.images.sobel = contexts.pass.current;
728 break;
729 case VisionOp::Scharr:
730 contexts.pass.result.images.scharr = contexts.pass.current;
731 break;
732 case VisionOp::Erode:
733 contexts.pass.result.images.erode = contexts.pass.current;
734 break;
735 case VisionOp::Dilate:
736 contexts.pass.result.images.dilate = contexts.pass.current;
737 break;
738 case VisionOp::MorphGradient:
739 contexts.pass.result.images.morph_gradient = contexts.pass.current;
740 break;
741 default:
742 break;
743 }
744
745 after_step(contexts, contexts.pass.index);
746 }
747
748 return std::move(contexts.pass.result);
749}
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
vk::Fence fence
IO::ImageData image
Definition Decoder.cpp:47
float radius
uint32_t h
Definition InkPress.cpp:29
bool done
Definition Picker.cpp:24
const std::vector< float > & gaussian_kernel_2d(uint32_t radius, float sigma)
2D Gaussian kernel for convolution, cached by (radius, sigma bit pattern).
Kinesis::Vision::GpuVisionPass::Completed op_canny(VisionGpuContexts &contexts, const Kinesis::Vision::VisionParams &params, const Kinesis::Vision::CannyParams &p)
Detect edges in the current image with the Canny pipeline.
static void op_snapshot(VisionGpuContexts &contexts)
Copy the working image into this snapshot's own image and append it to VisionResult::snapshots.
bool op_dense_flow(VisionGpuContexts &contexts, const Kinesis::Vision::VisionStep &step)
Dense optical flow from the previous frame to the current one.
static GpuComputeConfig config(Kinesis::Vision::VisionOp op, const Kinesis::Vision::VisionParams &params)
GpuComputeConfig for a given VisionOp and its parameters.
bool op_track_keypoints(VisionGpuContexts &contexts, const Kinesis::Vision::VisionStep &step)
Track the previous frame's keypoints into the current frame.
Kinesis::Vision::GpuVisionPass::Completed op_harris_response(VisionGpuContexts &contexts, const Kinesis::Vision::HarrisParams &p)
Compute the Harris response of the current image.
void op_connected_components(VisionGpuContexts &contexts, const Kinesis::Vision::ConnectedComponentsParams &p)
Label components in the current image on the GPU.
Kinesis::Vision::GpuVisionPass::Completed op_open_close(VisionGpuContexts &contexts, Kinesis::Vision::VisionOp op, const Kinesis::Vision::MorphParams &p)
Apply morphological opening or closing to the current image.
static void after_step(VisionGpuContexts &contexts, size_t index)
Run any work a finished step owes the flow context.
Kinesis::Vision::GpuVisionPass::Completed op_threshold_otsu(VisionGpuContexts &contexts, const Kinesis::Vision::OtsuParams &p)
Apply an Otsu threshold to the current image.
bool op_find_contours(VisionGpuContexts &contexts, const Kinesis::Vision::FindContoursParams &p)
Trace contours from the immediately preceding component labels.
Kinesis::Vision::GpuVisionPass::Completed op_confine(VisionGpuContexts &contexts, const Kinesis::Vision::ConfineParams &p)
Arbitrary rectangular sub-region of the current image, optionally resized back to the frame's own siz...
void op_extract_peaks(VisionGpuContexts &contexts, const Kinesis::Vision::ExtractPeaksParams &p)
Extract and rank keypoints from the current response image.
@ ComputeMatrix
Compute operations (Yantra - algorithms, matrices, DSP)
@ Yantra
DSP algorithms, computational units, matrix operations, Grammar.
size_t hash_vision_step(VisionOp op, const VisionParams &params)
Hash a VisionStep's op and parameters together.
Definition VisionOp.hpp:621
Tendency< D, float > scale(const Tendency< D, float > &t, float factor)
Uniform scaling of a scalar-output tendency.
Definition Tendency.hpp:98
std::shared_ptr< Vruta::Routine > sequence(std::vector< std::pair< double, std::function< void()> > > sequence, Vruta::ProcessingToken token)
Creates a temporal sequence that executes callbacks at specified time offsets.
Definition Tasks.cpp:46
constexpr ShaderID INVALID_SHADER
constexpr FenceID INVALID_FENCE
MAYAFLUX_API ShaderFoundry & get_shader_foundry()
Get the global shader compiler instance.

References MayaFlux::Kinesis::Vision::VisionPass< Handle >::begin(), MayaFlux::Yantra::VisionInternal::ThresholdAdaptivePC::block_size, MayaFlux::Yantra::VisionGpuContexts::bound_config, MayaFlux::Yantra::VisionGpuContexts::bound_staged, MayaFlux::Kinesis::Vision::VisionPass< Handle >::completed, MayaFlux::Yantra::VisionGpuContexts::component_contours, MayaFlux::Journal::ComputeMatrix, MayaFlux::Yantra::FlowState::curr_ready, MayaFlux::Kinesis::Vision::VisionPass< Handle >::current, MayaFlux::Kinesis::Vision::ImageOutputs::dilate, done, MayaFlux::Kinesis::Vision::ImageOutputs::downsample_2x, MayaFlux::Kinesis::Vision::ImageOutputs::erode, fence, MayaFlux::Yantra::VisionGpuContexts::Suspension::fence, MayaFlux::Kinesis::Vision::ImageOutputs::filter_separable, MayaFlux::Yantra::VisionGpuContexts::Suspension::finalize, MayaFlux::Yantra::VisionGpuContexts::flow_state, MayaFlux::Kinesis::Vision::ImageOutputs::gaussian_blur, MayaFlux::Portal::Graphics::get_shader_foundry(), MayaFlux::Kinesis::Vision::VisionResult::gray, MayaFlux::Kinesis::Vision::ImageOutputs::gray_to_rgba, MayaFlux::Kinesis::Vision::VisionPass< Handle >::h, h, MayaFlux::Kinesis::Vision::hash_vision_step(), image, MayaFlux::Kinesis::Vision::VisionResult::images, MayaFlux::Kinesis::Vision::VisionPass< Handle >::index, MayaFlux::Portal::Graphics::INVALID_FENCE, MayaFlux::Portal::Graphics::INVALID_SHADER, MayaFlux::Yantra::VisionGpuContexts::Suspension::is_active(), MayaFlux::Yantra::VisionGpuContexts::labels, MayaFlux::Yantra::VisionInternal::ThresholdBandsPC::lo0, MF_ERROR, MF_WARN, MayaFlux::Kinesis::Vision::ImageOutputs::morph_gradient, MayaFlux::Kinesis::Vision::ImageOutputs::normalize_inplace, MayaFlux::Kinesis::Vision::ImageOutputs::normalize_range, MayaFlux::Yantra::VisionGpuContexts::pass, MayaFlux::Yantra::VisionGpuContexts::pixel, radius, MayaFlux::Yantra::VisionInternal::MorphPC::radius, MayaFlux::Yantra::VisionInternal::GaussianPC::radius, MayaFlux::Kinesis::Vision::VisionPass< Handle >::result, MayaFlux::Kinesis::Vision::ImageOutputs::rgba_to_hsv, MayaFlux::Yantra::VisionInternal::NormalizePC::scale, MayaFlux::Kinesis::Vision::ImageOutputs::scharr, MayaFlux::Kinesis::Vision::VisionPass< Handle >::sequence, MayaFlux::Kinesis::Vision::VisionPass< Handle >::set_geometry(), MayaFlux::Portal::Graphics::GpuComputeConfig::shader_id, MayaFlux::Portal::Graphics::GpuComputeConfig::shader_path, MayaFlux::Kinesis::Vision::ImageOutputs::sobel, MayaFlux::Yantra::VisionGpuContexts::source, MayaFlux::Kinesis::Vision::VisionResult::status, MayaFlux::Kinesis::Vision::VisionPass< Handle >::storage_h, MayaFlux::Kinesis::Vision::VisionPass< Handle >::storage_w, MayaFlux::Yantra::VisionGpuContexts::structured, MayaFlux::Yantra::VisionGpuContexts::suspended, MayaFlux::Kinesis::Vision::VisionResult::suspended_at, MayaFlux::Kinesis::Vision::ImageOutputs::threshold, MayaFlux::Kinesis::Vision::ImageOutputs::threshold_adaptive, MayaFlux::Yantra::VisionInternal::ThresholdPC::value, MayaFlux::Kinesis::Vision::VisionPass< Handle >::w, MayaFlux::Yantra::VisionInternal::RgbaToGrayPC::wr, and MayaFlux::Journal::Yantra.

+ Here is the call graph for this function: