@motion-core/motion-gpu 0.4.0 → 0.4.2
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/dist/advanced.d.ts +1 -0
- package/dist/advanced.d.ts.map +1 -0
- package/dist/advanced.js +12 -6
- package/dist/core/advanced.d.ts +1 -0
- package/dist/core/advanced.d.ts.map +1 -0
- package/dist/core/advanced.js +12 -5
- 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 +1 -0
- package/dist/core/error-report.d.ts.map +1 -0
- package/dist/core/error-report.js +184 -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 +1 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +11 -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 +128 -151
- package/dist/core/material-preprocess.js.map +1 -0
- package/dist/core/material.d.ts +1 -0
- package/dist/core/material.d.ts.map +1 -0
- package/dist/core/material.js +263 -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 +1 -0
- package/dist/core/render-graph.d.ts.map +1 -0
- package/dist/core/render-graph.js +61 -68
- package/dist/core/render-graph.js.map +1 -0
- package/dist/core/render-targets.d.ts +2 -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 +942 -1081
- package/dist/core/renderer.js.map +1 -0
- package/dist/core/runtime-loop.d.ts +2 -0
- package/dist/core/runtime-loop.d.ts.map +1 -0
- package/dist/core/runtime-loop.js +305 -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 +1 -0
- package/dist/core/shader.d.ts.map +1 -0
- package/dist/core/shader.js +92 -117
- package/dist/core/shader.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 +205 -273
- package/dist/core/texture-loader.js.map +1 -0
- package/dist/core/textures.d.ts +2 -0
- package/dist/core/textures.d.ts.map +1 -0
- package/dist/core/textures.js +106 -116
- package/dist/core/textures.js.map +1 -0
- package/dist/core/types.d.ts +2 -0
- 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 +11 -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/CopyPass.d.ts +2 -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 +2 -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/ShaderPass.d.ts +1 -0
- package/dist/passes/ShaderPass.d.ts.map +1 -0
- package/dist/passes/ShaderPass.js +40 -37
- package/dist/passes/ShaderPass.js.map +1 -0
- package/dist/passes/index.d.ts +1 -0
- package/dist/passes/index.d.ts.map +1 -0
- package/dist/passes/index.js +4 -3
- package/dist/react/FragCanvas.d.ts +2 -0
- 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 +384 -48
- 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 +12 -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 +1 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +10 -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.d.ts +2 -0
- package/dist/svelte/FragCanvas.svelte.d.ts.map +1 -0
- package/dist/svelte/MotionGPUErrorOverlay.svelte +17 -20
- 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 +11 -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 +1 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +10 -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 +15 -7
- package/src/lib/advanced.ts +6 -0
- package/src/lib/core/advanced.ts +12 -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 +406 -0
- package/src/lib/core/frame-registry.ts +1189 -0
- package/src/lib/core/index.ts +77 -0
- package/src/lib/core/material-preprocess.ts +284 -0
- package/src/lib/core/material.ts +667 -0
- package/src/lib/core/recompile-policy.ts +31 -0
- package/src/lib/core/render-graph.ts +143 -0
- package/src/lib/core/render-targets.ts +107 -0
- package/src/lib/core/renderer.ts +1547 -0
- package/src/lib/core/runtime-loop.ts +458 -0
- package/src/lib/core/scheduler-helpers.ts +136 -0
- package/src/lib/core/shader.ts +258 -0
- package/src/lib/core/texture-loader.ts +476 -0
- package/src/lib/core/textures.ts +235 -0
- package/src/lib/core/types.ts +582 -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/CopyPass.ts +80 -0
- package/src/lib/passes/FullscreenPass.ts +173 -0
- package/src/lib/passes/ShaderPass.ts +88 -0
- package/src/lib/passes/index.ts +3 -0
- package/src/lib/react/MotionGPUErrorOverlay.tsx +392 -0
- package/src/lib/react/advanced.ts +36 -0
- package/src/lib/react/frame-context.ts +169 -0
- package/src/lib/react/index.ts +51 -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 +382 -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 +51 -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
- package/dist/react/MotionGPUErrorOverlay.tsx +0 -129
- /package/{dist → src/lib}/react/FragCanvas.tsx +0 -0
- /package/{dist → src/lib}/react/Portal.tsx +0 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { getContext, onDestroy, setContext } from 'svelte';
|
|
2
|
+
import {
|
|
3
|
+
createFrameRegistry,
|
|
4
|
+
type FrameCallback,
|
|
5
|
+
type FrameKey,
|
|
6
|
+
type FrameProfilingSnapshot,
|
|
7
|
+
type FrameRegistry,
|
|
8
|
+
type FrameRunTimings,
|
|
9
|
+
type FrameScheduleSnapshot,
|
|
10
|
+
type FrameStage,
|
|
11
|
+
type FrameStageCallback,
|
|
12
|
+
type FrameTask,
|
|
13
|
+
type FrameTaskInvalidation,
|
|
14
|
+
type FrameTaskInvalidationToken,
|
|
15
|
+
type UseFrameOptions,
|
|
16
|
+
type UseFrameResult
|
|
17
|
+
} from '../core/frame-registry.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Svelte context key for the active frame registry.
|
|
21
|
+
*/
|
|
22
|
+
const FRAME_CONTEXT_KEY = Symbol('motiongpu.frame-context');
|
|
23
|
+
|
|
24
|
+
export {
|
|
25
|
+
createFrameRegistry,
|
|
26
|
+
type FrameCallback,
|
|
27
|
+
type FrameKey,
|
|
28
|
+
type FrameProfilingSnapshot,
|
|
29
|
+
type FrameRegistry,
|
|
30
|
+
type FrameRunTimings,
|
|
31
|
+
type FrameScheduleSnapshot,
|
|
32
|
+
type FrameStage,
|
|
33
|
+
type FrameStageCallback,
|
|
34
|
+
type FrameTask,
|
|
35
|
+
type FrameTaskInvalidation,
|
|
36
|
+
type FrameTaskInvalidationToken,
|
|
37
|
+
type UseFrameOptions,
|
|
38
|
+
type UseFrameResult
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Provides a frame registry through Svelte context.
|
|
43
|
+
*/
|
|
44
|
+
export function provideFrameRegistry(registry: FrameRegistry): void {
|
|
45
|
+
setContext(FRAME_CONTEXT_KEY, registry);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Registers a frame callback using an auto-generated task key.
|
|
50
|
+
*/
|
|
51
|
+
export function useFrame(callback: FrameCallback, options?: UseFrameOptions): UseFrameResult;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Registers a frame callback with an explicit task key.
|
|
55
|
+
*/
|
|
56
|
+
export function useFrame(
|
|
57
|
+
key: FrameKey,
|
|
58
|
+
callback: FrameCallback,
|
|
59
|
+
options?: UseFrameOptions
|
|
60
|
+
): UseFrameResult;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Registers a callback in the active frame registry and auto-unsubscribes on destroy.
|
|
64
|
+
*/
|
|
65
|
+
export function useFrame(
|
|
66
|
+
keyOrCallback: FrameKey | FrameCallback,
|
|
67
|
+
callbackOrOptions?: FrameCallback | UseFrameOptions,
|
|
68
|
+
maybeOptions?: UseFrameOptions
|
|
69
|
+
): UseFrameResult {
|
|
70
|
+
const registry = getContext<FrameRegistry>(FRAME_CONTEXT_KEY);
|
|
71
|
+
if (!registry) {
|
|
72
|
+
throw new Error('useFrame must be used inside <FragCanvas>');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const registration =
|
|
76
|
+
typeof keyOrCallback === 'function'
|
|
77
|
+
? registry.register(keyOrCallback, callbackOrOptions as UseFrameOptions | undefined)
|
|
78
|
+
: registry.register(keyOrCallback, callbackOrOptions as FrameCallback, maybeOptions);
|
|
79
|
+
onDestroy(registration.unsubscribe);
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
task: registration.task,
|
|
83
|
+
start: registration.start,
|
|
84
|
+
stop: registration.stop,
|
|
85
|
+
started: registration.started
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelte adapter entrypoint for MotionGPU.
|
|
3
|
+
*/
|
|
4
|
+
export { default as FragCanvas } from './FragCanvas.svelte';
|
|
5
|
+
export { defineMaterial } from '../core/material.js';
|
|
6
|
+
export { BlitPass, CopyPass, ShaderPass } from '../passes/index.js';
|
|
7
|
+
export { useMotionGPU } from './motiongpu-context.js';
|
|
8
|
+
export { useFrame } from './frame-context.js';
|
|
9
|
+
export { useTexture } from './use-texture.js';
|
|
10
|
+
export type {
|
|
11
|
+
FrameInvalidationToken,
|
|
12
|
+
FrameState,
|
|
13
|
+
OutputColorSpace,
|
|
14
|
+
RenderPass,
|
|
15
|
+
RenderPassContext,
|
|
16
|
+
RenderPassFlags,
|
|
17
|
+
RenderPassInputSlot,
|
|
18
|
+
RenderPassOutputSlot,
|
|
19
|
+
RenderMode,
|
|
20
|
+
RenderTarget,
|
|
21
|
+
RenderTargetDefinition,
|
|
22
|
+
RenderTargetDefinitionMap,
|
|
23
|
+
TextureData,
|
|
24
|
+
TextureDefinition,
|
|
25
|
+
TextureDefinitionMap,
|
|
26
|
+
TextureUpdateMode,
|
|
27
|
+
TextureMap,
|
|
28
|
+
TextureSource,
|
|
29
|
+
TextureValue,
|
|
30
|
+
TypedUniform,
|
|
31
|
+
UniformMat4Value,
|
|
32
|
+
UniformMap,
|
|
33
|
+
UniformType,
|
|
34
|
+
UniformValue
|
|
35
|
+
} from '../core/types.js';
|
|
36
|
+
export type {
|
|
37
|
+
LoadedTexture,
|
|
38
|
+
TextureDecodeOptions,
|
|
39
|
+
TextureLoadOptions
|
|
40
|
+
} from '../core/texture-loader.js';
|
|
41
|
+
export type {
|
|
42
|
+
FragMaterial,
|
|
43
|
+
FragMaterialInput,
|
|
44
|
+
MaterialIncludes,
|
|
45
|
+
MaterialDefineValue,
|
|
46
|
+
MaterialDefines,
|
|
47
|
+
TypedMaterialDefineValue
|
|
48
|
+
} from '../core/material.js';
|
|
49
|
+
export type { MotionGPUContext } from './motiongpu-context.js';
|
|
50
|
+
export type { UseFrameOptions, UseFrameResult } from './frame-context.js';
|
|
51
|
+
export type { TextureUrlInput, UseTextureResult } from './use-texture.js';
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { getContext, setContext } from 'svelte';
|
|
2
|
+
import type { RenderMode } from '../core/types.js';
|
|
3
|
+
import type { CurrentReadable, CurrentWritable } from '../core/current-value.js';
|
|
4
|
+
import type {
|
|
5
|
+
FrameProfilingSnapshot,
|
|
6
|
+
FrameRunTimings,
|
|
7
|
+
FrameScheduleSnapshot
|
|
8
|
+
} from '../core/frame-registry.js';
|
|
9
|
+
import type { MotionGPUScheduler as CoreMotionGPUScheduler } from '../core/scheduler-helpers.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Svelte context key used to expose `FragCanvas` runtime state.
|
|
13
|
+
*/
|
|
14
|
+
const MOTIONGPU_CONTEXT_KEY = Symbol('motiongpu.context');
|
|
15
|
+
|
|
16
|
+
export type MotionGPUScheduler = CoreMotionGPUScheduler;
|
|
17
|
+
export type { FrameProfilingSnapshot, FrameRunTimings, FrameScheduleSnapshot };
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Namespace identifier for user-owned context entries.
|
|
21
|
+
*/
|
|
22
|
+
export type MotionGPUUserNamespace = string | symbol;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Shared user context store exposed by `FragCanvas`.
|
|
26
|
+
*/
|
|
27
|
+
export type MotionGPUUserContext = CurrentWritable<Record<MotionGPUUserNamespace, unknown>>;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Public `FragCanvas` runtime context available to hooks and user components.
|
|
31
|
+
*/
|
|
32
|
+
export interface MotionGPUContext {
|
|
33
|
+
/**
|
|
34
|
+
* Underlying canvas element used by the renderer.
|
|
35
|
+
*/
|
|
36
|
+
canvas: HTMLCanvasElement | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Reactive canvas pixel size.
|
|
39
|
+
*/
|
|
40
|
+
size: CurrentReadable<{ width: number; height: number }>;
|
|
41
|
+
/**
|
|
42
|
+
* Device pixel ratio multiplier.
|
|
43
|
+
*/
|
|
44
|
+
dpr: CurrentWritable<number>;
|
|
45
|
+
/**
|
|
46
|
+
* Max frame delta clamp passed to scheduled callbacks.
|
|
47
|
+
*/
|
|
48
|
+
maxDelta: CurrentWritable<number>;
|
|
49
|
+
/**
|
|
50
|
+
* Scheduler render mode (`always`, `on-demand`, `manual`).
|
|
51
|
+
*/
|
|
52
|
+
renderMode: CurrentWritable<RenderMode>;
|
|
53
|
+
/**
|
|
54
|
+
* Global toggle for automatic rendering.
|
|
55
|
+
*/
|
|
56
|
+
autoRender: CurrentWritable<boolean>;
|
|
57
|
+
/**
|
|
58
|
+
* Namespaced user context store shared within the canvas subtree.
|
|
59
|
+
*/
|
|
60
|
+
user: MotionGPUUserContext;
|
|
61
|
+
/**
|
|
62
|
+
* Marks current frame as invalidated.
|
|
63
|
+
*/
|
|
64
|
+
invalidate: () => void;
|
|
65
|
+
/**
|
|
66
|
+
* Requests one manual frame advance.
|
|
67
|
+
*/
|
|
68
|
+
advance: () => void;
|
|
69
|
+
/**
|
|
70
|
+
* Public scheduler API.
|
|
71
|
+
*/
|
|
72
|
+
scheduler: MotionGPUScheduler;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Registers the motiongpu context in the current Svelte component tree.
|
|
77
|
+
*
|
|
78
|
+
* @param context - Context payload to provide.
|
|
79
|
+
*/
|
|
80
|
+
export function provideMotionGPUContext(context: MotionGPUContext): void {
|
|
81
|
+
setContext(MOTIONGPU_CONTEXT_KEY, context);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Returns the active motiongpu context.
|
|
86
|
+
*
|
|
87
|
+
* @returns Active context.
|
|
88
|
+
* @throws {Error} When called outside `<FragCanvas>`.
|
|
89
|
+
*/
|
|
90
|
+
export function useMotionGPU(): MotionGPUContext {
|
|
91
|
+
const context = getContext<MotionGPUContext>(MOTIONGPU_CONTEXT_KEY);
|
|
92
|
+
if (!context) {
|
|
93
|
+
throw new Error('useMotionGPU must be used inside <FragCanvas>');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return context;
|
|
97
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { CurrentReadable } from '../core/current-value.js';
|
|
2
|
+
import { useMotionGPU, type MotionGPUUserNamespace } from './motiongpu-context.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Internal shape of the user context store.
|
|
6
|
+
*/
|
|
7
|
+
type UserContextStore = Record<MotionGPUUserNamespace, unknown>;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Object-like context payload used by merge semantics.
|
|
11
|
+
*/
|
|
12
|
+
type UserContextEntry = Record<string, unknown>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Controls how a namespaced user context value behaves when already present.
|
|
16
|
+
*/
|
|
17
|
+
export interface SetMotionGPUUserContextOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Conflict strategy when namespace already exists:
|
|
20
|
+
* - `skip`: keep current value
|
|
21
|
+
* - `replace`: replace current value
|
|
22
|
+
* - `merge`: shallow merge object values, fallback to replace otherwise
|
|
23
|
+
*
|
|
24
|
+
* @default 'skip'
|
|
25
|
+
*/
|
|
26
|
+
existing?: 'merge' | 'replace' | 'skip';
|
|
27
|
+
/**
|
|
28
|
+
* How function inputs should be interpreted:
|
|
29
|
+
* - `factory`: call function and store its return value
|
|
30
|
+
* - `value`: store function itself
|
|
31
|
+
*
|
|
32
|
+
* @default 'factory'
|
|
33
|
+
*/
|
|
34
|
+
functionValue?: 'factory' | 'value';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Checks whether a value is a non-array object suitable for shallow merge.
|
|
39
|
+
*/
|
|
40
|
+
function isObjectEntry(value: unknown): value is UserContextEntry {
|
|
41
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Returns a read-only view of the entire motiongpu user context store.
|
|
46
|
+
*/
|
|
47
|
+
export function useMotionGPUUserContext<
|
|
48
|
+
UC extends UserContextStore = UserContextStore
|
|
49
|
+
>(): CurrentReadable<UC>;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Reads a namespaced user context value as a reactive readable store.
|
|
53
|
+
*/
|
|
54
|
+
export function useMotionGPUUserContext<
|
|
55
|
+
UC extends UserContextStore = UserContextStore,
|
|
56
|
+
K extends keyof UC & MotionGPUUserNamespace = keyof UC & MotionGPUUserNamespace
|
|
57
|
+
>(namespace: K): CurrentReadable<UC[K] | undefined>;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Read-only user context hook:
|
|
61
|
+
* - no args: returns full user context store
|
|
62
|
+
* - namespace: returns namespaced store view
|
|
63
|
+
*
|
|
64
|
+
* @param namespace - Optional namespace key.
|
|
65
|
+
*/
|
|
66
|
+
export function useMotionGPUUserContext<
|
|
67
|
+
UC extends UserContextStore = UserContextStore,
|
|
68
|
+
K extends keyof UC & MotionGPUUserNamespace = keyof UC & MotionGPUUserNamespace
|
|
69
|
+
>(namespace?: K): CurrentReadable<UC> | CurrentReadable<UC[K] | undefined> {
|
|
70
|
+
const userStore = useMotionGPU().user;
|
|
71
|
+
|
|
72
|
+
if (namespace === undefined) {
|
|
73
|
+
const allStore: CurrentReadable<UC> = {
|
|
74
|
+
get current() {
|
|
75
|
+
return userStore.current as UC;
|
|
76
|
+
},
|
|
77
|
+
subscribe(run) {
|
|
78
|
+
return userStore.subscribe((context) => run(context as UC));
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
return allStore;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const scopedStore: CurrentReadable<UC[K] | undefined> = {
|
|
86
|
+
get current() {
|
|
87
|
+
return userStore.current[namespace] as UC[K] | undefined;
|
|
88
|
+
},
|
|
89
|
+
subscribe(run) {
|
|
90
|
+
return userStore.subscribe((context) => run(context[namespace] as UC[K] | undefined));
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
return scopedStore;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Sets a namespaced user context value with explicit write semantics.
|
|
99
|
+
*
|
|
100
|
+
* Returns the effective value stored under the namespace.
|
|
101
|
+
*/
|
|
102
|
+
export function setMotionGPUUserContext<UCT = unknown>(
|
|
103
|
+
namespace: MotionGPUUserNamespace,
|
|
104
|
+
value: UCT | (() => UCT),
|
|
105
|
+
options?: SetMotionGPUUserContextOptions
|
|
106
|
+
): UCT | undefined {
|
|
107
|
+
const userStore = useMotionGPU().user;
|
|
108
|
+
const mode = options?.existing ?? 'skip';
|
|
109
|
+
const functionValueMode = options?.functionValue ?? 'factory';
|
|
110
|
+
let resolvedValue: UCT | undefined;
|
|
111
|
+
|
|
112
|
+
userStore.update((context) => {
|
|
113
|
+
const hasExisting = namespace in context;
|
|
114
|
+
if (hasExisting && mode === 'skip') {
|
|
115
|
+
resolvedValue = context[namespace] as UCT | undefined;
|
|
116
|
+
return context;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const nextValue =
|
|
120
|
+
typeof value === 'function' && functionValueMode === 'factory'
|
|
121
|
+
? (value as () => UCT)()
|
|
122
|
+
: (value as UCT);
|
|
123
|
+
if (hasExisting && mode === 'merge') {
|
|
124
|
+
const currentValue = context[namespace];
|
|
125
|
+
if (isObjectEntry(currentValue) && isObjectEntry(nextValue)) {
|
|
126
|
+
resolvedValue = {
|
|
127
|
+
...currentValue,
|
|
128
|
+
...nextValue
|
|
129
|
+
} as UCT;
|
|
130
|
+
return {
|
|
131
|
+
...context,
|
|
132
|
+
[namespace]: resolvedValue
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
resolvedValue = nextValue;
|
|
138
|
+
return {
|
|
139
|
+
...context,
|
|
140
|
+
[namespace]: nextValue
|
|
141
|
+
};
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
return resolvedValue;
|
|
145
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { onDestroy } from 'svelte';
|
|
2
|
+
import {
|
|
3
|
+
createCurrentWritable as currentWritable,
|
|
4
|
+
type CurrentReadable
|
|
5
|
+
} from '../core/current-value.js';
|
|
6
|
+
import {
|
|
7
|
+
isAbortError,
|
|
8
|
+
loadTexturesFromUrls,
|
|
9
|
+
type LoadedTexture,
|
|
10
|
+
type TextureLoadOptions
|
|
11
|
+
} from '../core/texture-loader.js';
|
|
12
|
+
import { toMotionGPUErrorReport, type MotionGPUErrorReport } from '../core/error-report.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Reactive state returned by {@link useTexture}.
|
|
16
|
+
*/
|
|
17
|
+
export interface UseTextureResult {
|
|
18
|
+
/**
|
|
19
|
+
* Loaded textures or `null` when unavailable/failed.
|
|
20
|
+
*/
|
|
21
|
+
textures: CurrentReadable<LoadedTexture[] | null>;
|
|
22
|
+
/**
|
|
23
|
+
* `true` while an active load request is running.
|
|
24
|
+
*/
|
|
25
|
+
loading: CurrentReadable<boolean>;
|
|
26
|
+
/**
|
|
27
|
+
* Last loading error.
|
|
28
|
+
*/
|
|
29
|
+
error: CurrentReadable<Error | null>;
|
|
30
|
+
/**
|
|
31
|
+
* Last loading error normalized to MotionGPU diagnostics report shape.
|
|
32
|
+
*/
|
|
33
|
+
errorReport: CurrentReadable<MotionGPUErrorReport | null>;
|
|
34
|
+
/**
|
|
35
|
+
* Reloads all textures using current URL input.
|
|
36
|
+
*/
|
|
37
|
+
reload: () => Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Supported URL input variants for `useTexture`.
|
|
42
|
+
*/
|
|
43
|
+
export type TextureUrlInput = string[] | (() => string[]);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Supported options input variants for `useTexture`.
|
|
47
|
+
*/
|
|
48
|
+
export type TextureOptionsInput = TextureLoadOptions | (() => TextureLoadOptions);
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Normalizes unknown thrown values to an `Error` instance.
|
|
52
|
+
*/
|
|
53
|
+
function toError(error: unknown): Error {
|
|
54
|
+
if (error instanceof Error) {
|
|
55
|
+
return error;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return new Error('Unknown texture loading error');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Releases GPU-side resources for a list of loaded textures.
|
|
63
|
+
*/
|
|
64
|
+
function disposeTextures(list: LoadedTexture[] | null): void {
|
|
65
|
+
for (const texture of list ?? []) {
|
|
66
|
+
texture.dispose();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface MergedAbortSignal {
|
|
71
|
+
signal: AbortSignal;
|
|
72
|
+
dispose: () => void;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function mergeAbortSignals(
|
|
76
|
+
primary: AbortSignal,
|
|
77
|
+
secondary: AbortSignal | undefined
|
|
78
|
+
): MergedAbortSignal {
|
|
79
|
+
if (!secondary) {
|
|
80
|
+
return {
|
|
81
|
+
signal: primary,
|
|
82
|
+
dispose: () => {}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (typeof AbortSignal.any === 'function') {
|
|
87
|
+
return {
|
|
88
|
+
signal: AbortSignal.any([primary, secondary]),
|
|
89
|
+
dispose: () => {}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const fallback = new AbortController();
|
|
94
|
+
let disposed = false;
|
|
95
|
+
const cleanup = (): void => {
|
|
96
|
+
if (disposed) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
disposed = true;
|
|
100
|
+
primary.removeEventListener('abort', abort);
|
|
101
|
+
secondary.removeEventListener('abort', abort);
|
|
102
|
+
};
|
|
103
|
+
const abort = (): void => fallback.abort();
|
|
104
|
+
|
|
105
|
+
primary.addEventListener('abort', abort, { once: true });
|
|
106
|
+
secondary.addEventListener('abort', abort, { once: true });
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
signal: fallback.signal,
|
|
110
|
+
dispose: cleanup
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Loads textures from URLs and exposes reactive loading/error state.
|
|
116
|
+
*
|
|
117
|
+
* @param urlInput - URLs array or lazy URL provider.
|
|
118
|
+
* @param optionsInput - Loader options object or lazy options provider.
|
|
119
|
+
* @returns Reactive texture loading state with reload support.
|
|
120
|
+
*/
|
|
121
|
+
export function useTexture(
|
|
122
|
+
urlInput: TextureUrlInput,
|
|
123
|
+
optionsInput: TextureOptionsInput = {}
|
|
124
|
+
): UseTextureResult {
|
|
125
|
+
const textures = currentWritable<LoadedTexture[] | null>(null);
|
|
126
|
+
const loading = currentWritable(true);
|
|
127
|
+
const error = currentWritable<Error | null>(null);
|
|
128
|
+
const errorReport = currentWritable<MotionGPUErrorReport | null>(null);
|
|
129
|
+
let disposed = false;
|
|
130
|
+
let requestVersion = 0;
|
|
131
|
+
let activeController: AbortController | null = null;
|
|
132
|
+
let runningLoad: Promise<void> | null = null;
|
|
133
|
+
let reloadQueued = false;
|
|
134
|
+
const getUrls = typeof urlInput === 'function' ? urlInput : () => urlInput;
|
|
135
|
+
const getOptions =
|
|
136
|
+
typeof optionsInput === 'function'
|
|
137
|
+
? (optionsInput as () => TextureLoadOptions)
|
|
138
|
+
: () => optionsInput;
|
|
139
|
+
|
|
140
|
+
const executeLoad = async (): Promise<void> => {
|
|
141
|
+
if (disposed) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const version = ++requestVersion;
|
|
146
|
+
const controller = new AbortController();
|
|
147
|
+
activeController = controller;
|
|
148
|
+
loading.set(true);
|
|
149
|
+
error.set(null);
|
|
150
|
+
errorReport.set(null);
|
|
151
|
+
|
|
152
|
+
const previous = textures.current;
|
|
153
|
+
const options = getOptions() ?? {};
|
|
154
|
+
const mergedSignal = mergeAbortSignals(controller.signal, options.signal);
|
|
155
|
+
try {
|
|
156
|
+
const loaded = await loadTexturesFromUrls(getUrls(), {
|
|
157
|
+
...options,
|
|
158
|
+
signal: mergedSignal.signal
|
|
159
|
+
});
|
|
160
|
+
if (disposed || version !== requestVersion) {
|
|
161
|
+
disposeTextures(loaded);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
textures.set(loaded);
|
|
166
|
+
disposeTextures(previous);
|
|
167
|
+
} catch (nextError) {
|
|
168
|
+
if (disposed || version !== requestVersion) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (isAbortError(nextError)) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
disposeTextures(previous);
|
|
177
|
+
textures.set(null);
|
|
178
|
+
const normalizedError = toError(nextError);
|
|
179
|
+
error.set(normalizedError);
|
|
180
|
+
errorReport.set(toMotionGPUErrorReport(normalizedError, 'initialization'));
|
|
181
|
+
} finally {
|
|
182
|
+
if (!disposed && version === requestVersion) {
|
|
183
|
+
loading.set(false);
|
|
184
|
+
}
|
|
185
|
+
if (activeController === controller) {
|
|
186
|
+
activeController = null;
|
|
187
|
+
}
|
|
188
|
+
mergedSignal.dispose();
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const runLoadLoop = async (): Promise<void> => {
|
|
193
|
+
do {
|
|
194
|
+
reloadQueued = false;
|
|
195
|
+
await executeLoad();
|
|
196
|
+
} while (reloadQueued && !disposed);
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const load = (): Promise<void> => {
|
|
200
|
+
activeController?.abort();
|
|
201
|
+
if (runningLoad) {
|
|
202
|
+
reloadQueued = true;
|
|
203
|
+
return runningLoad;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const pending = runLoadLoop();
|
|
207
|
+
const trackedPending = pending.finally(() => {
|
|
208
|
+
if (runningLoad === trackedPending) {
|
|
209
|
+
runningLoad = null;
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
runningLoad = trackedPending;
|
|
213
|
+
return trackedPending;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
void load();
|
|
217
|
+
|
|
218
|
+
onDestroy(() => {
|
|
219
|
+
disposed = true;
|
|
220
|
+
requestVersion += 1;
|
|
221
|
+
activeController?.abort();
|
|
222
|
+
disposeTextures(textures.current);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
textures,
|
|
227
|
+
loading,
|
|
228
|
+
error,
|
|
229
|
+
errorReport,
|
|
230
|
+
reload: load
|
|
231
|
+
};
|
|
232
|
+
}
|