flame-engine / flame-engine/flame
[flame_tiled] skwasm renderer crashes ("memory access out of bounds") on tilemaps with >~2000 batched tiles — upstream Flutter bug
- Dominant language
- Dart
- Stars
- 10.8k
- Forks
- 1k
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 21
Description
## 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
- Flutter issue: flutter/flutter#185995
- Proposed fix (chunking on the skwasm side): flutter/flutter#185997
Once that fix lands, no `flame_tiled` change is needed.
## Workaround until upstream fix is released
```dart
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
Contributor guide
Assessment
This issue has not been assessed yet.