fix(recording): capture the window itself on Windows, not a crop of the display - #2301
Open
Stephen-Psaradellis wants to merge 1 commit into
Open
Stephen-Psaradellis wants to merge 1 commit into
Stephen-Psaradellis wants to merge 1 commit into
Conversation
…he display Window recording on Windows built its GraphicsCaptureItem from the display and cropped it to the window's bounds, so anything stacked over the window was recorded with it and switching virtual desktops recorded whatever took its place. WindowImpl::try_as_capture_item already existed and was unused; a window target now uses it, and drops the crop that carved the window out of the display. That makes the capture item resizable for the first time, which exposed a second bug: the frame pool is created at the item's size and never recreated, so after a resize the frames carry the new ContentSize while the pool's surfaces are still the old size and every later frame reads back black. The pool is now recreated when the content size changes, and the crop path skips frames whose crop box no longer fits the source. Measured on Windows 11 with a blue window under a red always-on-top window: the display-cropped capture contains 60000 red pixels, the window's own capture item contains none. After resizing 786x593 -> 1086x693 the frames were entirely black before this change and carry the window's contents after it. Both are pinned by tests in crates/scap-direct3d/tests.
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.
Fixes #2151 in
CapSoftware/Cap.The bug. On Windows, a window recording never captures the window.
create_d3d_capturerbuilds itsGraphicsCaptureItemfrom the display and crops it to the window's bounds, so the recording is a rectangle of the screen rather than the window: anything dragged over the window is recorded with it, and switching virtual desktops records whatever now occupies that rectangle.WindowImpl::try_as_capture_item(CreateForWindow) already existed inscap-targetsand had no caller.The change.
ScreenCaptureTarget::Windownow carries its window id through the capture config, and the Windows capturer builds the item from the window. The crop that carved the window out of the display is dropped for window targets — WGC already hands back just that window, and the crop additionally pinned the recording to wherever the window started out.scap-direct3d: the frame pool is created at the item's size and never recreated, so after a resize each frame reports the newContentSizewhile the pool's surfaces are still the old size, and every later frame reads back black. The pool is now recreated when the content size changes. The crop path also skips frames whose crop box no longer fits the source, instead of copying an out-of-range region and vending a stale surface.macOS is untouched: it builds its content filter from the display and crops, and
SCContentFilter's window path is a separate change.How it was tested. On Windows 11 (24H2, Rust 1.88.0), with a blue window under a red always-on-top window covering part of it:
and across a resize from 786x593 to 1086x693 while recording, sampling every frame: before the frame-pool change every frame after the resize was empty (
red: 0, blue: 0at the new size); after it the frames carry the window's contents (blue: 716496).Both are pinned by new tests in
crates/scap-direct3d/tests/window_capture.rs, which drive real windows from a fixture process the waycrates/scap-targets/tests/windows_targets.rsdoes (Window::list()skips the calling process, so the windows have to live in another one).frames_survive_the_capture_item_resizingfails on the unpatched library with exactly the black frame above, and passes with it. Two unit tests inwindows.rspin that a window target drops its crop and a display target keeps it.Also run:
cargo test -p scap-direct3d(7 passed),cargo test -p cap-recording --lib(552 passed, 0 failed, 4 ignored),cargo clippy -p scap-direct3d -p cap-recording --all-featuresclean,cargo fmt --all --checkclean.Disclosure. An AI system running an autonomous software project found the issue, wrote the fix and the tests, and wrote this description; I'm the account holder submitting it and I have reviewed the patch. Take it, change it or drop it as you see fit.
This fix is offered at a fixed $100, the floor I price fixes at (CapSoftware's completed Algora bounties run a $70 median and $550 largest). If you want to pay for it, comment
/bounty $100on the issue before merging and Algora pays it on merge. If you would rather not pay, merge it anyway - the code is yours either way.