@motion-core/motion-gpu 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -0
- package/dist/advanced.d.ts +1 -0
- package/dist/advanced.d.ts.map +1 -0
- package/dist/advanced.js +14 -6
- package/dist/core/advanced.d.ts +1 -0
- package/dist/core/advanced.d.ts.map +1 -0
- package/dist/core/advanced.js +14 -5
- package/dist/core/compute-shader.d.ts +87 -0
- package/dist/core/compute-shader.d.ts.map +1 -0
- package/dist/core/compute-shader.js +205 -0
- package/dist/core/compute-shader.js.map +1 -0
- package/dist/core/current-value.d.ts +1 -0
- package/dist/core/current-value.d.ts.map +1 -0
- package/dist/core/current-value.js +35 -34
- package/dist/core/current-value.js.map +1 -0
- package/dist/core/error-diagnostics.d.ts +1 -0
- package/dist/core/error-diagnostics.d.ts.map +1 -0
- package/dist/core/error-diagnostics.js +70 -137
- package/dist/core/error-diagnostics.js.map +1 -0
- package/dist/core/error-report.d.ts +2 -1
- package/dist/core/error-report.d.ts.map +1 -0
- package/dist/core/error-report.js +247 -233
- package/dist/core/error-report.js.map +1 -0
- package/dist/core/frame-registry.d.ts +1 -0
- package/dist/core/frame-registry.d.ts.map +1 -0
- package/dist/core/frame-registry.js +546 -662
- package/dist/core/frame-registry.js.map +1 -0
- package/dist/core/index.d.ts +6 -2
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +13 -12
- package/dist/core/material-preprocess.d.ts +1 -0
- package/dist/core/material-preprocess.d.ts.map +1 -0
- package/dist/core/material-preprocess.js +131 -152
- package/dist/core/material-preprocess.js.map +1 -0
- package/dist/core/material.d.ts +23 -6
- package/dist/core/material.d.ts.map +1 -0
- package/dist/core/material.js +290 -317
- package/dist/core/material.js.map +1 -0
- package/dist/core/recompile-policy.d.ts +1 -0
- package/dist/core/recompile-policy.d.ts.map +1 -0
- package/dist/core/recompile-policy.js +18 -13
- package/dist/core/recompile-policy.js.map +1 -0
- package/dist/core/render-graph.d.ts +8 -3
- package/dist/core/render-graph.d.ts.map +1 -0
- package/dist/core/render-graph.js +77 -68
- package/dist/core/render-graph.js.map +1 -0
- package/dist/core/render-targets.d.ts +1 -0
- package/dist/core/render-targets.d.ts.map +1 -0
- package/dist/core/render-targets.js +52 -53
- package/dist/core/render-targets.js.map +1 -0
- package/dist/core/renderer.d.ts +1 -0
- package/dist/core/renderer.d.ts.map +1 -0
- package/dist/core/renderer.js +1337 -1081
- package/dist/core/renderer.js.map +1 -0
- package/dist/core/runtime-loop.d.ts +3 -2
- package/dist/core/runtime-loop.d.ts.map +1 -0
- package/dist/core/runtime-loop.js +353 -362
- package/dist/core/runtime-loop.js.map +1 -0
- package/dist/core/scheduler-helpers.d.ts +1 -0
- package/dist/core/scheduler-helpers.d.ts.map +1 -0
- package/dist/core/scheduler-helpers.js +52 -51
- package/dist/core/scheduler-helpers.js.map +1 -0
- package/dist/core/shader.d.ts +10 -1
- package/dist/core/shader.d.ts.map +1 -0
- package/dist/core/shader.js +109 -115
- package/dist/core/shader.js.map +1 -0
- package/dist/core/storage-buffers.d.ts +37 -0
- package/dist/core/storage-buffers.d.ts.map +1 -0
- package/dist/core/storage-buffers.js +95 -0
- package/dist/core/storage-buffers.js.map +1 -0
- package/dist/core/texture-loader.d.ts +1 -0
- package/dist/core/texture-loader.d.ts.map +1 -0
- package/dist/core/texture-loader.js +209 -273
- package/dist/core/texture-loader.js.map +1 -0
- package/dist/core/textures.d.ts +13 -0
- package/dist/core/textures.d.ts.map +1 -0
- package/dist/core/textures.js +111 -116
- package/dist/core/textures.js.map +1 -0
- package/dist/core/types.d.ts +147 -4
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +0 -4
- package/dist/core/uniforms.d.ts +1 -0
- package/dist/core/uniforms.d.ts.map +1 -0
- package/dist/core/uniforms.js +170 -191
- package/dist/core/uniforms.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -6
- package/dist/passes/BlitPass.d.ts +1 -0
- package/dist/passes/BlitPass.d.ts.map +1 -0
- package/dist/passes/BlitPass.js +23 -18
- package/dist/passes/BlitPass.js.map +1 -0
- package/dist/passes/ComputePass.d.ts +83 -0
- package/dist/passes/ComputePass.d.ts.map +1 -0
- package/dist/passes/ComputePass.js +92 -0
- package/dist/passes/ComputePass.js.map +1 -0
- package/dist/passes/CopyPass.d.ts +1 -0
- package/dist/passes/CopyPass.d.ts.map +1 -0
- package/dist/passes/CopyPass.js +58 -52
- package/dist/passes/CopyPass.js.map +1 -0
- package/dist/passes/FullscreenPass.d.ts +1 -0
- package/dist/passes/FullscreenPass.d.ts.map +1 -0
- package/dist/passes/FullscreenPass.js +127 -130
- package/dist/passes/FullscreenPass.js.map +1 -0
- package/dist/passes/PingPongComputePass.d.ts +104 -0
- package/dist/passes/PingPongComputePass.d.ts.map +1 -0
- package/dist/passes/PingPongComputePass.js +132 -0
- package/dist/passes/PingPongComputePass.js.map +1 -0
- package/dist/passes/ShaderPass.d.ts +1 -0
- package/dist/passes/ShaderPass.d.ts.map +1 -0
- package/dist/passes/ShaderPass.js +41 -37
- package/dist/passes/ShaderPass.js.map +1 -0
- package/dist/passes/index.d.ts +3 -0
- package/dist/passes/index.d.ts.map +1 -0
- package/dist/passes/index.js +6 -3
- package/dist/react/FragCanvas.d.ts +3 -2
- package/dist/react/FragCanvas.d.ts.map +1 -0
- package/dist/react/FragCanvas.js +234 -211
- package/dist/react/FragCanvas.js.map +1 -0
- package/dist/react/MotionGPUErrorOverlay.d.ts +1 -0
- package/dist/react/MotionGPUErrorOverlay.d.ts.map +1 -0
- package/dist/react/MotionGPUErrorOverlay.js +200 -14
- package/dist/react/MotionGPUErrorOverlay.js.map +1 -0
- package/dist/react/Portal.d.ts +1 -0
- package/dist/react/Portal.d.ts.map +1 -0
- package/dist/react/Portal.js +18 -21
- package/dist/react/Portal.js.map +1 -0
- package/dist/react/advanced.d.ts +1 -0
- package/dist/react/advanced.d.ts.map +1 -0
- package/dist/react/advanced.js +14 -6
- package/dist/react/frame-context.d.ts +1 -0
- package/dist/react/frame-context.d.ts.map +1 -0
- package/dist/react/frame-context.js +88 -94
- package/dist/react/frame-context.js.map +1 -0
- package/dist/react/index.d.ts +6 -2
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +12 -9
- package/dist/react/motiongpu-context.d.ts +1 -0
- package/dist/react/motiongpu-context.d.ts.map +1 -0
- package/dist/react/motiongpu-context.js +18 -15
- package/dist/react/motiongpu-context.js.map +1 -0
- package/dist/react/use-motiongpu-user-context.d.ts +1 -0
- package/dist/react/use-motiongpu-user-context.d.ts.map +1 -0
- package/dist/react/use-motiongpu-user-context.js +83 -82
- package/dist/react/use-motiongpu-user-context.js.map +1 -0
- package/dist/react/use-texture.d.ts +1 -0
- package/dist/react/use-texture.d.ts.map +1 -0
- package/dist/react/use-texture.js +132 -152
- package/dist/react/use-texture.js.map +1 -0
- package/dist/svelte/FragCanvas.svelte +2 -2
- package/dist/svelte/FragCanvas.svelte.d.ts +3 -2
- package/dist/svelte/FragCanvas.svelte.d.ts.map +1 -0
- package/dist/svelte/MotionGPUErrorOverlay.svelte +137 -7
- package/dist/svelte/MotionGPUErrorOverlay.svelte.d.ts +1 -0
- package/dist/svelte/MotionGPUErrorOverlay.svelte.d.ts.map +1 -0
- package/dist/svelte/Portal.svelte.d.ts +1 -0
- package/dist/svelte/Portal.svelte.d.ts.map +1 -0
- package/dist/svelte/advanced.d.ts +1 -0
- package/dist/svelte/advanced.d.ts.map +1 -0
- package/dist/svelte/advanced.js +13 -6
- package/dist/svelte/frame-context.d.ts +1 -0
- package/dist/svelte/frame-context.d.ts.map +1 -0
- package/dist/svelte/frame-context.js +27 -27
- package/dist/svelte/frame-context.js.map +1 -0
- package/dist/svelte/index.d.ts +6 -2
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +12 -9
- package/dist/svelte/motiongpu-context.d.ts +1 -0
- package/dist/svelte/motiongpu-context.d.ts.map +1 -0
- package/dist/svelte/motiongpu-context.js +24 -21
- package/dist/svelte/motiongpu-context.js.map +1 -0
- package/dist/svelte/use-motiongpu-user-context.d.ts +1 -0
- package/dist/svelte/use-motiongpu-user-context.d.ts.map +1 -0
- package/dist/svelte/use-motiongpu-user-context.js +69 -70
- package/dist/svelte/use-motiongpu-user-context.js.map +1 -0
- package/dist/svelte/use-texture.d.ts +1 -0
- package/dist/svelte/use-texture.d.ts.map +1 -0
- package/dist/svelte/use-texture.js +125 -147
- package/dist/svelte/use-texture.js.map +1 -0
- package/package.json +12 -7
- package/src/lib/advanced.ts +6 -0
- package/src/lib/core/advanced.ts +12 -0
- package/src/lib/core/compute-shader.ts +326 -0
- package/src/lib/core/current-value.ts +64 -0
- package/src/lib/core/error-diagnostics.ts +236 -0
- package/src/lib/core/error-report.ts +535 -0
- package/src/lib/core/frame-registry.ts +1190 -0
- package/src/lib/core/index.ts +94 -0
- package/src/lib/core/material-preprocess.ts +295 -0
- package/src/lib/core/material.ts +748 -0
- package/src/lib/core/recompile-policy.ts +31 -0
- package/src/lib/core/render-graph.ts +173 -0
- package/src/lib/core/render-targets.ts +107 -0
- package/src/lib/core/renderer.ts +2161 -0
- package/src/lib/core/runtime-loop.ts +537 -0
- package/src/lib/core/scheduler-helpers.ts +136 -0
- package/src/lib/core/shader.ts +301 -0
- package/src/lib/core/storage-buffers.ts +142 -0
- package/src/lib/core/texture-loader.ts +482 -0
- package/src/lib/core/textures.ts +257 -0
- package/src/lib/core/types.ts +743 -0
- package/src/lib/core/uniforms.ts +282 -0
- package/src/lib/index.ts +6 -0
- package/src/lib/passes/BlitPass.ts +54 -0
- package/src/lib/passes/ComputePass.ts +136 -0
- package/src/lib/passes/CopyPass.ts +80 -0
- package/src/lib/passes/FullscreenPass.ts +173 -0
- package/src/lib/passes/PingPongComputePass.ts +180 -0
- package/src/lib/passes/ShaderPass.ts +89 -0
- package/src/lib/passes/index.ts +9 -0
- package/src/lib/react/FragCanvas.tsx +345 -0
- package/src/lib/react/MotionGPUErrorOverlay.tsx +524 -0
- package/src/lib/react/Portal.tsx +34 -0
- package/src/lib/react/advanced.ts +36 -0
- package/src/lib/react/frame-context.ts +169 -0
- package/src/lib/react/index.ts +68 -0
- package/src/lib/react/motiongpu-context.ts +88 -0
- package/src/lib/react/use-motiongpu-user-context.ts +186 -0
- package/src/lib/react/use-texture.ts +233 -0
- package/src/lib/svelte/FragCanvas.svelte +249 -0
- package/src/lib/svelte/MotionGPUErrorOverlay.svelte +512 -0
- package/src/lib/svelte/Portal.svelte +31 -0
- package/src/lib/svelte/advanced.ts +32 -0
- package/src/lib/svelte/frame-context.ts +87 -0
- package/src/lib/svelte/index.ts +68 -0
- package/src/lib/svelte/motiongpu-context.ts +97 -0
- package/src/lib/svelte/use-motiongpu-user-context.ts +145 -0
- package/src/lib/svelte/use-texture.ts +232 -0
|
@@ -1,696 +1,580 @@
|
|
|
1
|
-
import { createCurrentWritable } from
|
|
1
|
+
import { createCurrentWritable } from "./current-value.js";
|
|
2
|
+
//#region src/lib/core/frame-registry.ts
|
|
2
3
|
/**
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
* Default stage key used when task stage is not explicitly specified.
|
|
5
|
+
*/
|
|
6
|
+
var MAIN_STAGE_KEY = Symbol("motiongpu-main-stage");
|
|
7
|
+
var RENDER_MODE_INVALIDATION_TOKEN = Symbol("motiongpu-render-mode-change");
|
|
7
8
|
/**
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
* Default stage callback that runs tasks immediately.
|
|
10
|
+
*/
|
|
11
|
+
var DEFAULT_STAGE_CALLBACK = (_state, runTasks) => runTasks();
|
|
11
12
|
/**
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
* Normalizes scalar-or-array options to array form.
|
|
14
|
+
*/
|
|
14
15
|
function asArray(value) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
return Array.isArray(value) ? value : [value];
|
|
16
|
+
if (!value) return [];
|
|
17
|
+
return Array.isArray(value) ? value : [value];
|
|
19
18
|
}
|
|
20
19
|
/**
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
* Normalizes frame keys to readable string labels.
|
|
21
|
+
*/
|
|
23
22
|
function frameKeyToString(key) {
|
|
24
|
-
|
|
23
|
+
return typeof key === "symbol" ? key.toString() : key;
|
|
25
24
|
}
|
|
26
25
|
/**
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
* Extracts task key from either direct key or task reference.
|
|
27
|
+
*/
|
|
29
28
|
function toTaskKey(reference) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
return reference.key;
|
|
29
|
+
if (typeof reference === "string" || typeof reference === "symbol") return reference;
|
|
30
|
+
return reference.key;
|
|
34
31
|
}
|
|
35
32
|
/**
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
* Extracts stage key from either direct key or stage reference.
|
|
34
|
+
*/
|
|
38
35
|
function toStageKey(reference) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
return reference.key;
|
|
36
|
+
if (typeof reference === "string" || typeof reference === "symbol") return reference;
|
|
37
|
+
return reference.key;
|
|
43
38
|
}
|
|
44
39
|
/**
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
* Resolves invalidation token from static value or resolver callback.
|
|
41
|
+
*/
|
|
47
42
|
function resolveInvalidationToken(token) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (resolved === null || resolved === undefined) {
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
return resolved;
|
|
43
|
+
if (token === void 0) return null;
|
|
44
|
+
const resolved = typeof token === "function" ? token() : token;
|
|
45
|
+
if (resolved === null || resolved === void 0) return null;
|
|
46
|
+
return resolved;
|
|
56
47
|
}
|
|
57
48
|
/**
|
|
58
|
-
|
|
59
|
-
|
|
49
|
+
* Normalizes task invalidation options to runtime representation.
|
|
50
|
+
*/
|
|
60
51
|
function normalizeTaskInvalidation(key, options) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
lastToken: null,
|
|
109
|
-
hasToken: false
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
return {
|
|
113
|
-
mode,
|
|
114
|
-
token: token ?? key,
|
|
115
|
-
lastToken: null,
|
|
116
|
-
hasToken: false
|
|
117
|
-
};
|
|
52
|
+
const explicit = options.invalidation;
|
|
53
|
+
if (explicit === void 0) {
|
|
54
|
+
if (options.autoInvalidate === false) return {
|
|
55
|
+
mode: "never",
|
|
56
|
+
lastToken: null,
|
|
57
|
+
hasToken: false
|
|
58
|
+
};
|
|
59
|
+
return {
|
|
60
|
+
mode: "always",
|
|
61
|
+
token: key,
|
|
62
|
+
lastToken: null,
|
|
63
|
+
hasToken: false
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (explicit === "never" || explicit === "always") {
|
|
67
|
+
if (explicit === "never") return {
|
|
68
|
+
mode: explicit,
|
|
69
|
+
lastToken: null,
|
|
70
|
+
hasToken: false
|
|
71
|
+
};
|
|
72
|
+
return {
|
|
73
|
+
mode: explicit,
|
|
74
|
+
token: key,
|
|
75
|
+
lastToken: null,
|
|
76
|
+
hasToken: false
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const mode = explicit.mode ?? "always";
|
|
80
|
+
const token = explicit.token;
|
|
81
|
+
if (mode === "on-change" && token === void 0) throw new Error("Task invalidation mode \"on-change\" requires a token");
|
|
82
|
+
if (mode === "never") return {
|
|
83
|
+
mode,
|
|
84
|
+
lastToken: null,
|
|
85
|
+
hasToken: false
|
|
86
|
+
};
|
|
87
|
+
if (mode === "on-change") return {
|
|
88
|
+
mode,
|
|
89
|
+
token,
|
|
90
|
+
lastToken: null,
|
|
91
|
+
hasToken: false
|
|
92
|
+
};
|
|
93
|
+
return {
|
|
94
|
+
mode,
|
|
95
|
+
token: token ?? key,
|
|
96
|
+
lastToken: null,
|
|
97
|
+
hasToken: false
|
|
98
|
+
};
|
|
118
99
|
}
|
|
119
100
|
/**
|
|
120
|
-
|
|
121
|
-
|
|
101
|
+
* Computes aggregate timing stats from sampled durations.
|
|
102
|
+
*/
|
|
122
103
|
function buildTimingStats(samples, last) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
last,
|
|
146
|
-
avg: sum / samples.length,
|
|
147
|
-
min,
|
|
148
|
-
max,
|
|
149
|
-
count: samples.length
|
|
150
|
-
};
|
|
104
|
+
if (samples.length === 0) return {
|
|
105
|
+
last,
|
|
106
|
+
avg: 0,
|
|
107
|
+
min: 0,
|
|
108
|
+
max: 0,
|
|
109
|
+
count: 0
|
|
110
|
+
};
|
|
111
|
+
let sum = 0;
|
|
112
|
+
let min = Number.POSITIVE_INFINITY;
|
|
113
|
+
let max = Number.NEGATIVE_INFINITY;
|
|
114
|
+
for (const value of samples) {
|
|
115
|
+
sum += value;
|
|
116
|
+
if (value < min) min = value;
|
|
117
|
+
if (value > max) max = value;
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
last,
|
|
121
|
+
avg: sum / samples.length,
|
|
122
|
+
min,
|
|
123
|
+
max,
|
|
124
|
+
count: samples.length
|
|
125
|
+
};
|
|
151
126
|
}
|
|
152
127
|
/**
|
|
153
|
-
|
|
154
|
-
|
|
128
|
+
* Deterministically sorts dependency keys for stable traversal and diagnostics.
|
|
129
|
+
*/
|
|
155
130
|
function sortDependencyKeys(keys) {
|
|
156
|
-
|
|
131
|
+
return Array.from(keys).sort((a, b) => frameKeyToString(a).localeCompare(frameKeyToString(b)));
|
|
157
132
|
}
|
|
158
133
|
/**
|
|
159
|
-
|
|
160
|
-
|
|
134
|
+
* Finds one deterministic cycle path in the directed dependency graph.
|
|
135
|
+
*/
|
|
161
136
|
function findDependencyCycle(items, edges) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
if (visit(item.key)) {
|
|
193
|
-
return cycle;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
return null;
|
|
137
|
+
const visitState = /* @__PURE__ */ new Map();
|
|
138
|
+
const stack = [];
|
|
139
|
+
let cycle = null;
|
|
140
|
+
const sortedItems = [...items].sort((a, b) => a.order - b.order);
|
|
141
|
+
const visit = (key) => {
|
|
142
|
+
visitState.set(key, 1);
|
|
143
|
+
stack.push(key);
|
|
144
|
+
for (const childKey of sortDependencyKeys(edges.get(key) ?? [])) {
|
|
145
|
+
const state = visitState.get(childKey) ?? 0;
|
|
146
|
+
if (state === 0) {
|
|
147
|
+
if (visit(childKey)) return true;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (state === 1) {
|
|
151
|
+
const cycleStartIndex = stack.findIndex((entry) => entry === childKey);
|
|
152
|
+
cycle = [...cycleStartIndex === -1 ? [childKey] : stack.slice(cycleStartIndex), childKey];
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
stack.pop();
|
|
157
|
+
visitState.set(key, 2);
|
|
158
|
+
return false;
|
|
159
|
+
};
|
|
160
|
+
for (const item of sortedItems) {
|
|
161
|
+
if ((visitState.get(item.key) ?? 0) !== 0) continue;
|
|
162
|
+
if (visit(item.key)) return cycle;
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
197
165
|
}
|
|
198
166
|
/**
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
167
|
+
* Topologically sorts items by `before`/`after` dependencies.
|
|
168
|
+
*
|
|
169
|
+
* Throws deterministic errors when dependencies are missing or cyclic.
|
|
170
|
+
*/
|
|
203
171
|
function sortByDependencies(items, getBefore, getAfter, options) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
if (ordered.length !== items.length) {
|
|
258
|
-
const cycle = findDependencyCycle(items, edges);
|
|
259
|
-
if (cycle) {
|
|
260
|
-
throw new Error(`${options.graphName} dependency cycle detected: ${cycle.map((key) => frameKeyToString(key)).join(' -> ')}`);
|
|
261
|
-
}
|
|
262
|
-
throw new Error(`${options.graphName} dependency resolution failed.`);
|
|
263
|
-
}
|
|
264
|
-
return ordered;
|
|
172
|
+
const itemsByKey = /* @__PURE__ */ new Map();
|
|
173
|
+
for (const item of items) itemsByKey.set(item.key, item);
|
|
174
|
+
const indegree = /* @__PURE__ */ new Map();
|
|
175
|
+
const edges = /* @__PURE__ */ new Map();
|
|
176
|
+
for (const item of items) {
|
|
177
|
+
indegree.set(item.key, 0);
|
|
178
|
+
edges.set(item.key, /* @__PURE__ */ new Set());
|
|
179
|
+
}
|
|
180
|
+
for (const item of items) {
|
|
181
|
+
for (const dependencyKey of getAfter(item)) {
|
|
182
|
+
if (!itemsByKey.has(dependencyKey)) {
|
|
183
|
+
if (options.isKnownExternalDependency?.(dependencyKey)) continue;
|
|
184
|
+
throw new Error(`${options.graphName} dependency error: ${options.getItemLabel(item)} references missing dependency "${frameKeyToString(dependencyKey)}" in "after".`);
|
|
185
|
+
}
|
|
186
|
+
edges.get(dependencyKey)?.add(item.key);
|
|
187
|
+
indegree.set(item.key, (indegree.get(item.key) ?? 0) + 1);
|
|
188
|
+
}
|
|
189
|
+
for (const dependencyKey of getBefore(item)) {
|
|
190
|
+
if (!itemsByKey.has(dependencyKey)) {
|
|
191
|
+
if (options.isKnownExternalDependency?.(dependencyKey)) continue;
|
|
192
|
+
throw new Error(`${options.graphName} dependency error: ${options.getItemLabel(item)} references missing dependency "${frameKeyToString(dependencyKey)}" in "before".`);
|
|
193
|
+
}
|
|
194
|
+
edges.get(item.key)?.add(dependencyKey);
|
|
195
|
+
indegree.set(dependencyKey, (indegree.get(dependencyKey) ?? 0) + 1);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const queue = items.filter((item) => (indegree.get(item.key) ?? 0) === 0);
|
|
199
|
+
queue.sort((a, b) => a.order - b.order);
|
|
200
|
+
const ordered = [];
|
|
201
|
+
while (queue.length > 0) {
|
|
202
|
+
const current = queue.shift();
|
|
203
|
+
if (!current) break;
|
|
204
|
+
ordered.push(current);
|
|
205
|
+
for (const childKey of edges.get(current.key) ?? []) {
|
|
206
|
+
const nextDegree = (indegree.get(childKey) ?? 0) - 1;
|
|
207
|
+
indegree.set(childKey, nextDegree);
|
|
208
|
+
if (nextDegree === 0) {
|
|
209
|
+
const child = itemsByKey.get(childKey);
|
|
210
|
+
if (child) {
|
|
211
|
+
queue.push(child);
|
|
212
|
+
queue.sort((a, b) => a.order - b.order);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (ordered.length !== items.length) {
|
|
218
|
+
const cycle = findDependencyCycle(items, edges);
|
|
219
|
+
if (cycle) throw new Error(`${options.graphName} dependency cycle detected: ${cycle.map((key) => frameKeyToString(key)).join(" -> ")}`);
|
|
220
|
+
throw new Error(`${options.graphName} dependency resolution failed.`);
|
|
221
|
+
}
|
|
222
|
+
return ordered;
|
|
265
223
|
}
|
|
266
224
|
/**
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
if (!enabled) {
|
|
619
|
-
clearProfiling();
|
|
620
|
-
}
|
|
621
|
-
},
|
|
622
|
-
setProfilingWindow(window) {
|
|
623
|
-
profilingWindow = assertProfilingWindow(window);
|
|
624
|
-
while (profilingHistory.length > profilingWindow) {
|
|
625
|
-
profilingHistory.shift();
|
|
626
|
-
}
|
|
627
|
-
},
|
|
628
|
-
resetProfiling() {
|
|
629
|
-
clearProfiling();
|
|
630
|
-
},
|
|
631
|
-
setDiagnosticsEnabled(enabled) {
|
|
632
|
-
profilingEnabled = enabled;
|
|
633
|
-
if (!enabled) {
|
|
634
|
-
clearProfiling();
|
|
635
|
-
}
|
|
636
|
-
},
|
|
637
|
-
getRenderMode() {
|
|
638
|
-
return renderMode;
|
|
639
|
-
},
|
|
640
|
-
getAutoRender() {
|
|
641
|
-
return autoRender;
|
|
642
|
-
},
|
|
643
|
-
getMaxDelta() {
|
|
644
|
-
return maxDelta;
|
|
645
|
-
},
|
|
646
|
-
getProfilingEnabled() {
|
|
647
|
-
return profilingEnabled;
|
|
648
|
-
},
|
|
649
|
-
getProfilingWindow() {
|
|
650
|
-
return profilingWindow;
|
|
651
|
-
},
|
|
652
|
-
getProfilingSnapshot() {
|
|
653
|
-
return buildProfilingSnapshot();
|
|
654
|
-
},
|
|
655
|
-
getDiagnosticsEnabled() {
|
|
656
|
-
return profilingEnabled;
|
|
657
|
-
},
|
|
658
|
-
getLastRunTimings() {
|
|
659
|
-
return lastRunTimings;
|
|
660
|
-
},
|
|
661
|
-
getSchedule() {
|
|
662
|
-
syncSchedule();
|
|
663
|
-
return scheduleSnapshot;
|
|
664
|
-
},
|
|
665
|
-
createStage(key, options) {
|
|
666
|
-
const stageOptions = options
|
|
667
|
-
? {
|
|
668
|
-
...(Object.prototype.hasOwnProperty.call(options, 'before')
|
|
669
|
-
? { before: asArray(options.before) }
|
|
670
|
-
: {}),
|
|
671
|
-
...(Object.prototype.hasOwnProperty.call(options, 'after')
|
|
672
|
-
? { after: asArray(options.after) }
|
|
673
|
-
: {}),
|
|
674
|
-
...(Object.prototype.hasOwnProperty.call(options, 'callback')
|
|
675
|
-
? { callback: options.callback ?? null }
|
|
676
|
-
: {})
|
|
677
|
-
}
|
|
678
|
-
: undefined;
|
|
679
|
-
const stage = ensureStage(key, stageOptions);
|
|
680
|
-
return { key: stage.key };
|
|
681
|
-
},
|
|
682
|
-
getStage(key) {
|
|
683
|
-
const stage = stages.get(key);
|
|
684
|
-
if (!stage) {
|
|
685
|
-
return undefined;
|
|
686
|
-
}
|
|
687
|
-
return { key: stage.key };
|
|
688
|
-
},
|
|
689
|
-
clear() {
|
|
690
|
-
for (const stage of stages.values()) {
|
|
691
|
-
stage.tasks.clear();
|
|
692
|
-
}
|
|
693
|
-
markScheduleDirty();
|
|
694
|
-
}
|
|
695
|
-
};
|
|
225
|
+
* Creates a frame registry used by `FragCanvas` and `useFrame`.
|
|
226
|
+
*
|
|
227
|
+
* @param options - Initial scheduler options.
|
|
228
|
+
* @returns Mutable frame registry instance.
|
|
229
|
+
*/
|
|
230
|
+
function createFrameRegistry(options) {
|
|
231
|
+
let renderMode = options?.renderMode ?? "always";
|
|
232
|
+
let autoRender = options?.autoRender ?? true;
|
|
233
|
+
let maxDelta = options?.maxDelta ?? .1;
|
|
234
|
+
let profilingEnabled = options?.profilingEnabled ?? options?.diagnosticsEnabled ?? false;
|
|
235
|
+
let profilingWindow = options?.profilingWindow ?? 120;
|
|
236
|
+
let lastRunTimings = null;
|
|
237
|
+
const profilingHistory = [];
|
|
238
|
+
let hasUntokenizedInvalidation = true;
|
|
239
|
+
const invalidationTokens = /* @__PURE__ */ new Set();
|
|
240
|
+
let shouldAdvance = false;
|
|
241
|
+
let orderCounter = 0;
|
|
242
|
+
const assertMaxDelta = (value) => {
|
|
243
|
+
if (!Number.isFinite(value) || value <= 0) throw new Error("maxDelta must be a finite number greater than 0");
|
|
244
|
+
return value;
|
|
245
|
+
};
|
|
246
|
+
const assertProfilingWindow = (value) => {
|
|
247
|
+
if (!Number.isFinite(value) || value <= 0) throw new Error("profilingWindow must be a finite number greater than 0");
|
|
248
|
+
return Math.floor(value);
|
|
249
|
+
};
|
|
250
|
+
maxDelta = assertMaxDelta(maxDelta);
|
|
251
|
+
profilingWindow = assertProfilingWindow(profilingWindow);
|
|
252
|
+
const stages = /* @__PURE__ */ new Map();
|
|
253
|
+
let scheduleDirty = true;
|
|
254
|
+
let sortedStages = [];
|
|
255
|
+
const sortedTasksByStage = /* @__PURE__ */ new Map();
|
|
256
|
+
let scheduleSnapshot = { stages: [] };
|
|
257
|
+
const markScheduleDirty = () => {
|
|
258
|
+
scheduleDirty = true;
|
|
259
|
+
};
|
|
260
|
+
const syncSchedule = () => {
|
|
261
|
+
if (!scheduleDirty) return;
|
|
262
|
+
const stageList = sortByDependencies(Array.from(stages.values()), (stage) => stage.before, (stage) => stage.after, {
|
|
263
|
+
graphName: "Frame stage graph",
|
|
264
|
+
getItemLabel: (stage) => `stage "${frameKeyToString(stage.key)}"`
|
|
265
|
+
});
|
|
266
|
+
const nextTasksByStage = /* @__PURE__ */ new Map();
|
|
267
|
+
const globalTaskKeys = /* @__PURE__ */ new Set();
|
|
268
|
+
for (const stage of stageList) for (const task of stage.tasks.values()) globalTaskKeys.add(task.task.key);
|
|
269
|
+
for (const stage of stageList) {
|
|
270
|
+
const taskList = sortByDependencies(Array.from(stage.tasks.values()).map((task) => ({
|
|
271
|
+
key: task.task.key,
|
|
272
|
+
order: task.order,
|
|
273
|
+
task
|
|
274
|
+
})), (task) => task.task.before, (task) => task.task.after, {
|
|
275
|
+
graphName: `Frame task graph for stage "${frameKeyToString(stage.key)}"`,
|
|
276
|
+
getItemLabel: (task) => `task "${frameKeyToString(task.key)}"`,
|
|
277
|
+
isKnownExternalDependency: (key) => globalTaskKeys.has(key)
|
|
278
|
+
}).map((task) => task.task);
|
|
279
|
+
nextTasksByStage.set(stage.key, taskList);
|
|
280
|
+
}
|
|
281
|
+
sortedStages = stageList;
|
|
282
|
+
sortedTasksByStage.clear();
|
|
283
|
+
for (const [stageKey, taskList] of nextTasksByStage) sortedTasksByStage.set(stageKey, taskList);
|
|
284
|
+
scheduleSnapshot = { stages: sortedStages.map((stage) => ({
|
|
285
|
+
key: frameKeyToString(stage.key),
|
|
286
|
+
tasks: (sortedTasksByStage.get(stage.key) ?? []).map((task) => frameKeyToString(task.task.key))
|
|
287
|
+
})) };
|
|
288
|
+
scheduleDirty = false;
|
|
289
|
+
};
|
|
290
|
+
const pushProfile = (timings) => {
|
|
291
|
+
profilingHistory.push(timings);
|
|
292
|
+
while (profilingHistory.length > profilingWindow) profilingHistory.shift();
|
|
293
|
+
};
|
|
294
|
+
const clearProfiling = () => {
|
|
295
|
+
profilingHistory.length = 0;
|
|
296
|
+
lastRunTimings = null;
|
|
297
|
+
};
|
|
298
|
+
const buildProfilingSnapshot = () => {
|
|
299
|
+
if (!profilingEnabled) return null;
|
|
300
|
+
const stageBuckets = /* @__PURE__ */ new Map();
|
|
301
|
+
const totalDurations = [];
|
|
302
|
+
for (const frame of profilingHistory) {
|
|
303
|
+
totalDurations.push(frame.total);
|
|
304
|
+
for (const [stageKey, stageTiming] of Object.entries(frame.stages)) {
|
|
305
|
+
const stageBucket = stageBuckets.get(stageKey) ?? {
|
|
306
|
+
durations: [],
|
|
307
|
+
taskDurations: /* @__PURE__ */ new Map()
|
|
308
|
+
};
|
|
309
|
+
stageBucket.durations.push(stageTiming.duration);
|
|
310
|
+
for (const [taskKey, taskDuration] of Object.entries(stageTiming.tasks)) {
|
|
311
|
+
const bucket = stageBucket.taskDurations.get(taskKey) ?? [];
|
|
312
|
+
bucket.push(taskDuration);
|
|
313
|
+
stageBucket.taskDurations.set(taskKey, bucket);
|
|
314
|
+
}
|
|
315
|
+
stageBuckets.set(stageKey, stageBucket);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const stagesSnapshot = {};
|
|
319
|
+
for (const [stageKey, stageBucket] of stageBuckets) {
|
|
320
|
+
const lastStageDuration = lastRunTimings?.stages[stageKey]?.duration ?? 0;
|
|
321
|
+
const taskSnapshot = {};
|
|
322
|
+
for (const [taskKey, taskDurations] of stageBucket.taskDurations) taskSnapshot[taskKey] = buildTimingStats(taskDurations, lastRunTimings?.stages[stageKey]?.tasks[taskKey] ?? 0);
|
|
323
|
+
stagesSnapshot[stageKey] = {
|
|
324
|
+
timings: buildTimingStats(stageBucket.durations, lastStageDuration),
|
|
325
|
+
tasks: taskSnapshot
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
window: profilingWindow,
|
|
330
|
+
frameCount: profilingHistory.length,
|
|
331
|
+
lastFrame: lastRunTimings,
|
|
332
|
+
total: buildTimingStats(totalDurations, lastRunTimings?.total ?? 0),
|
|
333
|
+
stages: stagesSnapshot
|
|
334
|
+
};
|
|
335
|
+
};
|
|
336
|
+
const ensureStage = (stageReference, stageOptions) => {
|
|
337
|
+
const stageKey = toStageKey(stageReference);
|
|
338
|
+
const existing = stages.get(stageKey);
|
|
339
|
+
if (existing) {
|
|
340
|
+
if (stageOptions?.before !== void 0) {
|
|
341
|
+
existing.before = new Set(stageOptions.before.map((entry) => toStageKey(entry)));
|
|
342
|
+
markScheduleDirty();
|
|
343
|
+
}
|
|
344
|
+
if (stageOptions?.after !== void 0) {
|
|
345
|
+
existing.after = new Set(stageOptions.after.map((entry) => toStageKey(entry)));
|
|
346
|
+
markScheduleDirty();
|
|
347
|
+
}
|
|
348
|
+
if (stageOptions && Object.prototype.hasOwnProperty.call(stageOptions, "callback")) existing.callback = stageOptions.callback ?? DEFAULT_STAGE_CALLBACK;
|
|
349
|
+
return existing;
|
|
350
|
+
}
|
|
351
|
+
const stage = {
|
|
352
|
+
key: stageKey,
|
|
353
|
+
order: orderCounter++,
|
|
354
|
+
started: true,
|
|
355
|
+
before: new Set((stageOptions?.before ?? []).map((entry) => toStageKey(entry))),
|
|
356
|
+
after: new Set((stageOptions?.after ?? []).map((entry) => toStageKey(entry))),
|
|
357
|
+
callback: stageOptions?.callback ?? DEFAULT_STAGE_CALLBACK,
|
|
358
|
+
tasks: /* @__PURE__ */ new Map()
|
|
359
|
+
};
|
|
360
|
+
stages.set(stageKey, stage);
|
|
361
|
+
markScheduleDirty();
|
|
362
|
+
return stage;
|
|
363
|
+
};
|
|
364
|
+
ensureStage(MAIN_STAGE_KEY);
|
|
365
|
+
const resolveEffectiveRunning = (task) => {
|
|
366
|
+
const running = task.started && (task.running?.() ?? true);
|
|
367
|
+
if (task.lastRunning !== running) {
|
|
368
|
+
task.lastRunning = running;
|
|
369
|
+
task.startedStoreSet(running);
|
|
370
|
+
}
|
|
371
|
+
return running;
|
|
372
|
+
};
|
|
373
|
+
const hasPendingInvalidation = () => {
|
|
374
|
+
return hasUntokenizedInvalidation || invalidationTokens.size > 0;
|
|
375
|
+
};
|
|
376
|
+
const invalidateWithToken = (token) => {
|
|
377
|
+
if (token === void 0) {
|
|
378
|
+
hasUntokenizedInvalidation = true;
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
invalidationTokens.add(token);
|
|
382
|
+
};
|
|
383
|
+
const applyTaskInvalidation = (task) => {
|
|
384
|
+
const config = task.invalidation;
|
|
385
|
+
if (config.mode === "never") return;
|
|
386
|
+
if (config.mode === "always") {
|
|
387
|
+
invalidateWithToken(resolveInvalidationToken(config.token) ?? task.task.key);
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
const token = resolveInvalidationToken(config.token);
|
|
391
|
+
if (token === null) {
|
|
392
|
+
config.hasToken = false;
|
|
393
|
+
config.lastToken = null;
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
const changed = !config.hasToken || config.lastToken !== token;
|
|
397
|
+
config.hasToken = true;
|
|
398
|
+
config.lastToken = token;
|
|
399
|
+
if (changed) invalidateWithToken(token);
|
|
400
|
+
};
|
|
401
|
+
return {
|
|
402
|
+
register(keyOrCallback, callbackOrOptions, maybeOptions) {
|
|
403
|
+
const key = typeof keyOrCallback === "function" ? Symbol("motiongpu-task") : keyOrCallback;
|
|
404
|
+
const callback = typeof keyOrCallback === "function" ? keyOrCallback : callbackOrOptions;
|
|
405
|
+
const taskOptions = typeof keyOrCallback === "function" ? callbackOrOptions ?? {} : maybeOptions ?? {};
|
|
406
|
+
if (typeof callback !== "function") throw new Error("useFrame requires a callback");
|
|
407
|
+
const before = asArray(taskOptions.before);
|
|
408
|
+
const after = asArray(taskOptions.after);
|
|
409
|
+
const inferredStage = [...before, ...after].find((entry) => typeof entry === "object" && entry !== null && "stage" in entry);
|
|
410
|
+
const stage = ensureStage(taskOptions.stage ? toStageKey(taskOptions.stage) : inferredStage?.stage ?? MAIN_STAGE_KEY);
|
|
411
|
+
const startedWritable = createCurrentWritable(taskOptions.autoStart ?? true);
|
|
412
|
+
const internalTask = {
|
|
413
|
+
task: {
|
|
414
|
+
key,
|
|
415
|
+
stage: stage.key
|
|
416
|
+
},
|
|
417
|
+
callback,
|
|
418
|
+
order: orderCounter++,
|
|
419
|
+
started: taskOptions.autoStart ?? true,
|
|
420
|
+
lastRunning: taskOptions.autoStart ?? true,
|
|
421
|
+
startedStoreSet: startedWritable.set,
|
|
422
|
+
startedStore: { subscribe: startedWritable.subscribe },
|
|
423
|
+
before: new Set(before.map((entry) => toTaskKey(entry))),
|
|
424
|
+
after: new Set(after.map((entry) => toTaskKey(entry))),
|
|
425
|
+
invalidation: normalizeTaskInvalidation(key, taskOptions)
|
|
426
|
+
};
|
|
427
|
+
if (taskOptions.running) internalTask.running = taskOptions.running;
|
|
428
|
+
stage.tasks.set(key, internalTask);
|
|
429
|
+
markScheduleDirty();
|
|
430
|
+
internalTask.startedStoreSet(resolveEffectiveRunning(internalTask));
|
|
431
|
+
const start = () => {
|
|
432
|
+
internalTask.started = true;
|
|
433
|
+
resolveEffectiveRunning(internalTask);
|
|
434
|
+
};
|
|
435
|
+
const stop = () => {
|
|
436
|
+
internalTask.started = false;
|
|
437
|
+
resolveEffectiveRunning(internalTask);
|
|
438
|
+
};
|
|
439
|
+
return {
|
|
440
|
+
task: internalTask.task,
|
|
441
|
+
start,
|
|
442
|
+
stop,
|
|
443
|
+
started: internalTask.startedStore,
|
|
444
|
+
unsubscribe: () => {
|
|
445
|
+
if (stage.tasks.get(key) === internalTask && stage.tasks.delete(key)) markScheduleDirty();
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
},
|
|
449
|
+
run(state) {
|
|
450
|
+
const clampedDelta = Math.min(state.delta, maxDelta);
|
|
451
|
+
const frameState = clampedDelta === state.delta ? state : {
|
|
452
|
+
...state,
|
|
453
|
+
delta: clampedDelta
|
|
454
|
+
};
|
|
455
|
+
syncSchedule();
|
|
456
|
+
const frameStart = profilingEnabled ? performance.now() : 0;
|
|
457
|
+
const stageTimings = {};
|
|
458
|
+
for (const stage of sortedStages) {
|
|
459
|
+
if (!stage.started) continue;
|
|
460
|
+
const stageStart = profilingEnabled ? performance.now() : 0;
|
|
461
|
+
const taskTimings = {};
|
|
462
|
+
const taskList = sortedTasksByStage.get(stage.key) ?? [];
|
|
463
|
+
stage.callback(frameState, () => {
|
|
464
|
+
for (const task of taskList) {
|
|
465
|
+
if (!resolveEffectiveRunning(task)) continue;
|
|
466
|
+
const taskStart = profilingEnabled ? performance.now() : 0;
|
|
467
|
+
task.callback(frameState);
|
|
468
|
+
if (profilingEnabled) taskTimings[frameKeyToString(task.task.key)] = performance.now() - taskStart;
|
|
469
|
+
applyTaskInvalidation(task);
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
if (profilingEnabled) stageTimings[frameKeyToString(stage.key)] = {
|
|
473
|
+
duration: performance.now() - stageStart,
|
|
474
|
+
tasks: taskTimings
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
if (profilingEnabled) {
|
|
478
|
+
const timings = {
|
|
479
|
+
total: performance.now() - frameStart,
|
|
480
|
+
stages: stageTimings
|
|
481
|
+
};
|
|
482
|
+
lastRunTimings = timings;
|
|
483
|
+
pushProfile(timings);
|
|
484
|
+
}
|
|
485
|
+
},
|
|
486
|
+
invalidate(token) {
|
|
487
|
+
invalidateWithToken(token);
|
|
488
|
+
},
|
|
489
|
+
advance() {
|
|
490
|
+
shouldAdvance = true;
|
|
491
|
+
invalidateWithToken();
|
|
492
|
+
},
|
|
493
|
+
shouldRender() {
|
|
494
|
+
if (!autoRender) return false;
|
|
495
|
+
if (renderMode === "always") return true;
|
|
496
|
+
if (renderMode === "on-demand") return shouldAdvance || hasPendingInvalidation();
|
|
497
|
+
return shouldAdvance;
|
|
498
|
+
},
|
|
499
|
+
endFrame() {
|
|
500
|
+
hasUntokenizedInvalidation = false;
|
|
501
|
+
invalidationTokens.clear();
|
|
502
|
+
shouldAdvance = false;
|
|
503
|
+
},
|
|
504
|
+
setRenderMode(mode) {
|
|
505
|
+
if (renderMode === mode) return;
|
|
506
|
+
renderMode = mode;
|
|
507
|
+
shouldAdvance = false;
|
|
508
|
+
if (mode === "on-demand") invalidateWithToken(RENDER_MODE_INVALIDATION_TOKEN);
|
|
509
|
+
},
|
|
510
|
+
setAutoRender(enabled) {
|
|
511
|
+
autoRender = enabled;
|
|
512
|
+
},
|
|
513
|
+
setMaxDelta(value) {
|
|
514
|
+
maxDelta = assertMaxDelta(value);
|
|
515
|
+
},
|
|
516
|
+
setProfilingEnabled(enabled) {
|
|
517
|
+
profilingEnabled = enabled;
|
|
518
|
+
if (!enabled) clearProfiling();
|
|
519
|
+
},
|
|
520
|
+
setProfilingWindow(window) {
|
|
521
|
+
profilingWindow = assertProfilingWindow(window);
|
|
522
|
+
while (profilingHistory.length > profilingWindow) profilingHistory.shift();
|
|
523
|
+
},
|
|
524
|
+
resetProfiling() {
|
|
525
|
+
clearProfiling();
|
|
526
|
+
},
|
|
527
|
+
setDiagnosticsEnabled(enabled) {
|
|
528
|
+
profilingEnabled = enabled;
|
|
529
|
+
if (!enabled) clearProfiling();
|
|
530
|
+
},
|
|
531
|
+
getRenderMode() {
|
|
532
|
+
return renderMode;
|
|
533
|
+
},
|
|
534
|
+
getAutoRender() {
|
|
535
|
+
return autoRender;
|
|
536
|
+
},
|
|
537
|
+
getMaxDelta() {
|
|
538
|
+
return maxDelta;
|
|
539
|
+
},
|
|
540
|
+
getProfilingEnabled() {
|
|
541
|
+
return profilingEnabled;
|
|
542
|
+
},
|
|
543
|
+
getProfilingWindow() {
|
|
544
|
+
return profilingWindow;
|
|
545
|
+
},
|
|
546
|
+
getProfilingSnapshot() {
|
|
547
|
+
return buildProfilingSnapshot();
|
|
548
|
+
},
|
|
549
|
+
getDiagnosticsEnabled() {
|
|
550
|
+
return profilingEnabled;
|
|
551
|
+
},
|
|
552
|
+
getLastRunTimings() {
|
|
553
|
+
return lastRunTimings;
|
|
554
|
+
},
|
|
555
|
+
getSchedule() {
|
|
556
|
+
syncSchedule();
|
|
557
|
+
return scheduleSnapshot;
|
|
558
|
+
},
|
|
559
|
+
createStage(key, options) {
|
|
560
|
+
return { key: ensureStage(key, options ? {
|
|
561
|
+
...Object.prototype.hasOwnProperty.call(options, "before") ? { before: asArray(options.before) } : {},
|
|
562
|
+
...Object.prototype.hasOwnProperty.call(options, "after") ? { after: asArray(options.after) } : {},
|
|
563
|
+
...Object.prototype.hasOwnProperty.call(options, "callback") ? { callback: options.callback ?? null } : {}
|
|
564
|
+
} : void 0).key };
|
|
565
|
+
},
|
|
566
|
+
getStage(key) {
|
|
567
|
+
const stage = stages.get(key);
|
|
568
|
+
if (!stage) return;
|
|
569
|
+
return { key: stage.key };
|
|
570
|
+
},
|
|
571
|
+
clear() {
|
|
572
|
+
for (const stage of stages.values()) stage.tasks.clear();
|
|
573
|
+
markScheduleDirty();
|
|
574
|
+
}
|
|
575
|
+
};
|
|
696
576
|
}
|
|
577
|
+
//#endregion
|
|
578
|
+
export { createFrameRegistry };
|
|
579
|
+
|
|
580
|
+
//# sourceMappingURL=frame-registry.js.map
|