Repository navigation
Description
This issue tracks Linux desktop frame pacing, display synchronization, presentation cadence, and high-refresh-rate support.
The target is smooth normal-mode Flutter animation, including concurrent animations, without relying on reduced-motion settings or disabling animations.
Last reviewed: 2026-11-08
Current status
Linux rendering and presentation infrastructure has improved substantially since this issue was opened:
- Add FlViewRendererSubsurface #191389 introduced the Wayland child-subsurface renderer.
- [Linux] Wait for frames to be rendered before using them in another context #192094 and [Linux] Use fences to synchronize frames between OpenGL contexts #192098 improved cross-context synchronization, including GPU fences where supported.
- [Linux] Keep the frame at the top of a resizing subsurface #192408, [Linux] Clear the frame when there is nothing to render #192461, Fix black tooltips and popups on Wayland #192910, [Linux] Request a frame when a view starts rendering #192911, and [Linux] Handle FlView being destroyed before rendering is complete. #193268 fixed several resize, first-frame, parent-surface, and lifecycle problems.
- Fix use-after-free of FlTaskRunner timeout on shutdown #194525 fixed the
FlTaskRunnershutdown race tracked by [Linux] Use-after-free of FlTaskRunner on app shutdown: timeout source outlives the task runner #193742. - [Linux] Desynchronize the Wayland subsurface so frames don't redraw the GTK window #195287 now targets the remaining per-frame GTK redraw in the Wayland subsurface path and has received positive maintainer review.
The main frame-scheduling problem is still unresolved.
On the checked master revision:
- Linux does not install
FlutterProjectArgs.vsync_callback. - The engine therefore still falls back to a fixed 60 Hz
VsyncWaiterFallback. - Display refresh-rate metadata does not drive or phase-lock frame scheduling.
- The merged Wayland subsurface path still schedules GTK work after each presented frame.
- There is no compositor presentation-timestamp feedback integrated into this path.
- Mixed-refresh, multi-view, and VRR behavior remain undefined.
So there is still no merged end-to-end Linux frame-pacing solution.
The most significant recent progress is #195287: the proposed Wayland presentation change has now been tested by a Linux maintainer and appears close to landing, but it does not address the missing VSync source.
Checked master:
acade0f6b53278737d9af89df87afeffe040c3eb
Relevant source:
Wayland update: #195287
#195287 changes the Wayland child subsurface from synchronized to desynchronized operation during normal rendering.
Today, each Flutter frame is committed to the child surface and followed by GTK-thread work which queues a redraw of the parent window. Because a synchronized wl_subsurface only makes pending state visible when the parent commits, that GTK repaint effectively becomes part of frame presentation.
#195287 removes that dependency during steady-state rendering:
- the child surface is desynchronized by default;
- raster-thread commits can become visible without repainting the GTK parent;
- resize temporarily re-synchronizes parent and child so geometry changes remain atomic;
- after GTK commits the resized parent, the child becomes desynchronized again.
This directly addresses one of the main presentation-path inefficiencies without changing the engine's VSync source.
Reported performance
The PR author measured a continuously animating CircularProgressIndicator in a 2560×1400 release-mode window on GNOME 50.1, Wayland, Mesa 26.0.8, radeonsi:
| Configuration | App CPU | gnome-shell CPU |
|---|---|---|
master (66b3ad9) |
16.4% | 13.4% |
| #195287 | 3.2% | 2.8% |
| Flutter 3.47 stable | 33.6% | 5.9% |
GNOME Shell idle baseline was reported as 1.9%.
An empty continuously animating Flame application reportedly dropped from 15.6% to 2.3% application CPU.
A perf profile attributed much of the previous cost to pixman_fill and cairo_fill under gdk_window_begin_draw_frame, consistent with redundant parent-window repainting.
These results show that the current Wayland path can do substantial unnecessary GTK work during animation. They do not show that Flutter is synchronized to the physical display: #195287 does not change the fixed-60 Hz fallback or add compositor presentation timing.
Maintainer review
On November 6, Linux maintainer Robert Ancell tested the PR and reported that it worked in his environment. The remaining review comments are primarily API and style cleanup:
- keep
FlSubsurfaceas a lightweight wrapper; - expose explicit sync/desync operations rather than a boolean setter;
- simplify where the initial synchronization state is selected;
- simplify the
after-paintsignal connection.
The maintainer indicated that after these changes the PR should be ready to land.
#194211, which tracks the full-window GTK repaint and pixman_fill cost caused by the current subsurface path, has also been explicitly pointed to #195287 by the maintainer as the expected fix.
As of this review, #195287 is still open and not merged. Its current head still predates the November 6 review changes, so the reviewed cleanup has not yet appeared on the PR branch.
The X11 renderer is unchanged.
Remaining gaps
| Area | Current state |
|---|---|
| VSync | Linux still falls back to a fixed 60 Hz scheduling source |
| High refresh | 120/144/165 Hz display modes do not directly drive frame scheduling |
| Wayland presentation | #195287 is close to removing redundant per-frame GTK redraws, but has not landed yet |
| Wayland scheduling | Compositor frame opportunities still do not drive Flutter frame production |
| Presentation feedback | Child commits are not physical presentation timestamps |
| Mixed refresh / multi-view | No clear per-view/per-output scheduling model |
| VRR | A single nominal frame interval is insufficient for adaptive presentation |
| X11 | Uses a different GTK/GLX/EGL path and needs separate investigation |
| Compatibility | Wayland behavior still varies across compositor, driver, scaling, and GPU combinations |
Frame production and presentation should continue to be treated separately. A frame can be built, rasterized, submitted, and committed without being displayed at the intended time.
Likewise:
- Flutter frame count is not display frame count.
- GTK paint count is not presentation count.
- Wayland commit count is not presentation count.
wl_surface.framecallback count is not precise presentation timing.
Related work
- #194211 — tracks the per-frame GTK repaint /
pixman_fillcost in the current Wayland subsurface path; maintainer expects [Linux] Desynchronize the Wayland subsurface so frames don't redraw the GTK window #195287 to fix it. - #194525 — merged October 21; fixes the
FlTaskRunnertimeout use-after-free tracked by [Linux] Use-after-free of FlTaskRunner on app shutdown: timeout source outlives the task runner #193742. - #194698 — tracks GTK3 refactors preparing the Linux embedder for GTK4.
- #193943, #194913, #194921, and #195324 — GTK4 preparation/refactoring work.
- #193405 — larger experimental GTK4 embedder; currently an open draft.
- #193530 and #193533 — multi-window GL/EGL synchronization and renderer-lifetime work.
- #193126 — frame-wait cancellation and window teardown handling.
- #189584 — experimental Linux Impeller Vulkan backend.
- #193003 — adds Impeller rendering into Vulkan embedder backing stores; relevant to Vulkan integration, but not a display-pacing solution.
- #196083 — merged November 7; fixes Linux layer positioning when compositing without
glBlitFramebuffer. - #191775 — NVIDIA/Wayland maximize and resize stability.
- #192603 — embedded GLES memory growth and subsurface compatibility.
- #192653 — Linux windowing regression coverage.
#192342 remains closed without merge. Its experiments are still useful, but it is not an active implementation.
#192873 was fixed by the already-merged #193268.
Experimental evidence
Existing experiments in #183703 and #189584 show why simply increasing the engine timer rate is not enough:
- one GTK path produced roughly 90–95 engine frames/s while GTK paint remained around 60/s;
- one NVIDIA/KDE Wayland 144 Hz Vulkan experiment committed the synchronized parent surface at roughly 48/s;
- a separate experiment using the child surface's
wl_surface.framecallback with a desynchronized subsurface produced roughly 144 frames/s and 137–144 child commits/s.
The last result is particularly relevant to the direction taken by #195287, but child commits still do not tell us when buffers were actually shown.
The remaining scheduling problem is therefore not just "run the ticker faster". The frame source should follow the mechanism that actually owns presentation.
There have been no new Linux PRs since the previous review that implement FlutterProjectArgs.vsync_callback, wl_surface.frame-driven scheduling, or an equivalent compositor-driven VSync source.
Compatibility findings
Recent reports also reinforce that presentation changes need testing on real Wayland driver/compositor combinations.
#195822 reports a black client area with the subsurface renderer on:
- GNOME 50.6 / Wayland;
- NVIDIA proprietary driver 615;
- fractional scaling;
- Flutter 3.50 beta.
The same application reportedly renders through X11.
Flutter triage could not reproduce the issue in a Wayland environment using llvmpipe, so it has been routed to team-linux for further investigation.
This does not establish a general subsurface regression, but it is a useful validation case for #195287 and future Wayland presentation work.
#195821 separately reports a long-running stale-frame problem on Flutter 3.47's older shared-framebuffer path with NVIDIA/Wayland. That path has already been replaced by the subsurface renderer on newer builds, so it is primarily relevant as additional evidence that Linux presentation behavior can be highly driver-dependent.
Suggested direction
1. Land and validate independent Wayland child-surface presentation
#195287 should be validated across:
- normal animation;
- resize and maximize/restore;
- fractional scaling;
- minimize/restore;
- output changes;
- secondary views;
- surface replacement;
- shutdown and destruction;
- GNOME and KDE/Plasma;
- Mesa and NVIDIA proprietary drivers.
The #195822 NVIDIA/GNOME case should be included where possible.
Steady-state presentation and resize synchronization should continue to be handled separately.
2. Drive scheduling from presentation opportunities
For a Flutter-controlled Wayland child surface, investigate using an appropriate compositor-facing mechanism such as wl_surface.frame to provide frame opportunities to the embedder instead of relying on the fixed 60 Hz fallback.
GTK-controlled and X11 presentation paths should be evaluated separately.
A faster periodic timer can remain a fallback, but it is not a substitute for compositor-aligned scheduling.
3. Add presentation feedback
wl_surface.frame provides a next-frame opportunity, not an accurate presentation timestamp.
wp_presentation_feedback should be investigated for measuring:
- actual presentation time;
- submission-to-presentation latency;
- discarded or superseded buffers;
- repeated frames;
- presentation interval distribution.
Tracing should distinguish frame request, rasterization, submission, child commit, parent commit, and compositor presentation.
4. Define multi-display behavior
A complete solution also needs to handle:
- windows moving between outputs;
- mixed-refresh displays;
- multiple Flutter views;
- VRR/adaptive sync.
A single process-wide fixed interval is unlikely to be sufficient for all of these cases.
Validation
At minimum, test:
- 60, 120, 144, and 165 Hz;
- fixed refresh and VRR;
- GNOME and KDE/Plasma Wayland;
- X11 separately;
- Mesa and NVIDIA proprietary drivers;
- current OpenGL and relevant Vulkan paths;
- single and mixed-refresh displays;
- fractional scaling;
- resize, maximize, minimize/restore, output changes, view creation/destruction, and shutdown.
Useful measurements include:
- delivered frame opportunities;
- produced and rasterized frames;
- submitted buffers;
- child and parent commits;
- actually presented or discarded frames where feedback is available;
- presentation interval distribution;
- produced-to-presented ratio;
- submission-to-presentation latency;
- GTK main-thread cost;
- raster blocking;
- CPU/GPU usage and idle behavior.
Tests should record the exact engine revision, renderer, compositor, desktop environment, GPU/driver, display mode, scaling configuration, VRR state, and workload.
Scope and success condition
#49757 remains the canonical cross-platform desktop display-synchronization issue.
#183703 remains the Linux high-refresh investigation.
This issue focuses on the Linux interaction between frame scheduling, GTK/GDK, Wayland/X11 presentation, renderer synchronization, high-refresh displays, presentation feedback, multi-view behavior, and lifecycle correctness.
Success means normal-mode Flutter animation follows useful presentation opportunities with stable visible cadence, low latency, minimal wasted rendering work, and correct behavior across high-refresh, mixed-refresh, resize, and lifecycle scenarios.
A higher engine FPS counter, a rendering-backend change, removal of redundant GTK redraws, or reduced animation alone is not sufficient.
Activity
[-][Linux][Desktop] Track frame pacing and display synchronization gaps affecting animation smoothness[/-][+][Linux][Desktop] Tracking issue for frame pacing and display synchronization gaps affecting animation smoothness[/+]suryaKommana2662 commentedon Aug 18, 2026
@Axiaobo7788 Thanks for the issue and for collecting the related Linux frame pacing, display synchronization, high-refresh-rate, and presentation issues. Since this is a tracking/meta issue covering multiple existing Linux desktop issues rather than a single independently reproducible bug, I’m routing it to
team-linuxfor further evaluation of the proposed tracking scope.LFRon commentedon Nov 11, 2026
So when flutter will change from GTK3 to GTK4, I can't wait it ((
o-murphy commentedon Nov 13, 2026
I can reproduce the black client area on a Wayland session with a plain
flutter createapp and no plugins, and it looks like a regression between3.47 stable and 3.50. Adding data points from a different setup than the
original report.
Environment
Reproduction
flutter create --platforms=linux plain_applib/main.dart, replace the template with a minimal app (no dotshorthands, so it also builds on 3.47):
flutter build linux --release, then runbuild/linux/x64/release/bundle/plain_app.Results
GDK_BACKEND=x11)LIBGL_ALWAYS_SOFTWARE=1Notes:
smaller than the window, which is consistent with a subsurface being placed
or sized incorrectly.
GDK_BACKEND=x11rendered correctly. Possibly a different driver or compositor combination.
3.49.0-0.2.pre and 3.50.0 fail. So the regression appears between
3.48.0-0.5.pre and 3.49.0-0.1.pre.
say whether they are affected.
The steps above are enough to reproduce on any Linux machine with Flutter 3.47 and 3.50 installed.