Skip to content

Impeller regression: gradients render incorrectly vs. Skia #119175

@isaaclyman

Description

@isaaclyman

A two-part Impeller-specific bug here:

  1. Regression in the way gradients are rendered
  2. Jank when animating said gradients

MCVE

Minimal example repo available here: https://github.com/isaaclyman/flutter-impeller-gradient-bug

Steps to Reproduce

To see the first bug, a simple widget like this one will do:

class FlutterImpellerGradientBug extends StatefulWidget {
  final color1 = Colors.red;
  final color2 = Colors.green;

  const FlutterImpellerGradientBug({super.key});

  @override
  State<FlutterImpellerGradientBug> createState() =>
      _FlutterImpellerGradientBugState();
}

class _FlutterImpellerGradientBugState
    extends State<FlutterImpellerGradientBug> {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: SizedBox(
        height: 100,
        width: 100,
        child: DecoratedBox(
          decoration: BoxDecoration(
            gradient: LinearGradient(
              begin: Alignment.topCenter,
              end: Alignment.bottomCenter,
              colors: [
                widget.color1,
                widget.color1,
                widget.color2,
                widget.color2,
              ],
              stops: [
                0.0,
                0.5,
                0.5,
                1.0,
              ],
            ),
          ),
        ),
      ),
    );
  }
}

This is a (perhaps naive) way of rendering a box with a simple horizontal color split: red on top, green on bottom. With Skia you get the following, which is what you'd expect:

With Impeller enabled, you get this instead:

Things get worse when you try to animate the boundary between the colors. Here's a widget that does exactly that:

class _FlutterImpellerAnimatedGradientBug extends StatefulWidget {
  final color1 = Colors.red;
  final color2 = Colors.green;

  const _FlutterImpellerAnimatedGradientBug();

  @override
  State<_FlutterImpellerAnimatedGradientBug> createState() =>
      _FlutterImpellerAnimatedGradientBugState();
}

class _FlutterImpellerAnimatedGradientBugState
    extends State<_FlutterImpellerAnimatedGradientBug>
    with TickerProviderStateMixin {
  late final AnimationController _animation =
      AnimationController(duration: const Duration(seconds: 2), vsync: this);

  @override
  void initState() {
    super.initState();
    _animation.repeat(reverse: true);
  }

  @override
  void dispose() {
    _animation.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: SizedBox(
        height: 100,
        width: 100,
        child: AnimatedBuilder(
            animation: _animation,
            builder: (context, _) {
              final animationProgress =
                  Tween(begin: 0.0, end: 1.0).evaluate(_animation);
              return DecoratedBox(
                decoration: BoxDecoration(
                  gradient: LinearGradient(
                    begin: Alignment.topCenter,
                    end: Alignment.bottomCenter,
                    colors: [
                      widget.color1,
                      widget.color1,
                      widget.color2,
                      widget.color2,
                    ],
                    stops: [
                      0.0,
                      animationProgress,
                      animationProgress,
                      1.0,
                    ],
                  ),
                ),
              );
            }),
      ),
    );
  }
}

With Skia, the boundary animates smoothly up and down, as expected.

Screen recording (mp4)

With Impeller, it's hard to tell what the animation is trying to do. If my TV did this when I was a kid, it meant the antenna needed to be adjusted. :)

Screen recording (mp4)

Code sample

See https://github.com/isaaclyman/flutter-impeller-gradient-bug.

Logs

GitHub won't let me submit the ticket if I include the full output of flutter run --verbose.

There was an error creating your Issue: body is too long, body is too long (maximum is 65536 characters).

Analyzing flutter_impeller_gradient_bug...                              
No issues found! (ran in 1.0s)
[✓] Flutter (Channel stable, 3.7.0, on macOS 13.0.1 22A400 darwin-arm64, locale en-US)
    • Flutter version 3.7.0 on channel stable at /Users/isaaclyman/code/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision b06b8b2710 (2 days ago), 2023-01-23 16:55:55 -0800
    • Engine revision b24591ed32
    • Dart version 2.19.0
    • DevTools version 2.20.1

[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
    • Android SDK at /Users/isaaclyman/Library/Android/sdk
    • Platform android-33, build-tools 33.0.0
    • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 14.2)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 14C18
    • CocoaPods version 1.11.3

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

[✓] Android Studio (version 2021.3)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)

[✓] VS Code (version 1.74.3)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.58.0

[✓] VS Code (version 1.71.2)
    • VS Code at /Users/isaaclyman/Downloads/Visual Studio Code.app/Contents
    • Flutter extension version 3.58.0

[✓] Connected device (3 available)
    • iPhone 13 Pro Max (mobile) • 9AAEC81E-3ECD-4455-BD0A-90603C2C9025 • ios            • com.apple.CoreSimulator.SimRuntime.iOS-16-2 (simulator)
    • macOS (desktop)            • macos                                • darwin-arm64   • macOS 13.0.1 22A400 darwin-arm64
    • Chrome (web)               • chrome                               • web-javascript • Google Chrome 108.0.5359.98

[✓] HTTP Host Availability
    • All required HTTP hosts are available

• No issues found!

Metadata

Metadata

Assignees

Labels

c: renderingUI glitches reported at the engine/skia or impeller rendering levele: impellerImpeller rendering backend issues and features requestsengineflutter/engine related. See also e: labels.found in release: 3.7Found to occur in 3.7has reproducible stepsThe issue has been confirmed reproducible and is ready to work onplatform-iosiOS applications specificallyr: fixedIssue is closed as already fixed in a newer version

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions