Skip to content

[Impeller] Canvas.drawVertices() does not work on iOS simulator/device. #127486

@badlogic

Description

@badlogic

Is there an existing issue for this?

Steps to reproduce

  1. Clone this minimal reproduction project: https://github.com/badlogic/flutter-mali-crash
  2. Compile it with Flutter SDK 3.10.1 and run it on an iOS simulator or device via flutter run

Expected results

Screenshot 2023-05-24 at 13 21 15

The expected results shows up when running the app without Impeller via flutter run --no-enable-impeller

Actual results

Screenshot 2023-05-24 at 13 20 26

The result shown when running the app on the iOS simulator or device with Impeller enabled, i.e. flutter run

Code sample

Code sample

A minimal reproduction sample can be found here: https://github.com/badlogic/flutter-mali-crash
The sample loads mesh data from a text file and converts it to a Vertices instance. It also loads an image and constructs an ImageShader based Paint from it. It then proceeds to render the vertices instance 12 times at random locations on the screen via Canvas.drawVertices() using the modulate blend mode together with a trivial ImageShader. Other blend modes also fail.

The app uses Flame to minimize the LOC count. Flame itself does not interfere with the rendering in any meaningful way other than setting a transform on the Canvas instance used for rendering.

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

import 'package:flame/game.dart';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart' as painting;

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

class MaliCrash extends FlameGame {
  late ui.Image _texture;
  late Paint _paint;
  late ui.Vertices _vertices;
  late List<Vector2> _positions = [];

  Future<void> _loadPaint() async {
    final imageData = (await rootBundle.load("assets/spineboy.png")).buffer.asUint8List();
    final codec = await ui.instantiateImageCodec(imageData);
    final frameInfo = await codec.getNextFrame();
    _texture = frameInfo.image;
    _paint = Paint()
      ..shader = ImageShader(_texture, TileMode.clamp, TileMode.clamp, Matrix4.identity().storage, filterQuality: FilterQuality.high)
      ..isAntiAlias = true;
  }

  Future<void> _loadVertices() async {
    final lines = LineSplitter().convert(await rootBundle.loadString("assets/spineboy.mesh"));
    final numVertices = int.parse(lines[0]);
    final numIndices = int.parse(lines[1]);
    final positions = Float32List(numVertices * 2);
    final uvs = Float32List(numVertices * 2);
    final colors = Int32List(numVertices);
    final indices = Uint16List(numIndices);
    int idx = 2;
    for (int i = 0; i < numVertices * 2; i++) {
      positions[i] = double.parse(lines[idx++]) * 0.2;
    }
    for (int i = 0; i < numVertices * 2; i++) {
      uvs[i] = double.parse(lines[idx++]) * (i % 2 == 0 ? _texture.width : _texture.height);
    }
    for (int i = 0; i < numVertices; i++) {
      colors[i] = int.parse(lines[idx++]);
    }
    for (int i = 0; i < numIndices; i++) {
      indices[i] = int.parse(lines[idx++]);
    }

    _vertices = ui.Vertices.raw(VertexMode.triangles, positions, textureCoordinates: uvs, colors: colors, indices: indices);
  }

  @override
  Color backgroundColor() => const Color(0xffcccccc);

  @override
  Future<void> onLoad() async {
    await _loadPaint();
    await _loadVertices();
    final rng = Random();
    for (int i = 0; i < 12; i++) {
      _positions.add(Vector2(rng.nextDouble() * size.x, rng.nextDouble() * size.y));
    }
  }

  @override
  void render(Canvas canvas) {
    for (var position in _positions) {
      canvas.save();
      canvas.translate(position.x, position.y);
      canvas.drawVertices(_vertices, painting.BlendMode.modulate, _paint);
      canvas.restore();
    }
  }
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Mali Crash',
      home: GameWidget(game: MaliCrash())
    );
  }
}

Screenshots or Video

Screenshots / Video demonstration See expected and actual results sections above.

Logs

Logs
flutter-mali-crash git:(main) ✗ flutter run
Launching lib/main.dart on iPhone 14 Pro in debug mode...
Running Xcode build...
 └─Compiling, linking and signing...                      1,853ms
Xcode build done.                                            9.4s
[VERBOSE-2:FlutterDarwinContextMetalImpeller.mm(35)] Using the Impeller rendering backend.
Syncing files to device iPhone 14 Pro...                            39ms

Flutter run key commands.
r Hot reload. 🔥🔥🔥
R Hot restart.
h List all available interactive commands.
d Detach (terminate "flutter run" but leave application running).
c Clear the screen
q Quit (terminate the application on the device).

A Dart VM Service on iPhone 14 Pro is available at: http://127.0.0.1:63740/ECaaw6ByFgw=/
The Flutter DevTools debugger and profiler on iPhone 14 Pro is available at: http://127.0.0.1:9103?uri=http://127.0.0.1:63740/ECaaw6ByFgw=/

Flutter Doctor output

Doctor output
flutter-mali-crash git:(main) ✗ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.10.1, on macOS 13.0 22A380 darwin-arm64, locale en-AT)
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 14.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2022.1)
[✓] IntelliJ IDEA Community Edition (version 2022.3.3)
[✓] VS Code (version 1.78.2)
[✓] Connected device (3 available)
[✓] Network resources

• No issues found!

Metadata

Metadata

Assignees

Labels

a: gamedevIssues related to game development with Fluttere: impellerImpeller rendering backend issues and features requests

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions