Skip to content

drawPoints is slow for large numbers of points #152702

@Hixie

Description

@Hixie

Beyond about 10,000 points, the raster overhead of a drawPoints call becomes too slow to be usable in interactive UIs or animations.

It seems to be worse on Skia on Linux than on Impeller and Android, though it is a problem in both cases.

Benchmark code
import 'dart:math' as math;
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';

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

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

  @override
  State<Test> createState() => _TestState();
}

class _TestState extends State<Test> {
  final List<Offset> points = [];

  final math.Random random = math.Random(0);
  
  @override
  void initState() {
    super.initState();
    print('count,vsyncOverhead,buildDuration,rasterDuration,totalSpan');
    addPoints(1024);
    SchedulerBinding.instance.addTimingsCallback(_timings);
  }

  @override
  void reassemble() {
    super.reassemble();
  }

  @override
  void dispose() {
    SchedulerBinding.instance.removeTimingsCallback(_timings);
    super.dispose();
  }

  void _timings(List<FrameTiming> timings) {
    for (FrameTiming timing in timings) {
      print('${points.length},${timing.vsyncOverhead.inMilliseconds},${timing.buildDuration.inMilliseconds},${timing.rasterDuration.inMilliseconds},${timing.totalSpan.inMilliseconds}');
    }
    setState(() {
      int more = (points.length / 16).ceil();
      addPoints(more);
    });
  }
  
  void addPoints(int more) {
    for (int index = 0; index < more; index += 1) {
      points.add(Offset(random.nextDouble() * 1000, random.nextDouble() * 1000));
    }
    if (points.length >= 1<<21) {
      points.clear();
      addPoints(1024);
    }
  }
      
  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: Painter(
        points: points.toList(),
      ),
    );
  }
}

class Painter extends CustomPainter {
  Painter({
    required this.points,
  });

  final List<Offset> points;
  
  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawPaint(Paint()..color = const Color(0xFF000000));
    canvas.drawPoints(PointMode.points, points, Paint()..strokeWidth = 2.0..color = const Color(0xFFFFFFFF));
  }
  
  @override
  bool shouldRepaint(Painter oldDelegate) => points != oldDelegate.points;
}

Measuring the raster time for drawPoints at various numbers of points gives the following results:

raster time for drawPoints, by number of points - linux _ skia (1)
raster time for drawPoints, by number of points - android (Pixel 6A) _ impeller (1)
Source: raw data

Drawing many points is useful when rendering extensive data sets, such as stars in a galaxy:

sample screenshot

...but unfortunately the current performance makes this kind of UI impractical.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Issues that are less important to the Flutter projectengineflutter/engine related. See also e: labels.found in release: 3.22Found to occur in 3.22found in release: 3.24Found to occur in 3.24has reproducible stepsThe issue has been confirmed reproducible and is ready to work onteam-engineOwned by Engine teamtriaged-engineTriaged by Engine team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions