Skip to content

GPU: read export YUV frames back asynchronously - #1632

Open
MatejKozlovsky wants to merge 2 commits into
storytold:mainfrom
MatejKozlovsky:async-readback
Open

MatejKozlovsky wants to merge 2 commits into
storytold:mainfrom
MatejKozlovsky:async-readback

Conversation

@MatejKozlovsky

Copy link
Copy Markdown
Contributor

Stacked on #1631, so the diff includes it until that lands.

On top of #1631, also from #898.

Adds Accelerator::comp_frame_yuv420_async: render, submit, return. Planes come later in a per-frame callback from poll_readbacks, so the next frame can record while the previous copy runs. Nothing calls it yet; export still uses the sync path. Switching needs a small reorder buffer, I'd do that separately once the API looks right.

  • done runs exactly once: planes, or None on failure/device loss (uses the context's existing bounded readbacks).
  • Declined or can't start → Err(done), callback untouched, caller can go CPU. (GpuContext::try_map_readback; map_readback wraps it.)
  • Staging ring of 4; buffers go back only after unmap.
  • Max 4 frames in flight, slot reserved before rendering; the 5th call polls (and sleeps briefly if the other frames are still rendering) until one frees.
  • done runs on whichever thread polls, outside locks. Don't block in it or call back into the accelerator.
  • poll_readbacks uses a new opt-in poll_readbacks_busy_ok, where a wait timeout means "busy". Everything else, wait() included, still retires on timeout. (Adds a crate-private Gpu::wait_for(Duration) for that test.)

Tests:

  • frames_in_flight_each_get_their_own_planes
  • in_flight_cap_holds_across_threads (8 threads × 6 frames, never >4 pending)
  • a_device_failure_fails_every_pending_readback_once
  • cancelled_readbacks_return_their_buffers_only_once_unmapped
  • wait_past_its_deadline_retires_but_the_yuv_poll_does_not
  • declined_async_frames_hand_the_callback_back_unused
  • async_yuv_defaults_to_the_synchronous_conversion (render crate)

Same test setup as #1631.

Accelerator::comp_frame_yuv420 (default None) renders a top-level comp frame and
converts it over the background to the H.264 export's 8-bit BT.709 limited-range
4:2:0, every byte within one level of the CPU conversion (the GPU kernel follows
it operation for operation, including NaN and half-away-from-zero ties, but a
driver may contract its arithmetic). A 4K frame then reads back 12 MB of planes
instead of 133 MB of RGBA f32.

The H.264 export asks the accelerator for planes when a frame needs nothing after
the render (progressive, project depth, no crop/resize, rendered at the output
size) and otherwise renders and converts on the CPU as before. Deferred
readbacks (a browser worker) keep the pass-based path. Sizes the kernel can't
take (width not a multiple of 8, odd height) are declined before any GPU work.

rgba_to_yuv420 moves to effectcraft_raster::yuv so the export and the GPU tests
share one reference. The GPU tests are #[ignore]d and fail without an adapter
instead of passing untested.
Accelerator::comp_frame_yuv420_async submits the frame and hands the planes to a
per-frame callback from a later poll_readbacks, so the caller can record the next
frame while the copy runs. The callback runs exactly once (planes, or None when
the readback fails or the device is lost) through the context's bounded
readbacks. A frame that is declined, or whose readback can't start, returns the
callback unused. The default implementation converts synchronously.

- GpuContext::try_map_readback hands done back when the request can't start and
  reports when the buffer is unmapped; staging buffers return to the ring only
  then, so a cancelled readback never leaves a pending-mapped buffer for reuse.
- At most 4 asynchronous frames are in flight: a slot is reserved atomically
  before rendering, and a further call polls until one frees up.
- Gpu::poll_readbacks polls through the context's bridge with an opt-in that
  reads a timed-out wait as a busy queue (poll_readbacks_busy_ok). Every other
  caller, Gpu::wait included, still retires the context on a timeout.

This branch has not been deployed

No deployments
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