Skip to content

[Impeller] Drawing a lot of large circles corrupts further rendering #184091

Description

@flar

Steps to reproduce

Compile and run the following example. The results I describe below are from running this app on my Macbook Pro with M1 Max. The slider and textfield allow you to adjust the radius. Clicking the "Turn" button will initiate a 5 full 360 degree turn animation that demonstrates if the rendering is smooth at that radius size.

On Skia:

  • the app renders fine at the default setting of 150 radius
  • the app will continue to render fine up to around 200 radius
  • exceeding 250 or so radius will start to have stuttering frames that take significantly longer

On Impeller:

  • the app has rendering glitches at the default setting of 150 radius (the bottom app bar fails to render correctly as you pass the mouse over it)
  • Animating the radius of 150 with the "Turn" button has many frequent stutters that get worse as you increase the radius
  • Setting the radius down to around 100 eliminates the rendering glitches and removes the stuttering.

Expected results

Normal rendering at a radius of 100:

Impeller results at radius == 100 Image

Actual results

Corrupted rendering:

Corrupted Impeller rendering at radius == 150 Image

the results are more dramatic if you move the mouse over the bottom app bar while it is animating and increase the radius.

Code sample

Code sample
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: .fromSeed(seedColor: Colors.deepPurple),
      ),
      showPerformanceOverlay: true, // Enables the performance overlay
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  static const double _minRadius = 1;
  static const double _maxRadius = 500;
  static const double _defaultRadius = 150;

  double _turns = 0;
  double _radius = _defaultRadius;
  final _controller = TextEditingController(text: '$_defaultRadius');

  void _turn() {
    setState(() {
      _turns += 5;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: .center,
          children: [
            AnimatedRotation(
              turns: _turns,
              duration: Duration(seconds: 10),
              child: CustomPaint(
                foregroundPainter: _MyCirclePainter(rows: 50, cols: 50, radius: _radius),
                size: Size(400, 400),
              ),
            )
          ],
        ),
      ),
      bottomNavigationBar: BottomAppBar(
        child: Row(
          mainAxisAlignment: .center,
          children: <Widget>[
            ElevatedButton(onPressed: _turn, child: Text('Turn')),
            SizedBox(width: 20),
            Text('Radius'),
            Slider(
              value: _radius,
              min: _minRadius,
              max: _maxRadius,
              onChanged: (value) {
                setState(() {
                  _radius = value;
                  _controller.text = _radius.toStringAsFixed(1);
                });
              },
            ),
            SizedBox(
              width: 100,
              child: TextField(
                controller: _controller,
                keyboardType: .number,
                onSubmitted: (newString) {
                  double? newValue = double.tryParse(newString);
                  if (newValue != null) {
                    setState(() {
                      _radius = newValue.clamp(_minRadius, _maxRadius);
                    });
                  }
                },
                decoration: InputDecoration(
                  filled: true,
                  fillColor: Colors.white,
                  border: OutlineInputBorder(),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class _MyCirclePainter extends CustomPainter {
  static final List<Color> colors = <Color>[
    Colors.purple,
    Colors.blue,
    Colors.green,
    Colors.yellow,
    Colors.orange,
    Colors.red,
  ];

  _MyCirclePainter({required this.rows, required this.cols, required this.radius});

  final int rows;
  final int cols;
  final double radius;

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
        ..style = PaintingStyle.fill;
    paint.color = Colors.white;
    canvas.drawRect(Offset.zero & size, paint);
    Size gridSize = Size(2 * radius * cols, 2 * radius * rows);
    Size gridSpace = Size(
      size.width - gridSize.width,
      size.height - gridSize.height,
    );
    Size cellPadding = Size(
      gridSpace.width / (cols + 1),
      gridSpace.height / (rows + 1),
    );
    canvas.translate(cellPadding.width + radius, cellPadding.height + radius);

    int colorIndex = 0;
    for (int y = 0; y < rows; y++) {
      double circleY = (cellPadding.height + radius * 2) * y;
      for (int x = 0; x < cols; x++) {
        double circleX = (cellPadding.width + radius * 2) * x;
        paint.color = colors[colorIndex++ % colors.length];
        canvas.drawCircle(Offset(circleX, circleY), radius, paint);
      }
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}

Screenshots or Video

No response

Logs

The following errors were noted on the console from which the app was launched.

Logs
[ERROR:flutter/lib/ui/window/platform_configuration.cc(448)] Reported frame time is older than the last one; clamping. 382305749530 < 382305768923 ~= 19393
[ERROR:flutter/lib/ui/window/platform_configuration.cc(448)] Reported frame time is older than the last one; clamping. 382681442288 < 382681443050 ~= 762
[ERROR:flutter/lib/ui/window/platform_configuration.cc(448)] Reported frame time is older than the last one; clamping. 382698331921 < 382698333176 ~= 1255
[ERROR:flutter/lib/ui/window/platform_configuration.cc(448)] Reported frame time is older than the last one; clamping. 382700716091 < 382700743114 ~= 27023

Flutter Doctor output

Doctor output
[!] Flutter (Channel [user-branch], 3.43.0-1.0.pre-240, on macOS 26.3.1 25D771280a darwin-arm64, locale en) [794ms]
    ! Flutter version 3.43.0-1.0.pre-240 on channel [user-branch] at /Users/.../Development/flutter
      Currently on an unknown channel. Run `flutter channel` to switch to an official channel.
      If that doesn't fix the issue, reinstall Flutter by following instructions at https://flutter.dev/setup.
    ! Upstream repository unknown source is not a standard remote.
      Set environment variable "FLUTTER_GIT_URL" to unknown source to dismiss this error.
    • Framework revision ccca54fd8a (4 days ago), 2026-03-20 16:39:34 -0700
    • Engine revision b597ab2688
    • Dart version 3.12.0 (build 3.12.0-269.0.dev)
    • DevTools version 2.56.0
    • Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android,
      enable-ios, cli-animations, enable-native-assets, enable-swift-package-manager, omit-legacy-version-file,
      enable-lldb-debugging, enable-uiscene-migration, enable-riscv64
    • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly
      to perform update checks and upgrades.

[✓] Android toolchain - develop for Android devices (Android SDK version 36.1.0) [3.4s]
    • Android SDK at /Users/.../Library/Android/sdk
    • Emulator version 36.4.9.0 (build_id 14788078) (CL:N/A)
    • Platform android-36.1, build-tools 36.1.0
    • ANDROID_HOME = /Users/.../Library/Android/sdk
    • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
      This is the JDK bundled with the latest Android Studio installation on this machine.
      To manually set the JDK path, use: `flutter config --jdk-dir="path/to/jdk"`.
    • Java version OpenJDK Runtime Environment (build 21.0.9+-14649483-b1163.86)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 26.2) [3.2s]
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 17C52
    • CocoaPods version 1.16.2

[✓] Chrome - develop for the web [11ms]
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Connected device (4 available) [12.2s]
    • Pixel 6a (mobile) • 27251JEGR18342            • android-arm64  • Android 16 (API 36)
    • iPhone (mobile)   • 00008030-000325223A53C02E • ios            • iOS 26.2 23C55
    • macOS (desktop)   • macos                     • darwin-arm64   • macOS 26.3.1 25D771280a darwin-arm64
    • Chrome (web)      • chrome                    • web-javascript • Google Chrome 146.0.7680.154

[✓] Network resources [418ms]
    • All expected network resources are available.

! Doctor found issues in 1 category.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listc: renderingUI glitches reported at the engine/skia or impeller rendering levele: impellerImpeller rendering backend issues and features requestsengineflutter/engine related. See also e: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onplatform-macosBuilding on or for macOS specificallyteam-macosOwned by the macOS platform teamtriaged-macosTriaged by the macOS platform team

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions