@nadrif/thread 0.1.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/LICENSE +21 -0
- package/README.md +1156 -0
- package/package.json +133 -0
- package/src/adapters.js +404 -0
- package/src/config/adapters.js +188 -0
- package/src/config/define.js +171 -0
- package/src/config/env.js +218 -0
- package/src/config/frameworks.js +268 -0
- package/src/config/index.js +256 -0
- package/src/config/schema.js +375 -0
- package/src/config.js +52 -0
- package/src/deno.js +29 -0
- package/src/edge.js +38 -0
- package/src/env.js +361 -0
- package/src/error.js +340 -0
- package/src/factory.js +591 -0
- package/src/gpu/adapters.js +227 -0
- package/src/gpu/chains.js +261 -0
- package/src/gpu/env.js +372 -0
- package/src/gpu/gpu.js +1199 -0
- package/src/gpu/helpers.js +240 -0
- package/src/gpu/hooks.js +284 -0
- package/src/gpu/index.js +16 -0
- package/src/gpu/shaders.js +834 -0
- package/src/gpu/special.js +493 -0
- package/src/hooks.js +448 -0
- package/src/index.js +557 -0
- package/src/metrix.js +222 -0
- package/src/node.js +36 -0
- package/src/pool.js +740 -0
- package/src/serializer.js +139 -0
- package/src/thread.js +1246 -0
- package/src/types.js +1354 -0
- package/src/worker-factory.js +347 -0
package/src/gpu/env.js
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file GPU environment detection and platform-specific adapters.
|
|
3
|
+
*
|
|
4
|
+
* Detects WebGPU support across browser, Node.js, Deno, and Edge
|
|
5
|
+
* environments. Provides a unified interface for requesting GPU
|
|
6
|
+
* adapters and devices regardless of the runtime.
|
|
7
|
+
*
|
|
8
|
+
* **Supported environments:**
|
|
9
|
+
* - **Browser** — `navigator.gpu.requestAdapter()`
|
|
10
|
+
* - **Node.js** — `@aspect-build/webgpu-node`, `node-webgpu`, or GPUProcess
|
|
11
|
+
* - **Bun** — Bun's built-in WebGPU (experimental) or Node.js bindings
|
|
12
|
+
* - **Deno** — `Deno.gpu` (Deno's built-in WebGPU)
|
|
13
|
+
* - **Edge** — Browser-compatible WebGPU (Cloudflare, Vercel)
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```js
|
|
17
|
+
* import { gpuEnv, requestGPUAdapter, isGPUAvailable } from './env.js';
|
|
18
|
+
*
|
|
19
|
+
* if (await isGPUAvailable()) {
|
|
20
|
+
* const adapter = await requestGPUAdapter({ powerPreference: 'high-performance' });
|
|
21
|
+
* const device = await adapter.requestDevice();
|
|
22
|
+
* // Use device for compute shaders...
|
|
23
|
+
* } else {
|
|
24
|
+
* console.warn('WebGPU not available — falling back to CPU');
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @module gpu/env
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { env } from '../env.js';
|
|
32
|
+
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// GPU detection (cached)
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
/** @type {Promise<boolean>|null} Cached GPU availability check. */
|
|
38
|
+
let _gpuCheckPromise = null;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Check if WebGPU is available in the current environment.
|
|
42
|
+
*
|
|
43
|
+
* This performs async detection (some runtimes require `await` to
|
|
44
|
+
* determine GPU availability). The result is cached after the first call.
|
|
45
|
+
*
|
|
46
|
+
* @returns {Promise<boolean>} `true` if WebGPU is available and usable.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```js
|
|
50
|
+
* if (await isGPUAvailable()) {
|
|
51
|
+
* const gpu = new GPUCompute({ shader: myShader });
|
|
52
|
+
* await gpu.init();
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export async function isGPUAvailable() {
|
|
57
|
+
if (_gpuCheckPromise) return _gpuCheckPromise;
|
|
58
|
+
_gpuCheckPromise = _checkGPU();
|
|
59
|
+
return _gpuCheckPromise;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @private
|
|
64
|
+
*/
|
|
65
|
+
async function _checkGPU() {
|
|
66
|
+
// --- Browser / Edge ---
|
|
67
|
+
if (typeof navigator !== 'undefined' && navigator.gpu) {
|
|
68
|
+
try {
|
|
69
|
+
const adapter = await navigator.gpu.requestAdapter({ powerPreference: 'high-performance' });
|
|
70
|
+
return !!adapter;
|
|
71
|
+
} catch {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// --- Deno ---
|
|
77
|
+
if (env.isDeno && typeof Deno !== 'undefined' && Deno.gpu) {
|
|
78
|
+
try {
|
|
79
|
+
const adapter = await Deno.gpu.requestAdapter({ powerPreference: 'high-performance' });
|
|
80
|
+
return !!adapter;
|
|
81
|
+
} catch {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// --- Node.js / Bun — try dynamic imports of WebGPU bindings ---
|
|
87
|
+
if (env.isNode || env.isBun) {
|
|
88
|
+
return await _checkNodeGPU();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @private
|
|
96
|
+
*/
|
|
97
|
+
async function _checkNodeGPU() {
|
|
98
|
+
// Try various Node.js WebGPU implementations
|
|
99
|
+
const candidates = [
|
|
100
|
+
'@aspect-build/webgpu-node',
|
|
101
|
+
'node-webgpu',
|
|
102
|
+
'@aspect-build/webgpu',
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
for (const pkg of candidates) {
|
|
106
|
+
try {
|
|
107
|
+
const mod = await import(pkg);
|
|
108
|
+
if (mod && (mod.gpu || mod.navigator?.gpu || mod.default?.gpu)) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
} catch {
|
|
112
|
+
// Not installed — try next
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Try Bun's built-in (if available in the future)
|
|
117
|
+
if (env.isBun) {
|
|
118
|
+
try {
|
|
119
|
+
if (typeof globalThis.Bun?.gpu !== 'undefined') return true;
|
|
120
|
+
} catch { /* ignore */ }
|
|
121
|
+
|
|
122
|
+
// Try bun-webgpu (Dawn FFI bindings for Bun)
|
|
123
|
+
try {
|
|
124
|
+
const mod = await import('bun-webgpu');
|
|
125
|
+
if (mod && typeof mod.setupGlobals === 'function') {
|
|
126
|
+
mod.setupGlobals();
|
|
127
|
+
return typeof navigator !== 'undefined' && !!navigator.gpu;
|
|
128
|
+
}
|
|
129
|
+
} catch { /* not installed */ }
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Synchronous GPU availability check.
|
|
137
|
+
*
|
|
138
|
+
* Returns `true` if the `navigator.gpu` API is present in the global
|
|
139
|
+
* scope, or if `Bun.gpu` is available (Bun's experimental built-in).
|
|
140
|
+
* This is a fast, synchronous check — it does NOT verify that a GPU
|
|
141
|
+
* adapter can actually be obtained. Use {@link isGPUAvailable} for
|
|
142
|
+
* a reliable async check.
|
|
143
|
+
*
|
|
144
|
+
* @returns {boolean} `true` if `navigator.gpu` or `Bun.gpu` exists.
|
|
145
|
+
*/
|
|
146
|
+
export function isGPUSync() {
|
|
147
|
+
if (typeof navigator !== 'undefined' && navigator.gpu) return true;
|
|
148
|
+
if (env.isDeno && typeof Deno !== 'undefined' && Deno.gpu) return true;
|
|
149
|
+
if (env.isBun && typeof globalThis.Bun !== 'undefined' && globalThis.Bun.gpu) return true;
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
// GPU adapter request
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Request a GPU adapter from the current environment.
|
|
159
|
+
*
|
|
160
|
+
* Abstracts the differences between browser `navigator.gpu`,
|
|
161
|
+
* Deno `Deno.gpu`, and Node.js WebGPU bindings.
|
|
162
|
+
*
|
|
163
|
+
* @param {Object} [options={}]
|
|
164
|
+
* @param {'low-power'|'high-performance'|undefined} [options.powerPreference]
|
|
165
|
+
* @param {Object} [options.forceFallbackAdapter] - Force software renderer.
|
|
166
|
+
* @returns {Promise<GPUAdapter|null>} The requested adapter, or `null`.
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```js
|
|
170
|
+
* const adapter = await requestGPUAdapter({
|
|
171
|
+
* powerPreference: 'high-performance',
|
|
172
|
+
* });
|
|
173
|
+
*
|
|
174
|
+
* if (adapter) {
|
|
175
|
+
* const device = await adapter.requestDevice();
|
|
176
|
+
* // Ready for compute shaders
|
|
177
|
+
* }
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
180
|
+
export async function requestGPUAdapter(options = {}) {
|
|
181
|
+
const { powerPreference = 'high-performance', ...rest } = options;
|
|
182
|
+
|
|
183
|
+
// --- Browser ---
|
|
184
|
+
if (typeof navigator !== 'undefined' && navigator.gpu) {
|
|
185
|
+
try {
|
|
186
|
+
return await navigator.gpu.requestAdapter({ powerPreference, ...rest });
|
|
187
|
+
} catch {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// --- Deno ---
|
|
193
|
+
if (env.isDeno && typeof Deno !== 'undefined' && Deno.gpu) {
|
|
194
|
+
try {
|
|
195
|
+
return await Deno.gpu.requestAdapter({ powerPreference, ...rest });
|
|
196
|
+
} catch {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// --- Node.js ---
|
|
202
|
+
if (env.isNode || env.isBun) {
|
|
203
|
+
return await _requestNodeGPUAdapter(options);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* @private
|
|
211
|
+
*/
|
|
212
|
+
async function _requestNodeGPUAdapter(options) {
|
|
213
|
+
const candidates = [
|
|
214
|
+
'bun-webgpu',
|
|
215
|
+
'@aspect-build/webgpu-node',
|
|
216
|
+
'node-webgpu',
|
|
217
|
+
'@aspect-build/webgpu',
|
|
218
|
+
'webgpu',
|
|
219
|
+
];
|
|
220
|
+
|
|
221
|
+
for (const pkg of candidates) {
|
|
222
|
+
try {
|
|
223
|
+
const mod = await import(pkg);
|
|
224
|
+
// bun-webgpu requires setupGlobals() to set navigator.gpu
|
|
225
|
+
if (pkg === 'bun-webgpu' && typeof mod.setupGlobals === 'function') {
|
|
226
|
+
mod.setupGlobals();
|
|
227
|
+
}
|
|
228
|
+
const gpu = mod.gpu || mod.navigator?.gpu || mod.default?.gpu || (typeof navigator !== 'undefined' && navigator.gpu);
|
|
229
|
+
if (gpu && typeof gpu.requestAdapter === 'function') {
|
|
230
|
+
return await gpu.requestAdapter(options);
|
|
231
|
+
}
|
|
232
|
+
} catch {
|
|
233
|
+
// Not available
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ---------------------------------------------------------------------------
|
|
241
|
+
// GPU info / diagnostics
|
|
242
|
+
// ---------------------------------------------------------------------------
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Get a diagnostic snapshot of GPU support in the current environment.
|
|
246
|
+
*
|
|
247
|
+
* Useful for debugging GPU availability issues.
|
|
248
|
+
*
|
|
249
|
+
* @returns {Promise<Object>} Diagnostic information.
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* ```js
|
|
253
|
+
* const info = await gpuInfo();
|
|
254
|
+
* console.log(info);
|
|
255
|
+
* // {
|
|
256
|
+
* available: true,
|
|
257
|
+
* runtime: 'browser',
|
|
258
|
+
* adapterName: 'NVIDIA GeForce RTX 4090',
|
|
259
|
+
* features: ['timestamp-query', 'shader-f16'],
|
|
260
|
+
* limits: { maxBufferSize: 2147483648, ... },
|
|
261
|
+
* fallback: false
|
|
262
|
+
* }
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
export async function gpuInfo() {
|
|
266
|
+
const available = await isGPUAvailable();
|
|
267
|
+
const info = {
|
|
268
|
+
available,
|
|
269
|
+
runtime: env.runtime,
|
|
270
|
+
adapterName: null,
|
|
271
|
+
features: [],
|
|
272
|
+
limits: {},
|
|
273
|
+
fallback: false,
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
if (!available) return info;
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
const adapter = await requestGPUAdapter({ powerPreference: 'low-power' });
|
|
280
|
+
if (adapter) {
|
|
281
|
+
info.adapterName = adapter.name || 'unknown';
|
|
282
|
+
info.features = [...(adapter.features || [])];
|
|
283
|
+
info.limits = adapter.limits || {};
|
|
284
|
+
}
|
|
285
|
+
} catch { /* ignore */ }
|
|
286
|
+
|
|
287
|
+
return info;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Request a GPU device with sensible defaults.
|
|
292
|
+
*
|
|
293
|
+
* Convenience wrapper around {@link requestGPUAdapter} that also
|
|
294
|
+
* requests a device. Returns both the adapter and device.
|
|
295
|
+
*
|
|
296
|
+
* @param {Object} [options={}]
|
|
297
|
+
* @param {'low-power'|'high-performance'} [options.powerPreference]
|
|
298
|
+
* @param {Object} [options.requiredLimits] - Minimum device limits.
|
|
299
|
+
* @returns {Promise<{ adapter: GPUAdapter, device: GPUDevice }|null>}
|
|
300
|
+
* Adapter and device, or `null` if unavailable.
|
|
301
|
+
*
|
|
302
|
+
* @example
|
|
303
|
+
* ```js
|
|
304
|
+
* const result = await requestGPUDevice({ powerPreference: 'high-performance' });
|
|
305
|
+
* if (result) {
|
|
306
|
+
* const { device } = result;
|
|
307
|
+
* const buffer = device.createBuffer({ size: 1024, usage: GPUBufferUsage.STORAGE });
|
|
308
|
+
* }
|
|
309
|
+
* ```
|
|
310
|
+
*/
|
|
311
|
+
export async function requestGPUDevice(options = {}) {
|
|
312
|
+
const { requiredLimits, ...adapterOptions } = options;
|
|
313
|
+
const adapter = await requestGPUAdapter(adapterOptions);
|
|
314
|
+
if (!adapter) return null;
|
|
315
|
+
|
|
316
|
+
try {
|
|
317
|
+
const device = await adapter.requestDevice({
|
|
318
|
+
requiredLimits: requiredLimits || {},
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// Handle device loss
|
|
322
|
+
device.lost.then((info) => {
|
|
323
|
+
// Device lost — caller should handle re-init
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
return { adapter, device };
|
|
327
|
+
} catch {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ---------------------------------------------------------------------------
|
|
333
|
+
// GPU environment object (unified interface)
|
|
334
|
+
// ---------------------------------------------------------------------------
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Comprehensive GPU environment information.
|
|
338
|
+
*
|
|
339
|
+
* @type {{
|
|
340
|
+
* available: boolean,
|
|
341
|
+
* sync: boolean,
|
|
342
|
+
* runtime: string,
|
|
343
|
+
* isBrowser: boolean,
|
|
344
|
+
* isNode: boolean,
|
|
345
|
+
* isDeno: boolean,
|
|
346
|
+
* requestAdapter: typeof requestGPUAdapter,
|
|
347
|
+
* requestDevice: typeof requestGPUDevice,
|
|
348
|
+
* info: typeof gpuInfo,
|
|
349
|
+
* }}
|
|
350
|
+
*/
|
|
351
|
+
export const gpuEnv = Object.freeze({
|
|
352
|
+
/** @type {boolean} Async GPU availability check (cached). */
|
|
353
|
+
get available() { return isGPUAvailable(); },
|
|
354
|
+
/** @type {boolean} Sync check for `navigator.gpu`. */
|
|
355
|
+
get sync() { return isGPUSync(); },
|
|
356
|
+
/** @type {string} Current runtime identifier. */
|
|
357
|
+
runtime: env.runtime,
|
|
358
|
+
/** @type {boolean} `true` if in a browser environment. */
|
|
359
|
+
isBrowser: env.isBrowser,
|
|
360
|
+
/** @type {boolean} `true` if in Node.js or Bun. */
|
|
361
|
+
isNode: env.isNode || env.isBun,
|
|
362
|
+
/** @type {boolean} `true` if in Deno. */
|
|
363
|
+
isDeno: env.isDeno,
|
|
364
|
+
/** Request a GPU adapter. */
|
|
365
|
+
requestAdapter: requestGPUAdapter,
|
|
366
|
+
/** Request a GPU device. */
|
|
367
|
+
requestDevice: requestGPUDevice,
|
|
368
|
+
/** Get diagnostic info. */
|
|
369
|
+
info: gpuInfo,
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
export default gpuEnv;
|