flutter3d
Showcase Changelog 38 packages API reference

Frame timing and memory pressure

since 0.7.0 Widgets and the rest

Three small instruments an application reaches for once it is running for real: how long the last frame actually took, what a window of frames cost on average and at their worst, and giving memory back when the platform says it is short of it.

Step 1: The frame clock #

FrameClock.tick measures the wall between calls. The very first tick answers nought rather than a guessed sixtieth of a second, because nothing has actually been measured yet.

static (String, double) _clockLine() {
  final clock = FrameClock();
  final first = clock.tick();
  clock.secondsSince(const Duration(milliseconds: 16));
  clock.secondsSince(const Duration(milliseconds: 32));
  return (
    'first tick: ${first}s, elapsed after two frames: '
        '${clock.elapsed.toStringAsFixed(3)}s',
    first,
  );
}

Step 2: A window of frame costs #

FrameTimingLog.note takes a build duration and a raster duration and returns a summary line once its window of frames is full, and null on every frame before that.

static String? _timingLine() {
  final log = FrameTimingLog(label: 'demo', window: 2);
  log.note(
    build: const Duration(milliseconds: 4),
    raster: const Duration(milliseconds: 6),
  );
  return log.note(
    build: const Duration(milliseconds: 8),
    raster: const Duration(milliseconds: 10),
  );
}

Step 3: Give memory back #

MemoryPressureRelease calls Renderer.releaseTransientTargets when the platform warns about memory. Pooled render targets otherwise settle at a high-water mark and stay there for the rest of the session.

// What a memory warning does: give the renderer's pooled render targets
// back. `MemoryPressureRelease` calls exactly this when the platform
// warns; calling it directly here proves the release itself.
context.renderer.releaseTransientTargets();