Vertex cache ordering: the source
Open the live demo · Read the guide · View on GitHub
Regions the guide quotes: scramble (line 27), reorder (line 43), rebuild (line 52), check (line 94).
1/// A mesh whose triangle order is scrambled, then rebuilt for GPU cache reuse.
2library;
4import 'dart:math' as math;
5import 'dart:typed_data';
7import 'package:flutter3d/flutter3d.dart';
8import 'package:flutter3d_showcase/src/demo/demo.dart';
9import 'package:vector_math/vector_math.dart';
11final class VertexCacheDemo extends ShowcaseDemo {
12 double _beforeRatio = 0.0;
13 double _afterRatio = 0.0;
14 late final int _originalVertexCount;
15 late final int _reorderedIndexCount;
18 void configureView(DemoContext context) {
19 context.orbit
20 ..distance = 5.5
21 ..pitch = 0.24
22 ..yaw = 0.4;
23 }
26 Scene build(DemoContext context) {
27 final MeshData sphere = const SphereShape(
28 radius: 1.1,
29 segments: 32,
30 rings: 16,
31 ).build();
32 final int triangleCount = sphere.triangleCount;
33 final List<int> order = List<int>.generate(triangleCount, (int i) => i)
34 ..shuffle(math.Random(7));
35 final Uint32List scrambled = Uint32List(sphere.indices.length);
36 for (var t = 0; t < triangleCount; t++) {
37 final int from = order[t] * 3;
38 scrambled[t * 3] = sphere.indices[from];
39 scrambled[t * 3 + 1] = sphere.indices[from + 1];
40 scrambled[t * 3 + 2] = sphere.indices[from + 2];
41 }
43 _beforeRatio = averageCacheMissRatio(scrambled);
44 final Uint32List cacheOrdered = optimizeTriangleOrder(
45 scrambled,
46 sphere.vertexCount,
47 );
48 final ({Uint32List indices, Uint32List oldToNew}) fetch =
49 optimizeVertexFetch(cacheOrdered, sphere.vertexCount);
50 _afterRatio = averageCacheMissRatio(cacheOrdered);
52 final int stride = sphere.layout.floatsPerVertex;
53 final Float32List vertices = Float32List(sphere.vertices.length);
54 for (var oldV = 0; oldV < sphere.vertexCount; oldV++) {
55 final int newV = fetch.oldToNew[oldV];
56 vertices.setRange(
57 newV * stride,
58 newV * stride + stride,
59 sphere.vertices,
60 oldV * stride,
61 );
62 }
63 final MeshData reordered = MeshData(
64 layout: sphere.layout,
65 vertices: vertices,
66 indices: fetch.indices,
67 );
68 _originalVertexCount = sphere.vertexCount;
69 _reorderedIndexCount = reordered.indexCount;
71 final Scene scene = Scene()
72 ..ambientColor = Vector3(0.44, 0.5, 0.66)
73 ..ambientIntensity = 0.16
74 ..add(
75 MeshNode(
76 DeviceMesh.upload(context.device, reordered),
77 Material(
78 name: 'sphere',
79 baseColor: Vector4(0.3, 0.62, 0.86, 1.0),
80 roughness: 0.5,
81 ),
82 name: 'sphere',
83 ),
84 )
85 ..add(
86 LightNode(name: 'key', intensity: 3.2)
87 ..setLocalForward(Vector3(-0.42, -0.8, -0.36)),
88 );
89 return scene;
90 }
93 void verify(Scene scene, FrameResult frame) {
94 if (_reorderedIndexCount == 0 ||
95 _originalVertexCount == 0 ||
96 _afterRatio > _beforeRatio ||
97 frame.drawCalls < 1) {
98 throw StateError('reordering the mesh did not improve cache locality');
99 }
100 }
101}