@aethervtt/shard-gpu 0.0.1 → 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/dist/context.js CHANGED
@@ -1,6 +1,21 @@
1
1
  import { ShardError } from "@aethervtt/shard-core";
2
2
  import { LayoutCache, PipelineCache } from "./caches.js";
3
3
  import { toShardError } from "./errors.js";
4
+ import {
5
+ blockOf,
6
+ bufferCategory,
7
+ DATA_TEXTURE_PREFIX,
8
+ Ledger,
9
+ SHARED_OWNER,
10
+ textureBytes,
11
+ textureCategory
12
+ } from "./ledger.js";
13
+ import { Surface } from "./surface.js";
14
+ import {
15
+ capabilitiesOf,
16
+ isCoreDevice,
17
+ probeHdrSampleCount
18
+ } from "./tier.js";
4
19
  const COMPRESSED_TEXTURE_FEATURES = [
5
20
  "texture-compression-bc",
6
21
  "texture-compression-astc",
@@ -16,45 +31,147 @@ class GpuContext {
16
31
  adapter;
17
32
  device;
18
33
  format;
19
- canvas;
20
- context;
21
34
  features;
35
+ /** The graphics API the device runs on (0064). */
36
+ backend;
37
+ /**
38
+ * The tier the engine runs at (0064): `full` on a core WebGPU device, `baseline` on anything
39
+ * else that runs the baseline subset. Kept across device replacement.
40
+ */
41
+ tier;
42
+ /** What the device can do, as far as the tiers care. */
43
+ capabilities;
44
+ /** Why better options were skipped while opening the device (no core adapter, say). */
45
+ reasons;
22
46
  pipelines;
23
47
  layouts;
24
48
  /** Increments when the device is replaced. */
25
49
  generation = 0;
26
- /** Canvas pixels per CSS pixel (devicePixelRatio) at the last resize; 1 without a canvas. */
27
- pixelRatio = 1;
50
+ /**
51
+ * How long the first adapter and device request took, in ms (0062's `coldStart.device`). Set by
52
+ * `createGpuContext`; 0 for a context built directly.
53
+ */
54
+ deviceMs = 0;
55
+ /**
56
+ * Who new buffers and textures count against. An app sets it to its owner while it runs (the
57
+ * render plugin's scope); otherwise it's `'gpu'`, the device's own.
58
+ */
59
+ owner = SHARED_OWNER;
28
60
  /** Most recent GPU errors, newest last. */
29
61
  errors = [];
62
+ /** Whether the device works now (0061). */
63
+ status = "ok";
30
64
  options;
31
65
  errorListeners = /* @__PURE__ */ new Set();
32
66
  lostListeners = /* @__PURE__ */ new Set();
33
- constructor(options, adapter, device, format, context) {
67
+ surfaces_ = [];
68
+ ledger = new Ledger();
69
+ shared_ = /* @__PURE__ */ new Map();
70
+ recreating;
71
+ destroyed = false;
72
+ /** The WebGL2 shim's canvas contexts, when that's the backend. */
73
+ canvasContext;
74
+ constructor(options, adapter, device, format, opened = {}) {
34
75
  this.options = options;
35
76
  this.adapter = adapter;
36
77
  this.device = device;
37
78
  this.format = format;
38
- this.canvas = options.canvas;
39
- this.context = context;
40
79
  this.features = new Set(device.features);
80
+ this.backend = opened.backend ?? "webgpu";
81
+ this.tier = opened.tier ?? (isCoreDevice(device) ? "full" : "baseline");
82
+ this.capabilities = capabilitiesOf(device, this.backend, opened.hdrSampleCount);
83
+ this.reasons = opened.reasons ?? [];
84
+ this.canvasContext = opened.canvasContext;
41
85
  this.pipelines = new PipelineCache(this);
42
86
  this.layouts = new LayoutCache(this);
43
87
  this.attach(device);
44
88
  }
45
- /** Match the canvas backing size to its CSS size. Returns true if it changed. */
46
- resize() {
47
- const canvas = this.canvas;
48
- if (!canvas || !("clientWidth" in canvas)) return false;
49
- const dpr = globalThis.devicePixelRatio ?? 1;
50
- this.pixelRatio = dpr;
51
- const width = Math.max(1, Math.floor(canvas.clientWidth * dpr));
52
- const height = Math.max(1, Math.floor(canvas.clientHeight * dpr));
53
- if (canvas.width === width && canvas.height === height) return false;
54
- canvas.width = width;
55
- canvas.height = height;
56
- return true;
89
+ // --- surfaces ----------------------------------------------------------------
90
+ /** The canvases this device draws into, in the order they were added. */
91
+ get surfaces() {
92
+ return this.surfaces_;
93
+ }
94
+ /** Configures a canvas as a surface of this device. `surface.remove()` gives it back. */
95
+ addSurface(canvas, options = {}) {
96
+ const context = this.canvasContext ? this.canvasContext(canvas) : canvas.getContext("webgpu");
97
+ if (!context) {
98
+ const api = this.backend === "webgl2" ? "WebGL2" : "WebGPU";
99
+ throw new ShardError("gpu/no-context", `Could not get a ${api} canvas context`);
100
+ }
101
+ if (this.surfaces_.some((s) => s.canvas === canvas)) {
102
+ throw new ShardError("gpu/duplicate-surface", "This canvas is already a surface", {
103
+ hint: "Share the Surface itself (renderPlugin({ gpu, surface })), or remove it first."
104
+ });
105
+ }
106
+ const n = this.surfaces_.length;
107
+ const surface = new Surface(this, canvas, context, {
108
+ alpha: options.alpha ?? "opaque",
109
+ label: options.label ?? (n === 0 ? "surface" : `surface-${n + 1}`)
110
+ });
111
+ this.surfaces_.push(surface);
112
+ return surface;
113
+ }
114
+ /** @internal Called by `Surface.remove()`. */
115
+ forgetSurface(surface) {
116
+ const i = this.surfaces_.indexOf(surface);
117
+ if (i !== -1) this.surfaces_.splice(i, 1);
118
+ }
119
+ /** WebGL2: where shader translations came from, and what naga translated (0064). */
120
+ shaderCache() {
121
+ return this.device.shaderCache;
122
+ }
123
+ /**
124
+ * WebGL2: every shader translation this session used, as a baked set to ship as
125
+ * `webgl2.shaders` (0064). Undefined on WebGPU.
126
+ */
127
+ exportShaderCache() {
128
+ return this.device.exportShaders?.();
129
+ }
130
+ // --- accounting ----------------------------------------------------------------
131
+ /** Runs `fn` with new buffers and textures counted against `owner`. */
132
+ withOwner(owner, fn) {
133
+ const previous = this.owner;
134
+ this.owner = owner;
135
+ try {
136
+ return fn();
137
+ } finally {
138
+ this.owner = previous;
139
+ }
140
+ }
141
+ /** Live buffers and textures this device made for `owner`, or for everyone (0052). */
142
+ stats(owner) {
143
+ return this.ledger.stats(owner);
144
+ }
145
+ /** Live bytes for `owner` (or everyone) by what they're for: targets, textures, geometry... (0062). */
146
+ memory(owner) {
147
+ return this.ledger.memory(owner);
148
+ }
149
+ /**
150
+ * Bytes `owner` has written to the GPU since the device was made, by upload category, and the
151
+ * buffers and textures it created (0055). Cumulative: diff two reads for a frame's worth.
152
+ */
153
+ uploads(owner) {
154
+ return this.ledger.uploads(owner);
155
+ }
156
+ /** Owners with live objects, sorted. */
157
+ owners() {
158
+ return this.ledger.names();
57
159
  }
160
+ /** Destroys every buffer and texture `owner` still has. Returns how many. */
161
+ release(owner) {
162
+ return this.ledger.release(owner);
163
+ }
164
+ /**
165
+ * A per-device object every app on this device shares, made once by `create` and counted against
166
+ * the device, not whichever app asked first. Made again after a device loss.
167
+ */
168
+ shared(key, create) {
169
+ if (this.shared_.has(key)) return this.shared_.get(key);
170
+ const value = this.withOwner(SHARED_OWNER, () => create(this.device));
171
+ this.shared_.set(key, value);
172
+ return value;
173
+ }
174
+ // --- errors and loss -------------------------------------------------------------
58
175
  onError(listener) {
59
176
  this.errorListeners.add(listener);
60
177
  return () => this.errorListeners.delete(listener);
@@ -63,6 +180,10 @@ class GpuContext {
63
180
  this.lostListeners.add(listener);
64
181
  return () => this.lostListeners.delete(listener);
65
182
  }
183
+ /** Error and device-loss listeners, for teardown tests. */
184
+ get listenerCount() {
185
+ return { error: this.errorListeners.size, lost: this.lostListeners.size };
186
+ }
66
187
  reportError(error) {
67
188
  this.errors.push(error);
68
189
  if (this.errors.length > MAX_ERRORS) this.errors.shift();
@@ -80,27 +201,77 @@ class GpuContext {
80
201
  });
81
202
  return result;
82
203
  }
83
- /** Gets a fresh adapter and device after a loss, and reconfigures the canvas. */
84
- async recreate() {
85
- const { adapter, device } = await requestDevice(this.options);
86
- this.adapter = adapter;
87
- this.device = device;
88
- this.context?.configure(canvasConfig(device, this.format));
89
- this.pipelines.clear();
90
- this.layouts.clear();
91
- this.generation++;
92
- this.attach(device);
204
+ /**
205
+ * Gets a fresh adapter and device after a loss and reconfigures every surface with its alpha
206
+ * mode. Apps sharing the device all call it; they share one recreation.
207
+ */
208
+ recreate() {
209
+ this.recreating ??= this.recover().finally(() => {
210
+ this.recreating = void 0;
211
+ });
212
+ return this.recreating;
213
+ }
214
+ /** Replaces the device, retrying as `options.recovery` says; `failed` once every try failed. */
215
+ async recover() {
216
+ const attempts = Math.max(1, this.options.recovery?.attempts ?? 3);
217
+ const interval = this.options.recovery?.intervalMs ?? 1e3;
218
+ this.status = "lost";
219
+ let last;
220
+ for (let i = 0; i < attempts; i++) {
221
+ if (i > 0) await new Promise((resolve) => setTimeout(resolve, interval));
222
+ if (this.destroyed) return;
223
+ try {
224
+ await this.replaceDevice();
225
+ this.status = "ok";
226
+ return;
227
+ } catch (err) {
228
+ last = err;
229
+ }
230
+ }
231
+ this.status = "failed";
232
+ const error = new ShardError(
233
+ "gpu/recovery-failed",
234
+ `The GPU device couldn't be replaced after ${attempts} tries`,
235
+ { hint: "Tell the user 3D is unavailable; reloading the page tries again.", cause: last }
236
+ );
237
+ this.reportError(error);
238
+ throw error;
93
239
  }
94
240
  /** For tests and debugging: destroys the device and reports it as an unexpected loss. */
95
241
  simulateDeviceLoss() {
96
242
  this.handleLoss({ reason: "unknown", message: "Simulated device loss" });
97
243
  this.device.destroy();
98
244
  }
245
+ /** Removes every surface and destroys the device. */
99
246
  destroy() {
247
+ if (this.destroyed) return;
248
+ this.destroyed = true;
100
249
  this.lostListeners.clear();
250
+ this.errorListeners.clear();
251
+ for (const surface of [...this.surfaces_]) surface.remove();
252
+ this.ledger.clear();
253
+ this.shared_.clear();
101
254
  this.device.destroy();
102
255
  }
256
+ async replaceDevice() {
257
+ const { adapter, device, hdrSampleCount } = await openDevice(
258
+ this.options,
259
+ this.tier,
260
+ this.backend
261
+ );
262
+ this.adapter = adapter;
263
+ this.device = device;
264
+ this.capabilities = capabilitiesOf(device, this.backend, hdrSampleCount);
265
+ this.ledger.clear();
266
+ this.shared_.clear();
267
+ this.pipelines.clear();
268
+ this.layouts.clear();
269
+ this.generation++;
270
+ this.attach(device);
271
+ for (const surface of this.surfaces_) surface.configure();
272
+ }
103
273
  attach(device) {
274
+ this.instrument(device);
104
275
  device.addEventListener("uncapturederror", (event) => {
105
276
  this.reportError(toShardError(event.error, void 0));
106
277
  });
@@ -110,30 +281,184 @@ class GpuContext {
110
281
  }
111
282
  });
112
283
  }
284
+ /**
285
+ * Counts every buffer and texture the device makes against the current owner, and every byte
286
+ * written into one against its owner and upload category.
287
+ */
288
+ instrument(device) {
289
+ const ledger = this.ledger;
290
+ const createBuffer = device.createBuffer.bind(device);
291
+ const createTexture = device.createTexture.bind(device);
292
+ device.createBuffer = (descriptor) => {
293
+ const buffer = createBuffer(descriptor);
294
+ const category = bufferCategory(descriptor.usage);
295
+ ledger.track(buffer, this.owner, false, descriptor.size, category, descriptor.label);
296
+ return buffer;
297
+ };
298
+ device.createTexture = (descriptor) => {
299
+ const texture = createTexture(descriptor);
300
+ const data = descriptor.label?.startsWith(DATA_TEXTURE_PREFIX) === true;
301
+ const category = data ? "storage" : textureCategory(descriptor.usage);
302
+ ledger.track(texture, this.owner, true, textureBytes(descriptor), category, descriptor.label);
303
+ return texture;
304
+ };
305
+ const queue = device.queue;
306
+ const writeBuffer = queue.writeBuffer.bind(queue);
307
+ const writeTexture = queue.writeTexture.bind(queue);
308
+ const copyExternal = queue.copyExternalImageToTexture?.bind(queue);
309
+ queue.writeBuffer = (buffer, offset, data, dataOffset, size) => {
310
+ writeBuffer(buffer, offset, data, dataOffset, size);
311
+ ledger.upload(buffer, writtenBytes(data, dataOffset, size));
312
+ };
313
+ queue.writeTexture = (destination, data, layout, size) => {
314
+ writeTexture(destination, data, layout, size);
315
+ ledger.upload(destination.texture, textureWriteBytes(destination.texture, data, layout, size));
316
+ };
317
+ if (copyExternal) {
318
+ queue.copyExternalImageToTexture = (source, destination, size) => {
319
+ copyExternal(source, destination, size);
320
+ const [w, h, d] = extent(size);
321
+ ledger.upload(destination.texture, w * h * d * 4);
322
+ };
323
+ }
324
+ }
113
325
  handleLoss(info) {
326
+ this.status = "lost";
114
327
  for (const listener of this.lostListeners) listener(info);
115
328
  }
116
329
  }
117
- function canvasConfig(device, format) {
330
+ function writtenBytes(data, dataOffset, size) {
331
+ if (ArrayBuffer.isView(data)) {
332
+ const unit = data.BYTES_PER_ELEMENT ?? 1;
333
+ return (size ?? data.byteLength / unit - (dataOffset ?? 0)) * unit;
334
+ }
335
+ return size ?? data.byteLength - (dataOffset ?? 0);
336
+ }
337
+ function extent(size) {
338
+ if (Symbol.iterator in size) {
339
+ const a = [...size];
340
+ return [a[0] ?? 1, a[1] ?? 1, a[2] ?? 1];
341
+ }
342
+ const d = size;
343
+ return [d.width, d.height ?? 1, d.depthOrArrayLayers ?? 1];
344
+ }
345
+ function textureWriteBytes(texture, data, layout, size) {
346
+ const [w, h, d] = extent(size);
347
+ const bytes = ArrayBuffer.isView(data) ? data.byteLength : data.byteLength;
348
+ const total = bytes - (layout.offset ?? 0);
349
+ const [bw, bh, blockBytes] = blockOf(texture.format);
350
+ return Math.min(total, Math.ceil(w / bw) * Math.ceil(h / bh) * blockBytes * d);
351
+ }
352
+ async function openDevice(options, keep, keepBackend) {
353
+ const choice = keepBackend ?? options.backend ?? "auto";
354
+ const reasons = [];
355
+ if (choice !== "webgl2") {
356
+ const gpu = options.gpu ?? globalThis.navigator?.gpu;
357
+ if (gpu) {
358
+ try {
359
+ return await openWebGpu(gpu, options, keep, []);
360
+ } catch (err) {
361
+ if (choice === "webgpu") throw err;
362
+ const error = err instanceof ShardError ? err : void 0;
363
+ reasons.push({
364
+ backend: "webgpu",
365
+ code: error?.code.replace(/^gpu\//, "") ?? "device-failed",
366
+ message: error?.message ?? String(err)
367
+ });
368
+ }
369
+ } else if (choice === "webgpu") {
370
+ throw new ShardError("gpu/unsupported", "WebGPU is not available in this environment", {
371
+ hint: "Use a browser or webview with WebGPU, or pass `gpu` (e.g. from the `webgpu` package in Node)."
372
+ });
373
+ } else {
374
+ reasons.push({ backend: "webgpu", code: "no-webgpu", message: "This browser has no WebGPU" });
375
+ }
376
+ }
377
+ return openWebgl2(options, reasons);
378
+ }
379
+ async function openWebGpu(gpu, options, keep, reasons) {
380
+ const powerPreference = options.powerPreference ?? "high-performance";
381
+ const baseline = options.tier === "baseline" || keep === "baseline";
382
+ if (!baseline) {
383
+ const adapter2 = await gpu.requestAdapter({ powerPreference });
384
+ if (adapter2) {
385
+ const device2 = await requestDevice(adapter2, options);
386
+ const hdrSampleCount2 = await probeHdrSampleCount(device2);
387
+ return {
388
+ adapter: adapter2,
389
+ device: device2,
390
+ backend: "webgpu",
391
+ tier: tierOf(device2, false),
392
+ reasons,
393
+ hdrSampleCount: hdrSampleCount2,
394
+ gpu
395
+ };
396
+ }
397
+ if (keep === "full") throw new ShardError("gpu/no-adapter", "No WebGPU adapter was found");
398
+ reasons.push({
399
+ backend: "webgpu",
400
+ code: "no-core-adapter",
401
+ message: "No core WebGPU adapter; asking for a compatibility-mode one"
402
+ });
403
+ }
404
+ const adapter = await gpu.requestAdapter({ featureLevel: "compatibility", powerPreference });
405
+ if (!adapter) throw new ShardError("gpu/no-adapter", "No WebGPU adapter was found");
406
+ const device = await requestDevice(adapter, options);
407
+ const hdrSampleCount = await probeHdrSampleCount(device);
118
408
  return {
409
+ adapter,
119
410
  device,
120
- format,
121
- alphaMode: "opaque",
122
- // COPY_SRC lets the renderer capture screenshots straight from the swapchain.
123
- usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC
411
+ backend: "webgpu",
412
+ tier: tierOf(device, baseline),
413
+ reasons,
414
+ hdrSampleCount,
415
+ gpu
124
416
  };
125
417
  }
126
- async function requestDevice(options) {
127
- const gpu = options.gpu ?? globalThis.navigator?.gpu;
128
- if (!gpu) {
129
- throw new ShardError("gpu/unsupported", "WebGPU is not available in this environment", {
130
- hint: "Use a browser or webview with WebGPU, or pass `gpu` (e.g. from the `webgpu` package in Node)."
418
+ async function openWebgl2(options, reasons) {
419
+ const webgl2 = await import("@aethervtt/shard-gpu-webgl2");
420
+ const settings = options.webgl2 ?? {};
421
+ const gpu = webgl2.createWebgl2Gpu({
422
+ canvas: options.canvas,
423
+ context: settings.context,
424
+ profile: settings.profile,
425
+ checkErrors: settings.checkErrors,
426
+ shaders: settings.shaders,
427
+ persist: settings.persist
428
+ });
429
+ const powerPreference = options.powerPreference ?? "high-performance";
430
+ const adapter = await gpu.requestAdapter({ powerPreference });
431
+ if (!adapter) {
432
+ const code = gpu.unavailable ?? "no-webgl2";
433
+ reasons.push({ backend: "webgl2", code, message: WEBGL2_MESSAGES[code] ?? code });
434
+ throw new ShardError("gpu/unsupported", "Neither WebGPU nor WebGL2 can run Shard here", {
435
+ hint: reasons.map((r) => `${r.backend}: ${r.message}`).join("; "),
436
+ // Each backend's reason, its backend as the path (probeGraphics reads them back).
437
+ details: reasons.map((r) => new ShardError(`gpu/${r.code}`, r.message, { path: r.backend }))
131
438
  });
132
439
  }
133
- const adapter = await gpu.requestAdapter({
134
- powerPreference: options.powerPreference ?? "high-performance"
135
- });
136
- if (!adapter) throw new ShardError("gpu/no-adapter", "No WebGPU adapter was found");
440
+ const device = await requestDevice(adapter, options);
441
+ const hdrSampleCount = await probeHdrSampleCount(device);
442
+ return {
443
+ adapter,
444
+ device,
445
+ backend: "webgl2",
446
+ tier: "baseline",
447
+ reasons,
448
+ hdrSampleCount,
449
+ gpu,
450
+ canvasContext: webgl2.canvasContext
451
+ };
452
+ }
453
+ const WEBGL2_MESSAGES = {
454
+ "no-webgl2": "This browser has no WebGL2",
455
+ "context-lost": "The WebGL2 context is lost",
456
+ "no-float-render-targets": "WebGL2 here has no float render targets (EXT_color_buffer_float)"
457
+ };
458
+ function tierOf(device, baseline) {
459
+ return baseline || !isCoreDevice(device) ? "baseline" : "full";
460
+ }
461
+ async function requestDevice(adapter, options) {
137
462
  const missing = (options.requiredFeatures ?? []).filter((f) => !adapter.features.has(f));
138
463
  if (missing.length > 0) {
139
464
  throw new ShardError(
@@ -149,25 +474,19 @@ async function requestDevice(options) {
149
474
  ...options.compressedTextures === false ? [] : COMPRESSED_TEXTURE_FEATURES
150
475
  ].filter((f, i, all) => all.indexOf(f) === i && adapter.features.has(f))
151
476
  ];
152
- const device = await adapter.requestDevice({ label: "shard", requiredFeatures });
153
- return { adapter, device };
477
+ return adapter.requestDevice({ label: "shard", requiredFeatures });
154
478
  }
155
479
  async function createGpuContext(options = {}) {
156
- const { adapter, device } = await requestDevice(options);
157
- const gpu = options.gpu ?? globalThis.navigator.gpu;
158
- const format = gpu.getPreferredCanvasFormat();
159
- let context;
160
- if (options.canvas) {
161
- const ctx = options.canvas.getContext("webgpu");
162
- if (!ctx) throw new ShardError("gpu/no-context", "Could not get a WebGPU canvas context");
163
- ctx.configure(canvasConfig(device, format));
164
- context = ctx;
165
- }
166
- const result = new GpuContext(options, adapter, device, format, context);
167
- result.resize();
480
+ const start = performance.now();
481
+ const opened = await openDevice(options);
482
+ const format = opened.gpu.getPreferredCanvasFormat();
483
+ const result = new GpuContext(options, opened.adapter, opened.device, format, opened);
484
+ result.deviceMs = performance.now() - start;
485
+ if (options.canvas) result.addSurface(options.canvas, { alpha: options.alpha });
168
486
  return result;
169
487
  }
170
488
  export {
171
489
  GpuContext,
172
- createGpuContext
490
+ createGpuContext,
491
+ openDevice
173
492
  };
package/dist/errors.js CHANGED
@@ -1,7 +1,14 @@
1
1
  import { ShardError } from "@aethervtt/shard-core";
2
2
  function toShardError(error, label) {
3
3
  const what = label ? `"${label}"` : "an unlabeled GPU object";
4
- const isOom = typeof GPUOutOfMemoryError !== "undefined" && error instanceof GPUOutOfMemoryError;
4
+ if (error instanceof ShardError) {
5
+ return new ShardError(error.code, `${error.message} (in ${what})`, {
6
+ path: label,
7
+ hint: error.hint,
8
+ cause: error
9
+ });
10
+ }
11
+ const isOom = typeof GPUOutOfMemoryError !== "undefined" && error instanceof GPUOutOfMemoryError || error.name === "GPUOutOfMemoryError";
5
12
  return new ShardError(
6
13
  isOom ? "gpu/out-of-memory" : "gpu/validation",
7
14
  `WebGPU error in ${what}: ${error.message}`,
package/dist/index.d.ts CHANGED
@@ -1,6 +1,10 @@
1
+ export type { BakedTranslations } from '@aethervtt/shard-gpu-webgl2';
1
2
  export { GpuBuffer, type GpuBufferOptions } from './buffer.js';
2
3
  export { LayoutCache, PipelineCache } from './caches.js';
3
- export { type CreateGpuContextOptions, createGpuContext, type DeviceLostInfo, GpuContext, } from './context.js';
4
+ export { type CreateGpuContextOptions, createGpuContext, type DeviceLostInfo, type GpuBackendChoice, GpuContext, type GpuStatus, type ShaderCacheStats, type Webgl2ContextOptions, } from './context.js';
4
5
  export { type GpuErrorListener, toShardError } from './errors.js';
5
6
  export { descriptorKey } from './key.js';
6
- export { type ProbeWebGpuOptions, probeWebGpu, type WebGpuSupport, type WebGpuUnsupportedReason, } from './probe.js';
7
+ export { bufferCategory, DATA_TEXTURE_PREFIX, type GpuMemory, type GpuMemoryCategory, type GpuStats, type GpuUploads, SHARED_OWNER, textureBytes, textureCategory, UPLOAD_CATEGORIES, Upload, type UploadCategory, uploadCategory, } from './ledger.js';
8
+ export { type GraphicsSupport, type ProbeGraphicsOptions, type ProbeWebGpuOptions, probeGraphics, probeWebGpu, type WebGpuSupport, type WebGpuUnsupportedReason, } from './probe.js';
9
+ export { Surface, type SurfaceAlpha, type SurfaceOptions } from './surface.js';
10
+ export { capabilitiesOf, type GpuBackendName, type GpuCapabilities, type GpuTier, type GraphicsReason, isCoreDevice, } from './tier.js';
package/dist/index.js CHANGED
@@ -7,15 +7,43 @@ import {
7
7
  import { toShardError } from "./errors.js";
8
8
  import { descriptorKey } from "./key.js";
9
9
  import {
10
+ bufferCategory,
11
+ DATA_TEXTURE_PREFIX,
12
+ SHARED_OWNER,
13
+ textureBytes,
14
+ textureCategory,
15
+ UPLOAD_CATEGORIES,
16
+ Upload,
17
+ uploadCategory
18
+ } from "./ledger.js";
19
+ import {
20
+ probeGraphics,
10
21
  probeWebGpu
11
22
  } from "./probe.js";
23
+ import { Surface } from "./surface.js";
24
+ import {
25
+ capabilitiesOf,
26
+ isCoreDevice
27
+ } from "./tier.js";
12
28
  export {
29
+ DATA_TEXTURE_PREFIX,
13
30
  GpuBuffer,
14
31
  GpuContext,
15
32
  LayoutCache,
16
33
  PipelineCache,
34
+ SHARED_OWNER,
35
+ Surface,
36
+ UPLOAD_CATEGORIES,
37
+ Upload,
38
+ bufferCategory,
39
+ capabilitiesOf,
17
40
  createGpuContext,
18
41
  descriptorKey,
42
+ isCoreDevice,
43
+ probeGraphics,
19
44
  probeWebGpu,
20
- toShardError
45
+ textureBytes,
46
+ textureCategory,
47
+ toShardError,
48
+ uploadCategory
21
49
  };