@forgeax/engine-rhi-webgpu 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +124 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/__tests__/__mocks__/gpu-device.d.ts +233 -0
  5. package/dist/__tests__/__mocks__/gpu-device.d.ts.map +1 -0
  6. package/dist/__tests__/dawn-real-gpu.dawn.test.d.ts +2 -0
  7. package/dist/__tests__/dawn-real-gpu.dawn.test.d.ts.map +1 -0
  8. package/dist/__tests__/queue-write-range.browser.test.d.ts +2 -0
  9. package/dist/__tests__/queue-write-range.browser.test.d.ts.map +1 -0
  10. package/dist/__tests__/rgba16float-live-probe.browser.test.d.ts +2 -0
  11. package/dist/__tests__/rgba16float-live-probe.browser.test.d.ts.map +1 -0
  12. package/dist/__tests__/rgba16float-live-probe.d.ts +12 -0
  13. package/dist/__tests__/rgba16float-live-probe.d.ts.map +1 -0
  14. package/dist/__tests__/rgba16float-live-probe.dawn.test.d.ts +2 -0
  15. package/dist/__tests__/rgba16float-live-probe.dawn.test.d.ts.map +1 -0
  16. package/dist/__tests__/rhi-webgpu.unit.test.d.ts +2 -0
  17. package/dist/__tests__/rhi-webgpu.unit.test.d.ts.map +1 -0
  18. package/dist/device.d.ts +62 -0
  19. package/dist/device.d.ts.map +1 -0
  20. package/dist/errors.d.ts +47 -0
  21. package/dist/errors.d.ts.map +1 -0
  22. package/dist/index.d.ts +183 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.mjs +1704 -0
  25. package/dist/index.mjs.map +1 -0
  26. package/dist/internal/__tests__/timestamp-query.unit.test.d.ts +2 -0
  27. package/dist/internal/__tests__/timestamp-query.unit.test.d.ts.map +1 -0
  28. package/dist/internal/error-translation.d.ts +16 -0
  29. package/dist/internal/error-translation.d.ts.map +1 -0
  30. package/dist/internal/timestamp-query.d.ts +39 -0
  31. package/dist/internal/timestamp-query.d.ts.map +1 -0
  32. package/package.json +58 -0
  33. package/src/__tests__/__mocks__/gpu-device.ts +564 -0
  34. package/src/__tests__/dawn-real-gpu.dawn.test.ts +1488 -0
  35. package/src/__tests__/queue-write-range.browser.test.ts +60 -0
  36. package/src/__tests__/rgba16float-live-probe.browser.test.ts +13 -0
  37. package/src/__tests__/rgba16float-live-probe.dawn.test.ts +15 -0
  38. package/src/__tests__/rgba16float-live-probe.ts +44 -0
  39. package/src/__tests__/rhi-webgpu.unit.test.ts +2409 -0
  40. package/src/device.ts +1974 -0
  41. package/src/errors.ts +145 -0
  42. package/src/index.ts +599 -0
  43. package/src/internal/__tests__/timestamp-query.unit.test.ts +88 -0
  44. package/src/internal/error-translation.ts +187 -0
  45. package/src/internal/timestamp-query.ts +167 -0
package/dist/index.mjs ADDED
@@ -0,0 +1,1704 @@
1
+ import { err, RhiError, ok, resolveBufferWriteRange } from '@forgeax/engine-rhi';
2
+ export { RhiError as RhiErrorClass, err, ok } from '@forgeax/engine-rhi';
3
+
4
+ // src/index.ts
5
+ function adapterUnavailable() {
6
+ return err(
7
+ new RhiError({
8
+ code: "adapter-unavailable",
9
+ expected: "an available WebGPU adapter",
10
+ hint: "check whether the browser supports WebGPU or enable the relevant flag"
11
+ })
12
+ );
13
+ }
14
+ function featureNotEnabled(featureName) {
15
+ const fname = "compute";
16
+ return err(
17
+ new RhiError({
18
+ code: "feature-not-enabled",
19
+ expected: `feature ${fname} to be enabled`,
20
+ hint: `verify device.features.${fname} before calling this entry point`
21
+ })
22
+ );
23
+ }
24
+ function limitExceeded(limitName) {
25
+ const lname = "maxBindGroups";
26
+ return err(
27
+ new RhiError({
28
+ code: "limit-exceeded",
29
+ expected: `${lname} to be within bounds`,
30
+ hint: `verify device.limits.${lname}`
31
+ })
32
+ );
33
+ }
34
+ function shaderCompileFailed(compilerMessages) {
35
+ const detail = { compilerMessages };
36
+ return err(
37
+ new RhiError({
38
+ code: "shader-compile-failed",
39
+ expected: "valid WGSL source",
40
+ hint: "inspect RhiError.detail.compilerMessages (each entry: { message, type, lineNum, linePos, offset, length } per WebGPU GPUCompilationMessage shape)",
41
+ detail
42
+ })
43
+ );
44
+ }
45
+ function commandEncoderFinished() {
46
+ return err(
47
+ new RhiError({
48
+ code: "command-encoder-finished",
49
+ expected: "command encoder must not be finished before recording new commands",
50
+ hint: "create a new command encoder via device.createCommandEncoder() for each frame; do not reuse a finished encoder"
51
+ })
52
+ );
53
+ }
54
+ function renderPassNotEnded() {
55
+ return err(
56
+ new RhiError({
57
+ code: "render-pass-not-ended",
58
+ expected: "previous render pass must be ended before beginning a new pass or finishing the encoder",
59
+ hint: "call pass.end() before beginRenderPass() or encoder.finish()"
60
+ })
61
+ );
62
+ }
63
+ function queueSubmitFailed(detailMessage) {
64
+ const baseHint = "check if any referenced buffer / pipeline / texture has been destroyed before submit";
65
+ const hint = detailMessage !== void 0 && detailMessage.length > 0 ? `${baseHint}; underlying GPU error: ${detailMessage}` : baseHint;
66
+ return err(
67
+ new RhiError({
68
+ code: "queue-submit-failed",
69
+ expected: "command buffer references must be valid at submit time (not destroyed; not from a different device)",
70
+ hint
71
+ })
72
+ );
73
+ }
74
+ function queueWriteBufferOutOfBounds(args) {
75
+ return err(
76
+ new RhiError({
77
+ code: "queue-write-buffer-out-of-bounds",
78
+ expected: "writeBuffer offset + data.byteLength must be <= buffer.size; offset must be 4-byte aligned",
79
+ hint: `verify offset alignment and bounds: offset (got ${args.offset}) + data.byteLength (got ${args.byteLength}) must be <= buffer.size (got ${args.bufferSize})`
80
+ })
81
+ );
82
+ }
83
+ function timestampQueryHealth(input) {
84
+ if (!input.capability) {
85
+ return {
86
+ status: "fault",
87
+ code: "timestamp-query-unsupported",
88
+ expected: "device.caps.timestampQuery is true before timestamp queries are submitted",
89
+ hint: "use the CPU timing path or recreate the renderer with timestamp-query capability",
90
+ deviceGeneration: input.deviceGeneration,
91
+ expectedGeneration: input.expectedGeneration
92
+ };
93
+ }
94
+ if (input.expectedGeneration !== void 0 && input.deviceGeneration !== input.expectedGeneration) {
95
+ return {
96
+ status: "fault",
97
+ code: "timestamp-query-generation-mismatch",
98
+ expected: "timestamp encoder generation matches the live device generation",
99
+ hint: "discard the stale encoder and rebuild timestamp resources during device recovery",
100
+ deviceGeneration: input.deviceGeneration,
101
+ expectedGeneration: input.expectedGeneration
102
+ };
103
+ }
104
+ if (!input.encoderWriteTimestamp) {
105
+ return {
106
+ status: "fault",
107
+ code: "timestamp-query-encoder-missing",
108
+ expected: "underlying GPUCommandEncoder.writeTimestamp to be callable",
109
+ hint: "timestamp-query is advertised but the raw encoder has no writeTimestamp method",
110
+ deviceGeneration: input.deviceGeneration,
111
+ expectedGeneration: input.expectedGeneration
112
+ };
113
+ }
114
+ return {
115
+ status: "ready",
116
+ code: void 0,
117
+ expected: "timestamp-query capability and encoder are available for this device generation",
118
+ hint: "continue with the timestamp query operation",
119
+ deviceGeneration: input.deviceGeneration,
120
+ expectedGeneration: input.expectedGeneration
121
+ };
122
+ }
123
+ function resolveTimestampQueries(args) {
124
+ if (args.deviceGeneration !== void 0 && args.expectedGeneration !== void 0 && args.deviceGeneration !== args.expectedGeneration) {
125
+ const health = timestampQueryHealth({
126
+ capability: true,
127
+ encoderWriteTimestamp: true,
128
+ deviceGeneration: args.deviceGeneration,
129
+ expectedGeneration: args.expectedGeneration
130
+ });
131
+ return err(
132
+ new RhiError({
133
+ code: "webgpu-runtime-error",
134
+ expected: health.expected,
135
+ hint: `${health.code}: ${health.hint}`
136
+ })
137
+ );
138
+ }
139
+ try {
140
+ args.rawEncoder.resolveQuerySet(
141
+ args.rawQuerySet,
142
+ args.firstQuery,
143
+ args.queryCount,
144
+ args.rawDestination,
145
+ args.destinationOffset
146
+ );
147
+ return ok(void 0);
148
+ } catch (error) {
149
+ const message = error instanceof Error ? error.message : String(error);
150
+ return err(
151
+ new RhiError({
152
+ code: "webgpu-runtime-error",
153
+ expected: "underlying GPUCommandEncoder.resolveQuerySet to succeed",
154
+ hint: `resolveQuerySet raised: ${message}`
155
+ })
156
+ );
157
+ }
158
+ }
159
+ function writeTimestamp(args) {
160
+ const encoderWithTimestamp = args.rawEncoder;
161
+ const writeTimestamp2 = encoderWithTimestamp.writeTimestamp;
162
+ const health = timestampQueryHealth({
163
+ capability: true,
164
+ encoderWriteTimestamp: typeof writeTimestamp2 === "function",
165
+ deviceGeneration: args.deviceGeneration ?? 0,
166
+ expectedGeneration: args.expectedGeneration
167
+ });
168
+ if (health.status === "fault") {
169
+ throw new RhiError({
170
+ code: "webgpu-runtime-error",
171
+ expected: health.expected,
172
+ hint: health.code === "timestamp-query-encoder-missing" ? health.hint : `${health.code}: ${health.hint}`
173
+ });
174
+ }
175
+ if (typeof writeTimestamp2 !== "function") {
176
+ throw new RhiError({
177
+ code: "webgpu-runtime-error",
178
+ expected: "underlying GPUCommandEncoder.writeTimestamp to be callable",
179
+ hint: "timestamp-query is advertised but the raw encoder has no writeTimestamp method"
180
+ });
181
+ }
182
+ try {
183
+ writeTimestamp2(args.rawQuerySet, args.queryIndex);
184
+ } catch (error) {
185
+ const message = error instanceof Error ? error.message : String(error);
186
+ throw new RhiError({
187
+ code: "webgpu-runtime-error",
188
+ expected: "underlying GPUCommandEncoder.writeTimestamp to succeed",
189
+ hint: `writeTimestamp raised: ${message}`
190
+ });
191
+ }
192
+ }
193
+
194
+ // src/device.ts
195
+ function mirror(src, keys) {
196
+ const out = {};
197
+ for (const k of keys) {
198
+ if (k in src) {
199
+ out[k] = src[k];
200
+ }
201
+ }
202
+ return out;
203
+ }
204
+ var BUFFER_KEYS = ["label", "size", "usage", "mappedAtCreation"];
205
+ var TEXTURE_KEYS = [
206
+ "label",
207
+ "size",
208
+ "mipLevelCount",
209
+ "sampleCount",
210
+ "dimension",
211
+ "format",
212
+ "usage",
213
+ "viewFormats",
214
+ "textureBindingViewDimension"
215
+ ];
216
+ var SAMPLER_KEYS = [
217
+ "label",
218
+ "addressModeU",
219
+ "addressModeV",
220
+ "addressModeW",
221
+ "magFilter",
222
+ "minFilter",
223
+ "mipmapFilter",
224
+ "lodMinClamp",
225
+ "lodMaxClamp",
226
+ "compare",
227
+ "maxAnisotropy"
228
+ ];
229
+ var BGL_KEYS = ["label", "entries"];
230
+ var PL_KEYS = ["label", "bindGroupLayouts"];
231
+ var RPP_KEYS = [
232
+ "label",
233
+ "layout",
234
+ "vertex",
235
+ "primitive",
236
+ "depthStencil",
237
+ "multisample",
238
+ "fragment"
239
+ ];
240
+ var ENC_KEYS = ["label"];
241
+ var TEXTURE_VIEW_KEYS = [
242
+ "label",
243
+ "format",
244
+ "dimension",
245
+ "usage",
246
+ "aspect",
247
+ "baseMipLevel",
248
+ "mipLevelCount",
249
+ "baseArrayLayer",
250
+ "arrayLayerCount"
251
+ ];
252
+ var CP_KEYS = ["label", "layout", "compute"];
253
+ var QS_KEYS = ["label", "type", "count"];
254
+ var QUERY_SET_COUNT_LIMIT = 4096;
255
+ var RAW_DEVICE_MAP = /* @__PURE__ */ new WeakMap();
256
+ var BUFFER_RAW_MAP = /* @__PURE__ */ new WeakMap();
257
+ var ENCODER_STATE = /* @__PURE__ */ new WeakMap();
258
+ var PASS_STATE = /* @__PURE__ */ new WeakMap();
259
+ var COMMAND_BUFFER_RAW_MAP = /* @__PURE__ */ new WeakMap();
260
+ var TEXTURE_META_MAP = /* @__PURE__ */ new WeakMap();
261
+ var QUERY_SET_RAW_MAP = /* @__PURE__ */ new WeakMap();
262
+ var QUERY_SET_DESTROYED_MAP = /* @__PURE__ */ new WeakMap();
263
+ var BUFFER_META_MAP = /* @__PURE__ */ new WeakMap();
264
+ var BUFFER_USAGE_QUERY_RESOLVE = 512;
265
+ var QUERY_RESOLVE_ALIGNMENT = 256;
266
+ function _internal_getRawDevice(device) {
267
+ return RAW_DEVICE_MAP.get(device);
268
+ }
269
+ function probeRgba16floatRenderable(device) {
270
+ let tex;
271
+ try {
272
+ tex = device.createTexture({
273
+ label: "forgeax-caps-probe-rgba16float-renderable",
274
+ format: "rgba16float",
275
+ usage: 16,
276
+ // GPUTextureUsage.RENDER_ATTACHMENT
277
+ size: [1, 1, 1]
278
+ });
279
+ return true;
280
+ } catch {
281
+ return false;
282
+ } finally {
283
+ tex?.destroy?.();
284
+ }
285
+ }
286
+ function probeRg11b10ufloatRenderable(device, features) {
287
+ if (!features.has("rg11b10ufloat-renderable")) return false;
288
+ let tex;
289
+ try {
290
+ tex = device.createTexture({
291
+ label: "forgeax-caps-probe-rg11b10ufloat-renderable",
292
+ format: "rg11b10ufloat",
293
+ usage: 16,
294
+ // GPUTextureUsage.RENDER_ATTACHMENT
295
+ size: [1, 1, 1]
296
+ });
297
+ return true;
298
+ } catch {
299
+ return false;
300
+ } finally {
301
+ tex?.destroy?.();
302
+ }
303
+ }
304
+ function probeFloat32Filterable(device, features) {
305
+ if (!features.has("float32-filterable")) return false;
306
+ try {
307
+ device.createBindGroupLayout({
308
+ entries: [
309
+ { binding: 0, visibility: 2, sampler: { type: "filtering" } },
310
+ // GPUShaderStage.FRAGMENT = 2
311
+ { binding: 1, visibility: 2, texture: { sampleType: "float" } }
312
+ ]
313
+ });
314
+ device.createSampler({ minFilter: "linear", magFilter: "linear" });
315
+ return true;
316
+ } catch {
317
+ return false;
318
+ }
319
+ }
320
+ function deriveCaps(rawDevice, features, limits) {
321
+ const has = (name) => features.has(name);
322
+ const textureCompressionBc = has("texture-compression-bc");
323
+ const textureCompressionEtc2 = has("texture-compression-etc2");
324
+ const textureCompressionAstc = has("texture-compression-astc");
325
+ const hdrCaps = {
326
+ rgba16floatRenderable: probeRgba16floatRenderable(rawDevice),
327
+ rg11b10ufloatRenderable: probeRg11b10ufloatRenderable(rawDevice, features),
328
+ float32Filterable: probeFloat32Filterable(rawDevice, features)
329
+ };
330
+ return {
331
+ backendKind: "webgpu",
332
+ compute: true,
333
+ // WebGPU spec mandates compute-pipeline support.
334
+ timestampQuery: has("timestamp-query"),
335
+ timestampPeriodNanoseconds: has("timestamp-query") ? 1 : null,
336
+ indirectDrawing: true,
337
+ // WebGPU spec mandates drawIndirect / drawIndexedIndirect.
338
+ textureCompressionBc,
339
+ textureCompressionEtc2,
340
+ textureCompressionAstc,
341
+ multiDrawIndirect: false,
342
+ // wgpu native extension; unavailable on WebGPU browser path.
343
+ pushConstants: false,
344
+ // wgpu native extension; unavailable on WebGPU browser path.
345
+ textureBindingArray: false,
346
+ // wgpu native extension; unavailable on WebGPU browser path.
347
+ // 4 new fields (D-P3 / R-03 §3.1):
348
+ samplerAliasing: true,
349
+ // spec mandatory on browser backends.
350
+ firstInstanceIndirect: has("indirect-first-instance"),
351
+ storageBuffer: (limits.maxStorageBuffersPerShaderStage ?? 0) > 0,
352
+ storageTexture: (limits.maxStorageTexturesPerShaderStage ?? 0) > 0,
353
+ // HDR / filterable caps (feat-20260608 M1):
354
+ ...hdrCaps,
355
+ maxColorAttachments: limits.maxColorAttachments ?? 4
356
+ };
357
+ }
358
+ function makeRenderPassEncoder(rawPass, encoder, occlusionQuerySet) {
359
+ const pass = {
360
+ setPipeline(pipeline) {
361
+ rawPass.setPipeline(pipeline);
362
+ },
363
+ setVertexBuffer(slot, buffer, offset, size) {
364
+ const rawBuf = BUFFER_RAW_MAP.get(buffer) ?? buffer;
365
+ rawPass.setVertexBuffer(slot, rawBuf, offset, size);
366
+ },
367
+ setIndexBuffer(buffer, format, offset, size) {
368
+ const rawBuf = BUFFER_RAW_MAP.get(buffer) ?? buffer;
369
+ rawPass.setIndexBuffer(rawBuf, format, offset, size);
370
+ },
371
+ setBindGroup(index, bindGroup, arg3, arg4, arg5) {
372
+ if (arg3 instanceof Uint32Array) {
373
+ rawPass.setBindGroup(
374
+ index,
375
+ bindGroup,
376
+ arg3,
377
+ arg4 ?? 0,
378
+ arg5 ?? arg3.length
379
+ );
380
+ } else if (arg3 === void 0) {
381
+ rawPass.setBindGroup(index, bindGroup);
382
+ } else {
383
+ rawPass.setBindGroup(index, bindGroup, arg3);
384
+ }
385
+ },
386
+ draw(vertexCount, instanceCount, firstVertex, firstInstance) {
387
+ rawPass.draw(vertexCount, instanceCount, firstVertex, firstInstance);
388
+ },
389
+ drawIndexed(indexCount, instanceCount, firstIndex, baseVertex, firstInstance) {
390
+ rawPass.drawIndexed(indexCount, instanceCount, firstIndex, baseVertex, firstInstance);
391
+ },
392
+ setViewport(x, y, w, h, minDepth, maxDepth) {
393
+ rawPass.setViewport(x, y, w, h, minDepth, maxDepth);
394
+ },
395
+ setScissorRect(x, y, w, h) {
396
+ const setScissorRect = rawPass.setScissorRect;
397
+ setScissorRect?.call(rawPass, x, y, w, h);
398
+ },
399
+ setBlendConstant(color) {
400
+ rawPass.setBlendConstant(color);
401
+ },
402
+ setStencilReference(reference) {
403
+ rawPass.setStencilReference(reference);
404
+ },
405
+ drawIndirect(indirectBuffer, indirectOffset) {
406
+ const rawBuf = BUFFER_RAW_MAP.get(indirectBuffer) ?? indirectBuffer;
407
+ rawPass.drawIndirect(rawBuf, indirectOffset);
408
+ },
409
+ drawIndexedIndirect(indirectBuffer, indirectOffset) {
410
+ const rawBuf = BUFFER_RAW_MAP.get(indirectBuffer) ?? indirectBuffer;
411
+ rawPass.drawIndexedIndirect(rawBuf, indirectOffset);
412
+ },
413
+ pushDebugGroup(groupLabel) {
414
+ rawPass.pushDebugGroup(groupLabel);
415
+ },
416
+ popDebugGroup() {
417
+ rawPass.popDebugGroup();
418
+ },
419
+ insertDebugMarker(markerLabel) {
420
+ rawPass.insertDebugMarker(markerLabel);
421
+ },
422
+ executeBundles(_bundles) {
423
+ return err(
424
+ new RhiError({
425
+ code: "rhi-not-available",
426
+ expected: "render bundle creation requires future closed loop",
427
+ hint: "see feat-future-rhi-render-bundle"
428
+ })
429
+ );
430
+ },
431
+ beginOcclusionQuery(queryIndex) {
432
+ const state = PASS_STATE.get(pass);
433
+ if (state === void 0) {
434
+ return err(
435
+ new RhiError({
436
+ code: "webgpu-runtime-error",
437
+ expected: "render pass state must exist",
438
+ hint: "beginOcclusionQuery called on an untracked render pass"
439
+ })
440
+ );
441
+ }
442
+ if (state.occlusionQuerySet === null) {
443
+ return err(
444
+ new RhiError({
445
+ code: "webgpu-runtime-error",
446
+ expected: "GPURenderPassDescriptor.occlusionQuerySet must be set",
447
+ hint: "pass occlusionQuerySet in RenderPassDescriptor before beginOcclusionQuery"
448
+ })
449
+ );
450
+ }
451
+ if (state.occlusionQueryActive) {
452
+ return err(
453
+ new RhiError({
454
+ code: "webgpu-runtime-error",
455
+ expected: "[[occlusion_query_active]] == false; pair beginOcclusionQuery / endOcclusionQuery",
456
+ hint: "call endOcclusionQuery() before beginOcclusionQuery() again; occlusion queries cannot nest (spec \xA7render-passes)"
457
+ })
458
+ );
459
+ }
460
+ const rawQs = QUERY_SET_RAW_MAP.get(state.occlusionQuerySet);
461
+ const qsCount = rawQs !== void 0 && typeof rawQs.count === "number" ? rawQs.count : Number.MAX_SAFE_INTEGER;
462
+ if (queryIndex < 0 || queryIndex >= qsCount) {
463
+ return err(
464
+ new RhiError({
465
+ code: "webgpu-runtime-error",
466
+ expected: "queryIndex < querySet.count",
467
+ hint: `got queryIndex=${queryIndex}; querySet.count=${qsCount}`
468
+ })
469
+ );
470
+ }
471
+ if (state.occlusionQueryWritten.has(queryIndex)) {
472
+ return err(
473
+ new RhiError({
474
+ code: "webgpu-runtime-error",
475
+ expected: "queryIndex must not have been written in this pass",
476
+ hint: `queryIndex=${queryIndex} was already written; cross-pass reuse on the same querySet is legal but in-pass reuse is not (spec \xA7queries)`
477
+ })
478
+ );
479
+ }
480
+ try {
481
+ rawPass.beginOcclusionQuery(queryIndex);
482
+ state.occlusionQueryActive = true;
483
+ state.occlusionQueryWritten.add(queryIndex);
484
+ return ok(void 0);
485
+ } catch (e) {
486
+ const message = e instanceof Error ? e.message : String(e);
487
+ return err(
488
+ new RhiError({
489
+ code: "webgpu-runtime-error",
490
+ expected: "underlying GPURenderPassEncoder.beginOcclusionQuery to succeed",
491
+ hint: `beginOcclusionQuery raised: ${message}`
492
+ })
493
+ );
494
+ }
495
+ },
496
+ endOcclusionQuery() {
497
+ const state = PASS_STATE.get(pass);
498
+ if (state === void 0) {
499
+ return err(
500
+ new RhiError({
501
+ code: "webgpu-runtime-error",
502
+ expected: "render pass state must exist",
503
+ hint: "endOcclusionQuery called on an untracked render pass"
504
+ })
505
+ );
506
+ }
507
+ if (!state.occlusionQueryActive) {
508
+ return renderPassNotEnded();
509
+ }
510
+ try {
511
+ rawPass.endOcclusionQuery();
512
+ state.occlusionQueryActive = false;
513
+ return ok(void 0);
514
+ } catch (e) {
515
+ const message = e instanceof Error ? e.message : String(e);
516
+ return err(
517
+ new RhiError({
518
+ code: "webgpu-runtime-error",
519
+ expected: "underlying GPURenderPassEncoder.endOcclusionQuery to succeed",
520
+ hint: `endOcclusionQuery raised: ${message}`
521
+ })
522
+ );
523
+ }
524
+ },
525
+ end() {
526
+ const state = PASS_STATE.get(pass);
527
+ if (state !== void 0) {
528
+ state.ended = true;
529
+ }
530
+ rawPass.end();
531
+ const encState = ENCODER_STATE.get(encoder);
532
+ if (encState !== void 0 && encState.activePass === pass) {
533
+ encState.activePass = null;
534
+ }
535
+ }
536
+ };
537
+ PASS_STATE.set(pass, {
538
+ raw: rawPass,
539
+ ended: false,
540
+ encoder,
541
+ occlusionQuerySet,
542
+ occlusionQueryActive: false,
543
+ occlusionQueryWritten: /* @__PURE__ */ new Set()
544
+ });
545
+ return pass;
546
+ }
547
+ var ENCODER_FINISHED_ERROR_ARGS = {
548
+ code: "command-encoder-finished",
549
+ expected: "command encoder must not be finished before recording new commands",
550
+ hint: "create a new command encoder via device.createCommandEncoder() for each frame; do not reuse a finished encoder"
551
+ };
552
+ function throwIfFinished(state) {
553
+ if (state?.finished) {
554
+ throw new RhiError(ENCODER_FINISHED_ERROR_ARGS);
555
+ }
556
+ }
557
+ function makeCommandEncoder(rawEncoder, caps, fireFeatureNotEnabled) {
558
+ function mirrorComputePassDescriptor(desc) {
559
+ if (desc === void 0) return void 0;
560
+ const out = mirror(desc, ["label"]);
561
+ if ("timestampWrites" in desc) {
562
+ const writes = desc.timestampWrites;
563
+ out.timestampWrites = writes === void 0 ? void 0 : {
564
+ querySet: QUERY_SET_RAW_MAP.get(writes.querySet) ?? writes.querySet,
565
+ ...writes.beginningOfPassWriteIndex === void 0 ? {} : { beginningOfPassWriteIndex: writes.beginningOfPassWriteIndex },
566
+ ...writes.endOfPassWriteIndex === void 0 ? {} : { endOfPassWriteIndex: writes.endOfPassWriteIndex }
567
+ };
568
+ }
569
+ return out;
570
+ }
571
+ const enc = {
572
+ beginRenderPass(desc) {
573
+ const state = ENCODER_STATE.get(enc);
574
+ throwIfFinished(state);
575
+ const rawPass = rawEncoder.beginRenderPass(desc);
576
+ const occQs = desc.occlusionQuerySet;
577
+ const occlusionQuerySet = occQs === void 0 || occQs === null ? null : occQs;
578
+ const pass = makeRenderPassEncoder(rawPass, enc, occlusionQuerySet);
579
+ if (state !== void 0) state.activePass = pass;
580
+ return pass;
581
+ },
582
+ beginComputePass(desc) {
583
+ const state = ENCODER_STATE.get(enc);
584
+ throwIfFinished(state);
585
+ const rawDescriptor = mirrorComputePassDescriptor(desc);
586
+ const rawPass = rawDescriptor === void 0 ? rawEncoder.beginComputePass() : rawEncoder.beginComputePass(rawDescriptor);
587
+ const pass = {
588
+ setPipeline(pipeline) {
589
+ rawPass.setPipeline(pipeline);
590
+ },
591
+ setBindGroup(index, bindGroup, dynamicOffsets) {
592
+ if (dynamicOffsets === void 0) {
593
+ rawPass.setBindGroup(index, bindGroup);
594
+ } else {
595
+ rawPass.setBindGroup(index, bindGroup, dynamicOffsets);
596
+ }
597
+ },
598
+ dispatchWorkgroups(x, y, z) {
599
+ rawPass.dispatchWorkgroups(x, y, z);
600
+ },
601
+ dispatchWorkgroupsIndirect(indirectBuffer, indirectOffset) {
602
+ const rawBuffer = BUFFER_RAW_MAP.get(indirectBuffer) ?? indirectBuffer;
603
+ rawPass.dispatchWorkgroupsIndirect(rawBuffer, indirectOffset);
604
+ },
605
+ end() {
606
+ rawPass.end();
607
+ }
608
+ };
609
+ return pass;
610
+ },
611
+ copyBufferToBuffer(source, arg2, arg3, arg4, arg5) {
612
+ const state = ENCODER_STATE.get(enc);
613
+ throwIfFinished(state);
614
+ const rawSource = BUFFER_RAW_MAP.get(source) ?? source;
615
+ if (typeof arg2 === "number") {
616
+ const dst = arg3;
617
+ const rawDst = BUFFER_RAW_MAP.get(dst) ?? dst;
618
+ rawEncoder.copyBufferToBuffer(rawSource, arg2, rawDst, arg4 ?? 0, arg5 ?? 0);
619
+ } else {
620
+ const dst = arg2;
621
+ const rawDst = BUFFER_RAW_MAP.get(dst) ?? dst;
622
+ rawEncoder.copyBufferToBuffer(rawSource, rawDst, arg3);
623
+ }
624
+ },
625
+ copyBufferToTexture(source, destination, copySize) {
626
+ const state = ENCODER_STATE.get(enc);
627
+ throwIfFinished(state);
628
+ const rawSrc = {
629
+ ...source,
630
+ buffer: BUFFER_RAW_MAP.get(source.buffer) ?? source.buffer
631
+ };
632
+ rawEncoder.copyBufferToTexture(rawSrc, destination, copySize);
633
+ },
634
+ copyTextureToBuffer(source, destination, copySize) {
635
+ const state = ENCODER_STATE.get(enc);
636
+ throwIfFinished(state);
637
+ const rawDst = {
638
+ ...destination,
639
+ buffer: BUFFER_RAW_MAP.get(destination.buffer) ?? destination.buffer
640
+ };
641
+ rawEncoder.copyTextureToBuffer(source, rawDst, copySize);
642
+ },
643
+ copyTextureToTexture(source, destination, copySize) {
644
+ const state = ENCODER_STATE.get(enc);
645
+ throwIfFinished(state);
646
+ rawEncoder.copyTextureToTexture(source, destination, copySize);
647
+ },
648
+ clearBuffer(buffer, offset, size) {
649
+ const state = ENCODER_STATE.get(enc);
650
+ throwIfFinished(state);
651
+ const rawBuf = BUFFER_RAW_MAP.get(buffer) ?? buffer;
652
+ rawEncoder.clearBuffer(rawBuf, offset, size);
653
+ },
654
+ resolveQuerySet(querySet, firstQuery, queryCount, destination, destinationOffset) {
655
+ const state = ENCODER_STATE.get(enc);
656
+ if (state?.finished) {
657
+ return commandEncoderFinished();
658
+ }
659
+ if (destinationOffset % QUERY_RESOLVE_ALIGNMENT !== 0) {
660
+ return err(
661
+ new RhiError({
662
+ code: "webgpu-runtime-error",
663
+ expected: "destinationOffset % 256 == 0 (spec normative)",
664
+ hint: `got destinationOffset=${destinationOffset}; align to a multiple of 256 bytes (kQueryResolveAlignment)`
665
+ })
666
+ );
667
+ }
668
+ const dstMeta = BUFFER_META_MAP.get(destination);
669
+ if (dstMeta !== void 0 && (dstMeta.usage & BUFFER_USAGE_QUERY_RESOLVE) === 0) {
670
+ return err(
671
+ new RhiError({
672
+ code: "webgpu-runtime-error",
673
+ expected: "destination.usage must contain QUERY_RESOLVE",
674
+ hint: `got destination.usage=0x${dstMeta.usage.toString(16)}; create the buffer with GPUBufferUsage.QUERY_RESOLVE (0x200)`
675
+ })
676
+ );
677
+ }
678
+ const rawQs = QUERY_SET_RAW_MAP.get(querySet);
679
+ const qsCount = rawQs !== void 0 && typeof rawQs.count === "number" ? rawQs.count : Number.MAX_SAFE_INTEGER;
680
+ if (firstQuery < 0 || firstQuery + queryCount > qsCount) {
681
+ return err(
682
+ new RhiError({
683
+ code: "webgpu-runtime-error",
684
+ expected: "firstQuery + queryCount <= querySet.count",
685
+ hint: `got firstQuery=${firstQuery}, queryCount=${queryCount}; querySet.count=${qsCount}`
686
+ })
687
+ );
688
+ }
689
+ if (dstMeta !== void 0) {
690
+ const requiredBytes = destinationOffset + 8 * queryCount;
691
+ if (requiredBytes > dstMeta.size) {
692
+ return err(
693
+ new RhiError({
694
+ code: "webgpu-runtime-error",
695
+ expected: "destinationOffset + 8 * queryCount <= destination.size",
696
+ hint: `got destinationOffset=${destinationOffset}, queryCount=${queryCount} (8 * queryCount = ${8 * queryCount}); destination.size=${dstMeta.size}`
697
+ })
698
+ );
699
+ }
700
+ }
701
+ const rawQsHandle = QUERY_SET_RAW_MAP.get(querySet) ?? querySet;
702
+ const rawDstHandle = BUFFER_RAW_MAP.get(destination) ?? destination;
703
+ return resolveTimestampQueries({
704
+ rawEncoder,
705
+ rawQuerySet: rawQsHandle,
706
+ firstQuery,
707
+ queryCount,
708
+ rawDestination: rawDstHandle,
709
+ destinationOffset
710
+ });
711
+ },
712
+ pushDebugGroup(groupLabel) {
713
+ rawEncoder.pushDebugGroup(groupLabel);
714
+ },
715
+ popDebugGroup() {
716
+ rawEncoder.popDebugGroup();
717
+ },
718
+ insertDebugMarker(markerLabel) {
719
+ rawEncoder.insertDebugMarker(markerLabel);
720
+ },
721
+ writeTimestamp(querySet, queryIndex) {
722
+ const state = ENCODER_STATE.get(enc);
723
+ throwIfFinished(state);
724
+ if (caps.timestampQuery !== true) {
725
+ fireFeatureNotEnabled(
726
+ "timestamp-query",
727
+ "check device.caps.timestampQuery before calling writeTimestamp"
728
+ );
729
+ return;
730
+ }
731
+ const rawQs = QUERY_SET_RAW_MAP.get(querySet) ?? querySet;
732
+ writeTimestamp({ rawEncoder, rawQuerySet: rawQs, queryIndex });
733
+ },
734
+ finish() {
735
+ const state = ENCODER_STATE.get(enc);
736
+ if (state === void 0) {
737
+ return commandEncoderFinished();
738
+ }
739
+ if (state.finished) {
740
+ return commandEncoderFinished();
741
+ }
742
+ if (state.activePass !== null) {
743
+ const passState = PASS_STATE.get(state.activePass);
744
+ if (passState !== void 0 && !passState.ended) {
745
+ return renderPassNotEnded();
746
+ }
747
+ }
748
+ const rawCommandBuffer = rawEncoder.finish();
749
+ state.finished = true;
750
+ const cb = rawCommandBuffer;
751
+ COMMAND_BUFFER_RAW_MAP.set(cb, rawCommandBuffer);
752
+ return ok(cb);
753
+ }
754
+ };
755
+ ENCODER_STATE.set(enc, { raw: rawEncoder, finished: false, activePass: null });
756
+ return enc;
757
+ }
758
+ var MAP_MODE_READ = 1;
759
+ var MAP_MODE_WRITE = 2;
760
+ var BUFFER_USAGE_MAP_READ = 1;
761
+ var BUFFER_USAGE_MAP_WRITE = 2;
762
+ function rangeError(args) {
763
+ return err(
764
+ new RhiError({
765
+ code: "webgpu-runtime-error",
766
+ expected: args.expected,
767
+ hint: args.hint
768
+ })
769
+ );
770
+ }
771
+ function makeBufferWrapper(raw, size, usage) {
772
+ const initialState = typeof raw.mapState === "string" ? raw.mapState : "unmapped";
773
+ let mapState = initialState;
774
+ const wrapper = {
775
+ get mapState() {
776
+ const rs = raw.mapState;
777
+ if (typeof rs === "string") {
778
+ mapState = rs;
779
+ return rs;
780
+ }
781
+ return mapState;
782
+ },
783
+ async mapAsync(mode, offset, sizeArg) {
784
+ const cur = wrapper.mapState;
785
+ if (cur !== "unmapped") {
786
+ return rangeError({
787
+ expected: 'buffer.mapState === "unmapped" before mapAsync',
788
+ hint: `got mapState=${cur}; call buffer.unmap() before mapAsync, or wait for the previous mapAsync to settle`
789
+ });
790
+ }
791
+ const off = offset ?? 0;
792
+ const rangeSize = sizeArg === void 0 ? Math.max(0, size - off) : sizeArg;
793
+ if (off % 8 !== 0) {
794
+ return rangeError({
795
+ expected: "mapAsync offset % 8 == 0 (spec normative)",
796
+ hint: `got offset=${off}; align offset to 8 bytes`
797
+ });
798
+ }
799
+ if (rangeSize % 4 !== 0) {
800
+ return rangeError({
801
+ expected: "mapAsync rangeSize % 4 == 0 (spec normative)",
802
+ hint: `got rangeSize=${rangeSize}; align rangeSize to 4 bytes`
803
+ });
804
+ }
805
+ if (off + rangeSize > size) {
806
+ return rangeError({
807
+ expected: "mapAsync offset + rangeSize <= buffer.size",
808
+ hint: `got offset=${off}, rangeSize=${rangeSize}; buffer.size=${size}`
809
+ });
810
+ }
811
+ if ((mode & -4) !== 0) {
812
+ return rangeError({
813
+ expected: "mapAsync mode contains only READ or WRITE bits",
814
+ hint: `got mode=0x${mode.toString(16)}; pass GPUMapMode.READ (0x1) or GPUMapMode.WRITE (0x2)`
815
+ });
816
+ }
817
+ if (mode !== MAP_MODE_READ && mode !== MAP_MODE_WRITE) {
818
+ return rangeError({
819
+ expected: "mapAsync mode is exactly one of READ | WRITE (not both)",
820
+ hint: `got mode=0x${mode.toString(16)}; pass GPUMapMode.READ (0x1) or GPUMapMode.WRITE (0x2), not the OR-combined mask`
821
+ });
822
+ }
823
+ if ((mode & MAP_MODE_READ) !== 0 && (usage & BUFFER_USAGE_MAP_READ) === 0) {
824
+ return rangeError({
825
+ expected: "mapAsync mode READ requires buffer.usage to contain MAP_READ",
826
+ hint: `got mode=READ, buffer.usage=0x${usage.toString(16)}; create buffer with GPUBufferUsage.MAP_READ`
827
+ });
828
+ }
829
+ if ((mode & MAP_MODE_WRITE) !== 0 && (usage & BUFFER_USAGE_MAP_WRITE) === 0) {
830
+ return rangeError({
831
+ expected: "mapAsync mode WRITE requires buffer.usage to contain MAP_WRITE",
832
+ hint: `got mode=WRITE, buffer.usage=0x${usage.toString(16)}; create buffer with GPUBufferUsage.MAP_WRITE`
833
+ });
834
+ }
835
+ mapState = "pending";
836
+ try {
837
+ if (typeof raw.mapAsync === "function") {
838
+ if (sizeArg === void 0 && offset === void 0) {
839
+ await raw.mapAsync(mode);
840
+ } else if (sizeArg === void 0) {
841
+ await raw.mapAsync(mode, off);
842
+ } else {
843
+ await raw.mapAsync(mode, off, sizeArg);
844
+ }
845
+ }
846
+ mapState = "mapped";
847
+ return ok(wrapper);
848
+ } catch (e) {
849
+ mapState = "unmapped";
850
+ const message = e instanceof Error ? e.message : String(e);
851
+ return rangeError({
852
+ expected: "underlying GPUBuffer.mapAsync to succeed",
853
+ hint: `mapAsync raised: ${message}`
854
+ });
855
+ }
856
+ },
857
+ getMappedRange(offset, sizeArg) {
858
+ const cur = wrapper.mapState;
859
+ if (cur !== "mapped") {
860
+ return rangeError({
861
+ expected: 'buffer.mapState === "mapped" before getMappedRange',
862
+ hint: "call buffer.mapAsync(MODE) and await it before getMappedRange"
863
+ });
864
+ }
865
+ try {
866
+ if (typeof raw.getMappedRange !== "function") {
867
+ return rangeError({
868
+ expected: "underlying GPUBuffer.getMappedRange to be available",
869
+ hint: "mock or driver does not expose getMappedRange; use a real GPUBuffer"
870
+ });
871
+ }
872
+ const view = sizeArg === void 0 ? offset === void 0 ? raw.getMappedRange() : raw.getMappedRange(offset) : raw.getMappedRange(offset ?? 0, sizeArg);
873
+ return ok(view);
874
+ } catch (e) {
875
+ const message = e instanceof Error ? e.message : String(e);
876
+ return rangeError({
877
+ expected: "underlying GPUBuffer.getMappedRange to succeed",
878
+ hint: `getMappedRange raised: ${message}`
879
+ });
880
+ }
881
+ },
882
+ unmap() {
883
+ try {
884
+ if (typeof raw.unmap === "function") {
885
+ raw.unmap();
886
+ }
887
+ } catch {
888
+ }
889
+ mapState = "unmapped";
890
+ }
891
+ };
892
+ return wrapper;
893
+ }
894
+ function makeQueue(rawQueue) {
895
+ return {
896
+ writeBuffer(buffer, bufferOffset, data, dataOffset, size) {
897
+ const rawBuffer = BUFFER_RAW_MAP.get(buffer) ?? buffer;
898
+ const range = resolveBufferWriteRange(data, dataOffset, size);
899
+ const bufferSize = typeof rawBuffer.size === "number" ? rawBuffer.size : Number.MAX_SAFE_INTEGER;
900
+ if (bufferOffset % 4 !== 0) {
901
+ return queueWriteBufferOutOfBounds({
902
+ offset: bufferOffset,
903
+ byteLength: range?.byteSize ?? 0,
904
+ bufferSize
905
+ });
906
+ }
907
+ const writeSize = range?.byteSize ?? 0;
908
+ if (range === void 0 || bufferOffset + writeSize > bufferSize) {
909
+ return queueWriteBufferOutOfBounds({
910
+ offset: bufferOffset,
911
+ byteLength: writeSize,
912
+ bufferSize
913
+ });
914
+ }
915
+ try {
916
+ if (size !== void 0) {
917
+ rawQueue.writeBuffer(
918
+ rawBuffer,
919
+ bufferOffset,
920
+ data,
921
+ range.sourceOffset,
922
+ range.sourceSize
923
+ );
924
+ } else if (dataOffset !== void 0) {
925
+ rawQueue.writeBuffer(
926
+ rawBuffer,
927
+ bufferOffset,
928
+ data,
929
+ range.sourceOffset
930
+ );
931
+ } else {
932
+ rawQueue.writeBuffer(rawBuffer, bufferOffset, data);
933
+ }
934
+ return ok(void 0);
935
+ } catch (e) {
936
+ const message = e instanceof Error ? e.message : String(e);
937
+ if (/out of (bounds|range)|exceed/i.test(message)) {
938
+ return queueWriteBufferOutOfBounds({
939
+ offset: bufferOffset,
940
+ byteLength: writeSize,
941
+ bufferSize
942
+ });
943
+ }
944
+ return queueSubmitFailed(message);
945
+ }
946
+ },
947
+ submit(commandBuffers) {
948
+ const rawList = [];
949
+ for (const cb of commandBuffers) {
950
+ const raw = COMMAND_BUFFER_RAW_MAP.get(cb);
951
+ if (raw !== void 0) {
952
+ rawList.push(raw);
953
+ } else {
954
+ rawList.push(cb);
955
+ }
956
+ }
957
+ try {
958
+ rawQueue.submit(rawList);
959
+ return ok(void 0);
960
+ } catch (e) {
961
+ const message = e instanceof Error ? e.message : String(e);
962
+ return queueSubmitFailed(message);
963
+ }
964
+ },
965
+ writeTexture(destination, data, dataLayout, size) {
966
+ try {
967
+ rawQueue.writeTexture(
968
+ destination,
969
+ data,
970
+ dataLayout,
971
+ size
972
+ );
973
+ return ok(void 0);
974
+ } catch (e) {
975
+ const message = e instanceof Error ? e.message : String(e);
976
+ return err(
977
+ new RhiError({
978
+ code: "webgpu-runtime-error",
979
+ expected: "underlying GPUQueue.writeTexture to succeed",
980
+ hint: `writeTexture raised: ${message}`
981
+ })
982
+ );
983
+ }
984
+ },
985
+ copyExternalImageToTexture(source, destination, copySize) {
986
+ try {
987
+ rawQueue.copyExternalImageToTexture(source, destination, copySize);
988
+ return ok(void 0);
989
+ } catch (e) {
990
+ const message = e instanceof Error ? e.message : String(e);
991
+ return err(
992
+ new RhiError({
993
+ code: "webgpu-runtime-error",
994
+ expected: "underlying GPUQueue.copyExternalImageToTexture to succeed",
995
+ hint: `copyExternalImageToTexture raised: ${message}`
996
+ })
997
+ );
998
+ }
999
+ },
1000
+ // forgeax-async-whitelist: dom-native — spec `GPUQueue.onSubmittedWorkDone()` Promise passthrough
1001
+ onSubmittedWorkDone() {
1002
+ return rawQueue.onSubmittedWorkDone();
1003
+ }
1004
+ };
1005
+ }
1006
+ function makeRhiDevice(rawDevice) {
1007
+ const caps = deriveCaps(rawDevice, rawDevice.features, rawDevice.limits);
1008
+ const features = rawDevice.features;
1009
+ const limits = rawDevice.limits;
1010
+ const queue = makeQueue(rawDevice.queue);
1011
+ const device = {
1012
+ caps,
1013
+ features,
1014
+ limits,
1015
+ queue,
1016
+ lost: rawDevice.lost,
1017
+ createBuffer(desc) {
1018
+ const out = rawDevice.createBuffer(
1019
+ mirror(desc, BUFFER_KEYS)
1020
+ );
1021
+ const sizeField = typeof desc.size === "number" ? desc.size : 0;
1022
+ const usageField = desc.usage ?? 0;
1023
+ const handle = makeBufferWrapper(out, sizeField, usageField);
1024
+ BUFFER_RAW_MAP.set(handle, out);
1025
+ BUFFER_META_MAP.set(handle, {
1026
+ size: sizeField,
1027
+ usage: usageField,
1028
+ destroyed: false
1029
+ });
1030
+ return ok(handle);
1031
+ },
1032
+ createTexture(desc) {
1033
+ const out = rawDevice.createTexture(
1034
+ mirror(desc, TEXTURE_KEYS)
1035
+ );
1036
+ const handle = out;
1037
+ const viewFormats = desc.viewFormats === void 0 ? [] : Array.from(desc.viewFormats);
1038
+ TEXTURE_META_MAP.set(handle, {
1039
+ format: desc.format,
1040
+ usage: desc.usage,
1041
+ viewFormats,
1042
+ destroyed: false
1043
+ });
1044
+ return ok(handle);
1045
+ },
1046
+ destroyBuffer(buf) {
1047
+ const meta = BUFFER_META_MAP.get(buf);
1048
+ if (meta?.destroyed) {
1049
+ return err(
1050
+ new RhiError({
1051
+ code: "destroy-after-destroy",
1052
+ expected: "GPU buffer handle has not been destroyed yet",
1053
+ hint: "object already destroyed; track lifecycle in caller or check isDestroyed before re-destroy"
1054
+ })
1055
+ );
1056
+ }
1057
+ const rawBuf = BUFFER_RAW_MAP.get(buf);
1058
+ try {
1059
+ if (rawBuf !== void 0 && typeof rawBuf.destroy === "function") {
1060
+ rawBuf.destroy();
1061
+ }
1062
+ } catch (e) {
1063
+ const message = e instanceof Error ? e.message : String(e);
1064
+ return err(
1065
+ new RhiError({
1066
+ code: "webgpu-runtime-error",
1067
+ expected: "underlying GPUBuffer.destroy() to succeed",
1068
+ hint: `destroy raised: ${message}`
1069
+ })
1070
+ );
1071
+ }
1072
+ if (meta !== void 0) meta.destroyed = true;
1073
+ return ok(void 0);
1074
+ },
1075
+ destroyTexture(tex) {
1076
+ const meta = TEXTURE_META_MAP.get(tex);
1077
+ if (meta?.destroyed) {
1078
+ return err(
1079
+ new RhiError({
1080
+ code: "destroy-after-destroy",
1081
+ expected: "GPU texture handle has not been destroyed yet",
1082
+ hint: "object already destroyed; track lifecycle in caller or check isDestroyed before re-destroy"
1083
+ })
1084
+ );
1085
+ }
1086
+ const rawTex = tex;
1087
+ try {
1088
+ if (typeof rawTex.destroy === "function") {
1089
+ rawTex.destroy();
1090
+ }
1091
+ } catch (e) {
1092
+ const message = e instanceof Error ? e.message : String(e);
1093
+ return err(
1094
+ new RhiError({
1095
+ code: "webgpu-runtime-error",
1096
+ expected: "underlying GPUTexture.destroy() to succeed",
1097
+ hint: `destroy raised: ${message}`
1098
+ })
1099
+ );
1100
+ }
1101
+ if (meta !== void 0) meta.destroyed = true;
1102
+ return ok(void 0);
1103
+ },
1104
+ createTextureView(texture, desc) {
1105
+ const meta = TEXTURE_META_MAP.get(texture);
1106
+ if (meta !== void 0) {
1107
+ const fmt = desc.format;
1108
+ if (fmt !== void 0 && fmt !== meta.format && !meta.viewFormats.includes(fmt)) {
1109
+ return err(
1110
+ new RhiError({
1111
+ code: "webgpu-runtime-error",
1112
+ expected: "createTextureView format must be the source texture format or one of source.viewFormats",
1113
+ hint: `got format='${fmt}'; source.format='${meta.format}'; source.viewFormats=[${meta.viewFormats.join(", ")}]`
1114
+ })
1115
+ );
1116
+ }
1117
+ const reqUsage = desc.usage;
1118
+ if (reqUsage !== void 0 && reqUsage !== 0 && (reqUsage & ~meta.usage) !== 0) {
1119
+ return err(
1120
+ new RhiError({
1121
+ code: "webgpu-runtime-error",
1122
+ expected: "createTextureView usage must be a subset of source.usage",
1123
+ hint: `got usage=0x${reqUsage.toString(16)}; source.usage=0x${meta.usage.toString(16)}`
1124
+ })
1125
+ );
1126
+ }
1127
+ }
1128
+ const rawTexture = texture;
1129
+ try {
1130
+ const rawView = rawTexture.createView(
1131
+ mirror(desc, TEXTURE_VIEW_KEYS)
1132
+ );
1133
+ return ok(rawView);
1134
+ } catch (e) {
1135
+ const message = e instanceof Error ? e.message : String(e);
1136
+ return err(
1137
+ new RhiError({
1138
+ code: "webgpu-runtime-error",
1139
+ expected: "underlying GPUTexture.createView to succeed",
1140
+ hint: `createView raised: ${message}`
1141
+ })
1142
+ );
1143
+ }
1144
+ },
1145
+ createSampler(desc) {
1146
+ if (desc === void 0) {
1147
+ const out2 = rawDevice.createSampler();
1148
+ return ok(out2);
1149
+ }
1150
+ const out = rawDevice.createSampler(
1151
+ mirror(desc, SAMPLER_KEYS)
1152
+ );
1153
+ return ok(out);
1154
+ },
1155
+ createBindGroupLayout(desc) {
1156
+ const out = rawDevice.createBindGroupLayout(
1157
+ mirror(desc, BGL_KEYS)
1158
+ );
1159
+ return ok(out);
1160
+ },
1161
+ createBindGroup(desc) {
1162
+ const mirrored = {
1163
+ layout: desc.layout,
1164
+ entries: []
1165
+ };
1166
+ if ("label" in desc && desc.label !== void 0) mirrored.label = desc.label;
1167
+ for (const entry of desc.entries) {
1168
+ const resource = entry.resource;
1169
+ switch (resource.kind) {
1170
+ case "sampler": {
1171
+ mirrored.entries.push({
1172
+ binding: entry.binding,
1173
+ resource: resource.value
1174
+ });
1175
+ break;
1176
+ }
1177
+ case "buffer": {
1178
+ const { buffer, offset, size } = resource.value;
1179
+ const rawBuf = BUFFER_RAW_MAP.get(buffer) ?? buffer;
1180
+ const bufferBinding = { buffer: rawBuf };
1181
+ if (offset !== void 0) bufferBinding.offset = offset;
1182
+ if (size !== void 0) bufferBinding.size = size;
1183
+ mirrored.entries.push({ binding: entry.binding, resource: bufferBinding });
1184
+ break;
1185
+ }
1186
+ case "textureView": {
1187
+ mirrored.entries.push({
1188
+ binding: entry.binding,
1189
+ resource: resource.value
1190
+ });
1191
+ break;
1192
+ }
1193
+ case "externalTexture": {
1194
+ mirrored.entries.push({
1195
+ binding: entry.binding,
1196
+ resource: resource.value
1197
+ });
1198
+ break;
1199
+ }
1200
+ default: {
1201
+ throw new Error(`rhi-webgpu: unreachable RhiBindingResource kind in createBindGroup`);
1202
+ }
1203
+ }
1204
+ }
1205
+ const out = rawDevice.createBindGroup(mirrored);
1206
+ return ok(out);
1207
+ },
1208
+ createPipelineLayout(desc) {
1209
+ const out = rawDevice.createPipelineLayout(
1210
+ mirror(desc, PL_KEYS)
1211
+ );
1212
+ return ok(out);
1213
+ },
1214
+ createRenderPipeline(desc) {
1215
+ try {
1216
+ const out = rawDevice.createRenderPipeline(
1217
+ mirror(desc, RPP_KEYS)
1218
+ );
1219
+ return ok(out);
1220
+ } catch (e) {
1221
+ const message = e instanceof Error ? e.message : String(e);
1222
+ if (/compile|shader|wgsl/i.test(message)) {
1223
+ return err(
1224
+ new RhiError({
1225
+ code: "shader-compile-failed",
1226
+ expected: "render shader modules + entry points to be valid",
1227
+ hint: `compile error: ${message}`
1228
+ })
1229
+ );
1230
+ }
1231
+ return err(
1232
+ new RhiError({
1233
+ code: "webgpu-runtime-error",
1234
+ expected: "underlying GPUDevice.createRenderPipeline to succeed",
1235
+ hint: `createRenderPipeline raised: ${message}`
1236
+ })
1237
+ );
1238
+ }
1239
+ },
1240
+ createComputePipeline(desc) {
1241
+ if (caps.compute === false) {
1242
+ return err(
1243
+ new RhiError({
1244
+ code: "feature-not-enabled",
1245
+ expected: "caps.compute === true",
1246
+ hint: "check device.caps.compute before calling createComputePipeline"
1247
+ })
1248
+ );
1249
+ }
1250
+ try {
1251
+ const out = rawDevice.createComputePipeline(
1252
+ mirror(desc, CP_KEYS)
1253
+ );
1254
+ return ok(out);
1255
+ } catch (e) {
1256
+ const message = e instanceof Error ? e.message : String(e);
1257
+ if (/compile|shader|wgsl/i.test(message)) {
1258
+ return err(
1259
+ new RhiError({
1260
+ code: "shader-compile-failed",
1261
+ expected: "compute shader module + entry point to be valid",
1262
+ hint: `compile error: ${message}`
1263
+ })
1264
+ );
1265
+ }
1266
+ return err(
1267
+ new RhiError({
1268
+ code: "webgpu-runtime-error",
1269
+ expected: "underlying GPUDevice.createComputePipeline to succeed",
1270
+ hint: `createComputePipeline raised: ${message}`
1271
+ })
1272
+ );
1273
+ }
1274
+ },
1275
+ createQuerySet(desc) {
1276
+ const count = desc.count;
1277
+ if (typeof count === "number" && count > QUERY_SET_COUNT_LIMIT) {
1278
+ return err(
1279
+ new RhiError({
1280
+ code: "limit-exceeded",
1281
+ expected: "count <= 4096 (spec normative)",
1282
+ hint: "create multiple QuerySet instances if more than 4096 queries needed"
1283
+ })
1284
+ );
1285
+ }
1286
+ if (desc.type === "timestamp" && caps.timestampQuery !== true) {
1287
+ return err(
1288
+ new RhiError({
1289
+ code: "feature-not-enabled",
1290
+ expected: "caps.timestampQuery === true (timestamp-query feature)",
1291
+ hint: "request the timestamp-query feature at requestDevice and check device.caps.timestampQuery before creating timestamp QuerySets"
1292
+ })
1293
+ );
1294
+ }
1295
+ try {
1296
+ const out = rawDevice.createQuerySet(
1297
+ mirror(desc, QS_KEYS)
1298
+ );
1299
+ const handle = out;
1300
+ QUERY_SET_RAW_MAP.set(handle, out);
1301
+ QUERY_SET_DESTROYED_MAP.set(handle, { destroyed: false });
1302
+ return ok(handle);
1303
+ } catch (e) {
1304
+ const message = e instanceof Error ? e.message : String(e);
1305
+ return err(
1306
+ new RhiError({
1307
+ code: "webgpu-runtime-error",
1308
+ expected: "underlying GPUDevice.createQuerySet to succeed",
1309
+ hint: `createQuerySet raised: ${message}`
1310
+ })
1311
+ );
1312
+ }
1313
+ },
1314
+ destroyQuerySet(querySet) {
1315
+ const marker = QUERY_SET_DESTROYED_MAP.get(querySet);
1316
+ if (marker?.destroyed) {
1317
+ return err(
1318
+ new RhiError({
1319
+ code: "destroy-after-destroy",
1320
+ expected: "GPU query-set handle has not been destroyed yet",
1321
+ hint: "object already destroyed; release each timestamp QuerySet exactly once"
1322
+ })
1323
+ );
1324
+ }
1325
+ const rawQuery = QUERY_SET_RAW_MAP.get(querySet);
1326
+ try {
1327
+ if (rawQuery !== void 0 && typeof rawQuery.destroy === "function") rawQuery.destroy();
1328
+ } catch (e) {
1329
+ const message = e instanceof Error ? e.message : String(e);
1330
+ return err(
1331
+ new RhiError({
1332
+ code: "webgpu-runtime-error",
1333
+ expected: "underlying GPUQuerySet.destroy() to succeed",
1334
+ hint: `destroy raised: ${message}`
1335
+ })
1336
+ );
1337
+ }
1338
+ if (marker !== void 0) marker.destroyed = true;
1339
+ return ok(void 0);
1340
+ },
1341
+ createCommandEncoder(desc) {
1342
+ const rawEnc = desc === void 0 ? rawDevice.createCommandEncoder() : rawDevice.createCommandEncoder(
1343
+ mirror(desc, ENC_KEYS)
1344
+ );
1345
+ const fireFeatureNotEnabled = (featureName, hint) => {
1346
+ console.error(
1347
+ `[RhiError feature-not-enabled] expected: device.features.has('${featureName}') === true; hint: ${hint}`
1348
+ );
1349
+ };
1350
+ return ok(makeCommandEncoder(rawEnc, caps, fireFeatureNotEnabled));
1351
+ }
1352
+ // fix-f3: synchronous createShaderModule placeholder removed; the
1353
+ // shader-compile-failed path lives in the top-level async factory
1354
+ // (see ../index.ts).
1355
+ };
1356
+ RAW_DEVICE_MAP.set(device, rawDevice);
1357
+ return { device, raw: rawDevice };
1358
+ }
1359
+ var SUPPORTED_CONTEXT_FORMATS = /* @__PURE__ */ new Set([
1360
+ "bgra8unorm",
1361
+ "rgba8unorm",
1362
+ "rgba16float"
1363
+ ]);
1364
+ var CANVAS_CONFIG_KEYS = [
1365
+ "device",
1366
+ "format",
1367
+ "usage",
1368
+ "viewFormats",
1369
+ "colorSpace",
1370
+ "toneMapping",
1371
+ "alphaMode"
1372
+ ];
1373
+ function makeCanvasContext(rawContext) {
1374
+ return {
1375
+ configure(desc) {
1376
+ const fmt = desc.format;
1377
+ if (typeof fmt === "string" && !SUPPORTED_CONTEXT_FORMATS.has(fmt)) {
1378
+ return err(
1379
+ new RhiError({
1380
+ code: "webgpu-runtime-error",
1381
+ expected: "one of bgra8unorm/rgba8unorm/rgba16float",
1382
+ hint: `got format='${fmt}'; canvas configuration cannot use srgb formats \u2014 use the non-srgb form (e.g. 'bgra8unorm') and put the srgb format in viewFormats, then createView with the srgb format`
1383
+ })
1384
+ );
1385
+ }
1386
+ try {
1387
+ const mirrored = mirror(
1388
+ desc,
1389
+ CANVAS_CONFIG_KEYS
1390
+ );
1391
+ if ("device" in mirrored) {
1392
+ const forgeaxDevice = desc.device;
1393
+ const rawDev = RAW_DEVICE_MAP.get(forgeaxDevice);
1394
+ if (rawDev === void 0) {
1395
+ return err(
1396
+ new RhiError({
1397
+ code: "rhi-not-available",
1398
+ expected: "CanvasConfiguration.device must be a RhiDevice produced by rhi.requestAdapter().requestDevice() (or the deprecated rhi.requestDevice factory)",
1399
+ hint: "pass the device returned by the forgeax rhi.requestAdapter() / rhi.requestDevice() entries; passing a foreign RhiDevice or a raw GPUDevice is rejected because the canvas-context spec requires the same raw GPUDevice that the forgeax shim wraps"
1400
+ })
1401
+ );
1402
+ }
1403
+ mirrored.device = rawDev;
1404
+ }
1405
+ rawContext.configure(mirrored);
1406
+ return ok(void 0);
1407
+ } catch (e) {
1408
+ const msg = e instanceof Error ? e.message : String(e);
1409
+ if (e instanceof Error && (e.name === "InvalidStateError" || /lost|destroyed/i.test(msg))) {
1410
+ return err(
1411
+ new RhiError({
1412
+ code: "rhi-not-available",
1413
+ expected: "CanvasConfiguration.device must be valid (not lost / destroyed)",
1414
+ hint: `configure raised: ${msg}`
1415
+ })
1416
+ );
1417
+ }
1418
+ return err(
1419
+ new RhiError({
1420
+ code: "webgpu-runtime-error",
1421
+ expected: "underlying GPUCanvasContext.configure to succeed",
1422
+ hint: `configure raised: ${msg}`
1423
+ })
1424
+ );
1425
+ }
1426
+ },
1427
+ unconfigure() {
1428
+ rawContext.unconfigure();
1429
+ },
1430
+ getConfiguration() {
1431
+ const conf = rawContext.getConfiguration();
1432
+ if (conf === null) return void 0;
1433
+ const out = {};
1434
+ for (const k of CANVAS_CONFIG_KEYS) {
1435
+ if (k in conf) {
1436
+ out[k] = conf[k];
1437
+ }
1438
+ }
1439
+ return out;
1440
+ },
1441
+ getCurrentTexture() {
1442
+ try {
1443
+ const rawTex = rawContext.getCurrentTexture();
1444
+ return ok(rawTex);
1445
+ } catch (e) {
1446
+ const msg = e instanceof Error ? e.message : String(e);
1447
+ return err(
1448
+ new RhiError({
1449
+ code: "webgpu-runtime-error",
1450
+ expected: "GPUCanvasContext.getCurrentTexture to succeed (context configured)",
1451
+ hint: `getCurrentTexture raised: ${msg}`
1452
+ })
1453
+ );
1454
+ }
1455
+ }
1456
+ };
1457
+ }
1458
+ function translateErrorEventToRhiError(event) {
1459
+ if (typeof event === "object" && event !== null && "reason" in event && typeof event.reason === "string") {
1460
+ const info = event;
1461
+ return err(
1462
+ new RhiError({
1463
+ code: "device-lost",
1464
+ expected: "device must remain alive (driver / browser must not destroy the GPUDevice)",
1465
+ hint: `device-lost reason: ${info.reason}; message: ${info.message ?? "<empty>"}`
1466
+ })
1467
+ );
1468
+ }
1469
+ if (typeof event === "object" && event !== null && "error" in event && typeof event.error === "object" && event.error !== null) {
1470
+ const error = event.error;
1471
+ const message = typeof error.message === "string" ? error.message : "<no message>";
1472
+ if (error.constructor.name === "GPUOutOfMemoryError") {
1473
+ return err(
1474
+ new RhiError({
1475
+ code: "oom",
1476
+ expected: "sufficient GPU memory to satisfy the allocation",
1477
+ hint: `GPU out-of-memory: ${message}`
1478
+ })
1479
+ );
1480
+ }
1481
+ if (error.constructor.name === "GPUInternalError") {
1482
+ return err(
1483
+ new RhiError({
1484
+ code: "internal-error",
1485
+ expected: "driver / browser must report a recognised validation error",
1486
+ hint: `GPU internal error: ${message}`
1487
+ })
1488
+ );
1489
+ }
1490
+ if (error.constructor.name === "GPUValidationError") {
1491
+ if (/shader|compile|wgsl/i.test(message)) {
1492
+ return err(
1493
+ new RhiError({
1494
+ code: "shader-compile-failed",
1495
+ expected: "valid WGSL source + matching pipeline layout",
1496
+ hint: `GPU validation: ${message}`
1497
+ })
1498
+ );
1499
+ }
1500
+ if (/size|alignment|out of bounds/i.test(message)) {
1501
+ return err(
1502
+ new RhiError({
1503
+ code: "queue-write-buffer-out-of-bounds",
1504
+ expected: "writeBuffer offset + data.byteLength must be within buffer.size",
1505
+ hint: `GPU validation: ${message}`
1506
+ })
1507
+ );
1508
+ }
1509
+ if (/encoder.*finished|finished encoder/i.test(message)) {
1510
+ return err(
1511
+ new RhiError({
1512
+ code: "command-encoder-finished",
1513
+ expected: "command encoder must not be finished before recording new commands",
1514
+ hint: `GPU validation: ${message}`
1515
+ })
1516
+ );
1517
+ }
1518
+ if (/render pass.*not ended|pass.*not ended/i.test(message)) {
1519
+ return err(
1520
+ new RhiError({
1521
+ code: "render-pass-not-ended",
1522
+ expected: "previous render pass must be ended before beginning a new pass",
1523
+ hint: `GPU validation: ${message}`
1524
+ })
1525
+ );
1526
+ }
1527
+ if (/submit/i.test(message)) {
1528
+ return err(
1529
+ new RhiError({
1530
+ code: "queue-submit-failed",
1531
+ expected: "command buffer references must be valid at submit time",
1532
+ hint: `GPU validation: ${message}`
1533
+ })
1534
+ );
1535
+ }
1536
+ return err(
1537
+ new RhiError({
1538
+ code: "limit-exceeded",
1539
+ expected: "descriptor field values within device limits",
1540
+ hint: `GPU validation: ${message}`
1541
+ })
1542
+ );
1543
+ }
1544
+ }
1545
+ const repr = typeof event === "object" && event !== null && "toString" in event ? String(event) : "<unknown>";
1546
+ return err(
1547
+ new RhiError({
1548
+ code: "webgpu-runtime-error",
1549
+ expected: "spec-recognised GPUUncapturedErrorEvent or GPUDeviceLostInfo",
1550
+ hint: `unrecognised async-dispatch event: ${repr}`
1551
+ })
1552
+ );
1553
+ }
1554
+
1555
+ // src/index.ts
1556
+ function classifyRequestDeviceError(e) {
1557
+ const msg = e instanceof Error ? e.message : String(e);
1558
+ if (/feature/i.test(msg)) return featureNotEnabled();
1559
+ if (/limit/i.test(msg)) return limitExceeded();
1560
+ return featureNotEnabled();
1561
+ }
1562
+ async function requestDevice(opts = {}) {
1563
+ const injected = opts.gpu;
1564
+ const ambient = typeof globalThis !== "undefined" ? globalThis.navigator?.gpu : void 0;
1565
+ const gpu = injected ?? ambient;
1566
+ if (gpu === void 0 || gpu === null) {
1567
+ return adapterUnavailable();
1568
+ }
1569
+ const adapter = await gpu.requestAdapter(opts.adapterOptions);
1570
+ if (adapter === null) {
1571
+ return adapterUnavailable();
1572
+ }
1573
+ let rawDevice;
1574
+ try {
1575
+ rawDevice = await adapter.requestDevice(opts.deviceDescriptor);
1576
+ } catch (e) {
1577
+ return classifyRequestDeviceError(e);
1578
+ }
1579
+ const { device } = makeRhiDevice(rawDevice);
1580
+ return ok(device);
1581
+ }
1582
+ async function createShaderModule(device, desc) {
1583
+ const rawDevice = _internal_getRawDevice(device);
1584
+ if (rawDevice === void 0) {
1585
+ return shaderCompileFailed([
1586
+ {
1587
+ type: "error",
1588
+ message: "rhi-webgpu: createShaderModule called with unregistered RhiDevice",
1589
+ lineNum: 0,
1590
+ linePos: 0,
1591
+ offset: 0,
1592
+ length: 0
1593
+ }
1594
+ ]);
1595
+ }
1596
+ const mirrored = { code: desc.code };
1597
+ if ("label" in desc && desc.label !== void 0) mirrored.label = desc.label;
1598
+ let handle;
1599
+ try {
1600
+ handle = rawDevice.createShaderModule(mirrored);
1601
+ } catch (e) {
1602
+ const message = e instanceof Error ? e.message : String(e);
1603
+ return shaderCompileFailed([
1604
+ {
1605
+ type: "error",
1606
+ message,
1607
+ lineNum: 0,
1608
+ linePos: 0,
1609
+ offset: 0,
1610
+ length: 0
1611
+ }
1612
+ ]);
1613
+ }
1614
+ const handleWithInfo = handle;
1615
+ if (typeof handleWithInfo.getCompilationInfo !== "function") {
1616
+ return ok(handle);
1617
+ }
1618
+ let info;
1619
+ try {
1620
+ info = await handleWithInfo.getCompilationInfo();
1621
+ } catch {
1622
+ return ok(handle);
1623
+ }
1624
+ const errors = info.messages.filter((m) => m.type === "error");
1625
+ if (errors.length > 0) {
1626
+ return shaderCompileFailed(info.messages);
1627
+ }
1628
+ return ok(handle);
1629
+ }
1630
+ function makeRhiAdapter(rawAdapter) {
1631
+ const rawFeatures = rawAdapter.features;
1632
+ const features = rawFeatures !== void 0 && rawFeatures !== null ? new Set(rawFeatures) : /* @__PURE__ */ new Set();
1633
+ const limitsRaw = rawAdapter.limits ?? {};
1634
+ const limits = {};
1635
+ for (const key in limitsRaw) {
1636
+ const v = limitsRaw[key];
1637
+ if (typeof v === "number") {
1638
+ limits[key] = v;
1639
+ }
1640
+ }
1641
+ return {
1642
+ features,
1643
+ limits,
1644
+ async requestDevice(opts) {
1645
+ let rawDevice;
1646
+ try {
1647
+ rawDevice = await rawAdapter.requestDevice(
1648
+ opts
1649
+ );
1650
+ } catch (e) {
1651
+ return classifyRequestDeviceError(e);
1652
+ }
1653
+ const { device } = makeRhiDevice(rawDevice);
1654
+ return ok(device);
1655
+ }
1656
+ };
1657
+ }
1658
+ async function requestAdapter(opts, _compatibleSurface) {
1659
+ const ambient = typeof globalThis !== "undefined" ? globalThis.navigator?.gpu : void 0;
1660
+ if (ambient === void 0 || ambient === null) {
1661
+ return adapterUnavailable();
1662
+ }
1663
+ let adapter;
1664
+ try {
1665
+ adapter = await ambient.requestAdapter(opts);
1666
+ } catch {
1667
+ return adapterUnavailable();
1668
+ }
1669
+ if (adapter === null) {
1670
+ return adapterUnavailable();
1671
+ }
1672
+ return ok(
1673
+ makeRhiAdapter(
1674
+ adapter
1675
+ )
1676
+ );
1677
+ }
1678
+ function acquireCanvasContext(canvas) {
1679
+ let rawContext;
1680
+ try {
1681
+ rawContext = canvas.getContext("webgpu");
1682
+ } catch {
1683
+ rawContext = null;
1684
+ }
1685
+ if (rawContext === null) {
1686
+ return err(
1687
+ new RhiError({
1688
+ code: "rhi-not-available",
1689
+ expected: 'canvas.getContext("webgpu") to return a non-null GPUCanvasContext',
1690
+ hint: 'canvas does not support WebGPU \u2014 pass an HTMLCanvasElement (or OffscreenCanvas) whose getContext("webgpu") returns a valid GPUCanvasContext'
1691
+ })
1692
+ );
1693
+ }
1694
+ return ok(makeCanvasContext(rawContext));
1695
+ }
1696
+ var rhi = {
1697
+ requestAdapter,
1698
+ createShaderModule,
1699
+ acquireCanvasContext
1700
+ };
1701
+
1702
+ export { _internal_getRawDevice, acquireCanvasContext, createShaderModule, requestAdapter, requestDevice, rhi, translateErrorEventToRhiError };
1703
+ //# sourceMappingURL=index.mjs.map
1704
+ //# sourceMappingURL=index.mjs.map