@motion-core/motion-gpu 0.4.2 → 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.js +3 -1
- package/dist/core/advanced.js +3 -1
- 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/error-report.d.ts +1 -1
- package/dist/core/error-report.d.ts.map +1 -1
- package/dist/core/error-report.js +63 -0
- package/dist/core/error-report.js.map +1 -1
- package/dist/core/frame-registry.d.ts.map +1 -1
- package/dist/core/frame-registry.js +1 -1
- package/dist/core/frame-registry.js.map +1 -1
- package/dist/core/index.d.ts +5 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -1
- package/dist/core/material-preprocess.d.ts.map +1 -1
- package/dist/core/material-preprocess.js +5 -3
- package/dist/core/material-preprocess.js.map +1 -1
- package/dist/core/material.d.ts +22 -6
- package/dist/core/material.d.ts.map +1 -1
- package/dist/core/material.js +30 -3
- package/dist/core/material.js.map +1 -1
- package/dist/core/render-graph.d.ts +7 -3
- package/dist/core/render-graph.d.ts.map +1 -1
- package/dist/core/render-graph.js +22 -6
- package/dist/core/render-graph.js.map +1 -1
- package/dist/core/renderer.d.ts.map +1 -1
- package/dist/core/renderer.js +418 -23
- package/dist/core/renderer.js.map +1 -1
- package/dist/core/runtime-loop.d.ts +2 -2
- package/dist/core/runtime-loop.d.ts.map +1 -1
- package/dist/core/runtime-loop.js +49 -1
- package/dist/core/runtime-loop.js.map +1 -1
- package/dist/core/shader.d.ts +9 -1
- package/dist/core/shader.d.ts.map +1 -1
- package/dist/core/shader.js +21 -2
- package/dist/core/shader.js.map +1 -1
- 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.map +1 -1
- package/dist/core/texture-loader.js +4 -0
- package/dist/core/texture-loader.js.map +1 -1
- package/dist/core/textures.d.ts +12 -0
- package/dist/core/textures.d.ts.map +1 -1
- package/dist/core/textures.js +7 -2
- package/dist/core/textures.js.map +1 -1
- package/dist/core/types.d.ts +146 -4
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.js +3 -1
- 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/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.map +1 -1
- package/dist/passes/ShaderPass.js +2 -1
- package/dist/passes/ShaderPass.js.map +1 -1
- package/dist/passes/index.d.ts +2 -0
- package/dist/passes/index.d.ts.map +1 -1
- package/dist/passes/index.js +3 -1
- package/dist/react/FragCanvas.d.ts +2 -2
- package/dist/react/FragCanvas.d.ts.map +1 -1
- package/dist/react/FragCanvas.js.map +1 -1
- package/dist/react/MotionGPUErrorOverlay.d.ts.map +1 -1
- package/dist/react/MotionGPUErrorOverlay.js +123 -20
- package/dist/react/MotionGPUErrorOverlay.js.map +1 -1
- package/dist/react/advanced.js +3 -1
- package/dist/react/index.d.ts +5 -2
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +3 -1
- package/dist/svelte/FragCanvas.svelte +2 -2
- package/dist/svelte/FragCanvas.svelte.d.ts +2 -2
- package/dist/svelte/FragCanvas.svelte.d.ts.map +1 -1
- package/dist/svelte/MotionGPUErrorOverlay.svelte +137 -7
- package/dist/svelte/MotionGPUErrorOverlay.svelte.d.ts.map +1 -1
- package/dist/svelte/advanced.js +3 -1
- package/dist/svelte/index.d.ts +5 -2
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +3 -1
- package/package.json +1 -1
- package/src/lib/core/compute-shader.ts +326 -0
- package/src/lib/core/error-report.ts +129 -0
- package/src/lib/core/frame-registry.ts +2 -1
- package/src/lib/core/index.ts +18 -1
- package/src/lib/core/material-preprocess.ts +17 -6
- package/src/lib/core/material.ts +101 -20
- package/src/lib/core/render-graph.ts +39 -9
- package/src/lib/core/renderer.ts +655 -41
- package/src/lib/core/runtime-loop.ts +82 -3
- package/src/lib/core/shader.ts +45 -2
- package/src/lib/core/storage-buffers.ts +142 -0
- package/src/lib/core/texture-loader.ts +6 -0
- package/src/lib/core/textures.ts +24 -2
- package/src/lib/core/types.ts +165 -4
- package/src/lib/passes/ComputePass.ts +136 -0
- package/src/lib/passes/PingPongComputePass.ts +180 -0
- package/src/lib/passes/ShaderPass.ts +2 -1
- package/src/lib/passes/index.ts +6 -0
- package/src/lib/react/FragCanvas.tsx +3 -3
- package/src/lib/react/MotionGPUErrorOverlay.tsx +137 -5
- package/src/lib/react/index.ts +18 -1
- package/src/lib/svelte/FragCanvas.svelte +2 -2
- package/src/lib/svelte/MotionGPUErrorOverlay.svelte +137 -7
- package/src/lib/svelte/index.ts +18 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/react/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/react/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EACN,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,WAAW,EACX,mBAAmB,EACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EACX,sBAAsB,EACtB,UAAU,EACV,gBAAgB,EAChB,OAAO,EACP,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,UAAU,EACV,YAAY,EACZ,sBAAsB,EACtB,yBAAyB,EACzB,WAAW,EACX,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,UAAU,EACV,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,YAAY,EACZ,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACX,aAAa,EACb,oBAAoB,EACpB,kBAAkB,EAClB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACX,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,EACf,wBAAwB,EACxB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC1E,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,YAAY,EACX,mBAAmB,EACnB,uBAAuB,EACvB,0BAA0B,EAC1B,iBAAiB,EACjB,kBAAkB,EAClB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAC3F,YAAY,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC"}
|
package/dist/react/index.js
CHANGED
|
@@ -2,9 +2,11 @@ import { defineMaterial } from "../core/material.js";
|
|
|
2
2
|
import { BlitPass } from "../passes/BlitPass.js";
|
|
3
3
|
import { CopyPass } from "../passes/CopyPass.js";
|
|
4
4
|
import { ShaderPass } from "../passes/ShaderPass.js";
|
|
5
|
+
import { ComputePass } from "../passes/ComputePass.js";
|
|
6
|
+
import { PingPongComputePass } from "../passes/PingPongComputePass.js";
|
|
5
7
|
import "../passes/index.js";
|
|
6
8
|
import { useMotionGPU } from "./motiongpu-context.js";
|
|
7
9
|
import { useFrame } from "./frame-context.js";
|
|
8
10
|
import { FragCanvas } from "./FragCanvas.js";
|
|
9
11
|
import { useTexture } from "./use-texture.js";
|
|
10
|
-
export { BlitPass, CopyPass, FragCanvas, ShaderPass, defineMaterial, useFrame, useMotionGPU, useTexture };
|
|
12
|
+
export { BlitPass, ComputePass, CopyPass, FragCanvas, PingPongComputePass, ShaderPass, defineMaterial, useFrame, useMotionGPU, useTexture };
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
import MotionGPUErrorOverlay from './MotionGPUErrorOverlay.svelte';
|
|
8
8
|
import { createMotionGPURuntimeLoop } from '../core/runtime-loop';
|
|
9
9
|
import type {
|
|
10
|
+
AnyPass,
|
|
10
11
|
FrameInvalidationToken,
|
|
11
12
|
OutputColorSpace,
|
|
12
|
-
RenderPass,
|
|
13
13
|
RenderMode,
|
|
14
14
|
RenderTargetDefinitionMap
|
|
15
15
|
} from '../core/types';
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
interface Props {
|
|
20
20
|
material: FragMaterial;
|
|
21
21
|
renderTargets?: RenderTargetDefinitionMap;
|
|
22
|
-
passes?:
|
|
22
|
+
passes?: AnyPass[];
|
|
23
23
|
clearColor?: [number, number, number, number];
|
|
24
24
|
outputColorSpace?: OutputColorSpace;
|
|
25
25
|
renderMode?: RenderMode;
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import type { FragMaterial } from '../core/material';
|
|
4
4
|
import { type MotionGPUErrorReport } from '../core/error-report';
|
|
5
|
-
import type {
|
|
5
|
+
import type { AnyPass, OutputColorSpace, RenderMode, RenderTargetDefinitionMap } from '../core/types';
|
|
6
6
|
interface Props {
|
|
7
7
|
material: FragMaterial;
|
|
8
8
|
renderTargets?: RenderTargetDefinitionMap;
|
|
9
|
-
passes?:
|
|
9
|
+
passes?: AnyPass[];
|
|
10
10
|
clearColor?: [number, number, number, number];
|
|
11
11
|
outputColorSpace?: OutputColorSpace;
|
|
12
12
|
renderMode?: RenderMode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FragCanvas.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/svelte/FragCanvas.svelte"],"names":[],"mappings":";AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,OAAO,EAA0B,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAGzF,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"FragCanvas.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/svelte/FragCanvas.svelte"],"names":[],"mappings":";AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,OAAO,EAA0B,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAGzF,OAAO,KAAK,EACV,OAAO,EAEP,gBAAgB,EAChB,UAAU,EACV,yBAAyB,EACzB,MAAM,eAAe,CAAC;AAKvB,UAAU,KAAK;IACd,QAAQ,EAAE,YAAY,CAAC;IACvB,aAAa,CAAC,EAAE,yBAAyB,CAAC;IAC1C,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;IACnB,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,wBAAwB,CAAC;IAC1C,gBAAgB,CAAC,EAAE,mBAAmB,CAAC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACjD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,oBAAoB,EAAE,KAAK,IAAI,CAAC;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAiNF,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
|
|
@@ -16,7 +16,87 @@
|
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
const shouldShowErrorMessage = (value: MotionGPUErrorReport): boolean => {
|
|
19
|
-
return
|
|
19
|
+
return resolveDisplayMessage(value).length > 0;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const resolveDisplayMessage = (value: MotionGPUErrorReport): string => {
|
|
23
|
+
const rawMessage = value.message.trim();
|
|
24
|
+
if (rawMessage.length === 0) {
|
|
25
|
+
return '';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const normalizedMessage = normalizeErrorText(rawMessage);
|
|
29
|
+
const normalizedTitle = normalizeErrorText(value.title);
|
|
30
|
+
if (normalizedMessage === normalizedTitle) {
|
|
31
|
+
return '';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const escapedTitle = value.title.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
35
|
+
const prefixPattern = new RegExp(`^${escapedTitle}\\s*[:\\-|]\\s*`, 'i');
|
|
36
|
+
const stripped = rawMessage.replace(prefixPattern, '').trim();
|
|
37
|
+
return stripped.length > 0 ? stripped : rawMessage;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const formatRuntimeContext = (context: MotionGPUErrorReport['context']): string => {
|
|
41
|
+
if (!context) {
|
|
42
|
+
return '';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const indentBlock = (value: string, spaces = 2): string => {
|
|
46
|
+
const prefix = ' '.repeat(spaces);
|
|
47
|
+
return value
|
|
48
|
+
.split('\n')
|
|
49
|
+
.map((line) => `${prefix}${line}`)
|
|
50
|
+
.join('\n');
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const formatMaterialSignature = (value: string): string => {
|
|
54
|
+
const trimmed = value.trim();
|
|
55
|
+
if (trimmed.length === 0) {
|
|
56
|
+
return '<empty>';
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
return JSON.stringify(JSON.parse(trimmed), null, 2);
|
|
60
|
+
} catch {
|
|
61
|
+
return trimmed;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const lines: string[] = [];
|
|
66
|
+
if (context.materialSignature) {
|
|
67
|
+
lines.push('materialSignature:');
|
|
68
|
+
lines.push(indentBlock(formatMaterialSignature(context.materialSignature)));
|
|
69
|
+
}
|
|
70
|
+
if (context.passGraph) {
|
|
71
|
+
lines.push('passGraph:');
|
|
72
|
+
lines.push(` passCount: ${context.passGraph.passCount}`);
|
|
73
|
+
lines.push(` enabledPassCount: ${context.passGraph.enabledPassCount}`);
|
|
74
|
+
lines.push(' inputs:');
|
|
75
|
+
if (context.passGraph.inputs.length === 0) {
|
|
76
|
+
lines.push(' - <none>');
|
|
77
|
+
} else {
|
|
78
|
+
for (const input of context.passGraph.inputs) {
|
|
79
|
+
lines.push(` - ${input}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
lines.push(' outputs:');
|
|
83
|
+
if (context.passGraph.outputs.length === 0) {
|
|
84
|
+
lines.push(' - <none>');
|
|
85
|
+
} else {
|
|
86
|
+
for (const output of context.passGraph.outputs) {
|
|
87
|
+
lines.push(` - ${output}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
lines.push('activeRenderTargets:');
|
|
92
|
+
if (context.activeRenderTargets.length === 0) {
|
|
93
|
+
lines.push(' - <none>');
|
|
94
|
+
} else {
|
|
95
|
+
for (const target of context.activeRenderTargets) {
|
|
96
|
+
lines.push(` - ${target}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return lines.join('\n');
|
|
20
100
|
};
|
|
21
101
|
</script>
|
|
22
102
|
|
|
@@ -30,16 +110,28 @@
|
|
|
30
110
|
data-testid="motiongpu-error"
|
|
31
111
|
>
|
|
32
112
|
<header class="motiongpu-error-header">
|
|
33
|
-
<div class="motiongpu-error-
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
|
|
113
|
+
<div class="motiongpu-error-header-top">
|
|
114
|
+
<div class="motiongpu-error-badges">
|
|
115
|
+
<div class="motiongpu-error-badge-wrap">
|
|
116
|
+
<p class="motiongpu-error-badge motiongpu-error-badge-phase">
|
|
117
|
+
{report.phase}
|
|
118
|
+
</p>
|
|
119
|
+
</div>
|
|
120
|
+
<div class="motiongpu-error-badge-wrap">
|
|
121
|
+
<p class="motiongpu-error-badge motiongpu-error-badge-severity">
|
|
122
|
+
{report.severity}
|
|
123
|
+
</p>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
37
126
|
</div>
|
|
38
127
|
<h2 class="motiongpu-error-title">{report.title}</h2>
|
|
128
|
+
<p class="motiongpu-error-recoverable">
|
|
129
|
+
Recoverable: <span>{report.recoverable ? 'yes' : 'no'}</span>
|
|
130
|
+
</p>
|
|
39
131
|
</header>
|
|
40
132
|
<div class="motiongpu-error-body">
|
|
41
133
|
{#if shouldShowErrorMessage(report)}
|
|
42
|
-
<p class="motiongpu-error-message">{report
|
|
134
|
+
<p class="motiongpu-error-message">{resolveDisplayMessage(report)}</p>
|
|
43
135
|
{/if}
|
|
44
136
|
<p class="motiongpu-error-hint">{report.hint}</p>
|
|
45
137
|
</div>
|
|
@@ -87,6 +179,12 @@
|
|
|
87
179
|
<pre>{report.stack.join('\n')}</pre>
|
|
88
180
|
</details>
|
|
89
181
|
{/if}
|
|
182
|
+
{#if report.context}
|
|
183
|
+
<details class="motiongpu-error-details">
|
|
184
|
+
<summary>Runtime context</summary>
|
|
185
|
+
<pre>{formatRuntimeContext(report.context)}</pre>
|
|
186
|
+
</details>
|
|
187
|
+
{/if}
|
|
90
188
|
</div>
|
|
91
189
|
</section>
|
|
92
190
|
</div>
|
|
@@ -162,6 +260,19 @@
|
|
|
162
260
|
border-bottom: 1px solid var(--motiongpu-color-border);
|
|
163
261
|
}
|
|
164
262
|
|
|
263
|
+
.motiongpu-error-header-top {
|
|
264
|
+
display: flex;
|
|
265
|
+
align-items: flex-start;
|
|
266
|
+
gap: 0.75rem;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.motiongpu-error-badges {
|
|
270
|
+
display: inline-flex;
|
|
271
|
+
align-items: center;
|
|
272
|
+
gap: 0.4rem;
|
|
273
|
+
flex-wrap: wrap;
|
|
274
|
+
}
|
|
275
|
+
|
|
165
276
|
.motiongpu-error-badge-wrap {
|
|
166
277
|
display: inline-flex;
|
|
167
278
|
align-items: center;
|
|
@@ -173,7 +284,7 @@
|
|
|
173
284
|
background: var(--motiongpu-color-background-muted);
|
|
174
285
|
}
|
|
175
286
|
|
|
176
|
-
.motiongpu-error-
|
|
287
|
+
.motiongpu-error-badge {
|
|
177
288
|
display: inline-flex;
|
|
178
289
|
align-items: center;
|
|
179
290
|
margin: 0;
|
|
@@ -193,6 +304,20 @@
|
|
|
193
304
|
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.24);
|
|
194
305
|
}
|
|
195
306
|
|
|
307
|
+
.motiongpu-error-recoverable {
|
|
308
|
+
margin: 0;
|
|
309
|
+
font-size: 0.67rem;
|
|
310
|
+
line-height: 1.2;
|
|
311
|
+
letter-spacing: 0.06em;
|
|
312
|
+
text-transform: uppercase;
|
|
313
|
+
color: var(--motiongpu-color-foreground-muted);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.motiongpu-error-recoverable span {
|
|
317
|
+
font-family: var(--motiongpu-font-mono);
|
|
318
|
+
color: var(--motiongpu-color-foreground);
|
|
319
|
+
}
|
|
320
|
+
|
|
196
321
|
.motiongpu-error-title {
|
|
197
322
|
margin: 0;
|
|
198
323
|
font-size: clamp(1.02rem, 1vw + 0.72rem, 1.32rem);
|
|
@@ -372,6 +497,11 @@
|
|
|
372
497
|
.motiongpu-error-title {
|
|
373
498
|
font-size: 1.02rem;
|
|
374
499
|
}
|
|
500
|
+
|
|
501
|
+
.motiongpu-error-header-top {
|
|
502
|
+
flex-direction: column;
|
|
503
|
+
align-items: flex-start;
|
|
504
|
+
}
|
|
375
505
|
}
|
|
376
506
|
|
|
377
507
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MotionGPUErrorOverlay.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/svelte/MotionGPUErrorOverlay.svelte"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAIhE,UAAU,KAAK;IACd,MAAM,EAAE,oBAAoB,CAAC;CAC7B;
|
|
1
|
+
{"version":3,"file":"MotionGPUErrorOverlay.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/svelte/MotionGPUErrorOverlay.svelte"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAIhE,UAAU,KAAK;IACd,MAAM,EAAE,oBAAoB,CAAC;CAC7B;AAoLF,QAAA,MAAM,qBAAqB,2CAAwC,CAAC;AACpE,KAAK,qBAAqB,GAAG,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACtE,eAAe,qBAAqB,CAAC"}
|
package/dist/svelte/advanced.js
CHANGED
|
@@ -2,10 +2,12 @@ import { defineMaterial } from "../core/material.js";
|
|
|
2
2
|
import { BlitPass } from "../passes/BlitPass.js";
|
|
3
3
|
import { CopyPass } from "../passes/CopyPass.js";
|
|
4
4
|
import { ShaderPass } from "../passes/ShaderPass.js";
|
|
5
|
+
import { ComputePass } from "../passes/ComputePass.js";
|
|
6
|
+
import { PingPongComputePass } from "../passes/PingPongComputePass.js";
|
|
5
7
|
import { applySchedulerPreset, captureSchedulerDebugSnapshot } from "../core/scheduler-helpers.js";
|
|
6
8
|
import { useMotionGPU } from "./motiongpu-context.js";
|
|
7
9
|
import { useFrame } from "./frame-context.js";
|
|
8
10
|
import { useTexture } from "./use-texture.js";
|
|
9
11
|
import { FragCanvas } from "./index.js";
|
|
10
12
|
import { setMotionGPUUserContext, useMotionGPUUserContext } from "./use-motiongpu-user-context.js";
|
|
11
|
-
export { BlitPass, CopyPass, FragCanvas, ShaderPass, applySchedulerPreset, captureSchedulerDebugSnapshot, defineMaterial, setMotionGPUUserContext, useFrame, useMotionGPU, useMotionGPUUserContext, useTexture };
|
|
13
|
+
export { BlitPass, ComputePass, CopyPass, FragCanvas, PingPongComputePass, ShaderPass, applySchedulerPreset, captureSchedulerDebugSnapshot, defineMaterial, setMotionGPUUserContext, useFrame, useMotionGPU, useMotionGPUUserContext, useTexture };
|
package/dist/svelte/index.d.ts
CHANGED
|
@@ -3,14 +3,17 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export { default as FragCanvas } from './FragCanvas.svelte';
|
|
5
5
|
export { defineMaterial } from '../core/material.js';
|
|
6
|
-
export { BlitPass, CopyPass, ShaderPass } from '../passes/index.js';
|
|
6
|
+
export { BlitPass, CopyPass, ShaderPass, ComputePass, PingPongComputePass } from '../passes/index.js';
|
|
7
7
|
export { useMotionGPU } from './motiongpu-context.js';
|
|
8
8
|
export { useFrame } from './frame-context.js';
|
|
9
9
|
export { useTexture } from './use-texture.js';
|
|
10
|
-
export type { FrameInvalidationToken, FrameState, OutputColorSpace, RenderPass, RenderPassContext, RenderPassFlags, RenderPassInputSlot, RenderPassOutputSlot, RenderMode, RenderTarget, RenderTargetDefinition, RenderTargetDefinitionMap, TextureData, TextureDefinition, TextureDefinitionMap, TextureUpdateMode, TextureMap, TextureSource, TextureValue, TypedUniform, UniformMat4Value, UniformMap, UniformType, UniformValue } from '../core/types.js';
|
|
10
|
+
export type { FrameInvalidationToken, FrameState, OutputColorSpace, AnyPass, ComputePassLike, RenderPass, RenderPassContext, RenderPassFlags, RenderPassInputSlot, RenderPassOutputSlot, RenderMode, RenderTarget, RenderTargetDefinition, RenderTargetDefinitionMap, TextureData, TextureDefinition, TextureDefinitionMap, TextureUpdateMode, TextureMap, TextureSource, TextureValue, TypedUniform, UniformMat4Value, UniformMap, UniformType, UniformValue } from '../core/types.js';
|
|
11
11
|
export type { LoadedTexture, TextureDecodeOptions, TextureLoadOptions } from '../core/texture-loader.js';
|
|
12
12
|
export type { FragMaterial, FragMaterialInput, MaterialIncludes, MaterialDefineValue, MaterialDefines, TypedMaterialDefineValue } from '../core/material.js';
|
|
13
13
|
export type { MotionGPUContext } from './motiongpu-context.js';
|
|
14
14
|
export type { UseFrameOptions, UseFrameResult } from './frame-context.js';
|
|
15
15
|
export type { TextureUrlInput, UseTextureResult } from './use-texture.js';
|
|
16
|
+
export type { StorageBufferAccess, StorageBufferDefinition, StorageBufferDefinitionMap, StorageBufferType, ComputePassContext } from '../core/types.js';
|
|
17
|
+
export type { ComputePassOptions, ComputeDispatchContext } from '../passes/ComputePass.js';
|
|
18
|
+
export type { PingPongComputePassOptions } from '../passes/PingPongComputePass.js';
|
|
16
19
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/svelte/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/svelte/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EACN,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,WAAW,EACX,mBAAmB,EACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EACX,sBAAsB,EACtB,UAAU,EACV,gBAAgB,EAChB,OAAO,EACP,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,UAAU,EACV,YAAY,EACZ,sBAAsB,EACtB,yBAAyB,EACzB,WAAW,EACX,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,UAAU,EACV,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,YAAY,EACZ,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACX,aAAa,EACb,oBAAoB,EACpB,kBAAkB,EAClB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACX,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,EACf,wBAAwB,EACxB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC1E,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,YAAY,EACX,mBAAmB,EACnB,uBAAuB,EACvB,0BAA0B,EAC1B,iBAAiB,EACjB,kBAAkB,EAClB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAC3F,YAAY,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC"}
|
package/dist/svelte/index.js
CHANGED
|
@@ -2,9 +2,11 @@ import { defineMaterial } from "../core/material.js";
|
|
|
2
2
|
import { BlitPass } from "../passes/BlitPass.js";
|
|
3
3
|
import { CopyPass } from "../passes/CopyPass.js";
|
|
4
4
|
import { ShaderPass } from "../passes/ShaderPass.js";
|
|
5
|
+
import { ComputePass } from "../passes/ComputePass.js";
|
|
6
|
+
import { PingPongComputePass } from "../passes/PingPongComputePass.js";
|
|
5
7
|
import "../passes/index.js";
|
|
6
8
|
import { useMotionGPU } from "./motiongpu-context.js";
|
|
7
9
|
import { useFrame } from "./frame-context.js";
|
|
8
10
|
import { useTexture } from "./use-texture.js";
|
|
9
11
|
import FragCanvas from "./FragCanvas.svelte";
|
|
10
|
-
export { BlitPass, CopyPass, FragCanvas, ShaderPass, defineMaterial, useFrame, useMotionGPU, useTexture };
|
|
12
|
+
export { BlitPass, ComputePass, CopyPass, FragCanvas, PingPongComputePass, ShaderPass, defineMaterial, useFrame, useMotionGPU, useTexture };
|
package/package.json
CHANGED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import type { StorageBufferAccess, StorageBufferType, UniformLayout } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Regex contract for compute entrypoint.
|
|
5
|
+
* Matches: @compute @workgroup_size(...) fn compute(
|
|
6
|
+
* with @builtin(global_invocation_id) parameter.
|
|
7
|
+
*/
|
|
8
|
+
export const COMPUTE_ENTRY_CONTRACT = /@compute\s+@workgroup_size\s*\([^)]+\)\s*fn\s+compute\s*\(/;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Regex to extract @workgroup_size values.
|
|
12
|
+
*/
|
|
13
|
+
const WORKGROUP_SIZE_PATTERN =
|
|
14
|
+
/@workgroup_size\s*\(\s*(\d+)(?:\s*,\s*(\d+))?(?:\s*,\s*(\d+))?\s*\)/;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Regex to verify @builtin(global_invocation_id) parameter.
|
|
18
|
+
*/
|
|
19
|
+
const GLOBAL_INVOCATION_ID_PATTERN = /@builtin\s*\(\s*global_invocation_id\s*\)/;
|
|
20
|
+
const WORKGROUP_DIMENSION_MIN = 1;
|
|
21
|
+
const WORKGROUP_DIMENSION_MAX = 65535;
|
|
22
|
+
|
|
23
|
+
function extractComputeParamList(compute: string): string | null {
|
|
24
|
+
const computeFnIndex = compute.indexOf('fn compute');
|
|
25
|
+
if (computeFnIndex === -1) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const openParenIndex = compute.indexOf('(', computeFnIndex);
|
|
30
|
+
if (openParenIndex === -1) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let depth = 0;
|
|
35
|
+
for (let index = openParenIndex; index < compute.length; index += 1) {
|
|
36
|
+
const char = compute[index];
|
|
37
|
+
if (char === '(') {
|
|
38
|
+
depth += 1;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (char === ')') {
|
|
43
|
+
depth -= 1;
|
|
44
|
+
if (depth === 0) {
|
|
45
|
+
return compute.slice(openParenIndex + 1, index);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function assertWorkgroupDimension(value: number): void {
|
|
54
|
+
if (
|
|
55
|
+
!Number.isFinite(value) ||
|
|
56
|
+
!Number.isInteger(value) ||
|
|
57
|
+
value < WORKGROUP_DIMENSION_MIN ||
|
|
58
|
+
value > WORKGROUP_DIMENSION_MAX
|
|
59
|
+
) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`@workgroup_size dimensions must be integers in range ${WORKGROUP_DIMENSION_MIN}-${WORKGROUP_DIMENSION_MAX}, got ${value}.`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Default uniform field used when no custom uniforms are provided in compute.
|
|
68
|
+
*/
|
|
69
|
+
const DEFAULT_UNIFORM_FIELD = 'motiongpu_unused: vec4f,';
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Validates compute shader user code matches the compute contract.
|
|
73
|
+
*
|
|
74
|
+
* @param compute - User compute shader WGSL source.
|
|
75
|
+
* @throws {Error} When shader does not match the compute contract.
|
|
76
|
+
*/
|
|
77
|
+
export function assertComputeContract(compute: string): void {
|
|
78
|
+
if (!COMPUTE_ENTRY_CONTRACT.test(compute)) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
'Compute shader must declare `@compute @workgroup_size(...) fn compute(...)`. ' +
|
|
81
|
+
'Ensure the function is named `compute` and includes @compute and @workgroup_size annotations.'
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const params = extractComputeParamList(compute);
|
|
86
|
+
if (!params || !GLOBAL_INVOCATION_ID_PATTERN.test(params)) {
|
|
87
|
+
throw new Error('Compute shader must include a `@builtin(global_invocation_id)` parameter.');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
extractWorkgroupSize(compute);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Extracts @workgroup_size values from WGSL compute shader.
|
|
95
|
+
*
|
|
96
|
+
* @param compute - Validated compute shader source.
|
|
97
|
+
* @returns Tuple [x, y, z] with defaults of 1 for omitted dimensions.
|
|
98
|
+
*/
|
|
99
|
+
export function extractWorkgroupSize(compute: string): [number, number, number] {
|
|
100
|
+
const match = compute.match(WORKGROUP_SIZE_PATTERN);
|
|
101
|
+
if (!match) {
|
|
102
|
+
throw new Error('Could not extract @workgroup_size from compute shader source.');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const x = Number.parseInt(match[1] ?? '1', 10);
|
|
106
|
+
const y = Number.parseInt(match[2] ?? '1', 10);
|
|
107
|
+
const z = Number.parseInt(match[3] ?? '1', 10);
|
|
108
|
+
assertWorkgroupDimension(x);
|
|
109
|
+
assertWorkgroupDimension(y);
|
|
110
|
+
assertWorkgroupDimension(z);
|
|
111
|
+
|
|
112
|
+
return [x, y, z];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Maps StorageBufferAccess to WGSL var qualifier.
|
|
117
|
+
*/
|
|
118
|
+
function toWgslAccessMode(access: StorageBufferAccess): string {
|
|
119
|
+
switch (access) {
|
|
120
|
+
case 'read':
|
|
121
|
+
return 'read';
|
|
122
|
+
case 'read-write':
|
|
123
|
+
return 'read_write';
|
|
124
|
+
default:
|
|
125
|
+
throw new Error(`Unsupported storage buffer access mode "${String(access)}".`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Builds WGSL struct fields for uniforms used in compute shader preamble.
|
|
131
|
+
*/
|
|
132
|
+
function buildUniformStructForCompute(layout: UniformLayout): string {
|
|
133
|
+
if (layout.entries.length === 0) {
|
|
134
|
+
return DEFAULT_UNIFORM_FIELD;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return layout.entries.map((entry) => `${entry.name}: ${entry.type},`).join('\n\t');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Builds storage buffer binding declarations for compute shader.
|
|
142
|
+
*
|
|
143
|
+
* @param storageBufferKeys - Sorted buffer keys.
|
|
144
|
+
* @param definitions - Type/access definitions per key.
|
|
145
|
+
* @param groupIndex - Bind group index for storage buffers.
|
|
146
|
+
* @returns WGSL binding declaration string.
|
|
147
|
+
*/
|
|
148
|
+
export function buildComputeStorageBufferBindings(
|
|
149
|
+
storageBufferKeys: string[],
|
|
150
|
+
definitions: Record<string, { type: StorageBufferType; access: StorageBufferAccess }>,
|
|
151
|
+
groupIndex: number
|
|
152
|
+
): string {
|
|
153
|
+
if (storageBufferKeys.length === 0) {
|
|
154
|
+
return '';
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const declarations: string[] = [];
|
|
158
|
+
|
|
159
|
+
for (let index = 0; index < storageBufferKeys.length; index += 1) {
|
|
160
|
+
const key = storageBufferKeys[index];
|
|
161
|
+
if (key === undefined) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const definition = definitions[key];
|
|
166
|
+
if (!definition) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const accessMode = toWgslAccessMode(definition.access);
|
|
171
|
+
declarations.push(
|
|
172
|
+
`@group(${groupIndex}) @binding(${index}) var<storage, ${accessMode}> ${key}: ${definition.type};`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return declarations.join('\n');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Builds storage texture binding declarations for compute shader.
|
|
181
|
+
*
|
|
182
|
+
* @param storageTextureKeys - Sorted storage texture keys.
|
|
183
|
+
* @param definitions - Format definitions per key.
|
|
184
|
+
* @param groupIndex - Bind group index for storage textures.
|
|
185
|
+
* @returns WGSL binding declaration string.
|
|
186
|
+
*/
|
|
187
|
+
export function buildComputeStorageTextureBindings(
|
|
188
|
+
storageTextureKeys: string[],
|
|
189
|
+
definitions: Record<string, { format: GPUTextureFormat }>,
|
|
190
|
+
groupIndex: number
|
|
191
|
+
): string {
|
|
192
|
+
if (storageTextureKeys.length === 0) {
|
|
193
|
+
return '';
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const declarations: string[] = [];
|
|
197
|
+
|
|
198
|
+
for (let index = 0; index < storageTextureKeys.length; index += 1) {
|
|
199
|
+
const key = storageTextureKeys[index];
|
|
200
|
+
if (key === undefined) {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const definition = definitions[key];
|
|
205
|
+
if (!definition) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
declarations.push(
|
|
210
|
+
`@group(${groupIndex}) @binding(${index}) var ${key}: texture_storage_2d<${definition.format}, write>;`
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return declarations.join('\n');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Maps storage texture format to sampled texture scalar type for `texture_2d<T>`.
|
|
219
|
+
*/
|
|
220
|
+
export function storageTextureSampleScalarType(format: GPUTextureFormat): 'f32' | 'u32' | 'i32' {
|
|
221
|
+
const normalized = String(format).toLowerCase();
|
|
222
|
+
if (normalized.endsWith('uint')) {
|
|
223
|
+
return 'u32';
|
|
224
|
+
}
|
|
225
|
+
if (normalized.endsWith('sint')) {
|
|
226
|
+
return 'i32';
|
|
227
|
+
}
|
|
228
|
+
return 'f32';
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Assembles compute shader WGSL for ping-pong workflows.
|
|
233
|
+
*
|
|
234
|
+
* Exposes two generated bindings under group(2):
|
|
235
|
+
* - `${target}A`: sampled read texture (`texture_2d<T>`)
|
|
236
|
+
* - `${target}B`: storage write texture (`texture_storage_2d<format, write>`)
|
|
237
|
+
*/
|
|
238
|
+
export function buildPingPongComputeShaderSource(options: {
|
|
239
|
+
compute: string;
|
|
240
|
+
uniformLayout: UniformLayout;
|
|
241
|
+
storageBufferKeys: string[];
|
|
242
|
+
storageBufferDefinitions: Record<
|
|
243
|
+
string,
|
|
244
|
+
{ type: StorageBufferType; access: StorageBufferAccess }
|
|
245
|
+
>;
|
|
246
|
+
target: string;
|
|
247
|
+
targetFormat: GPUTextureFormat;
|
|
248
|
+
}): string {
|
|
249
|
+
const uniformFields = buildUniformStructForCompute(options.uniformLayout);
|
|
250
|
+
const storageBufferBindings = buildComputeStorageBufferBindings(
|
|
251
|
+
options.storageBufferKeys,
|
|
252
|
+
options.storageBufferDefinitions,
|
|
253
|
+
1
|
|
254
|
+
);
|
|
255
|
+
const sampledType = storageTextureSampleScalarType(options.targetFormat);
|
|
256
|
+
const pingPongTextureBindings = [
|
|
257
|
+
`@group(2) @binding(0) var ${options.target}A: texture_2d<${sampledType}>;`,
|
|
258
|
+
`@group(2) @binding(1) var ${options.target}B: texture_storage_2d<${options.targetFormat}, write>;`
|
|
259
|
+
].join('\n');
|
|
260
|
+
|
|
261
|
+
return `struct MotionGPUFrame {
|
|
262
|
+
time: f32,
|
|
263
|
+
delta: f32,
|
|
264
|
+
resolution: vec2f,
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
struct MotionGPUUniforms {
|
|
268
|
+
${uniformFields}
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
@group(0) @binding(0) var<uniform> motiongpuFrame: MotionGPUFrame;
|
|
272
|
+
@group(0) @binding(1) var<uniform> motiongpuUniforms: MotionGPUUniforms;
|
|
273
|
+
${storageBufferBindings ? '\n' + storageBufferBindings : ''}
|
|
274
|
+
${pingPongTextureBindings ? '\n' + pingPongTextureBindings : ''}
|
|
275
|
+
|
|
276
|
+
${options.compute}
|
|
277
|
+
`;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Assembles full compute shader WGSL with preamble.
|
|
282
|
+
*
|
|
283
|
+
* @param options - Compute shader build options.
|
|
284
|
+
* @returns Complete WGSL source for compute stage.
|
|
285
|
+
*/
|
|
286
|
+
export function buildComputeShaderSource(options: {
|
|
287
|
+
compute: string;
|
|
288
|
+
uniformLayout: UniformLayout;
|
|
289
|
+
storageBufferKeys: string[];
|
|
290
|
+
storageBufferDefinitions: Record<
|
|
291
|
+
string,
|
|
292
|
+
{ type: StorageBufferType; access: StorageBufferAccess }
|
|
293
|
+
>;
|
|
294
|
+
storageTextureKeys: string[];
|
|
295
|
+
storageTextureDefinitions: Record<string, { format: GPUTextureFormat }>;
|
|
296
|
+
}): string {
|
|
297
|
+
const uniformFields = buildUniformStructForCompute(options.uniformLayout);
|
|
298
|
+
const storageBufferBindings = buildComputeStorageBufferBindings(
|
|
299
|
+
options.storageBufferKeys,
|
|
300
|
+
options.storageBufferDefinitions,
|
|
301
|
+
1
|
|
302
|
+
);
|
|
303
|
+
const storageTextureBindings = buildComputeStorageTextureBindings(
|
|
304
|
+
options.storageTextureKeys,
|
|
305
|
+
options.storageTextureDefinitions,
|
|
306
|
+
2
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
return `struct MotionGPUFrame {
|
|
310
|
+
time: f32,
|
|
311
|
+
delta: f32,
|
|
312
|
+
resolution: vec2f,
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
struct MotionGPUUniforms {
|
|
316
|
+
${uniformFields}
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
@group(0) @binding(0) var<uniform> motiongpuFrame: MotionGPUFrame;
|
|
320
|
+
@group(0) @binding(1) var<uniform> motiongpuUniforms: MotionGPUUniforms;
|
|
321
|
+
${storageBufferBindings ? '\n' + storageBufferBindings : ''}
|
|
322
|
+
${storageTextureBindings ? '\n' + storageTextureBindings : ''}
|
|
323
|
+
|
|
324
|
+
${options.compute}
|
|
325
|
+
`;
|
|
326
|
+
}
|