Skip to content

[flame_tiled] skwasm renderer crashes ("memory access out of bounds") on tilemaps with >~2000 batched tiles — upstream Flutter bug #3913

Description

@Ortes

Summary

Rendering a moderately large tilemap (≥ ~2000 visible/batched tiles in one layer) on Flutter web with the skwasm renderer crashes the engine with RuntimeError: memory access out of bounds.

Root cause is upstream in Flutter, not in flame_tiled itself, but reporting here so anyone hitting the crash finds the workaround quickly.

Steps to reproduce

  1. flutter run -d chrome --wasm with _flutter.loader.load({config: {renderer: "skwasm"}}) in web/flutter_bootstrap.js.
  2. Load any TiledComponent whose visible tile-layer batch exceeds ~2000 tiles. A 64×64 ground layer triggers it.

Trace

RuntimeError: memory access out of bounds
  at $malloc                       (skwasm.wasm)
  at $paint_create                 (skwasm.wasm)
  at $SkwasmPaint.toRawPaint       (paint.dart:16)
  at $SkwasmCanvas.drawAtlas       (canvas.dart, inside withStackScope)
  at $SpriteBatch.render           (flame sprite_batch.dart:520)
  at $FlameTileLayer.render        (tile_layer.dart:147)
  at $RenderableTiledMap.render    (renderable_tile_map.dart:487)
  at $TiledComponent.render        (tiled_component.dart:83)

FlameTileLayer.render calls SpriteBatch.render which calls Canvas.drawAtlas once per layer with one entry per tile. Skwasm's drawAtlas writes the per-entry arrays into the wasm stack (_emscripten_stack_alloc, default 64 KiB), and ~2000+ entries overflow the stack into the heap; the next malloc traps.

Upstream

Once that fix lands, no flame_tiled change is needed.

Workaround until upstream fix is released

final tiled = await TiledComponent.load(
  'wedding_pilgrimage.tmx',
  Vector2.all(32),
  useAtlas: false, // bypasses SpriteBatch.drawAtlas → uses drawImageRect per tile
);

useAtlas: false switches FlameTileLayer to a per-tile Canvas.drawImageRect path instead of SpriteBatch.drawAtlas. It boots fine, but expect ~10× lower FPS in debug and a measurable drop in release because the GPU batching is gone (in our case ~100 FPS skwasm + useAtlas: false vs ~120 FPS canvaskit + useAtlas: true). Acceptable as a stop-gap; not as a permanent solution.

The other workaround is to switch the web bootstrap to renderer: "canvaskit", which doesn't share this code path.

Suggested README/docs note

A short note in the flame_tiled README under "Web / known issues" pointing at flutter/flutter#185995 and the useAtlas: false escape hatch would save future users a lot of time — the crash signature points at paint_create rather than drawAtlas, so it's easy to misdiagnose. Happy to PR the docs change if useful.

Versions

  • flame: ^1.37.0
  • flame_tiled: ^3.1.1
  • Flutter 3.44.0-0.3.pre, Dart 3.12.0-327.4.beta, engine 285ae5207
  • macOS 15 / arm64, Chrome stable

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions