MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VisionFeatureDispatch.cpp
Go to the documentation of this file.
1#include "DispatchLayouts.hpp"
2
3namespace MayaFlux::Yantra {
4
5using namespace VisionInternal;
6
7using namespace Portal::Graphics;
8using namespace Kinesis::Vision;
9
11 VisionGpuContexts& contexts,
12 const HarrisParams& p)
13{
14 auto& pixel_ctx = contexts.pixel;
15 auto w = contexts.pass.w;
16 auto h = contexts.pass.h;
18
19 const auto radius = static_cast<uint32_t>(std::ceil(p.sigma * 3.0F));
20 const auto& weights = gaussian_kernel_2d(radius, p.sigma);
21
22 const auto harris_input = contexts.pass.current;
23
24 pixel_ctx.swap_shader({ .shader_path = "harris_grad_pack.comp.spv", .workgroup_size = k_wg2d });
25 pixel_ctx.stage_image(harris_input);
26 pixel_ctx.prepare_output_image(w, h);
27 {
28 const auto f = pixel_ctx.dispatch_async({});
29 foundry.wait_for_fence(f);
30 foundry.release_fence(f);
31 }
32 auto packed = pixel_ctx.get_output_image(0);
33
34 const auto blur_cfg = VisionGpuExecutor::config(VisionOp::GaussianBlur, GaussianBlurParams { .sigma = p.sigma });
35 pixel_ctx.swap_shader(blur_cfg);
36 pixel_ctx.stage_image(packed);
37 pixel_ctx.set_binding_data(2, std::span<const float>(weights));
38 pixel_ctx.set_push_constants(GaussianPC { .radius = radius, .width = w, .height = h });
39 pixel_ctx.prepare_output_image(w, h);
40 {
41 const auto f = pixel_ctx.dispatch_async({});
42 foundry.wait_for_fence(f);
43 foundry.release_fence(f);
44 }
45 auto smoothed = pixel_ctx.get_output_image(0);
46
47 const GpuComputeConfig harris_resp_cfg {
48 .shader_path = "harris_response.comp.spv",
49 .workgroup_size = k_wg2d,
50 .push_constant_size = sizeof(HarrisPC),
51 };
52 pixel_ctx.swap_shader(harris_resp_cfg);
53 pixel_ctx.stage_image(smoothed);
54
55 /** Zero PeakBuf (binding 2) before pass 0 so its atomicMax starts clean;
56 * clear the staged bytes before pass 1 so the accumulated max survives. */
57 const uint32_t peak_reset = 0U;
58 pixel_ctx.set_binding_data(2, std::span<const uint32_t>(&peak_reset, 1));
59 const auto rect = region_rect(p.region, w, h);
60 pixel_ctx.set_push_constants(HarrisPC { .k = p.k, .pass = 0U, .width = w, .height = h, .rect = rect });
61 pixel_ctx.prepare_output_image(w, h);
62 {
63 const auto f = pixel_ctx.dispatch_async({});
64 foundry.wait_for_fence(f);
65 foundry.release_fence(f);
66 }
67
68 pixel_ctx.set_binding_data(2, std::span<const uint32_t>(&peak_reset, 0));
69 pixel_ctx.set_push_constants(HarrisPC { .k = p.k, .pass = 1U, .width = w, .height = h, .rect = rect });
70 {
71 const auto f = pixel_ctx.dispatch_async({});
72 foundry.wait_for_fence(f);
73 foundry.release_fence(f);
74 }
75
76 contexts.pass.current = pixel_ctx.get_output_image(0);
77 contexts.pass.result.structured = std::monostate {};
78 contexts.pass.result.images.harris_response = contexts.pass.current;
79
80 contexts.bound_config = harris_resp_cfg;
81 contexts.bound_staged = smoothed;
82
83 return { .output = contexts.pass.current, .input = harris_input };
84}
85
87 VisionGpuContexts& contexts,
88 const ExtractPeaksParams& p)
89{
90 auto& structured_ctx = contexts.structured;
91 auto w = contexts.pass.w;
92 auto h = contexts.pass.h;
94
95 constexpr uint32_t k_max_kp = 4096;
96
97 const auto* tracker = contexts.pass.ahead();
98 if (tracker && tracker->op == VisionOp::TrackKeypoints) {
99 contexts.pass.result.structured = std::monostate {};
100 contexts.pass.result.w = w;
101 contexts.pass.result.h = h;
102 return;
103 }
104
105 structured_ctx.swap_shader({
106 .shader_path = "extract_peaks.comp.spv",
107 .workgroup_size = { 8, 8, 1 },
108 .push_constant_size = sizeof(ExtractPeaksPC),
109 });
110
111 structured_ctx.set_output_size(1, sizeof(uint32_t));
112 structured_ctx.set_output_size(2, static_cast<size_t>(k_max_kp) * 4 * sizeof(float));
113 structured_ctx.ensure_shared_buffer(0, 3, (static_cast<size_t>(k_max_kp) + 1U) * 4U,
115
116 structured_ctx.stage_image(contexts.pass.current);
117 structured_ctx.set_push_constants(ExtractPeaksPC {
118 .threshold = p.threshold,
119 .nms_radius = p.nms_radius,
120 .width = w,
121 .height = h,
122 .max_keypoints = k_max_kp,
123 .rect = region_rect(p.region, w, h),
124 });
125
126 structured_ctx.set_output_dimensions(w, h);
127 const auto fence = structured_ctx.dispatch_async({});
128 structured_ctx.clear_output_dimensions();
129 foundry.wait_for_fence(fence);
130 foundry.release_fence(fence);
131
132 const auto gpu_result = structured_ctx.collect_result();
133
134 uint32_t count = 0;
135 if (auto it = gpu_result.aux.find(1); it != gpu_result.aux.end())
136 std::memcpy(&count, it->second.data(), sizeof(uint32_t));
137 count = std::min(count, k_max_kp);
138
139 if (p.export_keypoints) {
140 const glm::vec4 header { std::bit_cast<float>(count), 0.0F, 0.0F, 0.0F };
141 structured_ctx.upload_shared_raw(0, 3, reinterpret_cast<const uint8_t*>(&header), sizeof(glm::vec4));
142 contexts.pass.result.buffers.keypoints = std::make_shared<Portal::Graphics::GpuBufferHandle>(
143 structured_ctx.shared_buffer_handle(0, 3));
144 }
145
146 struct GpuKp {
147 float x, y, response, pad;
148 };
149 std::vector<GpuKp> raw(count);
150 if (count > 0) {
151 if (auto it = gpu_result.aux.find(2); it != gpu_result.aux.end())
152 std::memcpy(raw.data(), it->second.data(), count * sizeof(GpuKp));
153 }
154
155 std::vector<Kinesis::Vision::Keypoint> kpts;
156 kpts.reserve(count);
157 for (const auto& kp : raw) {
158 kpts.push_back({ .position = { kp.x, kp.y },
159 .response = kp.response,
160 .scale = 1.0F,
161 .angle = 0.0F });
162 }
163 std::ranges::sort(kpts, [](const auto& a, const auto& b) { return a.response > b.response; });
164
165 contexts.pass.result.structured = std::move(kpts);
166 contexts.pass.result.w = 0;
167 contexts.pass.result.h = 0;
168}
169
170std::vector<Kinesis::Vision::Keypoint> VisionGpuExecutor::read_exported_keypoints(
171 const Kinesis::Vision::VisionResult& result)
172{
173 const auto& handle = result.buffers.keypoints;
174 const auto count = exported_record_count(handle, 1);
175 if (!count)
176 return {};
177
178 const auto* records = static_cast<const glm::vec4*>(handle->mapped_ptr) + 1;
179 std::vector<Kinesis::Vision::Keypoint> out;
180 out.reserve(*count);
181 for (uint32_t i = 0; i < *count; ++i) {
182 out.push_back({
183 .position = { records[i].x, records[i].y },
184 .response = records[i].z,
185 .scale = 1.0F,
186 .angle = 0.0F,
187 });
188 }
189 return out;
190}
191
192} // namespace MayaFlux::Yantra
vk::Fence fence
float radius
uint32_t h
Definition InkPress.cpp:29
size_t a
size_t b
std::vector< float > * out
size_t count
const std::vector< float > & gaussian_kernel_2d(uint32_t radius, float sigma)
2D Gaussian kernel for convolution, cached by (radius, sigma bit pattern).
static GpuComputeConfig config(Kinesis::Vision::VisionOp op, const Kinesis::Vision::VisionParams &params)
GpuComputeConfig for a given VisionOp and its parameters.
Kinesis::Vision::GpuVisionPass::Completed op_harris_response(VisionGpuContexts &contexts, const Kinesis::Vision::HarrisParams &p)
Compute the Harris response of the current image.
void op_extract_peaks(VisionGpuContexts &contexts, const Kinesis::Vision::ExtractPeaksParams &p)
Extract and rank keypoints from the current response image.
static std::vector< Kinesis::Vision::Keypoint > read_exported_keypoints(const Kinesis::Vision::VisionResult &result)
Host keypoints decoded from result.buffers.keypoints, or empty.
MAYAFLUX_API ShaderFoundry & get_shader_foundry()
Get the global shader compiler instance.
@ COMPUTE
Storage buffer for compute shaders (device-local)
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...
PixelRect region_rect(const std::optional< Kinesis::Vision::BoundingBox > &region, uint32_t w, uint32_t h)
Pixel rectangle of a normalised region over a w x h image, clamped to the image.
constexpr std::array< uint32_t, 3 > k_wg2d
Standard 2D workgroup used by all pixel-to-pixel vision shaders.
std::shared_ptr< Portal::Graphics::GpuBufferHandle > keypoints
std::optional< BoundingBox > region
Definition VisionOp.hpp:206
std::optional< BoundingBox > region
Definition VisionOp.hpp:193
Harris response parameters.
Definition VisionOp.hpp:190
std::shared_ptr< Core::VKImage > harris_response
const VisionStep * ahead(size_t offset=1) const noexcept
Op at offset steps ahead, or nullptr past the end.
BufferOutputs buffers
GPU-resident export buffers, one named slot per exporting op.
ImageOutputs images
Image output of whichever image-producing ops ran in this sequence, one named slot per op.
Result of executing a VisionSequence on one frame.
enum MayaFlux::Portal::Graphics::GpuBufferBinding::ElementType FLOAT32
Plain-data description of the compute shader to dispatch.
GpuComputeConfig bound_config
Shader currently bound on pixel, and the image staged into it.
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 > bound_staged
TextureExecutionContext pixel
Image pipeline.
TextureExecutionContext structured
Buffer-only readback.
Fixed set of TextureExecutionContexts covering every GPU-implemented VisionOp shape.