Skip to content

flutter web, slow rendering when decoding images and using single picture on every frame #74897

Description

@ta9ki

less than 30 sprites = 60 fps
more than 300 sprites = 2 fps

code sample
import 'dart:typed_data';
import 'dart:ui';
import 'package:flutter/material.dart' hide Image;

var sprite = {};
int spriteint = 300;

Picture backgroundPicture;
int lastLoop;
Paint red = Paint()..color = const Color(0xFFFF0000);
Paint orange = Paint()..color = const Color(0xFFFF8000);
Paint yellow = Paint()..color = const Color(0xFFFFFF05);

main() {
  WidgetsFlutterBinding.ensureInitialized();
  backgroundPicture = createBackgroundPicture();
  WidgetsBinding.instance.window.onBeginFrame = gameLoop;
  WidgetsBinding.instance.window.scheduleFrame();
  for (var i = 0; i < spriteint; i++) {
    sprite[i] = Sprite();
  }
}

Picture createBackgroundPicture() {
  final PictureRecorder pictureRecorder = PictureRecorder();
  final Canvas canvas = Canvas(
      pictureRecorder,
      Rect.fromLTWH(0.0, 0.0, WidgetsBinding.instance.window.physicalSize.width,
          WidgetsBinding.instance.window.physicalSize.height));
  canvas.drawPaint(red);
  canvas.drawCircle(Offset(200, 300), 150, yellow);
  canvas.drawCircle(Offset(500, 500), 250, orange);
  return pictureRecorder.endRecording();
}

void gameLoop(Duration d) {
  final loop = d.inMicroseconds;
  final loopDelta = loop - (lastLoop ?? loop);
  lastLoop = loop;
  for (var i = 0; i < spriteint; i++) {
    sprite[i].update(loopDelta);
  }

  final SceneBuilder builder = SceneBuilder();
  builder.addPicture(Offset.zero, backgroundPicture);
  for (var i = 0; i < spriteint; i++) {
    sprite[i].render(builder);
  }

  WidgetsBinding.instance.window.render(builder.build());
  WidgetsBinding.instance.window.scheduleFrame();
}

class Sprite {
  double x = 0, y = 100, dx = 8, dy = 0;
  Picture picture;

  void update(int loopDelta) {
    x += dx * loopDelta / 16000;
    y += dy * loopDelta / 16000;
    if ((x < 0 && dx < 0) || (x > 1000 && dx > 0)) dx = -dx;
    if ((y < 0 && dy < 0) || (y > 1000 && dy > 0)) dy = -dy;
  }

  void render(SceneBuilder builder) {
    if (picture != null) {
      builder.addPicture(Offset(x, y), picture);
    }
  }

  Sprite() {
    createSpriteFrame();
  }

  void createSpriteFrame() {
      Uint8List lst = Uint8List.fromList([
      137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 60,
      0, 0, 0, 72, 8, 6, 0, 0, 0, 170, 79, 150, 165, 0, 0, 0, 4, 103, 65, 77,
      65, 0, 0, 177, 142, 124, 251, 81, 147, 0, 0, 2, 94, 73, 68, 65, 84, 120,
      156, 237, 155, 61, 78, 195, 64, 16, 133, 199, 40, 161, 160, 74, 132, 92,
      80, 82, 70, 66, 17, 13, 13, 23, 160, 132, 130, 43, 32, 174, 225, 35, 208,
      162, 92, 129, 130, 156, 131, 46, 68, 226, 8, 41, 172, 40, 169, 168, 34,
      133, 110, 61, 94, 237, 120, 102, 227, 117, 136, 244, 252, 42, 139, 108,
      118, 231, 105, 62, 15, 251, 151, 140, 152, 246, 197, 158, 52, 101, 69,
      166, 182, 33, 34, 189, 163, 116, 10, 6, 36, 121, 57, 235, 52, 148, 19, 20,
      156, 225, 26, 14, 18, 6, 13, 24, 171, 232, 254, 188, 92, 170, 65, 76, 102,
      107, 181, 13, 239, 167, 161, 189, 138, 55, 92, 134, 225, 12, 15, 164, 15,
      98, 49, 182, 160, 203, 37, 97, 249, 187, 88, 184, 231, 139, 233, 52, 216,
      94, 106, 227, 197, 22, 52, 0, 151, 97, 56, 195, 34, 210, 158, 146, 99,
      204, 177, 148, 20, 219, 198, 195, 59, 40, 184, 12, 195, 25, 174, 33, 109,
      153, 39, 167, 170, 198, 22, 73, 136, 198, 98, 204, 5, 151, 97, 56, 195,
      77, 85, 58, 249, 18, 207, 82, 117, 185, 222, 63, 164, 16, 190, 131, 109,
      94, 159, 245, 87, 18, 46, 195, 112, 134, 155, 24, 112, 172, 196, 86, 102,
      46, 203, 156, 57, 149, 188, 138, 237, 188, 245, 203, 67, 36, 137, 85, 250,
      225, 250, 220, 33, 49, 153, 173, 15, 198, 219, 184, 83, 17, 37, 142, 110,
      236, 171, 1, 151, 97, 56, 195, 53, 164, 121, 53, 227, 243, 234, 187, 171,
      65, 18, 188, 83, 169, 77, 133, 135, 203, 48, 156, 97, 211, 142, 199, 215,
      106, 151, 100, 48, 254, 10, 88, 150, 117, 93, 76, 78, 224, 50, 12, 103,
      216, 116, 20, 72, 29, 159, 6, 74, 203, 64, 203, 114, 175, 65, 253, 92,
      154, 8, 208, 176, 105, 121, 200, 37, 239, 66, 28, 79, 30, 234, 42, 247,
      61, 210, 72, 242, 39, 30, 73, 48, 182, 84, 215, 46, 94, 13, 203, 29, 21,
      184, 12, 195, 25, 246, 217, 115, 76, 164, 194, 216, 114, 44, 210, 114,
      172, 254, 218, 82, 147, 224, 12, 91, 15, 196, 131, 178, 96, 44, 253, 157,
      227, 205, 251, 57, 160, 122, 171, 247, 58, 184, 224, 50, 12, 103, 88, 68,
      154, 99, 86, 20, 71, 137, 133, 136, 136, 86, 75, 125, 210, 194, 227, 137,
      141, 13, 46, 195, 112, 134, 125, 164, 57, 79, 106, 185, 148, 208, 146,
      142, 66, 164, 137, 199, 49, 95, 25, 184, 12, 195, 25, 54, 77, 60, 218, 84,
      197, 83, 192, 152, 11, 46, 195, 112, 134, 77, 27, 191, 121, 94, 61, 151,
      101, 85, 189, 255, 11, 75, 62, 110, 158, 87, 30, 202, 82, 255, 46, 92,
      134, 225, 12, 155, 14, 196, 57, 198, 167, 38, 75, 108, 247, 195, 161, 51,
      3, 151, 97, 56, 195, 173, 118, 60, 110, 223, 70, 169, 226, 112, 122, 218,
      110, 213, 54, 159, 163, 240, 184, 143, 155, 141, 123, 158, 143, 199, 188,
      79, 135, 61, 92, 134, 225, 12, 71, 35, 45, 225, 20, 43, 9, 191, 54, 99,
      73, 253, 112, 193, 101, 24, 206, 176, 248, 115, 90, 239, 23, 46, 193, 127,
      238, 169, 240, 110, 83, 153, 99, 251, 132, 203, 48, 156, 97, 107, 149, 14,
      110, 238, 89, 80, 60, 64, 73, 198, 186, 25, 84, 23, 98, 151, 187, 234, 38,
      33, 92, 134, 225, 12, 255, 1, 102, 172, 210, 13, 246, 211, 202, 107, 0, 0,
      0, 0, 73, 69, 78, 68, 174, 66, 96, 130]);
    instantiateImageCodec(lst).then((codec) {
      codec.getNextFrame().then((frameInfo) {
        final Image image = frameInfo.image;
        final PictureRecorder pictureRecorder = PictureRecorder();
        final Canvas canvas = Canvas(
            pictureRecorder,
            Rect.fromLTWH(
                0.0, 0.0, image.width.toDouble(), image.height.toDouble()));
        canvas.drawImage(image, Offset.zero, Paint());
        picture = pictureRecorder.endRecording();
      });
    });
  }
}

for comparison dart+webgl
http://www.stagexl.org/samples/performance/index.html
more than 120000 sprites = 60fps

Metadata

Metadata

Assignees

Labels

P3Issues that are less important to the Flutter projectassigned for triageissue is assigned to a domain expert for further triagec: performanceRelates to speed or footprint issues (see "perf:" labels)perf: speedPerformance issues related to (mostly rendering) speedplatform-webWeb applications specificallyr: fixedIssue is closed as already fixed in a newer version

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