From d4d5197b92270f43b932ebdeff9ed8bdf635bfa2 Mon Sep 17 00:00:00 2001 From: wenkaifan0720 Date: Tue, 29 Sep 2026 15:04:39 -0700 Subject: [PATCH] cef_host: say why the resize capture refresh is needed, accurately The #61 comment said the capturer drops refresh requests while it sees the content as animating. A trace with gpu.capture on shows the actual sequence: the renderer's first new-size frame is captured at the old size, CEF then sets the new capture size, the capture that asks for is rate-limited, and the capturer's own retries (every ~33 ms) are all turned away by the stricter check for non-compositor refreshes (nothing pending, no recent animation) until the stall kick. Invalidate's refresh demand is treated like a compositor update and captures at once. Comment and changelog only. Co-Authored-By: Claude Opus 5.5 (1M context) --- packages/flutter_cef_macos/CHANGELOG.md | 7 ++++--- .../native/cef_host/browser_ops.mm | 17 +++++++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/flutter_cef_macos/CHANGELOG.md b/packages/flutter_cef_macos/CHANGELOG.md index 14f40de..e67d222 100644 --- a/packages/flutter_cef_macos/CHANGELOG.md +++ b/packages/flutter_cef_macos/CHANGELOG.md @@ -2,9 +2,10 @@ * Fix: on macOS, a resize could wait a second or more for its first paint at the new size (up to about 4 s), most often mid-way through an animated device - switch. CEF paints by capturing the view, and after a big resize nothing asked - it to capture again once the renderer's new-size frame was in. `cef_host` now - asks for a fresh capture every 100 ms while a resize waits for its paint. + switch. CEF paints by capturing the view: the renderer's first frame at the + new size was captured at the old size, and the capturer's own retries were + turned away until the stall fallback kicked in. `cef_host` now asks for a + fresh capture every 100 ms while a resize waits for its paint. `device_frame` no longer needs the stall fallback (it did in 1 to 12 of 30 switches). The prebuilt must be republished. * Fix: a view could stop painting for good after a resize that changed the diff --git a/packages/flutter_cef_macos/native/cef_host/browser_ops.mm b/packages/flutter_cef_macos/native/cef_host/browser_ops.mm index 73cef18..add1a15 100644 --- a/packages/flutter_cef_macos/native/cef_host/browser_ops.mm +++ b/packages/flutter_cef_macos/native/cef_host/browser_ops.mm @@ -398,12 +398,17 @@ void DoSetVisible(const std::shared_ptr& slot, bool visible) { // in flight. Changes the plugin asks for meanwhile collapse into the latest one. // // The paint that ends a resize needs a capture after the resize lands. On macOS -// CEF paints by capturing the root frame at the size of the renderer's last -// activated frame. When the renderer's frame at the new size activates, the -// capture size changes, but a page with nothing else changing gives the -// capturer no damage, and it drops refresh requests while it still sees the -// content as animating. The one Invalidate at the start of a resize comes before -// all of that, so ask again every kResizeRefreshMs until the paint arrives. +// CEF paints by capturing the root frame (viz's FrameSinkVideoCapturer) at the +// size of the renderer's last activated frame. When the renderer's frame at the +// new size activates, it is captured at the OLD size, and only then does CEF set +// the new capture size. The capture that change asks for is rate-limited (one +// was just taken), and the capturer's own retries go through a stricter check +// (nothing pending, no recent animation) that can turn them all away. A page +// with nothing else changing gives it no damage either, so no paint at the new +// size came until the stall kick. Invalidate asks for a capture the way a +// compositor update does, which isn't subject to that check. The one at the +// start of a resize comes too early, so ask again every kResizeRefreshMs until +// the paint arrives. namespace { constexpr int kResizeRefreshMs = 100; constexpr int kResizePaintWaitMs = 1000;