@octane-xplat/shaders 0.13.0 → 0.15.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 +8 -8
- package/package.json +10 -10
- package/src/host.ts +34 -8
- package/src/host.web.ts +18 -4
- package/src/index.macos.ts +10 -2
- package/src/shared.ts +30 -12
- package/dist/native/index.js +0 -2
- package/dist/native/index2.js +0 -90
- package/dist/native/index3.js +0 -167
- package/dist/native/octane-client-build.json +0 -8
- package/dist/web/index.js +0 -2
- package/dist/web/index2.js +0 -90
- package/dist/web/index3.js +0 -43
- package/dist/web/octane-client-build.json +0 -8
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ pnpm add @octane-xplat/shaders
|
|
|
20
20
|
import { Canvas } from '@octane-xplat/canvas'
|
|
21
21
|
import { createShader } from '@octane-xplat/shaders'
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
;<Canvas
|
|
24
24
|
width={320}
|
|
25
25
|
height={180}
|
|
26
26
|
style={{ width: 320, height: 180 }}
|
|
@@ -66,13 +66,13 @@ static fallback, reduced-motion still frame, disposal on unmount).
|
|
|
66
66
|
|
|
67
67
|
## Target status
|
|
68
68
|
|
|
69
|
-
| Target
|
|
70
|
-
|
|
|
71
|
-
| Web (DOM)
|
|
72
|
-
| NativeScript iOS
|
|
73
|
-
| NativeScript Android
|
|
74
|
-
| AppKit macOS / desktop native
|
|
75
|
-
| Web code inside a desktop webview | Follows the browser's own WebGPU support; no separate integration.
|
|
69
|
+
| Target | Status |
|
|
70
|
+
| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
71
|
+
| Web (DOM) | Implemented. Upstream engine on the browser WebGPU canvas; reached ready + submissions in the headless-Chromium spike (SwiftShader). Hardware GPU correctness, sustained lifecycle, and visual output are unverified — a SwiftShader device-loss observed in the spike was reproduced without the library (raw WebGPU loop) and is attributed to the headless backend, not the package. |
|
|
72
|
+
| NativeScript iOS | Implemented, experimental. `@nativescript/canvas` WebGPU (wgpu over Metal). Build/bundle/type verified; device execution pending. |
|
|
73
|
+
| NativeScript Android | Implemented, experimental. Same native route over Vulkan. Build/type verified; device/driver execution pending. |
|
|
74
|
+
| AppKit macOS / desktop native | Explicitly unsupported. Importing the package is inert (no GPU work, no engine load); `createShader` resolves to a no-op instance with `getFailureReason() === 'unsupported'` so fallback handling is identical. |
|
|
75
|
+
| Web code inside a desktop webview | Follows the browser's own WebGPU support; no separate integration. |
|
|
76
76
|
|
|
77
77
|
Adapter-owned native defaults: `navigator.gpu` is bound lazily to the
|
|
78
78
|
`@nativescript/canvas` GPU shim on first use (upstream reads it unconditionally);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octane-xplat/shaders",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Thin adapter that runs the published `shaders` WebGPU library on Octane xplat Canvas surfaces — web (implemented) and NativeScript iOS/Android (experimental, device verification pending)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"canvas",
|
|
@@ -22,9 +22,10 @@
|
|
|
22
22
|
"directory": "packages/shaders"
|
|
23
23
|
},
|
|
24
24
|
"files": [
|
|
25
|
-
"dist",
|
|
26
25
|
"types",
|
|
27
|
-
"src"
|
|
26
|
+
"src",
|
|
27
|
+
"!src/**/*.test.*",
|
|
28
|
+
"!src/**/*.spec.*"
|
|
28
29
|
],
|
|
29
30
|
"type": "module",
|
|
30
31
|
"exports": {
|
|
@@ -34,24 +35,24 @@
|
|
|
34
35
|
"default": "./src/index.macos.ts"
|
|
35
36
|
},
|
|
36
37
|
"types": "./types/index.d.ts",
|
|
37
|
-
"web": "./
|
|
38
|
-
"native": "./
|
|
39
|
-
"default": "./
|
|
38
|
+
"web": "./src/index.web.ts",
|
|
39
|
+
"native": "./src/index.ts",
|
|
40
|
+
"default": "./src/index.ts"
|
|
40
41
|
}
|
|
41
42
|
},
|
|
42
43
|
"publishConfig": {
|
|
43
44
|
"access": "public"
|
|
44
45
|
},
|
|
45
46
|
"dependencies": {
|
|
46
|
-
"@octane-xplat/canvas": "0.
|
|
47
|
+
"@octane-xplat/canvas": "0.15.0",
|
|
47
48
|
"shaders": "4.0.0"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
|
-
"@nativescript-community/octane": "0.2.
|
|
51
|
+
"@nativescript-community/octane": "0.2.5",
|
|
51
52
|
"@nativescript/core": "9.1.3",
|
|
52
53
|
"@octanejs/vite-plugin": "0.1.61",
|
|
53
54
|
"octane": "0.11.0",
|
|
54
|
-
"tsrx-typegen": "0.
|
|
55
|
+
"tsrx-typegen": "0.3.0",
|
|
55
56
|
"typescript": "5.9.3",
|
|
56
57
|
"vite": "8.3.0"
|
|
57
58
|
},
|
|
@@ -70,7 +71,6 @@
|
|
|
70
71
|
}
|
|
71
72
|
},
|
|
72
73
|
"scripts": {
|
|
73
|
-
"build": "vite build && vite build --mode native && pnpm typegen && node ../../scripts/check-native-dist.mjs",
|
|
74
74
|
"typegen": "tsc -p tsconfig.types.json",
|
|
75
75
|
"check-types": "tsc -p tsconfig.check.json",
|
|
76
76
|
"test:packed": "node tests/packed-consumer.mjs",
|
package/src/host.ts
CHANGED
|
@@ -26,7 +26,14 @@ import {
|
|
|
26
26
|
isWebGPUSupported as upstreamIsWebGPUSupported,
|
|
27
27
|
} from 'shaders/js'
|
|
28
28
|
|
|
29
|
-
import type {
|
|
29
|
+
import type {
|
|
30
|
+
CreateShaderOptions,
|
|
31
|
+
PresetConfig,
|
|
32
|
+
SharedGpu,
|
|
33
|
+
ShaderInstance,
|
|
34
|
+
WebGPUSupportInfo,
|
|
35
|
+
} from './types'
|
|
36
|
+
|
|
30
37
|
import { resolveOptions, throwIfAborted, waitForPositiveSize, wrapInstance } from './shared'
|
|
31
38
|
import type { Size } from './shared'
|
|
32
39
|
|
|
@@ -46,10 +53,16 @@ function ensureGpu(): any {
|
|
|
46
53
|
}
|
|
47
54
|
|
|
48
55
|
const gpu = gpuShim
|
|
49
|
-
if (!gpu) {
|
|
56
|
+
if (!gpu) {
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
|
+
|
|
50
60
|
const g = globalThis as any
|
|
51
61
|
try {
|
|
52
|
-
if (!g.navigator) {
|
|
62
|
+
if (!g.navigator) {
|
|
63
|
+
g.navigator = {}
|
|
64
|
+
}
|
|
65
|
+
|
|
53
66
|
if (!g.navigator.gpu) {
|
|
54
67
|
try {
|
|
55
68
|
g.navigator.gpu = gpu
|
|
@@ -110,10 +123,16 @@ function hostCanvas(view: any): any {
|
|
|
110
123
|
|
|
111
124
|
async function acquireInjectedGpu(): Promise<SharedGpu | null> {
|
|
112
125
|
const gpu = ensureGpu()
|
|
113
|
-
if (!gpu?.requestAdapter) {
|
|
126
|
+
if (!gpu?.requestAdapter) {
|
|
127
|
+
return null
|
|
128
|
+
}
|
|
129
|
+
|
|
114
130
|
try {
|
|
115
131
|
const adapter = await gpu.requestAdapter()
|
|
116
|
-
if (!adapter) {
|
|
132
|
+
if (!adapter) {
|
|
133
|
+
return null
|
|
134
|
+
}
|
|
135
|
+
|
|
117
136
|
const device = await adapter.requestDevice()
|
|
118
137
|
return device ? { device, adapter } : null
|
|
119
138
|
} catch {
|
|
@@ -135,7 +154,9 @@ export async function createShader(
|
|
|
135
154
|
// cannot kill another's shared device.
|
|
136
155
|
if (!upstream.gpu) {
|
|
137
156
|
const gpu = await acquireInjectedGpu()
|
|
138
|
-
if (gpu) {
|
|
157
|
+
if (gpu) {
|
|
158
|
+
upstream.gpu = gpu
|
|
159
|
+
}
|
|
139
160
|
}
|
|
140
161
|
|
|
141
162
|
throwIfAborted(signal)
|
|
@@ -165,7 +186,9 @@ export async function createShader(
|
|
|
165
186
|
return wrapInstance(inner, attach)
|
|
166
187
|
}
|
|
167
188
|
|
|
168
|
-
export async function createSharedDevice(options?: {
|
|
189
|
+
export async function createSharedDevice(options?: {
|
|
190
|
+
powerPreference?: string
|
|
191
|
+
}): Promise<SharedGpu | null> {
|
|
169
192
|
ensureGpu()
|
|
170
193
|
return upstreamCreateSharedDevice(options as any)
|
|
171
194
|
}
|
|
@@ -190,7 +213,10 @@ export function prefersReducedMotion(): boolean {
|
|
|
190
213
|
if (isAndroid) {
|
|
191
214
|
const context = Application.android?.context
|
|
192
215
|
const settings = (globalThis as any).android?.provider?.Settings
|
|
193
|
-
if (!context || !settings?.Global) {
|
|
216
|
+
if (!context || !settings?.Global) {
|
|
217
|
+
return false
|
|
218
|
+
}
|
|
219
|
+
|
|
194
220
|
const scale = settings.Global.getFloat(
|
|
195
221
|
context.getContentResolver(),
|
|
196
222
|
settings.Global.ANIMATOR_DURATION_SCALE,
|
package/src/host.web.ts
CHANGED
|
@@ -12,7 +12,14 @@ import {
|
|
|
12
12
|
isWebGPUSupported as upstreamIsWebGPUSupported,
|
|
13
13
|
} from 'shaders/js'
|
|
14
14
|
|
|
15
|
-
import type {
|
|
15
|
+
import type {
|
|
16
|
+
CreateShaderOptions,
|
|
17
|
+
PresetConfig,
|
|
18
|
+
SharedGpu,
|
|
19
|
+
ShaderInstance,
|
|
20
|
+
WebGPUSupportInfo,
|
|
21
|
+
} from './types'
|
|
22
|
+
|
|
16
23
|
import { resolveOptions, throwIfAborted, waitForPositiveSize, wrapInstance } from './shared'
|
|
17
24
|
import type { Size } from './shared'
|
|
18
25
|
|
|
@@ -25,7 +32,10 @@ function measure(canvas: any): Size {
|
|
|
25
32
|
|
|
26
33
|
function attachVisibility(hide: () => void, show: () => void): () => void {
|
|
27
34
|
const doc = (globalThis as any).document
|
|
28
|
-
if (!doc?.addEventListener) {
|
|
35
|
+
if (!doc?.addEventListener) {
|
|
36
|
+
return () => {}
|
|
37
|
+
}
|
|
38
|
+
|
|
29
39
|
const onChange = () => (doc.visibilityState === 'hidden' ? hide() : show())
|
|
30
40
|
doc.addEventListener('visibilitychange', onChange)
|
|
31
41
|
return () => doc.removeEventListener('visibilitychange', onChange)
|
|
@@ -50,7 +60,9 @@ export async function createShader(
|
|
|
50
60
|
return wrapInstance(inner, attachVisibility)
|
|
51
61
|
}
|
|
52
62
|
|
|
53
|
-
export function createSharedDevice(options?: {
|
|
63
|
+
export function createSharedDevice(options?: {
|
|
64
|
+
powerPreference?: string
|
|
65
|
+
}): Promise<SharedGpu | null> {
|
|
54
66
|
return upstreamCreateSharedDevice(options as any)
|
|
55
67
|
}
|
|
56
68
|
|
|
@@ -64,5 +76,7 @@ export function getWebGPUSupport(): Promise<WebGPUSupportInfo> {
|
|
|
64
76
|
|
|
65
77
|
export function prefersReducedMotion(): boolean {
|
|
66
78
|
const matchMedia = (globalThis as any).matchMedia
|
|
67
|
-
return typeof matchMedia === 'function'
|
|
79
|
+
return typeof matchMedia === 'function'
|
|
80
|
+
? !!matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
81
|
+
: false
|
|
68
82
|
}
|
package/src/index.macos.ts
CHANGED
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
// no-op instance reporting the upstream-style 'unsupported' failure
|
|
5
5
|
// reason so callers keep their static fallback path identical to web's.
|
|
6
6
|
export * from './types'
|
|
7
|
-
import type {
|
|
7
|
+
import type {
|
|
8
|
+
CreateShaderOptions,
|
|
9
|
+
PresetConfig,
|
|
10
|
+
SharedGpu,
|
|
11
|
+
ShaderInstance,
|
|
12
|
+
WebGPUSupportInfo,
|
|
13
|
+
} from './types'
|
|
8
14
|
|
|
9
15
|
const UNSUPPORTED: ShaderInstance = {
|
|
10
16
|
getFailureReason: () => 'unsupported',
|
|
@@ -24,7 +30,9 @@ export async function createShader(
|
|
|
24
30
|
return UNSUPPORTED
|
|
25
31
|
}
|
|
26
32
|
|
|
27
|
-
export async function createSharedDevice(_options?: {
|
|
33
|
+
export async function createSharedDevice(_options?: {
|
|
34
|
+
powerPreference?: string
|
|
35
|
+
}): Promise<SharedGpu | null> {
|
|
28
36
|
return null
|
|
29
37
|
}
|
|
30
38
|
|
package/src/shared.ts
CHANGED
|
@@ -11,7 +11,9 @@ export function abortError(): Error {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export function throwIfAborted(signal?: AbortSignalLike): void {
|
|
14
|
-
if (signal?.aborted) {
|
|
14
|
+
if (signal?.aborted) {
|
|
15
|
+
throw abortError()
|
|
16
|
+
}
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
export interface Size {
|
|
@@ -21,16 +23,19 @@ export interface Size {
|
|
|
21
23
|
|
|
22
24
|
/** Defer until the surface reports a positive size (surface not attached /
|
|
23
25
|
* zero-size layout → wait, don't init). Aborts via `signal`. */
|
|
24
|
-
export function waitForPositiveSize(
|
|
25
|
-
measure: () => Size,
|
|
26
|
-
signal?: AbortSignalLike,
|
|
27
|
-
): Promise<void> {
|
|
26
|
+
export function waitForPositiveSize(measure: () => Size, signal?: AbortSignalLike): Promise<void> {
|
|
28
27
|
const first = measure()
|
|
29
|
-
if (first.width > 0 && first.height > 0) {
|
|
28
|
+
if (first.width > 0 && first.height > 0) {
|
|
29
|
+
return Promise.resolve()
|
|
30
|
+
}
|
|
31
|
+
|
|
30
32
|
return new Promise<void>((resolve, reject) => {
|
|
31
33
|
let timer: ReturnType<typeof setTimeout> | undefined
|
|
32
34
|
const stop = () => {
|
|
33
|
-
if (timer !== undefined) {
|
|
35
|
+
if (timer !== undefined) {
|
|
36
|
+
clearTimeout(timer)
|
|
37
|
+
}
|
|
38
|
+
|
|
34
39
|
signal?.removeEventListener?.('abort', onAbort)
|
|
35
40
|
}
|
|
36
41
|
|
|
@@ -40,7 +45,10 @@ export function waitForPositiveSize(
|
|
|
40
45
|
}
|
|
41
46
|
|
|
42
47
|
const poll = () => {
|
|
43
|
-
if (signal?.aborted) {
|
|
48
|
+
if (signal?.aborted) {
|
|
49
|
+
return onAbort()
|
|
50
|
+
}
|
|
51
|
+
|
|
44
52
|
const size = measure()
|
|
45
53
|
if (size.width > 0 && size.height > 0) {
|
|
46
54
|
stop()
|
|
@@ -88,13 +96,18 @@ export function wrapInstance(inner: ShaderInstance, attach: AttachLifecycle): Sh
|
|
|
88
96
|
let destroyed = false
|
|
89
97
|
const detach = attach(
|
|
90
98
|
() => {
|
|
91
|
-
if (destroyed) {
|
|
99
|
+
if (destroyed) {
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
92
103
|
hostSuspended = true
|
|
93
104
|
inner.pause()
|
|
94
105
|
},
|
|
95
106
|
() => {
|
|
96
107
|
hostSuspended = false
|
|
97
|
-
if (!destroyed && !callerPaused) {
|
|
108
|
+
if (!destroyed && !callerPaused) {
|
|
109
|
+
inner.resume()
|
|
110
|
+
}
|
|
98
111
|
},
|
|
99
112
|
)
|
|
100
113
|
|
|
@@ -108,10 +121,15 @@ export function wrapInstance(inner: ShaderInstance, attach: AttachLifecycle): Sh
|
|
|
108
121
|
},
|
|
109
122
|
resume: () => {
|
|
110
123
|
callerPaused = false
|
|
111
|
-
if (!hostSuspended) {
|
|
124
|
+
if (!hostSuspended) {
|
|
125
|
+
inner.resume()
|
|
126
|
+
}
|
|
112
127
|
},
|
|
113
128
|
destroy: () => {
|
|
114
|
-
if (destroyed) {
|
|
129
|
+
if (destroyed) {
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
|
|
115
133
|
destroyed = true
|
|
116
134
|
detach()
|
|
117
135
|
inner.destroy()
|
package/dist/native/index.js
DELETED
package/dist/native/index2.js
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
//#region src/shared.ts
|
|
2
|
-
function abortError() {
|
|
3
|
-
const error = /* @__PURE__ */ new Error("createShader aborted before initialization completed");
|
|
4
|
-
error.name = "AbortError";
|
|
5
|
-
return error;
|
|
6
|
-
}
|
|
7
|
-
function throwIfAborted(signal) {
|
|
8
|
-
if (signal?.aborted) throw abortError();
|
|
9
|
-
}
|
|
10
|
-
/** Defer until the surface reports a positive size (surface not attached /
|
|
11
|
-
* zero-size layout → wait, don't init). Aborts via `signal`. */
|
|
12
|
-
function waitForPositiveSize(measure, signal) {
|
|
13
|
-
const first = measure();
|
|
14
|
-
if (first.width > 0 && first.height > 0) return Promise.resolve();
|
|
15
|
-
return new Promise((resolve, reject) => {
|
|
16
|
-
let timer;
|
|
17
|
-
const stop = () => {
|
|
18
|
-
if (timer !== void 0) clearTimeout(timer);
|
|
19
|
-
signal?.removeEventListener?.("abort", onAbort);
|
|
20
|
-
};
|
|
21
|
-
const onAbort = () => {
|
|
22
|
-
stop();
|
|
23
|
-
reject(abortError());
|
|
24
|
-
};
|
|
25
|
-
const poll = () => {
|
|
26
|
-
if (signal?.aborted) return onAbort();
|
|
27
|
-
const size = measure();
|
|
28
|
-
if (size.width > 0 && size.height > 0) {
|
|
29
|
-
stop();
|
|
30
|
-
resolve();
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
timer = setTimeout(poll, 50);
|
|
34
|
-
};
|
|
35
|
-
signal?.addEventListener?.("abort", onAbort);
|
|
36
|
-
timer = setTimeout(poll, 50);
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
/** Merge caller options with adapter defaults. `disableTelemetry` defaults
|
|
40
|
-
* on for this package's local/preset-driven use; callers may re-enable it.
|
|
41
|
-
* The adapter-only `signal` key is stripped before reaching upstream. */
|
|
42
|
-
function resolveOptions(options, platformDefaults) {
|
|
43
|
-
const { signal, ...rest } = options ?? {};
|
|
44
|
-
return {
|
|
45
|
-
signal,
|
|
46
|
-
upstream: {
|
|
47
|
-
...platformDefaults,
|
|
48
|
-
...rest,
|
|
49
|
-
disableTelemetry: options?.disableTelemetry ?? true
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
/** Wrap the upstream instance so host suspension composes with caller
|
|
54
|
-
* pause instead of overriding it: host hide pauses, host show resumes
|
|
55
|
-
* only when the caller did not explicitly pause. `destroy` detaches all
|
|
56
|
-
* adapter-owned listeners before delegating. */
|
|
57
|
-
function wrapInstance(inner, attach) {
|
|
58
|
-
let callerPaused = false;
|
|
59
|
-
let hostSuspended = false;
|
|
60
|
-
let destroyed = false;
|
|
61
|
-
const detach = attach(() => {
|
|
62
|
-
if (destroyed) return;
|
|
63
|
-
hostSuspended = true;
|
|
64
|
-
inner.pause();
|
|
65
|
-
}, () => {
|
|
66
|
-
hostSuspended = false;
|
|
67
|
-
if (!destroyed && !callerPaused) inner.resume();
|
|
68
|
-
});
|
|
69
|
-
return {
|
|
70
|
-
getFailureReason: () => inner.getFailureReason(),
|
|
71
|
-
update: (id, props) => inner.update(id, props),
|
|
72
|
-
resize: (width, height) => inner.resize(width, height),
|
|
73
|
-
pause: () => {
|
|
74
|
-
callerPaused = true;
|
|
75
|
-
inner.pause();
|
|
76
|
-
},
|
|
77
|
-
resume: () => {
|
|
78
|
-
callerPaused = false;
|
|
79
|
-
if (!hostSuspended) inner.resume();
|
|
80
|
-
},
|
|
81
|
-
destroy: () => {
|
|
82
|
-
if (destroyed) return;
|
|
83
|
-
destroyed = true;
|
|
84
|
-
detach();
|
|
85
|
-
inner.destroy();
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
//#endregion
|
|
90
|
-
export { abortError, resolveOptions, throwIfAborted, waitForPositiveSize, wrapInstance };
|
package/dist/native/index3.js
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
import { resolveOptions, throwIfAborted, waitForPositiveSize, wrapInstance } from "./index2.js";
|
|
2
|
-
import { Application, View, isAndroid, isIOS } from "@nativescript/core";
|
|
3
|
-
import { getGPU } from "@octane-xplat/canvas";
|
|
4
|
-
import { createShader, createSharedDevice, getWebGPUSupport, isWebGPUSupported } from "shaders/js";
|
|
5
|
-
//#region src/host.ts
|
|
6
|
-
var gpuShim;
|
|
7
|
-
var gpuResolved = false;
|
|
8
|
-
/** The plugin's GPU shim is also installed as `navigator.gpu` — upstream
|
|
9
|
-
* (and TypeGPU's `configureContext`) reads it unconditionally. */
|
|
10
|
-
function ensureGpu() {
|
|
11
|
-
if (!gpuResolved) {
|
|
12
|
-
gpuResolved = true;
|
|
13
|
-
try {
|
|
14
|
-
gpuShim = getGPU();
|
|
15
|
-
} catch {
|
|
16
|
-
gpuShim = null;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
const gpu = gpuShim;
|
|
20
|
-
if (!gpu) return null;
|
|
21
|
-
const g = globalThis;
|
|
22
|
-
try {
|
|
23
|
-
if (!g.navigator) g.navigator = {};
|
|
24
|
-
if (!g.navigator.gpu) try {
|
|
25
|
-
g.navigator.gpu = gpu;
|
|
26
|
-
} catch {
|
|
27
|
-
Object.defineProperty(g.navigator, "gpu", {
|
|
28
|
-
configurable: true,
|
|
29
|
-
value: gpu
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
} catch {}
|
|
33
|
-
return gpu;
|
|
34
|
-
}
|
|
35
|
-
function measure(view) {
|
|
36
|
-
const rect = view?.getBoundingClientRect?.();
|
|
37
|
-
const actual = view?.getActualSize?.();
|
|
38
|
-
return {
|
|
39
|
-
width: Number(rect?.width) || Number(actual?.width) || Number(view?.width) || 0,
|
|
40
|
-
height: Number(rect?.height) || Number(actual?.height) || Number(view?.height) || 0
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
/** DOM-canvas-shaped facade over the plugin view. `width`/`height` writes
|
|
44
|
-
* (upstream's drawing-buffer bookkeeping) are stored locally so the
|
|
45
|
-
* plugin view's layout size is never mutated by buffer pixel counts. */
|
|
46
|
-
function hostCanvas(view) {
|
|
47
|
-
const bag = {
|
|
48
|
-
style: {},
|
|
49
|
-
width: 0,
|
|
50
|
-
height: 0
|
|
51
|
-
};
|
|
52
|
-
return {
|
|
53
|
-
getContext: (kind, options) => view.getContext(kind, options),
|
|
54
|
-
getBoundingClientRect: () => {
|
|
55
|
-
const { width, height } = measure(view);
|
|
56
|
-
return {
|
|
57
|
-
x: 0,
|
|
58
|
-
y: 0,
|
|
59
|
-
top: 0,
|
|
60
|
-
left: 0,
|
|
61
|
-
right: width,
|
|
62
|
-
bottom: height,
|
|
63
|
-
width,
|
|
64
|
-
height
|
|
65
|
-
};
|
|
66
|
-
},
|
|
67
|
-
get clientWidth() {
|
|
68
|
-
return measure(view).width;
|
|
69
|
-
},
|
|
70
|
-
get clientHeight() {
|
|
71
|
-
return measure(view).height;
|
|
72
|
-
},
|
|
73
|
-
get width() {
|
|
74
|
-
return bag.width || measure(view).width;
|
|
75
|
-
},
|
|
76
|
-
set width(value) {
|
|
77
|
-
bag.width = value;
|
|
78
|
-
},
|
|
79
|
-
get height() {
|
|
80
|
-
return bag.height || measure(view).height;
|
|
81
|
-
},
|
|
82
|
-
set height(value) {
|
|
83
|
-
bag.height = value;
|
|
84
|
-
},
|
|
85
|
-
get style() {
|
|
86
|
-
return bag.style;
|
|
87
|
-
},
|
|
88
|
-
parentElement: null
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
async function acquireInjectedGpu() {
|
|
92
|
-
const gpu = ensureGpu();
|
|
93
|
-
if (!gpu?.requestAdapter) return null;
|
|
94
|
-
try {
|
|
95
|
-
const adapter = await gpu.requestAdapter();
|
|
96
|
-
if (!adapter) return null;
|
|
97
|
-
const device = await adapter.requestDevice();
|
|
98
|
-
return device ? {
|
|
99
|
-
device,
|
|
100
|
-
adapter
|
|
101
|
-
} : null;
|
|
102
|
-
} catch {
|
|
103
|
-
return null;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
async function createShader$1(canvas, preset, options) {
|
|
107
|
-
const { upstream, signal } = resolveOptions(options, { observeElement: false });
|
|
108
|
-
throwIfAborted(signal);
|
|
109
|
-
const host = hostCanvas(canvas);
|
|
110
|
-
await waitForPositiveSize(() => measure(canvas), signal);
|
|
111
|
-
if (!upstream.gpu) {
|
|
112
|
-
const gpu = await acquireInjectedGpu();
|
|
113
|
-
if (gpu) upstream.gpu = gpu;
|
|
114
|
-
}
|
|
115
|
-
throwIfAborted(signal);
|
|
116
|
-
const inner = await createShader(host, preset, upstream);
|
|
117
|
-
if (signal?.aborted) {
|
|
118
|
-
inner.destroy();
|
|
119
|
-
throwIfAborted(signal);
|
|
120
|
-
}
|
|
121
|
-
const view = canvas;
|
|
122
|
-
const attach = (hide, show) => {
|
|
123
|
-
const onLayout = () => inner.resize();
|
|
124
|
-
Application.on(Application.suspendEvent, hide);
|
|
125
|
-
Application.on(Application.resumeEvent, show);
|
|
126
|
-
view?.on?.(View.unloadedEvent, hide);
|
|
127
|
-
view?.on?.(View.loadedEvent, show);
|
|
128
|
-
view?.on?.(View.layoutChangedEvent, onLayout);
|
|
129
|
-
return () => {
|
|
130
|
-
Application.off(Application.suspendEvent, hide);
|
|
131
|
-
Application.off(Application.resumeEvent, show);
|
|
132
|
-
view?.off?.(View.unloadedEvent, hide);
|
|
133
|
-
view?.off?.(View.loadedEvent, show);
|
|
134
|
-
view?.off?.(View.layoutChangedEvent, onLayout);
|
|
135
|
-
};
|
|
136
|
-
};
|
|
137
|
-
return wrapInstance(inner, attach);
|
|
138
|
-
}
|
|
139
|
-
async function createSharedDevice$1(options) {
|
|
140
|
-
ensureGpu();
|
|
141
|
-
return createSharedDevice(options);
|
|
142
|
-
}
|
|
143
|
-
function isWebGPUSupported$1() {
|
|
144
|
-
ensureGpu();
|
|
145
|
-
return isWebGPUSupported();
|
|
146
|
-
}
|
|
147
|
-
function getWebGPUSupport$1() {
|
|
148
|
-
ensureGpu();
|
|
149
|
-
return getWebGPUSupport();
|
|
150
|
-
}
|
|
151
|
-
function prefersReducedMotion() {
|
|
152
|
-
try {
|
|
153
|
-
if (isIOS) {
|
|
154
|
-
const check = globalThis.UIAccessibilityIsReduceMotionEnabled;
|
|
155
|
-
return typeof check === "function" ? !!check() : false;
|
|
156
|
-
}
|
|
157
|
-
if (isAndroid) {
|
|
158
|
-
const context = Application.android?.context;
|
|
159
|
-
const settings = globalThis.android?.provider?.Settings;
|
|
160
|
-
if (!context || !settings?.Global) return false;
|
|
161
|
-
return settings.Global.getFloat(context.getContentResolver(), settings.Global.ANIMATOR_DURATION_SCALE, 1) === 0;
|
|
162
|
-
}
|
|
163
|
-
} catch {}
|
|
164
|
-
return false;
|
|
165
|
-
}
|
|
166
|
-
//#endregion
|
|
167
|
-
export { createShader$1 as createShader, createSharedDevice$1 as createSharedDevice, getWebGPUSupport$1 as getWebGPUSupport, isWebGPUSupported$1 as isWebGPUSupported, prefersReducedMotion };
|
package/dist/web/index.js
DELETED
package/dist/web/index2.js
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
//#region src/shared.ts
|
|
2
|
-
function abortError() {
|
|
3
|
-
const error = /* @__PURE__ */ new Error("createShader aborted before initialization completed");
|
|
4
|
-
error.name = "AbortError";
|
|
5
|
-
return error;
|
|
6
|
-
}
|
|
7
|
-
function throwIfAborted(signal) {
|
|
8
|
-
if (signal?.aborted) throw abortError();
|
|
9
|
-
}
|
|
10
|
-
/** Defer until the surface reports a positive size (surface not attached /
|
|
11
|
-
* zero-size layout → wait, don't init). Aborts via `signal`. */
|
|
12
|
-
function waitForPositiveSize(measure, signal) {
|
|
13
|
-
const first = measure();
|
|
14
|
-
if (first.width > 0 && first.height > 0) return Promise.resolve();
|
|
15
|
-
return new Promise((resolve, reject) => {
|
|
16
|
-
let timer;
|
|
17
|
-
const stop = () => {
|
|
18
|
-
if (timer !== void 0) clearTimeout(timer);
|
|
19
|
-
signal?.removeEventListener?.("abort", onAbort);
|
|
20
|
-
};
|
|
21
|
-
const onAbort = () => {
|
|
22
|
-
stop();
|
|
23
|
-
reject(abortError());
|
|
24
|
-
};
|
|
25
|
-
const poll = () => {
|
|
26
|
-
if (signal?.aborted) return onAbort();
|
|
27
|
-
const size = measure();
|
|
28
|
-
if (size.width > 0 && size.height > 0) {
|
|
29
|
-
stop();
|
|
30
|
-
resolve();
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
timer = setTimeout(poll, 50);
|
|
34
|
-
};
|
|
35
|
-
signal?.addEventListener?.("abort", onAbort);
|
|
36
|
-
timer = setTimeout(poll, 50);
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
/** Merge caller options with adapter defaults. `disableTelemetry` defaults
|
|
40
|
-
* on for this package's local/preset-driven use; callers may re-enable it.
|
|
41
|
-
* The adapter-only `signal` key is stripped before reaching upstream. */
|
|
42
|
-
function resolveOptions(options, platformDefaults) {
|
|
43
|
-
const { signal, ...rest } = options ?? {};
|
|
44
|
-
return {
|
|
45
|
-
signal,
|
|
46
|
-
upstream: {
|
|
47
|
-
...platformDefaults,
|
|
48
|
-
...rest,
|
|
49
|
-
disableTelemetry: options?.disableTelemetry ?? true
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
/** Wrap the upstream instance so host suspension composes with caller
|
|
54
|
-
* pause instead of overriding it: host hide pauses, host show resumes
|
|
55
|
-
* only when the caller did not explicitly pause. `destroy` detaches all
|
|
56
|
-
* adapter-owned listeners before delegating. */
|
|
57
|
-
function wrapInstance(inner, attach) {
|
|
58
|
-
let callerPaused = false;
|
|
59
|
-
let hostSuspended = false;
|
|
60
|
-
let destroyed = false;
|
|
61
|
-
const detach = attach(() => {
|
|
62
|
-
if (destroyed) return;
|
|
63
|
-
hostSuspended = true;
|
|
64
|
-
inner.pause();
|
|
65
|
-
}, () => {
|
|
66
|
-
hostSuspended = false;
|
|
67
|
-
if (!destroyed && !callerPaused) inner.resume();
|
|
68
|
-
});
|
|
69
|
-
return {
|
|
70
|
-
getFailureReason: () => inner.getFailureReason(),
|
|
71
|
-
update: (id, props) => inner.update(id, props),
|
|
72
|
-
resize: (width, height) => inner.resize(width, height),
|
|
73
|
-
pause: () => {
|
|
74
|
-
callerPaused = true;
|
|
75
|
-
inner.pause();
|
|
76
|
-
},
|
|
77
|
-
resume: () => {
|
|
78
|
-
callerPaused = false;
|
|
79
|
-
if (!hostSuspended) inner.resume();
|
|
80
|
-
},
|
|
81
|
-
destroy: () => {
|
|
82
|
-
if (destroyed) return;
|
|
83
|
-
destroyed = true;
|
|
84
|
-
detach();
|
|
85
|
-
inner.destroy();
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
//#endregion
|
|
90
|
-
export { abortError, resolveOptions, throwIfAborted, waitForPositiveSize, wrapInstance };
|
package/dist/web/index3.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { resolveOptions, throwIfAborted, waitForPositiveSize, wrapInstance } from "./index2.js";
|
|
2
|
-
import { createShader, createSharedDevice, getWebGPUSupport, isWebGPUSupported } from "shaders/js";
|
|
3
|
-
//#region src/host.web.ts
|
|
4
|
-
function measure(canvas) {
|
|
5
|
-
const rect = canvas?.getBoundingClientRect?.();
|
|
6
|
-
return {
|
|
7
|
-
width: Number(rect?.width) || Number(canvas?.width) || 0,
|
|
8
|
-
height: Number(rect?.height) || Number(canvas?.height) || 0
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
function attachVisibility(hide, show) {
|
|
12
|
-
const doc = globalThis.document;
|
|
13
|
-
if (!doc?.addEventListener) return () => {};
|
|
14
|
-
const onChange = () => doc.visibilityState === "hidden" ? hide() : show();
|
|
15
|
-
doc.addEventListener("visibilitychange", onChange);
|
|
16
|
-
return () => doc.removeEventListener("visibilitychange", onChange);
|
|
17
|
-
}
|
|
18
|
-
async function createShader$1(canvas, preset, options) {
|
|
19
|
-
const { upstream, signal } = resolveOptions(options, {});
|
|
20
|
-
throwIfAborted(signal);
|
|
21
|
-
await waitForPositiveSize(() => measure(canvas), signal);
|
|
22
|
-
const inner = await createShader(canvas, preset, upstream);
|
|
23
|
-
if (signal?.aborted) {
|
|
24
|
-
inner.destroy();
|
|
25
|
-
throwIfAborted(signal);
|
|
26
|
-
}
|
|
27
|
-
return wrapInstance(inner, attachVisibility);
|
|
28
|
-
}
|
|
29
|
-
function createSharedDevice$1(options) {
|
|
30
|
-
return createSharedDevice(options);
|
|
31
|
-
}
|
|
32
|
-
function isWebGPUSupported$1() {
|
|
33
|
-
return isWebGPUSupported();
|
|
34
|
-
}
|
|
35
|
-
function getWebGPUSupport$1() {
|
|
36
|
-
return getWebGPUSupport();
|
|
37
|
-
}
|
|
38
|
-
function prefersReducedMotion() {
|
|
39
|
-
const matchMedia = globalThis.matchMedia;
|
|
40
|
-
return typeof matchMedia === "function" ? !!matchMedia("(prefers-reduced-motion: reduce)").matches : false;
|
|
41
|
-
}
|
|
42
|
-
//#endregion
|
|
43
|
-
export { createShader$1 as createShader, createSharedDevice$1 as createSharedDevice, getWebGPUSupport$1 as getWebGPUSupport, isWebGPUSupported$1 as isWebGPUSupported, prefersReducedMotion };
|