Repository navigation
osr: Fix external BeginFrame timing, optional tick age on SendExternalBeginFrame (see #4311) - #4312
Open
super-ienien wants to merge 2 commits into
Open
super-ienien wants to merge 2 commits into
super-ienien wants to merge 2 commits into
Conversation
…mbedded#4311) SendExternalBeginFrame() handed viz a BeginFrame dated at the time of the call, with a null deadline and an interval fixed to 60 Hz, and dropped a tick that arrived while the previous one was pending. A <video> that is not at the cadence of a 60 Hz tick stutters, and a page and a <video> that both animate are drawn one tick apart. - SendExternalBeginFrame() keeps its signature. The interval follows CefBrowserSettings.windowless_frame_rate and the deadline is frame_time + interval, as with Chromium's own BeginFrame sources. - New C++ overload SendExternalBeginFrame(int64_t tick_age_us), experimental (C API send_external_begin_frame2): the age of the external clock tick at the time of the call. The frame is dated on the tick instead of on the wake-up time of the thread that observed it; the interval is derived from the successive tick times (median of the last five plausible deltas, windowless_frame_rate for the first tick) and the deadline is one interval after the tick. A variable refresh rate is followed within a few ticks. 0 behaves like the no-argument form. - The external BeginFrame path of CefRenderWidgetHostViewOSR follows ui::ExternalBeginFrameAdapter: sequence numbers from BeginFrameArgsGenerator, wait_for_all_frame_sinks = false, vsync parameters following the interval, and a tick arriving while another is in flight kept one deep and issued at the acknowledgement if its interval is not half over. - The minimum capture period of the video consumer is 95 % of the tick interval, so that the capturer does not refuse a tick-paced frame when the tick dates drift.
super-ienien
force-pushed
the
ebf-timing-v4
branch
from
October 15, 2026 08:13
b0cb3f5 to
6e55867
Compare
The gate that keeps one IssueExternalBeginFrame in flight reopened only on the acknowledgement or when the controller client was recreated. A reply destroyed without running, as on GPU process loss, kept it closed until then, with the output frozen. Resetting the gate when the client is recreated was also wrong: ui::Compositor keeps a tick received before its frame sink exists and issues it right after creating the client, so a second BeginFrame could overlap it, which viz reports as a bad message. - Wrap the reply with mojo::WrapCallbackWithDropHandler, so that the gate is closed exactly while the reply is alive. - Leave the gate, the queued tick and the sequence numbers untouched when the controller client is recreated. - Log a warning when a tick stays unacknowledged for more than a second. Assisted-by: Claude Code
This branch has not been deployed
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 the BeginFrameArgs that
SendExternalBeginFrame()hands to viz (time of the call, null deadline, fixed 60 Hz interval) and the dropping of a tick that arrives while the previous one is pending. Analysis and measurements in #4311.What this does
SendExternalBeginFrame()keeps its signature. The interval now followswindowless_frame_rateand the deadline isframe_time + interval, as with Chromium's own BeginFrame sources.SendExternalBeginFrame(int64_t tick_age_us)(C APIsend_external_begin_frame2), experimental: the age of the external clock tick at the time of the call. CEF dates the frame on the tick instead of on the wake-up time of the thread that observed it, derives the interval from the successive tick times (median of the last five plausible deltas;windowless_frame_ratefor the first tick) and sets the deadline one interval after the tick. A variable refresh rate is followed within a few ticks. 0 behaves like the no-argument form.CefRenderWidgetHostViewOSRmoves to a smallCefExternalBeginFrameSourceOSR, modeled onui::ExternalBeginFrameAdapter(Wayland): sequence numbers fromBeginFrameArgsGenerator,wait_for_all_frame_sinks = false, vsync parameters following the interval.ui::Compositorkept pending until its frame sink existed and then replaced). It is not reset when the controller client is recreated, since the compositor issues its pending tick right after that. A warning is logged if a tick stays unacknowledged for more than a second.Measured (details in #4311): DeckLink 1080i50 output under CPU load, 8 interleaved passes per arm, same binary. Plain call vs tick age from the card's clock: 29 → 7 animation hiccups, 16 → 0 missing SDI fields, 35 → 8 repeated fields. Idle, the two are indistinguishable.
Testing
Built and run on branch 8078 (156.0.7+chromium-156.0.8078.17), Windows 11, D3D12 shared textures, windowless browser clocked by the monitor vsync and by SDI cards (Blackmagic DeckLink, Deltacast). Cold starts (20) clean. On a GPU process loss the BeginFrames now resume with the restart of the GPU process (about 0.2 s) instead of staying blocked until the controller client is recreated (70 s observed with the reset); recovering the content after a GPU process crash (frames referencing shared images of the dead process for a while) is a separate matter, not addressed here. Not yet compiled against current
master(same Chromium compatibility; the patch applies cleanly). The C API wrappers are generated at build time and are not part of this change.