Skip to content

feat(vrt-xfeat): device-side keypoint count and pixel keypoints - #31

Merged
edgarriba merged 1 commit into
mainfrom
feat/xfeat-device-count
Oct 8, 2026
Merged

edgarriba merged 1 commit into
mainfrom
feat/xfeat-device-count

Conversation

@edgarriba

@edgarriba edgarriba commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

Follow-up to #30. Lets a GPU consumer on the same stream use XFeat results without a host sync. The target is kornia-3d's stereo matcher (kornia/kornia-rs#1157), so extraction → stereo matching runs with no sync in between.

API (XFeatResult)

  • count_device() returns the keypoint count on the device, clamped to capacity, valid in stream order after submit/submit_pair. It plugs straight into kornia-3d: KeypointCount::Device { count: res.count_device(), capacity: res.capacity() }. count() stays the host copy and is valid only after a sync.
  • kpts_px() returns device keypoints in original-image pixels. The existing kpts are in model space (floor-32). That space is off by up to 2% when a side is not a multiple of 32: at EuRoC's 752 px width the model is 736 px, so the error reaches 16 px at the right edge, which is wrong for matching against the rectified frames.

How

Both values are written by the existing xfeat_sample_descs launch, so there are no extra launches or copies:

  • block 0 writes the clamped count;
  • lane 0 of each keypoint block writes x*sx, y*sy. That is the same single multiply kpts_to_host does on the host, so the two are bit-identical.

The scale is now stamped on the result before the post-processing launch.

Testing (Orin Nano, JetPack 6, TRT 10.3)

  • New gpu_device_count_and_pixel_kpts: raw atomic count 3 > capacity 2 is clamped to 2, and kpts_px is bit-identical to kpts_to_host. Mutation-checked: writing the unclamped count fails it.
  • gpu_batch2_routes_each_image_to_its_slot: each slot publishes its own device count.
  • gpu_pair on the real model at 1280×720 (720 → 704 rows, y scale ≠ 1): device count == count(), kpts_px bit-identical to the host keypoints, for both single and pair results.
  • xfeat_stereo 640×480: pair p50 5.43 ms vs 6.12 ms for 2× submit (1.13×, unchanged).
  • fmt, both stub clippy runs, stub lib tests: pass. gpu_match_kernel_only_timing fails identically on main (cudarc wants cuEventElapsedTime_v2, which this driver lacks).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • GPU results now provide a device-side keypoint count, clamped to the available result capacity, without requiring a host synchronization.
    • GPU results now expose keypoints in original-image pixel coordinates, alongside the existing model-space coordinates.
  • Bug Fixes
    • Batch results now apply image scaling before keypoint processing, ensuring pixel coordinates reflect each image’s dimensions.

Each XFeatResult now publishes, on the device and in stream order:
- count_device(): the keypoint count clamped to capacity. A consumer on
  the same stream reads it without a host sync, e.g. kornia-3d's stereo
  matcher through KeypointCount::Device, so extraction -> stereo
  matching needs no sync in between. count() remains the host copy.
- kpts_px(): keypoints in original-image pixels. `kpts` is model space
  (floor-32), which is off by up to 2% when a side is not a multiple of
  32 (EuRoC 752 -> 736: 16 px at the right edge), wrong for matching
  against the rectified frames.

Both are written by the existing xfeat_sample_descs launch (block 0
writes the clamped count; lane 0 writes x*sx, y*sy, the same single
multiply kpts_to_host does on the host, so the two are bit-identical).
No extra launches or copies. The scale is now stamped on the result
before the post-processing launch.

Tests: gpu_device_count_and_pixel_kpts (raw count 3 > capacity 2,
clamped; mutation-checked: an unclamped write fails it), per-slot
device count in the batch-2 test, and gpu_pair checks both on the real
model at 1280x720 (720 -> 704 rows, y scale != 1). xfeat_stereo pair
p50 5.43 ms vs 6.12 ms for 2x submit: unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@edgarriba

Copy link
Copy Markdown
Member Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: b2d4d725-f1f1-47bb-9106-fc11361ba615
📥 Commits

Reviewing files that changed from the base of the PR and between 731a36c and 414a759.

📒 Files selected for processing (4)
  • crates/vrt-xfeat/README.md
  • crates/vrt-xfeat/src/model.rs
  • crates/vrt-xfeat/src/postprocess.rs
  • crates/vrt-xfeat/tests/gpu_pair.rs

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

XFeat results now expose a device-side keypoint count and keypoints in original-image pixel coordinates. Batch post-processing sets each result’s scale before it writes these outputs. GPU tests check the device outputs against host results.

Changes

XFeat device outputs

Layer / File(s) Summary
Result buffers and accessors
crates/vrt-xfeat/src/postprocess.rs, crates/vrt-xfeat/README.md
XFeatResult allocates and exposes device buffers for the capacity-clamped count and pixel-coordinate keypoints. The README documents these accessors and distinguishes pixel coordinates from model-space kpts.
Batch post-processing outputs
crates/vrt-xfeat/src/model.rs, crates/vrt-xfeat/src/postprocess.rs, crates/vrt-xfeat/tests/gpu_pair.rs
submit_batch sets each result’s scale before top-K processing. The sampling kernel writes the clamped count and scaled keypoints to device buffers. GPU tests compare these outputs with host results.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant submit_batch
  participant launch_topk_batch
  participant xfeat_sample_descs
  participant XFeatResult
  submit_batch->>XFeatResult: Set result scale
  submit_batch->>launch_topk_batch: Launch top-K post-processing
  launch_topk_batch->>xfeat_sample_descs: Pass count buffers, pixel buffers, and scale
  xfeat_sample_descs->>XFeatResult: Write clamped count and scaled keypoints
Loading

Merge Risk: ⚪ Minimal · up to 414a7

The new device-side outputs appear ready to merge after normal checks; no actionable risk remains in the supplied evidence.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 414a7

The new outputs are capacity-bounded and use the existing GPU processing context. No introduced security attack path was established. Correct use still depends on stream ordering and successful submission; downstream integration and recovery behavior were not available for review.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The demonstrated exposure is the caller's GPU result buffers and subsequent work using their counts and coordinates. The inspected change does not establish a new service, tenant or privilege boundary. Deployment context and downstream inheritance are not supplied, so broader exposure cannot be determined.

Trust Boundaries and Controls

  • observed — Image-derived survivor counts can exceed capacity at the atomic selection stage, but selection bounds its writes and the newly exposed count is clamped before publication. Pixel-coordinate writes use the same bounded keypoint prefix. The count is data, not a cross-stream completion or readiness signal.

Resilience and Maintainability Implications

  • observed — Fresh allocations zero the device count, but reuse resets only processing scratch before launching work. Errors propagate between stages without rollback or result invalidation. Device outputs may therefore retain an earlier result or partial updates after failure. Nontransactional submission predates this PR, and no consumer treating failed submissions as valid was demonstrated.

Hardening Proposals

  • proposed — Make the device-consumer recovery contract explicit: enqueue consumers only after successful submission, do not interpret the device count as a completion flag, and treat outputs as invalid after submission or stream execution errors until recovery and a new successful production cycle.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the main changes: adding device-side keypoint counts and pixel-coordinate keypoints for vrt-xfeat.
Docstring Coverage ✅ Passed Docstring coverage is 91.67% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 3 files. (1 skipped: 1 …
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@edgarriba
edgarriba merged commit 46794a0 into main Oct 8, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant