Skip to content

perf(recording): optimize the Windows D3D11 frame-conversion path - #2999

Open
Addin wants to merge 16 commits into
CapSoftware:mainfrom
Addin:perf/d3d11-converter-frame-path
Open

Addin wants to merge 16 commits into
CapSoftware:mainfrom
Addin:perf/d3d11-converter-frame-path

Conversation

@Addin

@Addin Addin commented Oct 23, 2026 •

Copy link
Copy Markdown

Summary

Optimizes the Windows D3D11 frame-conversion hot path and adds the instrumentation used to verify it.

  • Reuse cached video-processor input/output views and staging textures instead of recreating them per frame.
  • Share one D3D11 converter/device across pool workers on Windows, with a staging ring sized for four workers.
  • Poll Map with D3D11_MAP_FLAG_DO_NOT_WAIT and release the shared context between retries so other workers can submit GPU work.
  • Keep one pair of staging textures for standalone converters and grow lazily up to the four-pair cap.
  • Add benchmark controls (BENCH_INPUT, BENCH_WORKERS, BENCH_SINGLE_ONLY, BENCH_POOL_ONLY, BENCH_D3D11_ONLY) and report the selected adapter.
  • Request D3D11_CREATE_DEVICE_VIDEO_SUPPORT on capture devices.
  • Instrument the Media Foundation encoder loop and the Windows M4S readback/encode stages, and report readback misses against the frames that actually attempted a readback.

Performance comparison

GTX 950M / i7-6700HQ, YUYV422 -> NV12, release build. Before is 8a71a02c; after is e8333db6, the last converter commit before the readback Flush correctness fix. Medians of three four-worker runs and two single-thread runs.

Workload Before After Improvement Speedup
Single-thread, 720p 2.23 ms 2.13 ms 4.2% 1.04x
Single-thread, 1080p 4.29 ms 3.88 ms 9.6% 1.11x
4-worker shared converter, 720p 2.30 ms 1.11 ms 51.8% 2.07x
4-worker shared converter, 1080p 4.48 ms 2.05 ms 54.3% 2.19x
4-worker from_config pool, 720p 1.07 ms 1.09 ms ~2% (noise) 0.98x
4-worker from_config pool, 1080p 1.99 ms 2.04 ms ~2% (noise) 0.98x

from_config is the production camera pool. At maximum submitted throughput it is within run-to-run noise of the old four-device configuration while using one D3D11 device instead of four; at the 30 fps camera rate the pool does not contend, so the single-thread latency and contention rows are the relevant gains. Without the readback polling changes, sharing the converter would have been about 2x slower.

Additional after-only measurements:

  • Real camera 640x480 @ 30 fps: conversion 1.65 ms avg / 2.08 ms P99, encode 0.84 ms, 29.6 fps, zero drops
  • Screen readback > 5 ms: 1/251 frames; encoder > 5 ms: 2.4%; no drops
  • DO_NOT_WAIT polling at maximum throughput uses 148% of one core (18.5% of the 8-thread machine); at the 30 fps production rate the workers idle between frames, so no backoff was added

The MF hardware encoder still falls back on this GPU: NVIDIA MFT returns 0x8000FFFF and Intel QSV returns E_INVALIDARG. The FFmpeg fallback remains.

Greptile review fixes

  • frame-converter: flush the immediate context after queuing the output copy and before the DO_NOT_WAIT polling loop, so standalone conversions cannot spin on work that was never submitted.
  • capture_pipeline: build the WARP fallback without D3D11_CREATE_DEVICE_VIDEO_SUPPORT.
  • win_fragmented_m4s: after draining, return the Media Foundation encoder error so a failed encoder cannot be finalized as a successful recording.
  • scap-ffmpeg (base perf(recording): cut Windows readback copies and encode from textures #2940): create the mapped readback AVBufferRef with AV_BUFFER_FLAG_READONLY.

Dependency

Stacked on #2940 (readback/MF groundwork). Until that merges, this diff includes its readback/MF commits. If #2940 lands with a merge commit, GitHub will reduce this PR to the converter/capture commits automatically; if it is squash-merged, this branch will need a rebase onto the new main.

Validation

  • cargo fmt --all -- --check
  • cargo clippy -p cap-frame-converter -p cap-enc-mediafoundation -p cap-recording --all-targets -- -D warnings
  • cargo test -p cap-recording --lib: 686 passed, 0 failed, 4 ignored
  • cap-frame-converter and cap-enc-mediafoundation tests pass
  • Release d3d11-benchmark rerun after the lazy-staging change; numbers above are unchanged from the eager-allocation baseline

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge; all previous findings are addressed.

Summary

Optimizes Windows frame conversion by sharing a D3D11 converter across workers and reusing views and staging textures.

  • Windows converter pools share D3D11 resources across workers.
  • D3D11 readback reuses mapped staging memory for FFmpeg frames.
  • Windows screen recordings can encode captured textures with Media Foundation.
  • Windows recording logs show time spent in capture and encoding stages.
  • The older Windows segmented muxers are removed.

Reviews (3) · Last reviewed commit: "fix(recording): report MF encoder failur..." · Reviewed by Greptile

Addin added 3 commits October 22, 2026 19:15
Read Windows capture back through a staging ring with DO_NOT_WAIT instead of a blocking Map plus a second full-frame copy, and let the fragmented/instant M4S muxer encode from the captured D3D11 texture through Media Foundation with the existing FFmpeg encoder as fallback.

Also removes the unused win_segmented muxers and the never-invoked DiskSpaceCallback plumbing.
- Keep the readback budget authoritative by default; CAP_READBACK_BLOCKING opts into an unbounded Map.
- Negotiate MFT types inside the candidate loop and handle WARP without VIDEO_SUPPORT.
- Validate harness --path values and count failures only for exercised paths.
- Evict idle stale-size staging textures before reporting StagingBusy.
- Size the benchmark copy from each mapped frame and rebuild old_frame on resize.
- Require METransformDrainComplete and return errors on failed or timed-out drains.
- Count drained samples in the encoder health output counters.
- Keep benchmark runs alive across capture idle timeouts.
@vercel

vercel Bot commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

@Addin is attempting to deploy a commit to the Cap Software Inc Team on Vercel.

A member of the Team first needs to authorize it.

Comment thread crates/frame-converter/src/d3d11.rs
Comment thread crates/recording/src/capture_pipeline.rs
Comment thread crates/scap-ffmpeg/src/direct3d.rs Outdated
Addin added 12 commits October 23, 2026 18:17
Cache the video processor input/output views at converter creation, replace the single staging pair with a reusable ring, and keep CPU upload/readback copies outside the GPU mutex. Share one D3D11 converter across pool workers so camera recording uses one device instead of one per worker.

Collapse contiguous plane copies into a single memcpy and add a benchmark example that compares the D3D11 path with single-threaded swscale.
Allow selecting the input format, worker counts, and single-thread, pool-only, or D3D11-only runs so the benchmark can isolate transfer, conversion, and concurrency costs.
Map the staging output with DO_NOT_WAIT and release the shared GPU mutex between retries so other camera workers can submit their blits and copies while one readback completes. The 4-worker 1080p pool drops from about 3.6 ms/frame to 2.2 ms/frame on the GTX 950M.
Four staging slots match the default camera pool so workers spend less time waiting for an upload/readback pair; deeper rings showed no additional gain.
Media Foundation hardware encoders need a video-capable D3D11 device to attach their DXGI device manager. Without it the texture-native screen encoder falls back to FFmpeg and the full-frame readback path. Keep the display-notch tests below the platform helpers so clippy accepts the module layout.
Time the Media Foundation encoder wait/input/process/output stages, the native camera upload/mux stages, and the screen D3D11 readback itself instead of including pacer waits in the conversion metric. Also make the camera benchmark run on Windows and honor an externally supplied RUST_LOG.
@Addin
Addin force-pushed the perf/d3d11-converter-frame-path branch from ff0a562 to 29ea30e Compare October 23, 2026 16:18
@Addin

Addin commented Oct 23, 2026

Copy link
Copy Markdown
Author

@greptileai review

Comment thread crates/recording/src/output_pipeline/win_fragmented_m4s.rs Outdated
@Addin

Addin commented Oct 23, 2026

Copy link
Copy Markdown
Author

@greptileai review

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