flutter3d_testing
Pixel regression tests for a game, on a machine with no GPU.
import 'package:flutter3d_testing/flutter3d_testing.dart';
test('the crypt still looks like the crypt', () async {
final frame = await renderFrame(
width: 320,
height: 180,
build: (device) {
final scene = Scene();
// ... put the level in it, uploading meshes to `device`
return (scene: scene, camera: camera);
},
);
await expectMatchesGolden(frame, 'test/goldens/crypt.png');
});
The first run records the reference and says so. Later runs fail when the picture changes, and the message says by how much and how to re-record.
Why this can exist here and nowhere else
flutter3d_cpu is a full backend — a software rasteriser that passes the same
conformance suite as Impeller and WebGL. So a frame can be drawn with no driver,
no display and no graphics hardware, which is what turns "does it still look
right" into something a continuous integration runner can answer.
Every other 3D engine on this platform needs a real device for that. What follows is either a machine nobody wants to pay for or a check nobody runs.
Why it is its own package
flutter3d_cpu must not depend on flutter3d: a backend that could not compile
without the engine would not be an implementation of an interface, it would be
part of the engine. And flutter3d must not depend on a backend at all — a scan
in tool/structure.dart holds it to that.
So neither of them can hold something that needs both, and this needs both.
The tolerance is zero, deliberately
The frame comes from a software rasteriser. The same scene drawn twice is the same bytes twice: there is no driver, no clock and no thread to disagree. A difference is therefore a change, not noise.
A test that allows a few pixels of drift is a test that has stopped watching the
drift. Raise tolerance only when something is measured to move, and say in the
call why.
What it does not do
It does not compare a software frame against a GPU one. This repository keeps
two separate golden sets on purpose: the same scene differs by a fraction of a
percent between a rasteriser and a driver, and one shared set would need a
tolerance — see the note at the top of
packages/flutter3d_cpu/test/cross_backend_test.dart.
A game's references made here say what the software backend draws. That is still the right thing to regress against: a change that alters the picture alters it on both.
Part of flutter3d, an independent
implementation of a 3D engine for Flutter — not a fork or a binding of
another engine, and not affiliated with the Flutter team. Three switchable
rendering backends: Impeller via Flutter GPU, WebGL2, and a software
rasteriser. glTF, OBJ and .f3d loading, six lighting models, shadows, bloom,
skinning, animation, BVH culling and picking; a deterministic fixed-step game
layer with collision, navigation, positional audio, and gamepad and touch
input. Three example games — shooter, platformer, racing — each built on its
genre package: flutter3d_game_shooter,
flutter3d_game_platformer,
flutter3d_game_racing. A new game starts from the
editor's scaffold, which writes one from a template: flutter3d.pleion.dev/first-project/.
Documentation: flutter3d.pleion.dev.
Libraries
- flutter3d_testing
- Pixel regression tests for a game, on a machine with no GPU.