Skip to content

[Impeller] Especially complex paths cause rendering errors, freezes #126212

@jonahwilliams

Description

@jonahwilliams

This application chugs along with the Skia backend, but with Impeller we fail to even render a frame until it crashes. No errors, perhaps tessellation is just taking too long? But something else is going as the frame that is finally rendered is also incorrect. Perhaps we're overflowing the index buffer?

This code was taken from https://medium.com/@dev.n/flutter-generative-art-the-easy-way-99252486a338 . Originally canvas.drawCircle was used, but this led to a massive overhead in Rtree computation (see #126202). To work around this, I collapsed them into a single path with multiple arcs.

Skia

flutter_05

Impeller

Apologies for the picture, but the tool screenshot doesn't work when this happens and I've had no luck with photos importing.

image

import 'dart:math';
import 'dart:typed_data';
import 'dart:ui';

import 'package:flutter/material.dart';

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

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  double iter = 0.0;

  // Choose a random color for this iteration
  var colors = List<ColorInfo>.generate(20, (int index) => ColorList[Random().nextInt(ColorList.length)]);

  @override
  void initState() {
    super.initState();
    Future.delayed(Duration(seconds: 1)).then((value) async {
      for (int i = 0; i < 2000000; i++) {
        setState(() {
          iter = iter + 0.00001;
        });
        await Future.delayed(Duration(milliseconds: 50));
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        body: CustomPaint(
          painter: DemoPainter(iter, colors),
          child: Container(),
        ),
      ),
    );
  }
}

class DemoPainter extends CustomPainter {
  DemoPainter(this.iter, this.colors);

  final double iter;
  final List<ColorInfo> colors;

  @override
  void paint(Canvas canvas, Size size) {
    renderDrawing(canvas, size);
  }

  void renderDrawing(Canvas canvas, Size size) {
    canvas.drawPaint(Paint()..color = Colors.black87);

    renderStructure2(canvas, size, iter, colors, 9);
  }

  void renderStructure2(Canvas canvas, Size size, double iter, List<ColorInfo> colors, int totalIter) {

    var paint = Paint();

    for (int j = 0; j < totalIter; j++) {
      double distance = 0.0;
      double tempIter = totalIter - j + iter;

      // We draw a LOT of points
      var path = Path();
      paint
        ..style = PaintingStyle.fill
        ..color = colors[j].color;
      for (double i = 0; i < 80; i = i + 0.01) {
        var radius =  5.0 - (0.5 * (totalIter - tempIter));
        var center = Offset(
          (size.width / 2) + (distance * cos(distance) * sin(distance) * atan(distance)),
          (size.height / 2) + (distance * cos(distance) * sin(distance) * tan(distance)),
        );
        path.addArc(
          Rect.fromCircle(center: center, radius: radius),
          0,
          2 * pi,
        );
        path.close();

        // Change the "0.1" for varying point distances
        distance = distance + (0.2 + (0.1 * totalIter - tempIter));
      }
      canvas.drawPath(path, paint);
    }
  }

  @override
  bool shouldRepaint(DemoPainter oldDelegate) {
    return iter != oldDelegate.iter;
  }
}

// This could have been a list of colors, I just picked the list off another project of mine
var ColorList = [
  //ColorInfo("black", Colors.black, Colors.black.toString()),
  ColorInfo("red", Colors.red, Colors.red[500].toString()),
  ColorInfo("green", Colors.green, Colors.green[500].toString()),
  ColorInfo("blue", Colors.blue, Colors.blue[500].toString()),
  ColorInfo("yellow", Colors.yellow, Colors.yellow[500].toString()),
  ColorInfo("purple", Colors.purple, Colors.purple[500].toString()),
  ColorInfo("amber", Colors.amber, Colors.amber[500].toString()),
  ColorInfo("cyan", Colors.cyan, Colors.cyan[500].toString()),
  ColorInfo("grey", Colors.grey, Colors.grey[500].toString()),
  ColorInfo("teal", Colors.teal, Colors.teal[500].toString()),
  ColorInfo("pink", Colors.pink, Colors.pink[500].toString()),
  ColorInfo("orange", Colors.orange, Colors.orange[500].toString()),
  //ColorInfo("white", Colors.white, Colors.white.toString()),
  //ColorInfo("transparent", Colors.transparent, Colors.transparent.toString()),
];

class ColorInfo {
  String name;
  MaterialColor color;
  String hex;

  ColorInfo(this.name, this.color, this.hex);
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work liste: impellerImpeller rendering backend issues and features requestsengineflutter/engine repository. See also e: labels.team-engineOwned by Engine teamtriaged-engineTriaged by Engine team

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions