Skip to content

fix: prevent viewport export clipping at high resolutions#1311

Merged
MrNeRF merged 1 commit into
MrNeRF:masterfrom
jacobvanbeets:fix/viewport-export-capacity-clamp
Jun 15, 2026
Merged

fix: prevent viewport export clipping at high resolutions#1311
MrNeRF merged 1 commit into
MrNeRF:masterfrom
jacobvanbeets:fix/viewport-export-capacity-clamp

Conversation

@jacobvanbeets

Copy link
Copy Markdown
Collaborator

Summary

Fixes the viewport export clipping the splat model along a curved edge at higher resolutions (worse the higher you go), which only happens on the first export from a new viewpoint — re-exporting from the same view produces a correct image.

Root cause

The viewport export renders once and reads the image back immediately. The VkSplat renderer sizes its per-frame scratch (visible-primitive and tile-instance capacity) from deferred, one-frame-late high-water marks. The first render at a new viewpoint/resolution can exceed those marks and render capacity-clamped — the depth/tile-ordered tail of primitives/tile-instances is dropped — which is the curved clip that grows with resolution (tile-instance count scales with resolution). The interactive viewport hides this because it self-heals on the next frame, but a single-shot export saves the clamped frame. Re-exporting from the same view works because the deferred readback has since grown the marks.

Fix

Drive that self-heal synchronously for one-shot captures instead of capturing the first frame:

  • Add VksplatViewportRenderer::previewCaptureSettled(), set from the start-of-frame deferred poll. It reports that the previously rendered frame produced complete, unclamped content using the steady-state macro chain (rejecting the legacy macro warm-up frame and partial-stats frames).
  • In RenderingManager::renderPreviewImageToPreviewSlotWithState, re-render the Preview slot until previewCaptureSettled() is true (bounded by kMaxPreviewSettlePasses = 8), then read back. A pass >= 1 guard ties the signal to the current view, which keeps the tiled (very-high-res) path correct since each tile is a different sub-view.
  • Settling is scoped to the export capture overloads (renderPreviewImageRgb8/Rgba8 + tiled path). Film-strip thumbnails (FloatRgb), asset previews (model overloads), and depth captures keep their single-render behavior, so there is no perf regression there.

Typical convergence is 2–4 passes; the cap only guards a pathological non-converging case.

Files changed

  • src/visualizer/rendering/vksplat_viewport_renderer.hpp / .cpp
  • src/visualizer/rendering/rendering_manager.hpp
  • src/visualizer/rendering/rendering_manager_viewport.cpp

Testing

  • Load a large model and export at 4K / 8K / 16K / 32K from a freshly moved viewpoint; the first export is now complete (no curved clip / black regions) and matches a re-export from the same view.
  • vksplat.render.visible_clamped / vksplat.render.macro_instances_clamped no longer fire for the frame that is saved.
  • Transparent PNG export and the tiled (>~264M px, e.g. 32K) path verified.

Closes #1304

The viewport export renders once and reads back immediately. The VkSplat renderer sizes its per-frame visible-primitive and tile-instance scratch from deferred, one-frame-late high-water marks, so the first render at a new viewpoint/resolution can exceed them and render capacity-clamped (the depth/tile-ordered tail is dropped). That produces the curved clip that worsens with resolution. The live viewport hides this because it self-heals on the next frame, but a one-shot export saves the clamped frame; re-exporting from the same view works because the marks have since grown.

Drive that self-heal synchronously for one-shot captures: re-render the Preview slot until the renderer confirms the previous pass produced complete, unclamped content (bounded by a max pass count), then read back. A new VksplatViewportRenderer::previewCaptureSettled() signal reports this from the deferred poll, rejecting the macro warm-up frame and requiring the steady-state chain; a pass>=1 guard keeps the signal tied to the current view so the tiled path stays correct. Settling is scoped to the export capture overloads, leaving film-strip thumbnails, asset previews, and depth captures on single-render behavior.

Closes MrNeRF#1304
Co-Authored-By: Oz <oz-agent@warp.dev>
@MrNeRF MrNeRF merged commit 6d591a3 into MrNeRF:master Jun 15, 2026
5 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.

Viewport export clips gaussian splat model in viewer at higher resolutions

2 participants