flutter3d
Showcase Changelog 38 packages API reference

Frame timing and memory pressure: the source

since 0.7.0 Widgets and the rest

Regions the guide quotes: clock (line 40), timing (line 52), pressure (line 65).

1/// How long since the last frame, what a window of frames cost, and giving
2/// pooled render targets back when the platform warns about memory.
3///
4/// Quoted by `diagnostics.md` and shown whole in the Source tab.
5library;
6
7import 'package:flutter/widgets.dart';
8import 'package:flutter3d/flutter3d.dart';
9import 'package:flutter3d_app/flutter3d_app.dart';
10import 'package:flutter3d_showcase/src/demo/demo.dart';
11import 'package:vector_math/vector_math.dart';
13final class DiagnosticsDemo extends ShowcaseDemo {
14 late final String _report;
15 late final double _firstTick;
16 late final bool _hasTimingLine;
18 @override
19 Scene build(DemoContext context) {
20 final (String report, double firstTick, bool hasTimingLine) = _run(context);
21 _report = report;
22 _firstTick = firstTick;
23 _hasTimingLine = hasTimingLine;
24 final material = Material(
25 name: 'clock',
26 baseColor: Vector4(0.7, 0.7, 0.4, 1.0),
27 );
28 final node = MeshNode(
29 DeviceMesh.upload(context.device, SphereShape(segments: 16).build()),
30 material,
31 );
32 return Scene()
33 ..add(node)
34 ..add(
35 LightNode(name: 'sun', intensity: 3.0)
36 ..setLocalForward(Vector3(-0.4, -1.0, -0.3)),
37 );
38 }
40 static (String, double) _clockLine() {
41 final clock = FrameClock();
42 final first = clock.tick();
43 clock.secondsSince(const Duration(milliseconds: 16));
44 clock.secondsSince(const Duration(milliseconds: 32));
45 return (
46 'first tick: ${first}s, elapsed after two frames: '
47 '${clock.elapsed.toStringAsFixed(3)}s',
48 first,
49 );
50 }
52 static String? _timingLine() {
53 final log = FrameTimingLog(label: 'demo', window: 2);
54 log.note(
55 build: const Duration(milliseconds: 4),
56 raster: const Duration(milliseconds: 6),
57 );
58 return log.note(
59 build: const Duration(milliseconds: 8),
60 raster: const Duration(milliseconds: 10),
61 );
62 }
64 static (String, double, bool) _run(DemoContext context) {
65 // What a memory warning does: give the renderer's pooled render targets
66 // back. `MemoryPressureRelease` calls exactly this when the platform
67 // warns; calling it directly here proves the release itself.
68 context.renderer.releaseTransientTargets();
70 final (String clockLine, double firstTick) = _clockLine();
71 final String? timingLine = _timingLine();
72 return (
73 '$clockLine\n${timingLine ?? 'no line yet'}',
74 firstTick,
75 timingLine != null,
76 );
77 }
79 @override
80 Widget? customBody(BuildContext buildContext, DemoContext context) =>
81 Container(
82 color: const Color(0xFF14161A),
83 padding: const EdgeInsets.all(24),
84 alignment: Alignment.topLeft,
85 child: DefaultTextStyle(
86 style: const TextStyle(color: Color(0xFFE8E8EC), fontSize: 16),
87 child: Text(_report),
88 ),
89 );
91 @override
92 void verify(Scene scene, FrameResult frame) {
93 if (frame.drawCalls < 1) {
94 throw StateError('the clock marker was not drawn');
95 }
96 // Compared as a number, not read back out of `_report`: the first tick
97 // is a whole-number double, and a web backend prints one of those
98 // without its trailing `.0` — a compiled `0` failing a substring match
99 // against `'tick: 0.0s'` would be this check catching its own string,
100 // not the clock.
101 if (_firstTick != 0.0) {
102 throw StateError(
103 'the very first tick of a frame clock should measure '
104 'as no time at all',
105 );
106 }
107 if (!_hasTimingLine) {
108 throw StateError(
109 'a timing log with a window of two should report '
110 'after its second frame',
111 );
112 }
113 }