Skip to content

[Impeller] Emulated dashed lines perform poorly due to PSO switching overhead #119881

@jonahwilliams

Description

@jonahwilliams

Or at least this is the current theory.

Applications like wonderous emulate support for dashed lines by making N calls to Canvas.drawLine. In theory we should be able to handle a large number of draw calls. profiles for wonderous here show that we're missing frames quite freuently and the xcode frame debugger shows some taking up to 30 ms.

Though I don't know for certain, this seems like it is possibly related to overhead of switching PSOs. To draw a stroked geometry, we first draw the path and then render a clip restore. The result is a pattern of solid fill, restore, solid fill, restore draw calls. I tested locally by commenting out the restore to remove the switching and performance recovered substantially.

Long term we could work around this situation by exposing support for dashed lines: #110723

But we should also investigate whether we could make this cheaper without adding new API. One indrect way to solve this problem would be to have some sort of batching approach. This would reduce the number of draw calls for highly repetitive pictures, and avoid the switching overhead.

Example app:

// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

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


void main() =>
  runApp(MaterialApp(home: ListView(children: [
    Container(
      height: 400,
    ),
    CustomPaint(painter: _DashedLinePainter(false)),
    Container(
      height: 400,
    ),
  ])));


class _DashedLinePainter extends CustomPainter {
  _DashedLinePainter(this.vertical);
  final bool vertical;

  @override
  void paint(Canvas canvas, Size size) {
    double dashPx = 3, gapPx = 3, pos = 0;
    final paint = Paint()..color = Colors.white;
    if (vertical) {
      while (pos < size.height) {
        canvas.drawLine(Offset(0, pos), Offset(0, pos + dashPx), paint);
        pos += dashPx + gapPx;
      }
    } else {
      while (pos < size.width) {
        canvas.drawLine(Offset(pos, 0), Offset(pos + dashPx, 0), paint);
        pos += dashPx + gapPx;
      }
    }
  }

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listc: performanceRelates to speed or footprint issues (see "perf:" labels)e: impellerImpeller rendering backend issues and features requestsengineflutter/engine related. See also e: labels.

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions