@motion-core/motion-gpu 0.2.0 → 0.4.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 +78 -1
- package/dist/core/current-value.js +3 -0
- package/dist/core/error-diagnostics.d.ts +14 -0
- package/dist/core/error-diagnostics.js +41 -1
- package/dist/core/error-report.d.ts +37 -0
- package/dist/core/error-report.js +60 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/material-preprocess.d.ts +5 -5
- package/dist/core/material-preprocess.js +1 -4
- package/dist/core/material.d.ts +32 -23
- package/dist/core/material.js +14 -7
- package/dist/core/renderer.d.ts +10 -0
- package/dist/core/renderer.js +66 -4
- package/dist/core/runtime-loop.d.ts +3 -0
- package/dist/core/runtime-loop.js +72 -1
- package/dist/core/types.d.ts +24 -10
- package/dist/passes/BlitPass.d.ts +6 -27
- package/dist/passes/BlitPass.js +10 -121
- package/dist/passes/FullscreenPass.d.ts +37 -0
- package/dist/passes/FullscreenPass.js +131 -0
- package/dist/passes/ShaderPass.d.ts +6 -26
- package/dist/passes/ShaderPass.js +10 -121
- package/dist/react/FragCanvas.d.ts +26 -0
- package/dist/react/FragCanvas.js +218 -0
- package/dist/react/FragCanvas.tsx +345 -0
- package/dist/react/MotionGPUErrorOverlay.d.ts +6 -0
- package/dist/react/MotionGPUErrorOverlay.js +52 -0
- package/dist/react/MotionGPUErrorOverlay.tsx +129 -0
- package/dist/react/Portal.d.ts +6 -0
- package/dist/react/Portal.js +24 -0
- package/dist/react/Portal.tsx +34 -0
- package/dist/react/advanced.d.ts +11 -0
- package/dist/react/advanced.js +6 -0
- package/dist/react/frame-context.d.ts +14 -0
- package/dist/react/frame-context.js +98 -0
- package/dist/react/index.d.ts +15 -0
- package/dist/react/index.js +9 -0
- package/dist/react/motiongpu-context.d.ts +73 -0
- package/dist/react/motiongpu-context.js +18 -0
- package/dist/react/use-motiongpu-user-context.d.ts +49 -0
- package/dist/react/use-motiongpu-user-context.js +94 -0
- package/dist/react/use-texture.d.ts +40 -0
- package/dist/react/use-texture.js +162 -0
- package/dist/svelte/FragCanvas.svelte +45 -16
- package/dist/svelte/FragCanvas.svelte.d.ts +2 -0
- package/dist/svelte/MotionGPUErrorOverlay.svelte +10 -19
- package/dist/svelte/Portal.svelte +6 -21
- package/dist/svelte/use-motiongpu-user-context.d.ts +9 -1
- package/dist/svelte/use-motiongpu-user-context.js +4 -1
- package/dist/svelte/use-texture.d.ts +11 -2
- package/dist/svelte/use-texture.js +13 -3
- package/package.json +28 -3
|
@@ -1,40 +1,20 @@
|
|
|
1
|
-
import
|
|
2
|
-
export interface ShaderPassOptions extends
|
|
1
|
+
import { FullscreenPass, type FullscreenPassOptions } from './FullscreenPass.js';
|
|
2
|
+
export interface ShaderPassOptions extends FullscreenPassOptions {
|
|
3
3
|
fragment: string;
|
|
4
|
-
enabled?: boolean;
|
|
5
|
-
needsSwap?: boolean;
|
|
6
|
-
input?: RenderPassInputSlot;
|
|
7
|
-
output?: RenderPassOutputSlot;
|
|
8
|
-
filter?: GPUFilterMode;
|
|
9
4
|
}
|
|
10
5
|
/**
|
|
11
6
|
* Fullscreen programmable shader pass.
|
|
12
7
|
*/
|
|
13
|
-
export declare class ShaderPass
|
|
14
|
-
enabled: boolean;
|
|
15
|
-
needsSwap: boolean;
|
|
16
|
-
input: RenderPassInputSlot;
|
|
17
|
-
output: RenderPassOutputSlot;
|
|
18
|
-
clear: boolean;
|
|
19
|
-
clearColor: [number, number, number, number];
|
|
20
|
-
preserve: boolean;
|
|
21
|
-
private readonly filter;
|
|
8
|
+
export declare class ShaderPass extends FullscreenPass {
|
|
22
9
|
private fragment;
|
|
23
10
|
private program;
|
|
24
|
-
private device;
|
|
25
|
-
private sampler;
|
|
26
|
-
private bindGroupLayout;
|
|
27
|
-
private shaderModule;
|
|
28
|
-
private readonly pipelineByFormat;
|
|
29
|
-
private bindGroupByView;
|
|
30
11
|
constructor(options: ShaderPassOptions);
|
|
31
12
|
/**
|
|
32
13
|
* Replaces current shader fragment and invalidates pipeline cache.
|
|
33
14
|
*/
|
|
34
15
|
setFragment(fragment: string): void;
|
|
35
16
|
getFragment(): string;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
dispose(): void;
|
|
17
|
+
protected getProgram(): string;
|
|
18
|
+
protected getVertexEntryPoint(): string;
|
|
19
|
+
protected getFragmentEntryPoint(): string;
|
|
40
20
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FullscreenPass } from './FullscreenPass.js';
|
|
1
2
|
const SHADER_PASS_CONTRACT = /\bfn\s+shade\s*\(\s*inputColor\s*:\s*vec4f\s*,\s*uv\s*:\s*vec2f\s*\)\s*->\s*vec4f/;
|
|
2
3
|
function buildShaderPassProgram(fragment) {
|
|
3
4
|
if (!SHADER_PASS_CONTRACT.test(fragment)) {
|
|
@@ -39,32 +40,11 @@ fn motiongpuShaderPassFragment(in: MotionGPUVertexOut) -> @location(0) vec4f {
|
|
|
39
40
|
/**
|
|
40
41
|
* Fullscreen programmable shader pass.
|
|
41
42
|
*/
|
|
42
|
-
export class ShaderPass {
|
|
43
|
-
enabled;
|
|
44
|
-
needsSwap;
|
|
45
|
-
input;
|
|
46
|
-
output;
|
|
47
|
-
clear;
|
|
48
|
-
clearColor;
|
|
49
|
-
preserve;
|
|
50
|
-
filter;
|
|
43
|
+
export class ShaderPass extends FullscreenPass {
|
|
51
44
|
fragment;
|
|
52
45
|
program;
|
|
53
|
-
device = null;
|
|
54
|
-
sampler = null;
|
|
55
|
-
bindGroupLayout = null;
|
|
56
|
-
shaderModule = null;
|
|
57
|
-
pipelineByFormat = new Map();
|
|
58
|
-
bindGroupByView = new WeakMap();
|
|
59
46
|
constructor(options) {
|
|
60
|
-
|
|
61
|
-
this.needsSwap = options.needsSwap ?? true;
|
|
62
|
-
this.input = options.input ?? 'source';
|
|
63
|
-
this.output = options.output ?? (this.needsSwap ? 'target' : 'source');
|
|
64
|
-
this.clear = options.clear ?? false;
|
|
65
|
-
this.clearColor = options.clearColor ?? [0, 0, 0, 1];
|
|
66
|
-
this.preserve = options.preserve ?? true;
|
|
67
|
-
this.filter = options.filter ?? 'linear';
|
|
47
|
+
super(options);
|
|
68
48
|
this.fragment = options.fragment;
|
|
69
49
|
this.program = buildShaderPassProgram(options.fragment);
|
|
70
50
|
}
|
|
@@ -74,109 +54,18 @@ export class ShaderPass {
|
|
|
74
54
|
setFragment(fragment) {
|
|
75
55
|
this.fragment = fragment;
|
|
76
56
|
this.program = buildShaderPassProgram(fragment);
|
|
77
|
-
this.
|
|
78
|
-
this.pipelineByFormat.clear();
|
|
79
|
-
this.bindGroupByView = new WeakMap();
|
|
57
|
+
this.invalidateFullscreenCache();
|
|
80
58
|
}
|
|
81
59
|
getFragment() {
|
|
82
60
|
return this.fragment;
|
|
83
61
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
this.device = device;
|
|
87
|
-
this.sampler = null;
|
|
88
|
-
this.bindGroupLayout = null;
|
|
89
|
-
this.shaderModule = null;
|
|
90
|
-
this.pipelineByFormat.clear();
|
|
91
|
-
this.bindGroupByView = new WeakMap();
|
|
92
|
-
}
|
|
93
|
-
if (!this.sampler) {
|
|
94
|
-
this.sampler = device.createSampler({
|
|
95
|
-
magFilter: this.filter,
|
|
96
|
-
minFilter: this.filter,
|
|
97
|
-
addressModeU: 'clamp-to-edge',
|
|
98
|
-
addressModeV: 'clamp-to-edge'
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
if (!this.bindGroupLayout) {
|
|
102
|
-
this.bindGroupLayout = device.createBindGroupLayout({
|
|
103
|
-
entries: [
|
|
104
|
-
{
|
|
105
|
-
binding: 0,
|
|
106
|
-
visibility: GPUShaderStage.FRAGMENT,
|
|
107
|
-
sampler: { type: 'filtering' }
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
binding: 1,
|
|
111
|
-
visibility: GPUShaderStage.FRAGMENT,
|
|
112
|
-
texture: {
|
|
113
|
-
sampleType: 'float',
|
|
114
|
-
viewDimension: '2d',
|
|
115
|
-
multisampled: false
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
]
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
if (!this.shaderModule) {
|
|
122
|
-
this.shaderModule = device.createShaderModule({ code: this.program });
|
|
123
|
-
}
|
|
124
|
-
let pipeline = this.pipelineByFormat.get(format);
|
|
125
|
-
if (!pipeline) {
|
|
126
|
-
const pipelineLayout = device.createPipelineLayout({
|
|
127
|
-
bindGroupLayouts: [this.bindGroupLayout]
|
|
128
|
-
});
|
|
129
|
-
pipeline = device.createRenderPipeline({
|
|
130
|
-
layout: pipelineLayout,
|
|
131
|
-
vertex: {
|
|
132
|
-
module: this.shaderModule,
|
|
133
|
-
entryPoint: 'motiongpuShaderPassVertex'
|
|
134
|
-
},
|
|
135
|
-
fragment: {
|
|
136
|
-
module: this.shaderModule,
|
|
137
|
-
entryPoint: 'motiongpuShaderPassFragment',
|
|
138
|
-
targets: [{ format }]
|
|
139
|
-
},
|
|
140
|
-
primitive: { topology: 'triangle-list' }
|
|
141
|
-
});
|
|
142
|
-
this.pipelineByFormat.set(format, pipeline);
|
|
143
|
-
}
|
|
144
|
-
return {
|
|
145
|
-
sampler: this.sampler,
|
|
146
|
-
bindGroupLayout: this.bindGroupLayout,
|
|
147
|
-
pipeline
|
|
148
|
-
};
|
|
62
|
+
getProgram() {
|
|
63
|
+
return this.program;
|
|
149
64
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
void height;
|
|
65
|
+
getVertexEntryPoint() {
|
|
66
|
+
return 'motiongpuShaderPassVertex';
|
|
153
67
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const inputView = context.input.view;
|
|
157
|
-
let bindGroup = this.bindGroupByView.get(inputView);
|
|
158
|
-
if (!bindGroup) {
|
|
159
|
-
bindGroup = context.device.createBindGroup({
|
|
160
|
-
layout: bindGroupLayout,
|
|
161
|
-
entries: [
|
|
162
|
-
{ binding: 0, resource: sampler },
|
|
163
|
-
{ binding: 1, resource: inputView }
|
|
164
|
-
]
|
|
165
|
-
});
|
|
166
|
-
this.bindGroupByView.set(inputView, bindGroup);
|
|
167
|
-
}
|
|
168
|
-
const pass = context.beginRenderPass();
|
|
169
|
-
pass.setPipeline(pipeline);
|
|
170
|
-
pass.setBindGroup(0, bindGroup);
|
|
171
|
-
pass.draw(3);
|
|
172
|
-
pass.end();
|
|
173
|
-
}
|
|
174
|
-
dispose() {
|
|
175
|
-
this.device = null;
|
|
176
|
-
this.sampler = null;
|
|
177
|
-
this.bindGroupLayout = null;
|
|
178
|
-
this.shaderModule = null;
|
|
179
|
-
this.pipelineByFormat.clear();
|
|
180
|
-
this.bindGroupByView = new WeakMap();
|
|
68
|
+
getFragmentEntryPoint() {
|
|
69
|
+
return 'motiongpuShaderPassFragment';
|
|
181
70
|
}
|
|
182
71
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type MotionGPUErrorReport } from '../core/error-report.js';
|
|
2
|
+
import type { FragMaterial } from '../core/material.js';
|
|
3
|
+
import type { OutputColorSpace, RenderPass, RenderMode, RenderTargetDefinitionMap } from '../core/types.js';
|
|
4
|
+
import { type CSSProperties, type ReactNode } from 'react';
|
|
5
|
+
export interface FragCanvasProps {
|
|
6
|
+
material: FragMaterial;
|
|
7
|
+
renderTargets?: RenderTargetDefinitionMap;
|
|
8
|
+
passes?: RenderPass[];
|
|
9
|
+
clearColor?: [number, number, number, number];
|
|
10
|
+
outputColorSpace?: OutputColorSpace;
|
|
11
|
+
renderMode?: RenderMode;
|
|
12
|
+
autoRender?: boolean;
|
|
13
|
+
maxDelta?: number;
|
|
14
|
+
adapterOptions?: GPURequestAdapterOptions;
|
|
15
|
+
deviceDescriptor?: GPUDeviceDescriptor;
|
|
16
|
+
dpr?: number;
|
|
17
|
+
showErrorOverlay?: boolean;
|
|
18
|
+
errorRenderer?: (report: MotionGPUErrorReport) => ReactNode;
|
|
19
|
+
onError?: (report: MotionGPUErrorReport) => void;
|
|
20
|
+
errorHistoryLimit?: number;
|
|
21
|
+
onErrorHistory?: (history: MotionGPUErrorReport[]) => void;
|
|
22
|
+
className?: string;
|
|
23
|
+
style?: CSSProperties;
|
|
24
|
+
children?: ReactNode;
|
|
25
|
+
}
|
|
26
|
+
export declare function FragCanvas({ material, renderTargets, passes, clearColor, outputColorSpace, renderMode, autoRender, maxDelta, adapterOptions, deviceDescriptor, dpr, showErrorOverlay, errorRenderer, onError, errorHistoryLimit, onErrorHistory, className, style, children }: FragCanvasProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { createCurrentWritable as currentWritable } from '../core/current-value.js';
|
|
3
|
+
import { toMotionGPUErrorReport } from '../core/error-report.js';
|
|
4
|
+
import { createFrameRegistry } from '../core/frame-registry.js';
|
|
5
|
+
import { createMotionGPURuntimeLoop } from '../core/runtime-loop.js';
|
|
6
|
+
import { useEffect, useRef, useState } from 'react';
|
|
7
|
+
import { FrameRegistryReactContext } from './frame-context.js';
|
|
8
|
+
import { MotionGPUErrorOverlay } from './MotionGPUErrorOverlay.js';
|
|
9
|
+
import { MotionGPUReactContext } from './motiongpu-context.js';
|
|
10
|
+
function getInitialDpr() {
|
|
11
|
+
if (typeof window === 'undefined') {
|
|
12
|
+
return 1;
|
|
13
|
+
}
|
|
14
|
+
return window.devicePixelRatio ?? 1;
|
|
15
|
+
}
|
|
16
|
+
function createRuntimeState(initialDpr) {
|
|
17
|
+
const registry = createFrameRegistry({ maxDelta: 0.1 });
|
|
18
|
+
const canvasRef = { current: undefined };
|
|
19
|
+
const requestFrameSignalRef = { current: null };
|
|
20
|
+
const requestFrame = () => {
|
|
21
|
+
requestFrameSignalRef.current?.();
|
|
22
|
+
};
|
|
23
|
+
const invalidateFrame = () => {
|
|
24
|
+
registry.invalidate();
|
|
25
|
+
requestFrame();
|
|
26
|
+
};
|
|
27
|
+
const advanceFrame = () => {
|
|
28
|
+
registry.advance();
|
|
29
|
+
requestFrame();
|
|
30
|
+
};
|
|
31
|
+
const size = currentWritable({ width: 0, height: 0 });
|
|
32
|
+
const dprState = currentWritable(initialDpr, requestFrame);
|
|
33
|
+
const maxDeltaState = currentWritable(0.1, (value) => {
|
|
34
|
+
registry.setMaxDelta(value);
|
|
35
|
+
requestFrame();
|
|
36
|
+
});
|
|
37
|
+
const renderModeState = currentWritable('always', (value) => {
|
|
38
|
+
registry.setRenderMode(value);
|
|
39
|
+
requestFrame();
|
|
40
|
+
});
|
|
41
|
+
const autoRenderState = currentWritable(true, (value) => {
|
|
42
|
+
registry.setAutoRender(value);
|
|
43
|
+
requestFrame();
|
|
44
|
+
});
|
|
45
|
+
const userState = currentWritable({});
|
|
46
|
+
const context = {
|
|
47
|
+
get canvas() {
|
|
48
|
+
return canvasRef.current;
|
|
49
|
+
},
|
|
50
|
+
size,
|
|
51
|
+
dpr: dprState,
|
|
52
|
+
maxDelta: maxDeltaState,
|
|
53
|
+
renderMode: renderModeState,
|
|
54
|
+
autoRender: autoRenderState,
|
|
55
|
+
user: userState,
|
|
56
|
+
invalidate: invalidateFrame,
|
|
57
|
+
advance: advanceFrame,
|
|
58
|
+
scheduler: {
|
|
59
|
+
createStage: registry.createStage,
|
|
60
|
+
getStage: registry.getStage,
|
|
61
|
+
setDiagnosticsEnabled: registry.setDiagnosticsEnabled,
|
|
62
|
+
getDiagnosticsEnabled: registry.getDiagnosticsEnabled,
|
|
63
|
+
getLastRunTimings: registry.getLastRunTimings,
|
|
64
|
+
getSchedule: registry.getSchedule,
|
|
65
|
+
setProfilingEnabled: registry.setProfilingEnabled,
|
|
66
|
+
setProfilingWindow: registry.setProfilingWindow,
|
|
67
|
+
resetProfiling: registry.resetProfiling,
|
|
68
|
+
getProfilingEnabled: registry.getProfilingEnabled,
|
|
69
|
+
getProfilingWindow: registry.getProfilingWindow,
|
|
70
|
+
getProfilingSnapshot: registry.getProfilingSnapshot
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
return {
|
|
74
|
+
registry,
|
|
75
|
+
context,
|
|
76
|
+
canvasRef,
|
|
77
|
+
size,
|
|
78
|
+
dprState,
|
|
79
|
+
maxDeltaState,
|
|
80
|
+
renderModeState,
|
|
81
|
+
autoRenderState,
|
|
82
|
+
requestFrameSignalRef,
|
|
83
|
+
requestFrame,
|
|
84
|
+
invalidateFrame,
|
|
85
|
+
advanceFrame
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function getNormalizedErrorHistoryLimit(value) {
|
|
89
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
return Math.floor(value);
|
|
93
|
+
}
|
|
94
|
+
export function FragCanvas({ material, renderTargets = {}, passes = [], clearColor = [0, 0, 0, 1], outputColorSpace = 'srgb', renderMode = 'always', autoRender = true, maxDelta = 0.1, adapterOptions = undefined, deviceDescriptor = undefined, dpr = getInitialDpr(), showErrorOverlay = true, errorRenderer, onError = undefined, errorHistoryLimit = 0, onErrorHistory = undefined, className = '', style, children }) {
|
|
95
|
+
const runtimeRef = useRef(null);
|
|
96
|
+
if (!runtimeRef.current) {
|
|
97
|
+
runtimeRef.current = createRuntimeState(getInitialDpr());
|
|
98
|
+
}
|
|
99
|
+
const runtime = runtimeRef.current;
|
|
100
|
+
const runtimePropsRef = useRef({
|
|
101
|
+
material,
|
|
102
|
+
renderTargets,
|
|
103
|
+
passes,
|
|
104
|
+
clearColor,
|
|
105
|
+
outputColorSpace,
|
|
106
|
+
adapterOptions,
|
|
107
|
+
deviceDescriptor,
|
|
108
|
+
onError,
|
|
109
|
+
errorHistoryLimit,
|
|
110
|
+
onErrorHistory
|
|
111
|
+
});
|
|
112
|
+
runtimePropsRef.current = {
|
|
113
|
+
material,
|
|
114
|
+
renderTargets,
|
|
115
|
+
passes,
|
|
116
|
+
clearColor,
|
|
117
|
+
outputColorSpace,
|
|
118
|
+
adapterOptions,
|
|
119
|
+
deviceDescriptor,
|
|
120
|
+
onError,
|
|
121
|
+
errorHistoryLimit,
|
|
122
|
+
onErrorHistory
|
|
123
|
+
};
|
|
124
|
+
const [errorReport, setErrorReport] = useState(null);
|
|
125
|
+
const [errorHistory, setErrorHistory] = useState([]);
|
|
126
|
+
useEffect(() => {
|
|
127
|
+
runtime.renderModeState.set(renderMode);
|
|
128
|
+
}, [renderMode, runtime]);
|
|
129
|
+
useEffect(() => {
|
|
130
|
+
runtime.autoRenderState.set(autoRender);
|
|
131
|
+
}, [autoRender, runtime]);
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
runtime.maxDeltaState.set(maxDelta);
|
|
134
|
+
}, [maxDelta, runtime]);
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
runtime.dprState.set(dpr);
|
|
137
|
+
}, [dpr, runtime]);
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
const limit = getNormalizedErrorHistoryLimit(errorHistoryLimit);
|
|
140
|
+
if (limit <= 0) {
|
|
141
|
+
if (errorHistory.length === 0) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
setErrorHistory([]);
|
|
145
|
+
onErrorHistory?.([]);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (errorHistory.length <= limit) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const trimmed = errorHistory.slice(errorHistory.length - limit);
|
|
152
|
+
setErrorHistory(trimmed);
|
|
153
|
+
onErrorHistory?.(trimmed);
|
|
154
|
+
}, [errorHistory, errorHistoryLimit, onErrorHistory]);
|
|
155
|
+
useEffect(() => {
|
|
156
|
+
const canvas = runtime.canvasRef.current;
|
|
157
|
+
if (!canvas) {
|
|
158
|
+
const report = toMotionGPUErrorReport(new Error('Canvas element is not available'), 'initialization');
|
|
159
|
+
setErrorReport(report);
|
|
160
|
+
const historyLimit = getNormalizedErrorHistoryLimit(runtimePropsRef.current.errorHistoryLimit);
|
|
161
|
+
if (historyLimit > 0) {
|
|
162
|
+
const nextHistory = [report].slice(-historyLimit);
|
|
163
|
+
setErrorHistory(nextHistory);
|
|
164
|
+
runtimePropsRef.current.onErrorHistory?.(nextHistory);
|
|
165
|
+
}
|
|
166
|
+
runtimePropsRef.current.onError?.(report);
|
|
167
|
+
return () => {
|
|
168
|
+
runtime.registry.clear();
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
const runtimeLoop = createMotionGPURuntimeLoop({
|
|
172
|
+
canvas,
|
|
173
|
+
registry: runtime.registry,
|
|
174
|
+
size: runtime.size,
|
|
175
|
+
dpr: runtime.dprState,
|
|
176
|
+
maxDelta: runtime.maxDeltaState,
|
|
177
|
+
getMaterial: () => runtimePropsRef.current.material,
|
|
178
|
+
getRenderTargets: () => runtimePropsRef.current.renderTargets,
|
|
179
|
+
getPasses: () => runtimePropsRef.current.passes,
|
|
180
|
+
getClearColor: () => runtimePropsRef.current.clearColor,
|
|
181
|
+
getOutputColorSpace: () => runtimePropsRef.current.outputColorSpace,
|
|
182
|
+
getAdapterOptions: () => runtimePropsRef.current.adapterOptions,
|
|
183
|
+
getDeviceDescriptor: () => runtimePropsRef.current.deviceDescriptor,
|
|
184
|
+
getOnError: () => runtimePropsRef.current.onError,
|
|
185
|
+
getErrorHistoryLimit: () => runtimePropsRef.current.errorHistoryLimit,
|
|
186
|
+
getOnErrorHistory: () => runtimePropsRef.current.onErrorHistory,
|
|
187
|
+
reportErrorHistory: (history) => {
|
|
188
|
+
setErrorHistory(history);
|
|
189
|
+
},
|
|
190
|
+
reportError: (report) => {
|
|
191
|
+
setErrorReport(report);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
runtime.requestFrameSignalRef.current = runtimeLoop.requestFrame;
|
|
195
|
+
return () => {
|
|
196
|
+
runtime.requestFrameSignalRef.current = null;
|
|
197
|
+
runtimeLoop.destroy();
|
|
198
|
+
};
|
|
199
|
+
}, [runtime]);
|
|
200
|
+
const canvasStyle = {
|
|
201
|
+
position: 'absolute',
|
|
202
|
+
inset: 0,
|
|
203
|
+
display: 'block',
|
|
204
|
+
width: '100%',
|
|
205
|
+
height: '100%',
|
|
206
|
+
...style
|
|
207
|
+
};
|
|
208
|
+
return (_jsx(FrameRegistryReactContext.Provider, { value: runtime.registry, children: _jsx(MotionGPUReactContext.Provider, { value: runtime.context, children: _jsxs("div", { className: "motiongpu-canvas-wrap", style: {
|
|
209
|
+
position: 'relative',
|
|
210
|
+
width: '100%',
|
|
211
|
+
height: '100%',
|
|
212
|
+
minWidth: 0,
|
|
213
|
+
minHeight: 0,
|
|
214
|
+
overflow: 'hidden'
|
|
215
|
+
}, children: [_jsx("canvas", { className: className, style: canvasStyle, ref: (node) => {
|
|
216
|
+
runtime.canvasRef.current = node ?? undefined;
|
|
217
|
+
} }), showErrorOverlay && errorReport ? (errorRenderer ? (errorRenderer(errorReport)) : (_jsx(MotionGPUErrorOverlay, { report: errorReport }))) : null, children] }) }) }));
|
|
218
|
+
}
|