reuse staging buffer when read_pixel gets perf: 18x faster read_pixels #397
Open
haixuanTao wants to merge 1 commit into
Open
reuse staging buffer when read_pixel gets perf: 18x faster read_pixels #397haixuanTao wants to merge 1 commit into
haixuanTao wants to merge 1 commit into
Conversation
…ing buffer read_pixels indexed the mapped staging buffer byte-by-byte during BGRA->RGB conversion. Mapped readback memory is uncached (write-combined), so scalar reads run at ~10 MB/s — 99 ms for a 640x480 frame on an RTX 5080, dwarfing the 4 ms render. memcpy each row into a cached local buffer first and convert from there: 5.4 ms (18x). Also: reuse the staging buffer across calls (grown on demand) instead of allocating per call, and wait on the copy's submission index instead of polling the device indefinitely. Per-frame capture loop (sim + render + snap, 640x480): 113 ms -> 12 ms. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
haixuanTao
added a commit
to haixuanTao/kiss3d
that referenced
this pull request
Jul 8, 2026
…back, fast read_pixels Off-screen video capture improvements, in three parts: - read_pixels 18x faster: convert BGRA->RGB from cached memory (mapped readback memory is uncached, ~10 MB/s scalar reads), reuse the staging buffer across calls, and wait on the copy's submission index instead of polling the whole device. - Window::new_headless_with_setup(width, height, setup): a full-featured Window backed by no OS window and no swapchain (same headless canvas as OffscreenSurface, but exposing the whole Window API — custom renderers, ray tracer, snap* readbacks). Never throttled by display vsync; works without a display server. - snap_begin()/snap_finish(): pipelined readback that enqueues the texture->buffer copy and map without waiting, so frame N's copy is collected after frame N+1 renders instead of stalling the GPU pipeline each frame. read_pixels() is refactored onto the same begin/finish path with unchanged behavior. Motivation: a windowed capture loop (render_frame + blocking snap) is vsync-locked — the blocking readback defeats swapchain frame pipelining, so every iteration eats ~2 vblanks (~30 fps at 60 Hz) regardless of GPU load. Headless + pipelined readback took a downstream nexus3d capture loop from 33 to 92 fps at 640x480 (2.7 fps before the read_pixels fix). Supersedes dimforge#397 (included here).
haixuanTao
added a commit
to haixuanTao/kiss3d
that referenced
this pull request
Jul 8, 2026
Two additions for off-screen video capture: - Window::new_headless_with_setup(width, height, setup): a full-featured Window backed by no OS window and no swapchain (same headless canvas as OffscreenSurface, but exposing the whole Window API — custom renderers, ray tracer, snap* readbacks). Rendering is never throttled by the display's vsync and works without a display server. - snap_begin()/snap_finish() (begin_read_pixels/finish_read_pixels on the canvas): pipelined readback that enqueues the texture->buffer copy and buffer map without waiting, so the copy of frame N can be collected after frame N+1 renders instead of stalling the GPU pipeline every frame the way the blocking snap()/read_pixels() does. read_pixels() is refactored to begin+finish with unchanged behavior. Motivation: a windowed capture loop (render_frame + blocking snap) is vsync-locked — the blocking readback defeats swapchain frame pipelining, so every iteration eats ~2 vblanks (~30 fps at 60 Hz) regardless of GPU load. Headless + pipelined readback took a downstream nexus3d capture loop from 33 to 92 fps at 640x480. Builds on feat/persistent-readback-staging (dimforge#397).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
read_pixels indexed the mapped staging buffer byte-by-byte during BGRA->RGB conversion. Mapped readback memory is uncached (write-combined), so scalar reads run at ~10 MB/s — 99 ms for a 640x480 frame on an RTX 5080, dwarfing the 4 ms render. memcpy each row into a cached local buffer first and convert from there: 5.4 ms (18x).
Also: reuse the staging buffer across calls (grown on demand) instead of allocating per call, and wait on the copy's submission index instead of polling the device indefinitely.
Per-frame capture loop (sim + render + snap, 640x480): 113 ms -> 12 ms.