@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.
Files changed (228) hide show
  1. package/README.md +99 -0
  2. package/dist/advanced.d.ts +1 -0
  3. package/dist/advanced.d.ts.map +1 -0
  4. package/dist/advanced.js +14 -6
  5. package/dist/core/advanced.d.ts +1 -0
  6. package/dist/core/advanced.d.ts.map +1 -0
  7. package/dist/core/advanced.js +14 -5
  8. package/dist/core/compute-shader.d.ts +87 -0
  9. package/dist/core/compute-shader.d.ts.map +1 -0
  10. package/dist/core/compute-shader.js +205 -0
  11. package/dist/core/compute-shader.js.map +1 -0
  12. package/dist/core/current-value.d.ts +1 -0
  13. package/dist/core/current-value.d.ts.map +1 -0
  14. package/dist/core/current-value.js +35 -34
  15. package/dist/core/current-value.js.map +1 -0
  16. package/dist/core/error-diagnostics.d.ts +1 -0
  17. package/dist/core/error-diagnostics.d.ts.map +1 -0
  18. package/dist/core/error-diagnostics.js +70 -137
  19. package/dist/core/error-diagnostics.js.map +1 -0
  20. package/dist/core/error-report.d.ts +2 -1
  21. package/dist/core/error-report.d.ts.map +1 -0
  22. package/dist/core/error-report.js +247 -233
  23. package/dist/core/error-report.js.map +1 -0
  24. package/dist/core/frame-registry.d.ts +1 -0
  25. package/dist/core/frame-registry.d.ts.map +1 -0
  26. package/dist/core/frame-registry.js +546 -662
  27. package/dist/core/frame-registry.js.map +1 -0
  28. package/dist/core/index.d.ts +6 -2
  29. package/dist/core/index.d.ts.map +1 -0
  30. package/dist/core/index.js +13 -12
  31. package/dist/core/material-preprocess.d.ts +1 -0
  32. package/dist/core/material-preprocess.d.ts.map +1 -0
  33. package/dist/core/material-preprocess.js +131 -152
  34. package/dist/core/material-preprocess.js.map +1 -0
  35. package/dist/core/material.d.ts +23 -6
  36. package/dist/core/material.d.ts.map +1 -0
  37. package/dist/core/material.js +290 -317
  38. package/dist/core/material.js.map +1 -0
  39. package/dist/core/recompile-policy.d.ts +1 -0
  40. package/dist/core/recompile-policy.d.ts.map +1 -0
  41. package/dist/core/recompile-policy.js +18 -13
  42. package/dist/core/recompile-policy.js.map +1 -0
  43. package/dist/core/render-graph.d.ts +8 -3
  44. package/dist/core/render-graph.d.ts.map +1 -0
  45. package/dist/core/render-graph.js +77 -68
  46. package/dist/core/render-graph.js.map +1 -0
  47. package/dist/core/render-targets.d.ts +1 -0
  48. package/dist/core/render-targets.d.ts.map +1 -0
  49. package/dist/core/render-targets.js +52 -53
  50. package/dist/core/render-targets.js.map +1 -0
  51. package/dist/core/renderer.d.ts +1 -0
  52. package/dist/core/renderer.d.ts.map +1 -0
  53. package/dist/core/renderer.js +1337 -1081
  54. package/dist/core/renderer.js.map +1 -0
  55. package/dist/core/runtime-loop.d.ts +3 -2
  56. package/dist/core/runtime-loop.d.ts.map +1 -0
  57. package/dist/core/runtime-loop.js +353 -362
  58. package/dist/core/runtime-loop.js.map +1 -0
  59. package/dist/core/scheduler-helpers.d.ts +1 -0
  60. package/dist/core/scheduler-helpers.d.ts.map +1 -0
  61. package/dist/core/scheduler-helpers.js +52 -51
  62. package/dist/core/scheduler-helpers.js.map +1 -0
  63. package/dist/core/shader.d.ts +10 -1
  64. package/dist/core/shader.d.ts.map +1 -0
  65. package/dist/core/shader.js +109 -115
  66. package/dist/core/shader.js.map +1 -0
  67. package/dist/core/storage-buffers.d.ts +37 -0
  68. package/dist/core/storage-buffers.d.ts.map +1 -0
  69. package/dist/core/storage-buffers.js +95 -0
  70. package/dist/core/storage-buffers.js.map +1 -0
  71. package/dist/core/texture-loader.d.ts +1 -0
  72. package/dist/core/texture-loader.d.ts.map +1 -0
  73. package/dist/core/texture-loader.js +209 -273
  74. package/dist/core/texture-loader.js.map +1 -0
  75. package/dist/core/textures.d.ts +13 -0
  76. package/dist/core/textures.d.ts.map +1 -0
  77. package/dist/core/textures.js +111 -116
  78. package/dist/core/textures.js.map +1 -0
  79. package/dist/core/types.d.ts +147 -4
  80. package/dist/core/types.d.ts.map +1 -0
  81. package/dist/core/types.js +0 -4
  82. package/dist/core/uniforms.d.ts +1 -0
  83. package/dist/core/uniforms.d.ts.map +1 -0
  84. package/dist/core/uniforms.js +170 -191
  85. package/dist/core/uniforms.js.map +1 -0
  86. package/dist/index.d.ts +1 -0
  87. package/dist/index.d.ts.map +1 -0
  88. package/dist/index.js +13 -6
  89. package/dist/passes/BlitPass.d.ts +1 -0
  90. package/dist/passes/BlitPass.d.ts.map +1 -0
  91. package/dist/passes/BlitPass.js +23 -18
  92. package/dist/passes/BlitPass.js.map +1 -0
  93. package/dist/passes/ComputePass.d.ts +83 -0
  94. package/dist/passes/ComputePass.d.ts.map +1 -0
  95. package/dist/passes/ComputePass.js +92 -0
  96. package/dist/passes/ComputePass.js.map +1 -0
  97. package/dist/passes/CopyPass.d.ts +1 -0
  98. package/dist/passes/CopyPass.d.ts.map +1 -0
  99. package/dist/passes/CopyPass.js +58 -52
  100. package/dist/passes/CopyPass.js.map +1 -0
  101. package/dist/passes/FullscreenPass.d.ts +1 -0
  102. package/dist/passes/FullscreenPass.d.ts.map +1 -0
  103. package/dist/passes/FullscreenPass.js +127 -130
  104. package/dist/passes/FullscreenPass.js.map +1 -0
  105. package/dist/passes/PingPongComputePass.d.ts +104 -0
  106. package/dist/passes/PingPongComputePass.d.ts.map +1 -0
  107. package/dist/passes/PingPongComputePass.js +132 -0
  108. package/dist/passes/PingPongComputePass.js.map +1 -0
  109. package/dist/passes/ShaderPass.d.ts +1 -0
  110. package/dist/passes/ShaderPass.d.ts.map +1 -0
  111. package/dist/passes/ShaderPass.js +41 -37
  112. package/dist/passes/ShaderPass.js.map +1 -0
  113. package/dist/passes/index.d.ts +3 -0
  114. package/dist/passes/index.d.ts.map +1 -0
  115. package/dist/passes/index.js +6 -3
  116. package/dist/react/FragCanvas.d.ts +3 -2
  117. package/dist/react/FragCanvas.d.ts.map +1 -0
  118. package/dist/react/FragCanvas.js +234 -211
  119. package/dist/react/FragCanvas.js.map +1 -0
  120. package/dist/react/MotionGPUErrorOverlay.d.ts +1 -0
  121. package/dist/react/MotionGPUErrorOverlay.d.ts.map +1 -0
  122. package/dist/react/MotionGPUErrorOverlay.js +200 -14
  123. package/dist/react/MotionGPUErrorOverlay.js.map +1 -0
  124. package/dist/react/Portal.d.ts +1 -0
  125. package/dist/react/Portal.d.ts.map +1 -0
  126. package/dist/react/Portal.js +18 -21
  127. package/dist/react/Portal.js.map +1 -0
  128. package/dist/react/advanced.d.ts +1 -0
  129. package/dist/react/advanced.d.ts.map +1 -0
  130. package/dist/react/advanced.js +14 -6
  131. package/dist/react/frame-context.d.ts +1 -0
  132. package/dist/react/frame-context.d.ts.map +1 -0
  133. package/dist/react/frame-context.js +88 -94
  134. package/dist/react/frame-context.js.map +1 -0
  135. package/dist/react/index.d.ts +6 -2
  136. package/dist/react/index.d.ts.map +1 -0
  137. package/dist/react/index.js +12 -9
  138. package/dist/react/motiongpu-context.d.ts +1 -0
  139. package/dist/react/motiongpu-context.d.ts.map +1 -0
  140. package/dist/react/motiongpu-context.js +18 -15
  141. package/dist/react/motiongpu-context.js.map +1 -0
  142. package/dist/react/use-motiongpu-user-context.d.ts +1 -0
  143. package/dist/react/use-motiongpu-user-context.d.ts.map +1 -0
  144. package/dist/react/use-motiongpu-user-context.js +83 -82
  145. package/dist/react/use-motiongpu-user-context.js.map +1 -0
  146. package/dist/react/use-texture.d.ts +1 -0
  147. package/dist/react/use-texture.d.ts.map +1 -0
  148. package/dist/react/use-texture.js +132 -152
  149. package/dist/react/use-texture.js.map +1 -0
  150. package/dist/svelte/FragCanvas.svelte +2 -2
  151. package/dist/svelte/FragCanvas.svelte.d.ts +3 -2
  152. package/dist/svelte/FragCanvas.svelte.d.ts.map +1 -0
  153. package/dist/svelte/MotionGPUErrorOverlay.svelte +137 -7
  154. package/dist/svelte/MotionGPUErrorOverlay.svelte.d.ts +1 -0
  155. package/dist/svelte/MotionGPUErrorOverlay.svelte.d.ts.map +1 -0
  156. package/dist/svelte/Portal.svelte.d.ts +1 -0
  157. package/dist/svelte/Portal.svelte.d.ts.map +1 -0
  158. package/dist/svelte/advanced.d.ts +1 -0
  159. package/dist/svelte/advanced.d.ts.map +1 -0
  160. package/dist/svelte/advanced.js +13 -6
  161. package/dist/svelte/frame-context.d.ts +1 -0
  162. package/dist/svelte/frame-context.d.ts.map +1 -0
  163. package/dist/svelte/frame-context.js +27 -27
  164. package/dist/svelte/frame-context.js.map +1 -0
  165. package/dist/svelte/index.d.ts +6 -2
  166. package/dist/svelte/index.d.ts.map +1 -0
  167. package/dist/svelte/index.js +12 -9
  168. package/dist/svelte/motiongpu-context.d.ts +1 -0
  169. package/dist/svelte/motiongpu-context.d.ts.map +1 -0
  170. package/dist/svelte/motiongpu-context.js +24 -21
  171. package/dist/svelte/motiongpu-context.js.map +1 -0
  172. package/dist/svelte/use-motiongpu-user-context.d.ts +1 -0
  173. package/dist/svelte/use-motiongpu-user-context.d.ts.map +1 -0
  174. package/dist/svelte/use-motiongpu-user-context.js +69 -70
  175. package/dist/svelte/use-motiongpu-user-context.js.map +1 -0
  176. package/dist/svelte/use-texture.d.ts +1 -0
  177. package/dist/svelte/use-texture.d.ts.map +1 -0
  178. package/dist/svelte/use-texture.js +125 -147
  179. package/dist/svelte/use-texture.js.map +1 -0
  180. package/package.json +12 -7
  181. package/src/lib/advanced.ts +6 -0
  182. package/src/lib/core/advanced.ts +12 -0
  183. package/src/lib/core/compute-shader.ts +326 -0
  184. package/src/lib/core/current-value.ts +64 -0
  185. package/src/lib/core/error-diagnostics.ts +236 -0
  186. package/src/lib/core/error-report.ts +535 -0
  187. package/src/lib/core/frame-registry.ts +1190 -0
  188. package/src/lib/core/index.ts +94 -0
  189. package/src/lib/core/material-preprocess.ts +295 -0
  190. package/src/lib/core/material.ts +748 -0
  191. package/src/lib/core/recompile-policy.ts +31 -0
  192. package/src/lib/core/render-graph.ts +173 -0
  193. package/src/lib/core/render-targets.ts +107 -0
  194. package/src/lib/core/renderer.ts +2161 -0
  195. package/src/lib/core/runtime-loop.ts +537 -0
  196. package/src/lib/core/scheduler-helpers.ts +136 -0
  197. package/src/lib/core/shader.ts +301 -0
  198. package/src/lib/core/storage-buffers.ts +142 -0
  199. package/src/lib/core/texture-loader.ts +482 -0
  200. package/src/lib/core/textures.ts +257 -0
  201. package/src/lib/core/types.ts +743 -0
  202. package/src/lib/core/uniforms.ts +282 -0
  203. package/src/lib/index.ts +6 -0
  204. package/src/lib/passes/BlitPass.ts +54 -0
  205. package/src/lib/passes/ComputePass.ts +136 -0
  206. package/src/lib/passes/CopyPass.ts +80 -0
  207. package/src/lib/passes/FullscreenPass.ts +173 -0
  208. package/src/lib/passes/PingPongComputePass.ts +180 -0
  209. package/src/lib/passes/ShaderPass.ts +89 -0
  210. package/src/lib/passes/index.ts +9 -0
  211. package/src/lib/react/FragCanvas.tsx +345 -0
  212. package/src/lib/react/MotionGPUErrorOverlay.tsx +524 -0
  213. package/src/lib/react/Portal.tsx +34 -0
  214. package/src/lib/react/advanced.ts +36 -0
  215. package/src/lib/react/frame-context.ts +169 -0
  216. package/src/lib/react/index.ts +68 -0
  217. package/src/lib/react/motiongpu-context.ts +88 -0
  218. package/src/lib/react/use-motiongpu-user-context.ts +186 -0
  219. package/src/lib/react/use-texture.ts +233 -0
  220. package/src/lib/svelte/FragCanvas.svelte +249 -0
  221. package/src/lib/svelte/MotionGPUErrorOverlay.svelte +512 -0
  222. package/src/lib/svelte/Portal.svelte +31 -0
  223. package/src/lib/svelte/advanced.ts +32 -0
  224. package/src/lib/svelte/frame-context.ts +87 -0
  225. package/src/lib/svelte/index.ts +68 -0
  226. package/src/lib/svelte/motiongpu-context.ts +97 -0
  227. package/src/lib/svelte/use-motiongpu-user-context.ts +145 -0
  228. package/src/lib/svelte/use-texture.ts +232 -0
@@ -0,0 +1,31 @@
1
+ import type { OutputColorSpace } from './types.js';
2
+
3
+ /**
4
+ * Inputs that affect renderer pipeline compilation.
5
+ */
6
+ export interface RendererPipelineSignatureInput {
7
+ /**
8
+ * Material pipeline signature (fragment preprocess + uniform/texture layout).
9
+ */
10
+ materialSignature: string;
11
+ /**
12
+ * Output color-space transform mode.
13
+ */
14
+ outputColorSpace: OutputColorSpace;
15
+ }
16
+
17
+ /**
18
+ * Returns deterministic renderer pipeline signature.
19
+ *
20
+ * Rebuild triggers:
21
+ * - material signature changes (shader/layout related)
22
+ * - output color space changes
23
+ *
24
+ * Non-triggers:
25
+ * - runtime uniform values
26
+ * - runtime texture sources
27
+ * - clear color changes
28
+ */
29
+ export function buildRendererPipelineSignature(input: RendererPipelineSignatureInput): string {
30
+ return `${input.materialSignature}|${input.outputColorSpace}`;
31
+ }
@@ -0,0 +1,173 @@
1
+ import type { AnyPass, RenderPass, RenderPassInputSlot, RenderPassOutputSlot } from './types.js';
2
+
3
+ /**
4
+ * Resolved render-pass step with defaults applied.
5
+ */
6
+ export interface RenderGraphStep {
7
+ /**
8
+ * Step kind. 'render' for existing passes, 'compute' for compute passes.
9
+ */
10
+ kind: 'render' | 'compute';
11
+ /**
12
+ * User pass instance.
13
+ */
14
+ pass: AnyPass;
15
+ /**
16
+ * Resolved input slot.
17
+ */
18
+ input: RenderPassInputSlot;
19
+ /**
20
+ * Resolved output slot.
21
+ */
22
+ output: RenderPassOutputSlot;
23
+ /**
24
+ * Whether ping-pong swap should be performed after render.
25
+ */
26
+ needsSwap: boolean;
27
+ /**
28
+ * Whether pass should clear output before drawing.
29
+ */
30
+ clear: boolean;
31
+ /**
32
+ * Effective clear color.
33
+ */
34
+ clearColor: [number, number, number, number];
35
+ /**
36
+ * Whether output should be preserved after pass ends.
37
+ */
38
+ preserve: boolean;
39
+ }
40
+
41
+ /**
42
+ * Immutable render-graph execution plan for one frame.
43
+ */
44
+ export interface RenderGraphPlan {
45
+ /**
46
+ * Resolved enabled steps in execution order.
47
+ */
48
+ steps: RenderGraphStep[];
49
+ /**
50
+ * Output slot holding final frame result before presentation.
51
+ */
52
+ finalOutput: RenderPassOutputSlot;
53
+ }
54
+
55
+ /**
56
+ * Creates a copy of RGBA clear color.
57
+ */
58
+ function cloneClearColor(
59
+ color: [number, number, number, number]
60
+ ): [number, number, number, number] {
61
+ return [color[0], color[1], color[2], color[3]];
62
+ }
63
+
64
+ /**
65
+ * Builds validated render graph plan from runtime pass list.
66
+ *
67
+ * @param passes - Runtime passes.
68
+ * @param defaultClearColor - Global clear color fallback.
69
+ * @returns Resolved render graph plan.
70
+ */
71
+ export function planRenderGraph(
72
+ passes: AnyPass[] | undefined,
73
+ defaultClearColor: [number, number, number, number],
74
+ renderTargetSlots?: Iterable<string>
75
+ ): RenderGraphPlan {
76
+ const steps: RenderGraphStep[] = [];
77
+ const declaredTargets = new Set(renderTargetSlots ?? []);
78
+ const availableSlots = new Set<RenderPassInputSlot | RenderPassOutputSlot>(['source']);
79
+ let finalOutput: RenderPassOutputSlot = 'canvas';
80
+ let enabledIndex = 0;
81
+
82
+ for (const pass of passes ?? []) {
83
+ if (pass.enabled === false) {
84
+ continue;
85
+ }
86
+
87
+ // Compute passes don't participate in slot routing
88
+ const isCompute = 'isCompute' in pass && (pass as { isCompute?: boolean }).isCompute === true;
89
+ if (isCompute) {
90
+ steps.push({
91
+ kind: 'compute',
92
+ pass,
93
+ input: 'source',
94
+ output: 'source',
95
+ needsSwap: false,
96
+ clear: false,
97
+ clearColor: cloneClearColor(defaultClearColor),
98
+ preserve: true
99
+ });
100
+ continue;
101
+ }
102
+
103
+ // After compute guard, pass is a render pass
104
+ const rp = pass as RenderPass;
105
+ const needsSwap = rp.needsSwap ?? true;
106
+ const input: RenderPassInputSlot = rp.input ?? 'source';
107
+ const output: RenderPassOutputSlot = rp.output ?? (needsSwap ? 'target' : 'source');
108
+
109
+ if (input === 'canvas') {
110
+ throw new Error(`Render pass #${enabledIndex} cannot read from "canvas".`);
111
+ }
112
+
113
+ const inputIsNamed = input !== 'source' && input !== 'target';
114
+ if (inputIsNamed && !declaredTargets.has(input)) {
115
+ throw new Error(`Render pass #${enabledIndex} reads unknown target "${input}".`);
116
+ }
117
+
118
+ const outputIsNamed = output !== 'source' && output !== 'target' && output !== 'canvas';
119
+ if (outputIsNamed && !declaredTargets.has(output)) {
120
+ throw new Error(`Render pass #${enabledIndex} writes unknown target "${output}".`);
121
+ }
122
+
123
+ if (needsSwap && (input !== 'source' || output !== 'target')) {
124
+ throw new Error(
125
+ `Render pass #${enabledIndex} uses needsSwap=true but does not follow source->target flow.`
126
+ );
127
+ }
128
+
129
+ if (!availableSlots.has(input)) {
130
+ throw new Error(`Render pass #${enabledIndex} reads "${input}" before it is written.`);
131
+ }
132
+
133
+ const clear = rp.clear ?? false;
134
+ const clearColor = cloneClearColor(rp.clearColor ?? defaultClearColor);
135
+ const preserve = rp.preserve ?? true;
136
+
137
+ steps.push({
138
+ kind: 'render',
139
+ pass,
140
+ input,
141
+ output,
142
+ needsSwap,
143
+ clear,
144
+ clearColor,
145
+ preserve
146
+ });
147
+
148
+ if (needsSwap) {
149
+ availableSlots.add('target');
150
+ availableSlots.add('source');
151
+ finalOutput = 'source';
152
+ } else {
153
+ if (output !== 'canvas') {
154
+ availableSlots.add(output);
155
+ }
156
+ finalOutput = output;
157
+ }
158
+
159
+ enabledIndex += 1;
160
+ }
161
+
162
+ // When steps exist (even compute-only) but no render pass changed
163
+ // finalOutput from 'canvas', the scene was drawn to 'source' and
164
+ // needs blitting to the canvas surface.
165
+ if (steps.length > 0 && enabledIndex === 0) {
166
+ finalOutput = 'source';
167
+ }
168
+
169
+ return {
170
+ steps,
171
+ finalOutput
172
+ };
173
+ }
@@ -0,0 +1,107 @@
1
+ import { assertUniformName } from './uniforms.js';
2
+ import type { RenderTargetDefinitionMap } from './types.js';
3
+
4
+ /**
5
+ * Concrete render target configuration resolved for current canvas size.
6
+ */
7
+ export interface ResolvedRenderTargetDefinition {
8
+ /**
9
+ * Render target key.
10
+ */
11
+ key: string;
12
+ /**
13
+ * Resolved width in pixels.
14
+ */
15
+ width: number;
16
+ /**
17
+ * Resolved height in pixels.
18
+ */
19
+ height: number;
20
+ /**
21
+ * Resolved format.
22
+ */
23
+ format: GPUTextureFormat;
24
+ }
25
+
26
+ /**
27
+ * Asserts positive finite numeric input for render target options.
28
+ */
29
+ function assertPositiveFinite(name: string, value: number): void {
30
+ if (!Number.isFinite(value) || value <= 0) {
31
+ throw new Error(`${name} must be a finite number greater than 0`);
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Resolves a render target dimension from explicit value or scaled canvas size.
37
+ */
38
+ function resolveDimension(
39
+ explicitValue: number | undefined,
40
+ canvasDimension: number,
41
+ scale: number
42
+ ): number {
43
+ if (explicitValue !== undefined) {
44
+ assertPositiveFinite('RenderTarget dimension', explicitValue);
45
+ return Math.max(1, Math.floor(explicitValue));
46
+ }
47
+
48
+ return Math.max(1, Math.floor(canvasDimension * scale));
49
+ }
50
+
51
+ /**
52
+ * Resolves all render target definitions for a specific canvas size.
53
+ *
54
+ * @param definitions - Declarative definitions.
55
+ * @param canvasWidth - Current canvas width in pixels.
56
+ * @param canvasHeight - Current canvas height in pixels.
57
+ * @param defaultFormat - Fallback texture format.
58
+ * @returns Sorted concrete render target definitions.
59
+ */
60
+ export function resolveRenderTargetDefinitions(
61
+ definitions: RenderTargetDefinitionMap | undefined,
62
+ canvasWidth: number,
63
+ canvasHeight: number,
64
+ defaultFormat: GPUTextureFormat
65
+ ): ResolvedRenderTargetDefinition[] {
66
+ if (!definitions) {
67
+ return [];
68
+ }
69
+
70
+ const keys = Object.keys(definitions).sort();
71
+ const resolved: ResolvedRenderTargetDefinition[] = [];
72
+
73
+ for (const key of keys) {
74
+ assertUniformName(key);
75
+ const definition = definitions[key];
76
+ const scale = definition?.scale ?? 1;
77
+ assertPositiveFinite('RenderTarget scale', scale);
78
+
79
+ const width = resolveDimension(definition?.width, canvasWidth, scale);
80
+ const height = resolveDimension(definition?.height, canvasHeight, scale);
81
+
82
+ resolved.push({
83
+ key,
84
+ width,
85
+ height,
86
+ format: definition?.format ?? defaultFormat
87
+ });
88
+ }
89
+
90
+ return resolved;
91
+ }
92
+
93
+ /**
94
+ * Builds a deterministic signature used to detect render target topology changes.
95
+ *
96
+ * @param resolvedDefinitions - Concrete target definitions.
97
+ * @returns Stable signature string.
98
+ */
99
+ export function buildRenderTargetSignature(
100
+ resolvedDefinitions: ResolvedRenderTargetDefinition[]
101
+ ): string {
102
+ return resolvedDefinitions
103
+ .map((definition) => {
104
+ return `${definition.key}:${definition.format}:${definition.width}x${definition.height}`;
105
+ })
106
+ .join('|');
107
+ }