feat(osr): smooth, never-stuck tile resize via begin-frame pump + GPU blit#8
Merged
Merged
Conversation
… blit Reworks the OSR present/resize path so webview tiles resize like a native browser instead of freezing or stuttering. - Own the frame clock: window_info.external_begin_frame_enabled + a per-slot SendExternalBeginFrame pump. CEF's internal damage-driven scheduler could skip the post-resize frame (a resize is viewport-only damage on an idle page), leaving the tile stuck at the old size until a scroll forced a tick. Driving begin-frames ourselves makes every resize produce a frame. The pump idles to a slow poll while the tile is hidden and dies when it's disposed. - Resize flow-control + watchdog (CefWebSession): keep one resize in flight, send the next when its present promotes, so the reflow paces to cef_host's actual rate instead of racing ahead and stranding presents on superseded surface ids (which froze the texture mid-drag). A generation-tagged watchdog re-kicks a wedged step and, worst case, force-shows the already-painted surface -- no permanent stuck on a static page's single post-resize frame. - Pending-surface promotion: serve the old surface to Flutter until cef_host paints the new one, so a resize no longer flashes blank. - GPU Metal blit present: copy CEF's accelerated IOSurface into the host surface with a Metal blit instead of a CPU IOSurfaceLock+memcpy. Keeps frame data on the GPU end-to-end (no GPU->CPU readback) -- a real win on discrete-GPU / Windows / Linux, ~neutral on unified-memory Apple Silicon, and the contract-mandated cross-platform shape (see the PORTING note on CompositeMetalLocked). Popups and the software-OSR OnPaint fallback stay on the CPU path; falls back to CPU if Metal is unavailable. - Dart resizes on every layout change now that the native side paces. True zero-copy (handing CEF's surface straight to Flutter) is impossible: CEF's pool contract reclaims the accelerated surface at callback return, so a copy into a client-owned texture inside the callback is mandated on every platform. This makes that copy a GPU blit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
What
Makes OSR webview tiles resize like a native browser instead of freezing or stuttering. Three problems, fixed in the present/resize path:
external_begin_frame_enabled+ a per-slotSendExternalBeginFramepump (idles while hidden, dies on dispose).Plus: the per-frame present copy is now a GPU Metal blit instead of a CPU
IOSurfaceLock+memcpy — keeps frame data on-GPU end-to-end (no readback). Popups and the software-OSR fallback stay CPU.Why not true zero-copy
CEF's pool contract (
cef_render_handler.h) reclaims the accelerated surface at callback return, so handing it straight to Flutter is impossible — a copy into a client-owned texture inside the callback is mandated on every platform. So we make that copy a GPU blit, which is also the contract-mandated cross-platform shape (Windows D3D11 shared-handle, Linux dmabuf).Test
Verified in the example app on M4 Pro: loads, resizes smoothly with no stuck/flash on flutter.dev (static) and YouTube (video),
<select>dropdown still correct (CPU path), GPU-blit path confirmed active via the host log.🤖 Generated with Claude Code