Skip to content

[Windows][Impeller] First opening of a framework-only rounded dialog stalls on Intel Iris Xe; warm rendering improves on master #193707

Description

@Trindoz

Steps to reproduce

Remaining issue: first-dialog raster stalls in a fresh native Windows process, reproduced on current master. The separate sustained warm-opening slowdown observed on stable 3.47.2 is substantially improved on master; this report does NOT claim that warm problem remains unfixed.

Standalone sample uses only Flutter framework widgets: no backend/application code, plugins, images, custom fonts, BackdropFilter, shader packages, startup shader warm-up or hidden dialog pre-render.

  1. Unzip the attached probe. Run flutter pub get, then flutter build windows --profile.
  2. Run a fresh process with Impeller and --auto --variant=baseline --renderer=Impeller. Repeat with Skia on the same monitor/client viewport. Exact child-process switches are in README. With flutter run use --enable-impeller versus --no-enable-impeller, --dart-define=PROBE_AUTO=true, and --dart-define=PROBE_RENDERER=Impeller or Skia. The label alone does NOT select the backend.
  3. Wait for six open/close cycles and the JSON report. Do not manually open before the batch. Compare opening cycle 1 separately from cycles 2-6 and closing. Close only after the report; omit the optional --exit flag (separate native teardown failure after JSON).
  4. Repeat a fresh process. Master traces additionally use --trace-skia. No performance overlay or video capture.

Expected: the first small opaque rounded dialog should not lose most of its 260ms fade/slide/scale animation to long raster stalls.

Observed on master framework d03768e, engine content hash ad9da2e50980fdf83889721559c19f5403ef8f87 (2026-10-02):

First opening / fresh process Captured opening frames Raster max ms Warm opening raster median ms
Impeller baseline 4 283.277 5.354
Skia baseline 15 91.622 2.431
Impeller noScale 4 396.714 5.487
Impeller baseline confirmation 5 183.074 4.674
Skia baseline, trace-skia capture 35 45.582 1.877
Impeller baseline, trace-skia capture 6 125.993 4.541

Both renderers can have first-opening spikes; observed Impeller spikes are greater. Results vary with startup/driver caches, so maxima are NOT fixed latency guarantees; first-opening sample counts are small. In the master traced Impeller run the first-opening raster p50 was 60.560ms, UI/build max 3.760ms; corresponding Skia raster p50 2.619ms, UI max 1.541ms. Raw frames and complete non-empty timelines are attached. No failures/dropped samples in the reports. Raster timing is engine FrameTiming, not GPU utilization or proof of an engine/driver/shader root cause.

Warm means pooled opening cycles 2-6 including settle/rest frames. On stable 3.47.2 warm Impeller median was 35.734ms versus Skia 2.097ms; noScale 6.277ms. On master this warm scale penalty disappears. Back-to-back old-SDK control still measured 34.702ms versus master 4.674ms. The remaining first-opening stalls also occur without scale.

Cold here means first dialog in a fresh process, NOT cleared GPU/disk caches. Same physical host, source, window, monitor and renderer selection method. Master SDK was separate; production SDK/PATH were not switched. Its depth-1 clone fetched one official version tag for dependency resolution; exact commit/content hash above are the tested identity.

Related context: #191207 uses BackdropFilter, absent here. #191175/#183495 concern Vulkan support; this is a measured first-opening performance report for the existing OpenGLESSDF backend, not a Vulkan feature request.

Code sample

The attached ZIP contains the complete self-contained lib/main.dart and a stock Windows Flutter runner (only the initial client size is adjusted). No plugins or extra assets are needed; uses-material-design: true. Copy lib/main.dart into a fresh flutter create --platforms=windows app if preferred. The native runner adjustment keeps the CLIENT viewport 540x960 logical pixels, rather than sizing the outer frame. The automatic collector is included to make cold/warm opening measurements repeatable.

Relevant transition excerpt from the measured source (complete runnable file in ZIP):

RawDialogRoute<void> createProbeRoute(
  BuildContext context,
  ProbeVariant variant, {
  bool interactive = true,
}) => RawDialogRoute<void>(
  barrierDismissible: interactive,
  barrierLabel: 'Close dialog',
  barrierColor: const Color(0x6b000000),
  transitionDuration: const Duration(milliseconds: 260),
  pageBuilder: (context, animation, secondaryAnimation) =>
      ProbeDialog(variant: variant, interactive: interactive),
  transitionBuilder: (context, animation, secondaryAnimation, child) {
    final curved = animation.drive(CurveTween(curve: Curves.easeOutCubic));
    Widget result = variant == ProbeVariant.withRepaintBoundary
        ? RepaintBoundary(child: child)
        : child;
    if (variant != ProbeVariant.noScale)
      result = ScaleTransition(
        scale: Tween<double>(begin: 0.96, end: 1).animate(curved),
        child: result,
      );
    result = SlideTransition(
      position: Tween<Offset>(
        begin: const Offset(0, 0.035),
        end: Offset.zero,
      ).animate(curved),
      child: result,
    );
    if (variant != ProbeVariant.noFade)
      result = FadeTransition(opacity: curved, child: result);
    return result;
  },
);

ProbeDialog is a transparent Dialog wrapping an opaque static-gradient rounded PhysicalModel panel, elevation 12, Clip.antiAlias, two rounded opaque gradient cards, ordinary Cyrillic Text and Material icons. noShadow changes only elevation to 0; noText hides text painting while retaining layout (icon-font glyphs remain). No BackdropFilter. The complete source includes all details and window-boundary/raw-frame reporting.

Performance profiling on master channel

  • The issue still persists on the master channel

Timeline Traces

ZIP below contains the complete framework-only source, raw FrameTiming JSON for stable/master runs, environment metadata and redacted doctor outputs, and non-empty .pftrace files. Open .pftrace with Perfetto UI. These are VM-service getPerfettoVMTimeline exports (default Dart/Embedder streams), not DevTools JSON exports. Master files ending master-traced additionally use the engine trace-skia switch; no performance overlay/recording. FrameTiming raw samples/window boundaries provide the quantitative first/warm opening comparisons separately. No source-application traces, order data, usernames, local paths, auth URLs or secrets are included.

flutter-windows-dialog-repro-2026-10-02.zip

Video demonstration

No video capture was used during the measurements. Source, raw FrameTiming results and non-empty Perfetto timelines are attached.

What target platforms are you seeing this bug on?

Windows

OS/Browser name and version | Device information

Native Windows desktop (not web, emulator, or simulator). Windows 11 Pro 23H2, build 22631.6199; x64; Intel Core i9-13900HK; Intel Iris Xe integrated graphics, driver 32.0.101.7082; 32 GB RAM. Built and measured in profile mode. Window client 540x960 logical pixels, DPR 2.0, 1080x1920 physical pixels; display reports 120 Hz; OS text scale 1.0. Same monitor/window/driver for the Impeller and Skia processes. No performance overlay or screen recording.

Does the problem occur on emulator/simulator as well as on physical devices?

Unknown

Is the problem only reproducible with Impeller?

No

Logs

Native profile builds succeeded on both SDKs; source and raw measurements are in ZIP. Backend banners:

Stable Impeller: embedder_surface_gl_impeller.cc(126): Using the Impeller rendering backend (OpenGLESSDF).
Master Impeller: embedder_surface_gl_impeller.cc(127): Using the Impeller rendering backend (OpenGLESSDF).

Skia processes use enable-impeller=false. The engine switch reader prepends -- itself; environment switch values intentionally have no -- prefix. Switches are child-process-only. Master trace runs use FLUTTER_ENGINE_SWITCHES=2, switch 1=enable-impeller=true/false, switch 2=trace-skia. Raw reports have failure=null, droppedFrameCount=0, explicit first/warm/closing sample windows and actual viewport/DPR. No per-frame console logging during transitions. VM-service auth URLs are omitted.

Flutter Doctor output

Full redacted doctor-stable.txt and doctor-master.txt are in the attached ZIP. Selected identity/toolchain output:

Stable: Flutter 3.47.2, channel stable
Framework d3b14c8769, engine a804b26164
Dart 3.13.2, DevTools 2.60.0

Master: Flutter 3.49.0-0.2.pre (depth-1 clone; use exact revision below), channel master
Framework d03768eef05516a030f4eb12457b0d297111d526
Engine content hash ad9da2e50980fdf83889721559c19f5403ef8f87
Dart 3.14.0-294.0.dev, DevTools 2.61.0

Windows 11 Pro 23H2, 22631.6199, x64
Visual Studio Build Tools 2019 16.11.50
Windows 10 SDK 10.0.19041.0
Windows desktop device available; native profile build successful
Android SDK unavailable (not relevant to Windows reproduction)

Master is a separate diagnostic SDK invoked by absolute path; default PATH intentionally remains on stable. For doctor only, FLUTTER_GIT_URL was scoped to the local official SDK clone to avoid fetching all historical tags after retrieving one official version tag. Doctor reports the intentional PATH/local-mirror warnings. This did not change the source revision, engine artifacts, native build or renderer selection.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions