@octane-xplat/shaders 0.0.0-nightly.g14544fe
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/LICENSE +21 -0
- package/README.md +108 -0
- package/package.json +79 -0
- package/src/host.ts +231 -0
- package/src/host.web.ts +82 -0
- package/src/index.macos.ts +49 -0
- package/src/index.ts +10 -0
- package/src/index.web.ts +9 -0
- package/src/shared.ts +138 -0
- package/src/types.ts +86 -0
- package/types/index.d.ts +1 -0
- package/types/index.macos.d.ts +5 -0
- package/types/types.d.ts +66 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alec Larson
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# `@octane-xplat/shaders`
|
|
2
|
+
|
|
3
|
+
Runs the published [`shaders`](https://www.npmjs.com/package/shaders) WebGPU
|
|
4
|
+
engine (pinned `4.0.0`, used unchanged — no vendoring, no shader port) on an
|
|
5
|
+
Octane Xplat `Canvas` surface from `@octane-xplat/canvas`. Use it for the
|
|
6
|
+
upstream engine's procedural presets — noise, gradients, and other generated
|
|
7
|
+
effects. The adapter deliberately does not create a new shader API; it only
|
|
8
|
+
adds the surface/lifecycle glue Xplat needs. Preset shapes, effect names,
|
|
9
|
+
parameters, options, and the returned instance's methods — `update(id, props)`,
|
|
10
|
+
`resize()`, `pause()`, `resume()`, `destroy()`, `getFailureReason()` — are
|
|
11
|
+
upstream's.
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
pnpm add @octane-xplat/shaders
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { Canvas } from '@octane-xplat/canvas'
|
|
21
|
+
import { createShader } from '@octane-xplat/shaders'
|
|
22
|
+
|
|
23
|
+
;<Canvas
|
|
24
|
+
width={320}
|
|
25
|
+
height={180}
|
|
26
|
+
style={{ width: 320, height: 180 }}
|
|
27
|
+
onReady={({ canvas }) => {
|
|
28
|
+
void createShader(canvas, {
|
|
29
|
+
components: [
|
|
30
|
+
{
|
|
31
|
+
id: 'background',
|
|
32
|
+
type: 'SimplexNoise',
|
|
33
|
+
props: { colorA: '#0f172a', colorB: '#7c3aed', scale: 2, seed: 7, speed: 0.5 },
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
})
|
|
37
|
+
}}
|
|
38
|
+
/>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Working demo: `ShaderDemo` in `packages/demos` (parameter updates, pause/resume,
|
|
42
|
+
static fallback, reduced-motion still frame, disposal on unmount).
|
|
43
|
+
|
|
44
|
+
## What the adapter adds
|
|
45
|
+
|
|
46
|
+
- `createShader(canvas, preset, options?)` — same signature as upstream
|
|
47
|
+
`shaders/js`'s `createShader`, except `canvas` is the Xplat Canvas surface
|
|
48
|
+
(`CanvasReady.canvas`) and options add `signal` (abort deferred/in-flight init;
|
|
49
|
+
a late completion is destroyed, never revived).
|
|
50
|
+
- `disableTelemetry` defaults to `true`. Upstream telemetry stays off unless you
|
|
51
|
+
pass `disableTelemetry: false`. No hosted account, preset download, or network
|
|
52
|
+
access is required for local presets.
|
|
53
|
+
- Deferred init: until the surface reports a positive size, nothing touches the
|
|
54
|
+
GPU. The static content behind your canvas stays put.
|
|
55
|
+
- Host suspension: app backgrounding (NativeScript `suspend`/`resume`), surface
|
|
56
|
+
detach (`loaded`/`unloaded`), and `document` visibility (web) pause rendering —
|
|
57
|
+
without overriding an explicit caller `pause()` when the host returns.
|
|
58
|
+
- `createSharedDevice()` — acquire `{ device, adapter }` through the platform
|
|
59
|
+
entry point (`navigator.gpu` on web, the `@nativescript/canvas` GPU shim on
|
|
60
|
+
native) for callers that want to own/share the device via `options.gpu`.
|
|
61
|
+
Instances never destroy a caller-provided or adapter-shared device.
|
|
62
|
+
- `isWebGPUSupported()` / `getWebGPUSupport()` — upstream capability helpers,
|
|
63
|
+
native-aware.
|
|
64
|
+
- `prefersReducedMotion()` — OS reduced-motion preference; pause after the first
|
|
65
|
+
frame to hold a still, or keep the static fallback.
|
|
66
|
+
|
|
67
|
+
## Target status
|
|
68
|
+
|
|
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
|
+
|
|
77
|
+
Adapter-owned native defaults: `navigator.gpu` is bound lazily to the
|
|
78
|
+
`@nativescript/canvas` GPU shim on first use (upstream reads it unconditionally);
|
|
79
|
+
`observeElement` defaults off on native (no DOM observers — the view's
|
|
80
|
+
`layoutChanged` drives `resize()`); frame pacing uses `requestAnimationFrame`
|
|
81
|
+
where the runtime provides it, else upstream's `setTimeout(~16ms)` fallback.
|
|
82
|
+
|
|
83
|
+
## Limits to know
|
|
84
|
+
|
|
85
|
+
- **Payload:** the engine is heavy — about **709 KB gzip** (minified browser
|
|
86
|
+
bundle for the JS host + SimplexNoise; ~705 KB via `shaders/core`). Per-effect
|
|
87
|
+
imports do not shrink it because the core renderer pulls the full registry.
|
|
88
|
+
This cost lands only in apps that install this optional package.
|
|
89
|
+
- **Upstream packaging defects:** `shaders@4.0.0` declares 34 export subpaths
|
|
90
|
+
that point to absent files (e.g. `./core/RadialLines`, `./core/GradientNoise`).
|
|
91
|
+
`shaders/js`, `shaders/core`, and `shaders/core/SimplexNoise` are intact. Treat
|
|
92
|
+
other subpaths as unverified until you check the file exists. Upstream `.d.ts`
|
|
93
|
+
files also use extensionless relative imports, so they do not resolve under
|
|
94
|
+
`moduleResolution: 'nodenext'` — consumers need bundler-style resolution (all
|
|
95
|
+
Xplat toolchain paths use it).
|
|
96
|
+
- **Catalog coverage:** only intact procedural effect definitions without
|
|
97
|
+
browser-only inputs are in the initial native compatibility claim. DOM capture,
|
|
98
|
+
HTML/text-specific facilities, cursor/mouse-driven effects, media sources,
|
|
99
|
+
hosted presets, and simulations may fail on native — upstream reports terminal
|
|
100
|
+
failures via `onError`/`getFailureReason()`; keep the static fallback mounted.
|
|
101
|
+
- **Recovery:** device loss uses upstream's own rebuild path (≤3 attempts); there
|
|
102
|
+
is no adapter-level retry loop. Terminal reasons stop rendering permanently.
|
|
103
|
+
|
|
104
|
+
## Publishing
|
|
105
|
+
|
|
106
|
+
The package joins the automated lockstep release (non-private `packages/*`). The
|
|
107
|
+
one-time npm stub + trusted-publisher setup is still required before the first
|
|
108
|
+
release can ship it — see `.agents/docs/releases.md`.
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@octane-xplat/shaders",
|
|
3
|
+
"version": "0.0.0-nightly.g14544fe",
|
|
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
|
+
"keywords": [
|
|
6
|
+
"canvas",
|
|
7
|
+
"cross-platform",
|
|
8
|
+
"gpu",
|
|
9
|
+
"graphics",
|
|
10
|
+
"nativescript",
|
|
11
|
+
"octane",
|
|
12
|
+
"shader",
|
|
13
|
+
"webgpu",
|
|
14
|
+
"wgsl",
|
|
15
|
+
"xplat"
|
|
16
|
+
],
|
|
17
|
+
"homepage": "https://github.com/octane-xplat/octane-xplat/tree/main/packages/shaders",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/octane-xplat/octane-xplat",
|
|
22
|
+
"directory": "packages/shaders"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"types",
|
|
26
|
+
"src",
|
|
27
|
+
"!src/**/*.test.*",
|
|
28
|
+
"!src/**/*.spec.*"
|
|
29
|
+
],
|
|
30
|
+
"type": "module",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"macos": {
|
|
34
|
+
"types": "./types/index.macos.d.ts",
|
|
35
|
+
"default": "./src/index.macos.ts"
|
|
36
|
+
},
|
|
37
|
+
"types": "./types/index.d.ts",
|
|
38
|
+
"web": "./src/index.web.ts",
|
|
39
|
+
"native": "./src/index.ts",
|
|
40
|
+
"default": "./src/index.ts"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@octane-xplat/canvas": "0.0.0-nightly.g14544fe",
|
|
48
|
+
"shaders": "4.0.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@nativescript-community/octane": "0.2.5",
|
|
52
|
+
"@nativescript/core": "9.1.3",
|
|
53
|
+
"@octanejs/vite-plugin": "0.1.61",
|
|
54
|
+
"octane": "0.11.0",
|
|
55
|
+
"tsrx-typegen": "0.3.0",
|
|
56
|
+
"typescript": "5.9.3",
|
|
57
|
+
"vite": "8.3.0"
|
|
58
|
+
},
|
|
59
|
+
"peerDependencies": {
|
|
60
|
+
"@nativescript/core": ">=9.1.0 <10"
|
|
61
|
+
},
|
|
62
|
+
"peerDependenciesMeta": {
|
|
63
|
+
"@nativescript/core": {
|
|
64
|
+
"optional": true
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"nativescript": {
|
|
68
|
+
"platforms": {
|
|
69
|
+
"ios": "7.0.0",
|
|
70
|
+
"android": "7.0.0"
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"scripts": {
|
|
74
|
+
"typegen": "tsc -p tsconfig.types.json",
|
|
75
|
+
"check-types": "tsc -p tsconfig.check.json",
|
|
76
|
+
"test:packed": "node tests/packed-consumer.mjs",
|
|
77
|
+
"pack:check": "tsrx-typegen --pack-check"
|
|
78
|
+
}
|
|
79
|
+
}
|
package/src/host.ts
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
// Native host (iOS/Android via NativeScript) — the Xplat Canvas surface is
|
|
2
|
+
// a `@nativescript/canvas` Canvas view. The engine consumes a DOM-canvas
|
|
3
|
+
// shape (`style`, `getBoundingClientRect`, `width`/`height` buffer
|
|
4
|
+
// writes), so the view is wrapped in a facade that delegates `getContext`
|
|
5
|
+
// and swallows style/buffer writes instead of resizing the view.
|
|
6
|
+
//
|
|
7
|
+
// Adapter-owned native defaults:
|
|
8
|
+
// - `navigator.gpu` is lazily bound to the plugin's GPU shim on first use
|
|
9
|
+
// (never at import) so upstream's own acquisition, capability checks and
|
|
10
|
+
// device-loss recovery work unchanged.
|
|
11
|
+
// - `observeElement` defaults off: there is no DOM
|
|
12
|
+
// ResizeObserver/IntersectionObserver; sizing is driven by the view's
|
|
13
|
+
// `layoutChanged` event and forwarded to `instance.resize()`.
|
|
14
|
+
// - App suspend/resume and view unload/load map to host suspension.
|
|
15
|
+
//
|
|
16
|
+
// Frame pacing falls back to upstream's `setTimeout(16)` loop when the
|
|
17
|
+
// runtime lacks `requestAnimationFrame`. Experimental: iOS/Android device
|
|
18
|
+
// verification is still pending.
|
|
19
|
+
|
|
20
|
+
import { Application, View, isAndroid, isIOS } from '@nativescript/core'
|
|
21
|
+
import { getGPU } from '@octane-xplat/canvas'
|
|
22
|
+
import {
|
|
23
|
+
createShader as upstreamCreateShader,
|
|
24
|
+
createSharedDevice as upstreamCreateSharedDevice,
|
|
25
|
+
getWebGPUSupport as upstreamGetWebGPUSupport,
|
|
26
|
+
isWebGPUSupported as upstreamIsWebGPUSupported,
|
|
27
|
+
} from 'shaders/js'
|
|
28
|
+
|
|
29
|
+
import type {
|
|
30
|
+
CreateShaderOptions,
|
|
31
|
+
PresetConfig,
|
|
32
|
+
SharedGpu,
|
|
33
|
+
ShaderInstance,
|
|
34
|
+
WebGPUSupportInfo,
|
|
35
|
+
} from './types'
|
|
36
|
+
|
|
37
|
+
import { resolveOptions, throwIfAborted, waitForPositiveSize, wrapInstance } from './shared'
|
|
38
|
+
import type { Size } from './shared'
|
|
39
|
+
|
|
40
|
+
let gpuShim: any
|
|
41
|
+
let gpuResolved = false
|
|
42
|
+
|
|
43
|
+
/** The plugin's GPU shim is also installed as `navigator.gpu` — upstream
|
|
44
|
+
* (and TypeGPU's `configureContext`) reads it unconditionally. */
|
|
45
|
+
function ensureGpu(): any {
|
|
46
|
+
if (!gpuResolved) {
|
|
47
|
+
gpuResolved = true
|
|
48
|
+
try {
|
|
49
|
+
gpuShim = getGPU()
|
|
50
|
+
} catch {
|
|
51
|
+
gpuShim = null
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const gpu = gpuShim
|
|
56
|
+
if (!gpu) {
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const g = globalThis as any
|
|
61
|
+
try {
|
|
62
|
+
if (!g.navigator) {
|
|
63
|
+
g.navigator = {}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!g.navigator.gpu) {
|
|
67
|
+
try {
|
|
68
|
+
g.navigator.gpu = gpu
|
|
69
|
+
} catch {
|
|
70
|
+
Object.defineProperty(g.navigator, 'gpu', { configurable: true, value: gpu })
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
// `navigator` may be a read-only global — injected `gpu` still applies.
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return gpu
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function measure(view: any): Size {
|
|
81
|
+
const rect = view?.getBoundingClientRect?.()
|
|
82
|
+
const actual = view?.getActualSize?.()
|
|
83
|
+
const width = Number(rect?.width) || Number(actual?.width) || Number(view?.width) || 0
|
|
84
|
+
const height = Number(rect?.height) || Number(actual?.height) || Number(view?.height) || 0
|
|
85
|
+
return { width, height }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** DOM-canvas-shaped facade over the plugin view. `width`/`height` writes
|
|
89
|
+
* (upstream's drawing-buffer bookkeeping) are stored locally so the
|
|
90
|
+
* plugin view's layout size is never mutated by buffer pixel counts. */
|
|
91
|
+
function hostCanvas(view: any): any {
|
|
92
|
+
const bag = { style: {} as Record<string, string>, width: 0, height: 0 }
|
|
93
|
+
return {
|
|
94
|
+
getContext: (kind: string, options?: any) => view.getContext(kind, options),
|
|
95
|
+
getBoundingClientRect: () => {
|
|
96
|
+
const { width, height } = measure(view)
|
|
97
|
+
return { x: 0, y: 0, top: 0, left: 0, right: width, bottom: height, width, height }
|
|
98
|
+
},
|
|
99
|
+
get clientWidth() {
|
|
100
|
+
return measure(view).width
|
|
101
|
+
},
|
|
102
|
+
get clientHeight() {
|
|
103
|
+
return measure(view).height
|
|
104
|
+
},
|
|
105
|
+
get width() {
|
|
106
|
+
return bag.width || measure(view).width
|
|
107
|
+
},
|
|
108
|
+
set width(value: number) {
|
|
109
|
+
bag.width = value
|
|
110
|
+
},
|
|
111
|
+
get height() {
|
|
112
|
+
return bag.height || measure(view).height
|
|
113
|
+
},
|
|
114
|
+
set height(value: number) {
|
|
115
|
+
bag.height = value
|
|
116
|
+
},
|
|
117
|
+
get style() {
|
|
118
|
+
return bag.style
|
|
119
|
+
},
|
|
120
|
+
parentElement: null,
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function acquireInjectedGpu(): Promise<SharedGpu | null> {
|
|
125
|
+
const gpu = ensureGpu()
|
|
126
|
+
if (!gpu?.requestAdapter) {
|
|
127
|
+
return null
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
const adapter = await gpu.requestAdapter()
|
|
132
|
+
if (!adapter) {
|
|
133
|
+
return null
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const device = await adapter.requestDevice()
|
|
137
|
+
return device ? { device, adapter } : null
|
|
138
|
+
} catch {
|
|
139
|
+
return null
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export async function createShader(
|
|
144
|
+
canvas: unknown,
|
|
145
|
+
preset: PresetConfig,
|
|
146
|
+
options?: CreateShaderOptions,
|
|
147
|
+
): Promise<ShaderInstance> {
|
|
148
|
+
const { upstream, signal } = resolveOptions(options, { observeElement: false })
|
|
149
|
+
throwIfAborted(signal)
|
|
150
|
+
const host = hostCanvas(canvas)
|
|
151
|
+
await waitForPositiveSize(() => measure(canvas), signal)
|
|
152
|
+
// Adapter-acquired device (shared default when the caller supplies no
|
|
153
|
+
// `gpu`) — never destroyed by the adapter, so one instance's destroy
|
|
154
|
+
// cannot kill another's shared device.
|
|
155
|
+
if (!upstream.gpu) {
|
|
156
|
+
const gpu = await acquireInjectedGpu()
|
|
157
|
+
if (gpu) {
|
|
158
|
+
upstream.gpu = gpu
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
throwIfAborted(signal)
|
|
163
|
+
const inner = await upstreamCreateShader(host, preset, upstream as any)
|
|
164
|
+
if (signal?.aborted) {
|
|
165
|
+
inner.destroy()
|
|
166
|
+
throwIfAborted(signal)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const view = canvas as any
|
|
170
|
+
const attach = (hide: () => void, show: () => void) => {
|
|
171
|
+
const onLayout = () => inner.resize()
|
|
172
|
+
Application.on(Application.suspendEvent, hide)
|
|
173
|
+
Application.on(Application.resumeEvent, show)
|
|
174
|
+
view?.on?.(View.unloadedEvent, hide)
|
|
175
|
+
view?.on?.(View.loadedEvent, show)
|
|
176
|
+
view?.on?.(View.layoutChangedEvent, onLayout)
|
|
177
|
+
return () => {
|
|
178
|
+
Application.off(Application.suspendEvent, hide)
|
|
179
|
+
Application.off(Application.resumeEvent, show)
|
|
180
|
+
view?.off?.(View.unloadedEvent, hide)
|
|
181
|
+
view?.off?.(View.loadedEvent, show)
|
|
182
|
+
view?.off?.(View.layoutChangedEvent, onLayout)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return wrapInstance(inner, attach)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export async function createSharedDevice(options?: {
|
|
190
|
+
powerPreference?: string
|
|
191
|
+
}): Promise<SharedGpu | null> {
|
|
192
|
+
ensureGpu()
|
|
193
|
+
return upstreamCreateSharedDevice(options as any)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function isWebGPUSupported(): boolean {
|
|
197
|
+
ensureGpu()
|
|
198
|
+
return upstreamIsWebGPUSupported()
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function getWebGPUSupport(): Promise<WebGPUSupportInfo> {
|
|
202
|
+
ensureGpu()
|
|
203
|
+
return upstreamGetWebGPUSupport()
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function prefersReducedMotion(): boolean {
|
|
207
|
+
try {
|
|
208
|
+
if (isIOS) {
|
|
209
|
+
const check = (globalThis as any).UIAccessibilityIsReduceMotionEnabled
|
|
210
|
+
return typeof check === 'function' ? !!check() : false
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (isAndroid) {
|
|
214
|
+
const context = Application.android?.context
|
|
215
|
+
const settings = (globalThis as any).android?.provider?.Settings
|
|
216
|
+
if (!context || !settings?.Global) {
|
|
217
|
+
return false
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const scale = settings.Global.getFloat(
|
|
221
|
+
context.getContentResolver(),
|
|
222
|
+
settings.Global.ANIMATOR_DURATION_SCALE,
|
|
223
|
+
1,
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
return scale === 0
|
|
227
|
+
}
|
|
228
|
+
} catch {}
|
|
229
|
+
|
|
230
|
+
return false
|
|
231
|
+
}
|
package/src/host.web.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Web host — the Xplat Canvas surface is already a real
|
|
2
|
+
// HTMLCanvasElement, so it reaches upstream unchanged. The adapter adds:
|
|
3
|
+
// deferred init until positive size, telemetry off by default, abortable
|
|
4
|
+
// init, and document-visibility suspension that composes with caller
|
|
5
|
+
// pause. Offscreen/detach suspension is upstream's own observers
|
|
6
|
+
// (`observeElement`, default on).
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
createShader as upstreamCreateShader,
|
|
10
|
+
createSharedDevice as upstreamCreateSharedDevice,
|
|
11
|
+
getWebGPUSupport as upstreamGetWebGPUSupport,
|
|
12
|
+
isWebGPUSupported as upstreamIsWebGPUSupported,
|
|
13
|
+
} from 'shaders/js'
|
|
14
|
+
|
|
15
|
+
import type {
|
|
16
|
+
CreateShaderOptions,
|
|
17
|
+
PresetConfig,
|
|
18
|
+
SharedGpu,
|
|
19
|
+
ShaderInstance,
|
|
20
|
+
WebGPUSupportInfo,
|
|
21
|
+
} from './types'
|
|
22
|
+
|
|
23
|
+
import { resolveOptions, throwIfAborted, waitForPositiveSize, wrapInstance } from './shared'
|
|
24
|
+
import type { Size } from './shared'
|
|
25
|
+
|
|
26
|
+
function measure(canvas: any): Size {
|
|
27
|
+
const rect = canvas?.getBoundingClientRect?.()
|
|
28
|
+
const width = Number(rect?.width) || Number(canvas?.width) || 0
|
|
29
|
+
const height = Number(rect?.height) || Number(canvas?.height) || 0
|
|
30
|
+
return { width, height }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function attachVisibility(hide: () => void, show: () => void): () => void {
|
|
34
|
+
const doc = (globalThis as any).document
|
|
35
|
+
if (!doc?.addEventListener) {
|
|
36
|
+
return () => {}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const onChange = () => (doc.visibilityState === 'hidden' ? hide() : show())
|
|
40
|
+
doc.addEventListener('visibilitychange', onChange)
|
|
41
|
+
return () => doc.removeEventListener('visibilitychange', onChange)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function createShader(
|
|
45
|
+
canvas: unknown,
|
|
46
|
+
preset: PresetConfig,
|
|
47
|
+
options?: CreateShaderOptions,
|
|
48
|
+
): Promise<ShaderInstance> {
|
|
49
|
+
const { upstream, signal } = resolveOptions(options, {})
|
|
50
|
+
throwIfAborted(signal)
|
|
51
|
+
await waitForPositiveSize(() => measure(canvas), signal)
|
|
52
|
+
const inner = await upstreamCreateShader(canvas as any, preset, upstream)
|
|
53
|
+
if (signal?.aborted) {
|
|
54
|
+
// Unmounted while the async engine was starting — release the late
|
|
55
|
+
// completion instead of reviving a disposed surface.
|
|
56
|
+
inner.destroy()
|
|
57
|
+
throwIfAborted(signal)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return wrapInstance(inner, attachVisibility)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function createSharedDevice(options?: {
|
|
64
|
+
powerPreference?: string
|
|
65
|
+
}): Promise<SharedGpu | null> {
|
|
66
|
+
return upstreamCreateSharedDevice(options as any)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function isWebGPUSupported(): boolean {
|
|
70
|
+
return upstreamIsWebGPUSupported()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function getWebGPUSupport(): Promise<WebGPUSupportInfo> {
|
|
74
|
+
return upstreamGetWebGPUSupport()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function prefersReducedMotion(): boolean {
|
|
78
|
+
const matchMedia = (globalThis as any).matchMedia
|
|
79
|
+
return typeof matchMedia === 'function'
|
|
80
|
+
? !!matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
81
|
+
: false
|
|
82
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Native-desktop entry — explicitly unsupported. No engine import, no GPU
|
|
2
|
+
// work at module load: this leaf never touches `shaders` at runtime, so
|
|
3
|
+
// importing the package on AppKit is inert. `createShader` resolves to a
|
|
4
|
+
// no-op instance reporting the upstream-style 'unsupported' failure
|
|
5
|
+
// reason so callers keep their static fallback path identical to web's.
|
|
6
|
+
export * from './types'
|
|
7
|
+
import type {
|
|
8
|
+
CreateShaderOptions,
|
|
9
|
+
PresetConfig,
|
|
10
|
+
SharedGpu,
|
|
11
|
+
ShaderInstance,
|
|
12
|
+
WebGPUSupportInfo,
|
|
13
|
+
} from './types'
|
|
14
|
+
|
|
15
|
+
const UNSUPPORTED: ShaderInstance = {
|
|
16
|
+
getFailureReason: () => 'unsupported',
|
|
17
|
+
update: () => {},
|
|
18
|
+
resize: () => {},
|
|
19
|
+
pause: () => {},
|
|
20
|
+
resume: () => {},
|
|
21
|
+
destroy: () => {},
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function createShader(
|
|
25
|
+
_canvas: unknown,
|
|
26
|
+
_preset: PresetConfig,
|
|
27
|
+
options?: CreateShaderOptions,
|
|
28
|
+
): Promise<ShaderInstance> {
|
|
29
|
+
options?.onError?.('unsupported')
|
|
30
|
+
return UNSUPPORTED
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function createSharedDevice(_options?: {
|
|
34
|
+
powerPreference?: string
|
|
35
|
+
}): Promise<SharedGpu | null> {
|
|
36
|
+
return null
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function isWebGPUSupported(): boolean {
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function getWebGPUSupport(): Promise<WebGPUSupportInfo> {
|
|
44
|
+
return { supported: false, reason: 'unsupported' }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function prefersReducedMotion(): boolean {
|
|
48
|
+
return false
|
|
49
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Native entry (iOS/Android via @nativescript/canvas) — same exports as
|
|
2
|
+
// index.web.ts and index.macos.ts.
|
|
3
|
+
export * from './types'
|
|
4
|
+
export {
|
|
5
|
+
createShader,
|
|
6
|
+
createSharedDevice,
|
|
7
|
+
getWebGPUSupport,
|
|
8
|
+
isWebGPUSupported,
|
|
9
|
+
prefersReducedMotion,
|
|
10
|
+
} from './host'
|
package/src/index.web.ts
ADDED
package/src/shared.ts
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Shared adapter internals — platform-free. No DOM globals, no
|
|
2
|
+
// NativeScript imports; each platform leaf injects its measure/attach
|
|
3
|
+
// hooks.
|
|
4
|
+
|
|
5
|
+
import type { AbortSignalLike, CreateShaderOptions, ShaderInstance } from './types'
|
|
6
|
+
|
|
7
|
+
export function abortError(): Error {
|
|
8
|
+
const error = new Error('createShader aborted before initialization completed')
|
|
9
|
+
error.name = 'AbortError'
|
|
10
|
+
return error
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function throwIfAborted(signal?: AbortSignalLike): void {
|
|
14
|
+
if (signal?.aborted) {
|
|
15
|
+
throw abortError()
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface Size {
|
|
20
|
+
width: number
|
|
21
|
+
height: number
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Defer until the surface reports a positive size (surface not attached /
|
|
25
|
+
* zero-size layout → wait, don't init). Aborts via `signal`. */
|
|
26
|
+
export function waitForPositiveSize(measure: () => Size, signal?: AbortSignalLike): Promise<void> {
|
|
27
|
+
const first = measure()
|
|
28
|
+
if (first.width > 0 && first.height > 0) {
|
|
29
|
+
return Promise.resolve()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return new Promise<void>((resolve, reject) => {
|
|
33
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
34
|
+
const stop = () => {
|
|
35
|
+
if (timer !== undefined) {
|
|
36
|
+
clearTimeout(timer)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
signal?.removeEventListener?.('abort', onAbort)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const onAbort = () => {
|
|
43
|
+
stop()
|
|
44
|
+
reject(abortError())
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const poll = () => {
|
|
48
|
+
if (signal?.aborted) {
|
|
49
|
+
return onAbort()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const size = measure()
|
|
53
|
+
if (size.width > 0 && size.height > 0) {
|
|
54
|
+
stop()
|
|
55
|
+
resolve()
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
timer = setTimeout(poll, 50)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
signal?.addEventListener?.('abort', onAbort)
|
|
63
|
+
timer = setTimeout(poll, 50)
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Merge caller options with adapter defaults. `disableTelemetry` defaults
|
|
68
|
+
* on for this package's local/preset-driven use; callers may re-enable it.
|
|
69
|
+
* The adapter-only `signal` key is stripped before reaching upstream. */
|
|
70
|
+
export function resolveOptions(
|
|
71
|
+
options: CreateShaderOptions | undefined,
|
|
72
|
+
platformDefaults: Partial<CreateShaderOptions>,
|
|
73
|
+
): { upstream: Omit<CreateShaderOptions, 'signal'>; signal?: AbortSignalLike } {
|
|
74
|
+
const { signal, ...rest } = options ?? {}
|
|
75
|
+
return {
|
|
76
|
+
signal,
|
|
77
|
+
upstream: {
|
|
78
|
+
...platformDefaults,
|
|
79
|
+
...rest,
|
|
80
|
+
disableTelemetry: options?.disableTelemetry ?? true,
|
|
81
|
+
},
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Subscribe/unsubscribe pair for host hide/show (backgrounding, detach,
|
|
86
|
+
* document visibility). */
|
|
87
|
+
export type AttachLifecycle = (hide: () => void, show: () => void) => () => void
|
|
88
|
+
|
|
89
|
+
/** Wrap the upstream instance so host suspension composes with caller
|
|
90
|
+
* pause instead of overriding it: host hide pauses, host show resumes
|
|
91
|
+
* only when the caller did not explicitly pause. `destroy` detaches all
|
|
92
|
+
* adapter-owned listeners before delegating. */
|
|
93
|
+
export function wrapInstance(inner: ShaderInstance, attach: AttachLifecycle): ShaderInstance {
|
|
94
|
+
let callerPaused = false
|
|
95
|
+
let hostSuspended = false
|
|
96
|
+
let destroyed = false
|
|
97
|
+
const detach = attach(
|
|
98
|
+
() => {
|
|
99
|
+
if (destroyed) {
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
hostSuspended = true
|
|
104
|
+
inner.pause()
|
|
105
|
+
},
|
|
106
|
+
() => {
|
|
107
|
+
hostSuspended = false
|
|
108
|
+
if (!destroyed && !callerPaused) {
|
|
109
|
+
inner.resume()
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
getFailureReason: () => inner.getFailureReason(),
|
|
116
|
+
update: (id, props) => inner.update(id, props),
|
|
117
|
+
resize: (width?, height?) => inner.resize(width, height),
|
|
118
|
+
pause: () => {
|
|
119
|
+
callerPaused = true
|
|
120
|
+
inner.pause()
|
|
121
|
+
},
|
|
122
|
+
resume: () => {
|
|
123
|
+
callerPaused = false
|
|
124
|
+
if (!hostSuspended) {
|
|
125
|
+
inner.resume()
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
destroy: () => {
|
|
129
|
+
if (destroyed) {
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
destroyed = true
|
|
134
|
+
detach()
|
|
135
|
+
inner.destroy()
|
|
136
|
+
},
|
|
137
|
+
}
|
|
138
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Public contract for @octane-xplat/shaders — re-exports the upstream
|
|
2
|
+
// `shaders` types verbatim and declares the adapter's added surface. Kept
|
|
3
|
+
// platform-free: every platform leaf (web / native / macos stub) exposes
|
|
4
|
+
// exactly these names.
|
|
5
|
+
|
|
6
|
+
// Upstream types are re-exported as aliases rather than `export type {…}
|
|
7
|
+
// from 'shaders/js'` so they stay unambiguously type-only for the pack
|
|
8
|
+
// check even where the specifier can't be resolved.
|
|
9
|
+
export type ComponentConfig = import('shaders/js').ComponentConfig
|
|
10
|
+
export type GpuFailureReason = import('shaders/js').GpuFailureReason
|
|
11
|
+
export type PresetConfig = import('shaders/js').PresetConfig
|
|
12
|
+
export type ShaderInstance = import('shaders/js').ShaderInstance
|
|
13
|
+
export type ShaderOptions = import('shaders/js').ShaderOptions
|
|
14
|
+
export type WebGPUSupportInfo = import('shaders/js').WebGPUSupportInfo
|
|
15
|
+
|
|
16
|
+
/** Structural AbortSignal — the real DOM AbortSignal satisfies this; so
|
|
17
|
+
* does NativeScript's. Kept structural so shared code never names DOM
|
|
18
|
+
* globals. */
|
|
19
|
+
export interface AbortSignalLike {
|
|
20
|
+
readonly aborted: boolean
|
|
21
|
+
addEventListener?(type: 'abort', listener: () => void): void
|
|
22
|
+
removeEventListener?(type: 'abort', listener: () => void): void
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Externally acquired WebGPU device + adapter pair — the caller owns its
|
|
26
|
+
* lifetime. Pass to `createShader` via `options.gpu` to share one device
|
|
27
|
+
* across instances. */
|
|
28
|
+
export interface SharedGpu {
|
|
29
|
+
device: any
|
|
30
|
+
adapter: any
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Upstream `ShaderOptions` plus adapter lifecycle glue. Preset, component
|
|
34
|
+
* names, parameters, and all other fields keep upstream semantics. */
|
|
35
|
+
export interface CreateShaderOptions extends ShaderOptions {
|
|
36
|
+
/** Aborts deferred or in-flight initialization — e.g. the surface
|
|
37
|
+
* unmounted while the engine was still starting. A late completion is
|
|
38
|
+
* destroyed, never revived. */
|
|
39
|
+
signal?: AbortSignalLike
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Create a shader on an Xplat Canvas surface (the `canvas` from
|
|
43
|
+
* `CanvasReady`: HTMLCanvasElement on web, `@nativescript/canvas` view on
|
|
44
|
+
* native). Same signature shape and return contract as upstream
|
|
45
|
+
* `createShader` — the resolved instance keeps upstream
|
|
46
|
+
* `update/resize/pause/resume/destroy/getFailureReason` semantics.
|
|
47
|
+
*
|
|
48
|
+
* Adapter additions, all documented:
|
|
49
|
+
* - `disableTelemetry` defaults to true (upstream default is off → on).
|
|
50
|
+
* - Init defers until the surface reports a positive size.
|
|
51
|
+
* - Host suspension (app background, surface detach, document hidden)
|
|
52
|
+
* pauses rendering without overriding an explicit caller `pause()`.
|
|
53
|
+
* - `options.signal` cancels before init completes.
|
|
54
|
+
* - On native, the canvas is wrapped in a DOM-canvas-shaped facade and
|
|
55
|
+
* `navigator.gpu` is lazily bound to the `@nativescript/canvas` GPU
|
|
56
|
+
* shim so upstream capability checks and device-loss recovery work.
|
|
57
|
+
*
|
|
58
|
+
* On unsupported hosts (native desktop) the returned promise resolves to
|
|
59
|
+
* a no-op instance whose `getFailureReason()` is `'unsupported'`. */
|
|
60
|
+
export declare function createShader(
|
|
61
|
+
canvas: unknown,
|
|
62
|
+
preset: PresetConfig,
|
|
63
|
+
options?: CreateShaderOptions,
|
|
64
|
+
): Promise<ShaderInstance>
|
|
65
|
+
|
|
66
|
+
/** Acquire a WebGPU `{device, adapter}` through the platform entry point
|
|
67
|
+
* (`navigator.gpu` on web, the `@nativescript/canvas` GPU shim on native).
|
|
68
|
+
* Resolves null where WebGPU is unavailable — keep the static fallback in
|
|
69
|
+
* that case. The caller owns the device; the adapter never destroys it. */
|
|
70
|
+
export declare function createSharedDevice(options?: {
|
|
71
|
+
powerPreference?: string
|
|
72
|
+
}): Promise<SharedGpu | null>
|
|
73
|
+
|
|
74
|
+
/** True when a WebGPU entry point exists. On native this reflects the
|
|
75
|
+
* `@nativescript/canvas` GPU shim, not actual driver capability — request
|
|
76
|
+
* an adapter via `createSharedDevice()`/`getWebGPUSupport()` for a real
|
|
77
|
+
* check. */
|
|
78
|
+
export declare function isWebGPUSupported(): boolean
|
|
79
|
+
|
|
80
|
+
/** Async capability probe — resolves `{supported, reason?}` using upstream
|
|
81
|
+
* failure-reason vocabulary ('unsupported', 'no-adapter', …). */
|
|
82
|
+
export declare function getWebGPUSupport(): Promise<WebGPUSupportInfo>
|
|
83
|
+
|
|
84
|
+
/** OS-level reduced-motion preference. Use it to hold a still frame
|
|
85
|
+
* (pause after the first rendered frame) or keep the static fallback. */
|
|
86
|
+
export declare function prefersReducedMotion(): boolean
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './types.js'
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Native desktop keeps the same public surface as the other targets —
|
|
2
|
+
// the functions resolve to the unsupported no-op implementations.
|
|
3
|
+
// Re-exports types.d.ts directly: under .macos moduleSuffixes, './index.js'
|
|
4
|
+
// would resolve back to this file.
|
|
5
|
+
export * from './types.js'
|
package/types/types.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export type ComponentConfig = import('shaders/js').ComponentConfig;
|
|
2
|
+
export type GpuFailureReason = import('shaders/js').GpuFailureReason;
|
|
3
|
+
export type PresetConfig = import('shaders/js').PresetConfig;
|
|
4
|
+
export type ShaderInstance = import('shaders/js').ShaderInstance;
|
|
5
|
+
export type ShaderOptions = import('shaders/js').ShaderOptions;
|
|
6
|
+
export type WebGPUSupportInfo = import('shaders/js').WebGPUSupportInfo;
|
|
7
|
+
/** Structural AbortSignal — the real DOM AbortSignal satisfies this; so
|
|
8
|
+
* does NativeScript's. Kept structural so shared code never names DOM
|
|
9
|
+
* globals. */
|
|
10
|
+
export interface AbortSignalLike {
|
|
11
|
+
readonly aborted: boolean;
|
|
12
|
+
addEventListener?(type: 'abort', listener: () => void): void;
|
|
13
|
+
removeEventListener?(type: 'abort', listener: () => void): void;
|
|
14
|
+
}
|
|
15
|
+
/** Externally acquired WebGPU device + adapter pair — the caller owns its
|
|
16
|
+
* lifetime. Pass to `createShader` via `options.gpu` to share one device
|
|
17
|
+
* across instances. */
|
|
18
|
+
export interface SharedGpu {
|
|
19
|
+
device: any;
|
|
20
|
+
adapter: any;
|
|
21
|
+
}
|
|
22
|
+
/** Upstream `ShaderOptions` plus adapter lifecycle glue. Preset, component
|
|
23
|
+
* names, parameters, and all other fields keep upstream semantics. */
|
|
24
|
+
export interface CreateShaderOptions extends ShaderOptions {
|
|
25
|
+
/** Aborts deferred or in-flight initialization — e.g. the surface
|
|
26
|
+
* unmounted while the engine was still starting. A late completion is
|
|
27
|
+
* destroyed, never revived. */
|
|
28
|
+
signal?: AbortSignalLike;
|
|
29
|
+
}
|
|
30
|
+
/** Create a shader on an Xplat Canvas surface (the `canvas` from
|
|
31
|
+
* `CanvasReady`: HTMLCanvasElement on web, `@nativescript/canvas` view on
|
|
32
|
+
* native). Same signature shape and return contract as upstream
|
|
33
|
+
* `createShader` — the resolved instance keeps upstream
|
|
34
|
+
* `update/resize/pause/resume/destroy/getFailureReason` semantics.
|
|
35
|
+
*
|
|
36
|
+
* Adapter additions, all documented:
|
|
37
|
+
* - `disableTelemetry` defaults to true (upstream default is off → on).
|
|
38
|
+
* - Init defers until the surface reports a positive size.
|
|
39
|
+
* - Host suspension (app background, surface detach, document hidden)
|
|
40
|
+
* pauses rendering without overriding an explicit caller `pause()`.
|
|
41
|
+
* - `options.signal` cancels before init completes.
|
|
42
|
+
* - On native, the canvas is wrapped in a DOM-canvas-shaped facade and
|
|
43
|
+
* `navigator.gpu` is lazily bound to the `@nativescript/canvas` GPU
|
|
44
|
+
* shim so upstream capability checks and device-loss recovery work.
|
|
45
|
+
*
|
|
46
|
+
* On unsupported hosts (native desktop) the returned promise resolves to
|
|
47
|
+
* a no-op instance whose `getFailureReason()` is `'unsupported'`. */
|
|
48
|
+
export declare function createShader(canvas: unknown, preset: PresetConfig, options?: CreateShaderOptions): Promise<ShaderInstance>;
|
|
49
|
+
/** Acquire a WebGPU `{device, adapter}` through the platform entry point
|
|
50
|
+
* (`navigator.gpu` on web, the `@nativescript/canvas` GPU shim on native).
|
|
51
|
+
* Resolves null where WebGPU is unavailable — keep the static fallback in
|
|
52
|
+
* that case. The caller owns the device; the adapter never destroys it. */
|
|
53
|
+
export declare function createSharedDevice(options?: {
|
|
54
|
+
powerPreference?: string;
|
|
55
|
+
}): Promise<SharedGpu | null>;
|
|
56
|
+
/** True when a WebGPU entry point exists. On native this reflects the
|
|
57
|
+
* `@nativescript/canvas` GPU shim, not actual driver capability — request
|
|
58
|
+
* an adapter via `createSharedDevice()`/`getWebGPUSupport()` for a real
|
|
59
|
+
* check. */
|
|
60
|
+
export declare function isWebGPUSupported(): boolean;
|
|
61
|
+
/** Async capability probe — resolves `{supported, reason?}` using upstream
|
|
62
|
+
* failure-reason vocabulary ('unsupported', 'no-adapter', …). */
|
|
63
|
+
export declare function getWebGPUSupport(): Promise<WebGPUSupportInfo>;
|
|
64
|
+
/** OS-level reduced-motion preference. Use it to hold a still frame
|
|
65
|
+
* (pause after the first rendered frame) or keep the static fallback. */
|
|
66
|
+
export declare function prefersReducedMotion(): boolean;
|