@forgeax/engine-rhi 0.0.0-dev.8d955ade1c79

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 (77) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +234 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/__tests__/async-form.test-d.d.ts +2 -0
  5. package/dist/__tests__/async-form.test-d.d.ts.map +1 -0
  6. package/dist/__tests__/binding-resource.test-d.d.ts +2 -0
  7. package/dist/__tests__/binding-resource.test-d.d.ts.map +1 -0
  8. package/dist/__tests__/buffer-mapping.test-d.d.ts +2 -0
  9. package/dist/__tests__/buffer-mapping.test-d.d.ts.map +1 -0
  10. package/dist/__tests__/canvasContext.test-d.d.ts +2 -0
  11. package/dist/__tests__/canvasContext.test-d.d.ts.map +1 -0
  12. package/dist/__tests__/caps-three-way.unit.test.d.ts +2 -0
  13. package/dist/__tests__/caps-three-way.unit.test.d.ts.map +1 -0
  14. package/dist/__tests__/caps.test-d.d.ts +2 -0
  15. package/dist/__tests__/caps.test-d.d.ts.map +1 -0
  16. package/dist/__tests__/command-encoder-spec-alignment.test-d.d.ts +2 -0
  17. package/dist/__tests__/command-encoder-spec-alignment.test-d.d.ts.map +1 -0
  18. package/dist/__tests__/createComputePipeline.test-d.d.ts +2 -0
  19. package/dist/__tests__/createComputePipeline.test-d.d.ts.map +1 -0
  20. package/dist/__tests__/createQuerySet.test-d.d.ts +2 -0
  21. package/dist/__tests__/createQuerySet.test-d.d.ts.map +1 -0
  22. package/dist/__tests__/createTextureView.test-d.d.ts +2 -0
  23. package/dist/__tests__/createTextureView.test-d.d.ts.map +1 -0
  24. package/dist/__tests__/descriptor-mirror.test-d.d.ts +2 -0
  25. package/dist/__tests__/descriptor-mirror.test-d.d.ts.map +1 -0
  26. package/dist/__tests__/draw-validation-errors.test.d.ts +2 -0
  27. package/dist/__tests__/draw-validation-errors.test.d.ts.map +1 -0
  28. package/dist/__tests__/errors-instancing.unit.test.d.ts +2 -0
  29. package/dist/__tests__/errors-instancing.unit.test.d.ts.map +1 -0
  30. package/dist/__tests__/errors.test-d.d.ts +2 -0
  31. package/dist/__tests__/errors.test-d.d.ts.map +1 -0
  32. package/dist/__tests__/limit-exceeded-detail.test-d.d.ts +2 -0
  33. package/dist/__tests__/limit-exceeded-detail.test-d.d.ts.map +1 -0
  34. package/dist/__tests__/mapped-buffer.test-d.d.ts +2 -0
  35. package/dist/__tests__/mapped-buffer.test-d.d.ts.map +1 -0
  36. package/dist/__tests__/opaque-handle.test-d.d.ts +2 -0
  37. package/dist/__tests__/opaque-handle.test-d.d.ts.map +1 -0
  38. package/dist/__tests__/owner-out-of-range-role.test.d.ts +2 -0
  39. package/dist/__tests__/owner-out-of-range-role.test.d.ts.map +1 -0
  40. package/dist/__tests__/queue-writes.test-d.d.ts +2 -0
  41. package/dist/__tests__/queue-writes.test-d.d.ts.map +1 -0
  42. package/dist/__tests__/render-pass-encoder-spec-alignment.test-d.d.ts +2 -0
  43. package/dist/__tests__/render-pass-encoder-spec-alignment.test-d.d.ts.map +1 -0
  44. package/dist/__tests__/requestAdapter.test-d.d.ts +2 -0
  45. package/dist/__tests__/requestAdapter.test-d.d.ts.map +1 -0
  46. package/dist/errors.d.ts +315 -0
  47. package/dist/errors.d.ts.map +1 -0
  48. package/dist/errors.mjs +56 -0
  49. package/dist/errors.mjs.map +1 -0
  50. package/dist/index.d.ts +1758 -0
  51. package/dist/index.d.ts.map +1 -0
  52. package/dist/index.mjs +87 -0
  53. package/dist/index.mjs.map +1 -0
  54. package/package.json +61 -0
  55. package/src/__tests__/async-form.test-d.ts +40 -0
  56. package/src/__tests__/binding-resource.test-d.ts +66 -0
  57. package/src/__tests__/buffer-mapping.test-d.ts +84 -0
  58. package/src/__tests__/canvasContext.test-d.ts +106 -0
  59. package/src/__tests__/caps-three-way.unit.test.ts +144 -0
  60. package/src/__tests__/caps.test-d.ts +83 -0
  61. package/src/__tests__/command-encoder-spec-alignment.test-d.ts +117 -0
  62. package/src/__tests__/createComputePipeline.test-d.ts +149 -0
  63. package/src/__tests__/createQuerySet.test-d.ts +81 -0
  64. package/src/__tests__/createTextureView.test-d.ts +127 -0
  65. package/src/__tests__/descriptor-mirror.test-d.ts +284 -0
  66. package/src/__tests__/draw-validation-errors.test.ts +155 -0
  67. package/src/__tests__/errors-instancing.unit.test.ts +167 -0
  68. package/src/__tests__/errors.test-d.ts +199 -0
  69. package/src/__tests__/limit-exceeded-detail.test-d.ts +33 -0
  70. package/src/__tests__/mapped-buffer.test-d.ts +65 -0
  71. package/src/__tests__/opaque-handle.test-d.ts +121 -0
  72. package/src/__tests__/owner-out-of-range-role.test.ts +84 -0
  73. package/src/__tests__/queue-writes.test-d.ts +108 -0
  74. package/src/__tests__/render-pass-encoder-spec-alignment.test-d.ts +202 -0
  75. package/src/__tests__/requestAdapter.test-d.ts +123 -0
  76. package/src/errors.ts +504 -0
  77. package/src/index.ts +2276 -0
@@ -0,0 +1,1758 @@
1
+ import type { AddressMode, CompareFunction, FilterMode, TextureFormat } from '@forgeax/engine-types';
2
+ import type { Result, RhiError } from './errors';
3
+ declare const RhiBufferBrand: unique symbol;
4
+ declare const RhiMappedBufferBrand: unique symbol;
5
+ /**
6
+ * GPU buffer opaque handle (vertex / index / uniform / storage / indirect).
7
+ *
8
+ * Spec anchor: W3C WebGPU §4 Buffers / [@webgpu/types.GPUBuffer]; research
9
+ * §4.1 mapState 3-state enum + §4.2 mapAsync 8-item validation + §4.4 unmap
10
+ * detach semantics.
11
+ *
12
+ * The buffer mapping surface is added in feat-20260510-rhi-resource-creation
13
+ * M5 (K-1: raw GPUMapMode bitmask; K-2: alignment / mode-usage / detach
14
+ * faults all ride 'webgpu-runtime-error' with structured .expected / .hint);
15
+ * re-shaped in feat-20260511-rhi-spec-realign-aggressive M1 (D-P2 #6):
16
+ * - `mapAsync` resolves to `Result<MappedBuffer, RhiError>` (success branch
17
+ * carries a branded handle subsequently used for getMappedRange / unmap).
18
+ * - `getMappedRange` / `unmap` are methods on `MappedBuffer` (not Buffer);
19
+ * calling them on a plain Buffer is a TS2345 compile-time red.
20
+ *
21
+ * The forgeax form keeps the spec verb names but routes failures via Result
22
+ * (charter proposition 4 explicit failure):
23
+ * mapAsync(mode, offset?, size?): Promise<Result<MappedBuffer, RhiError>>
24
+ * readonly mapState: 'unmapped' | 'pending' | 'mapped' - getter (research
25
+ * §4.1; same closed union as GPUBufferMapState).
26
+ */
27
+ export interface Buffer {
28
+ readonly [RhiBufferBrand]: void;
29
+ /**
30
+ * Map the buffer for CPU access.
31
+ *
32
+ * Spec anchor: W3C WebGPU §gpubuffer-mapasync /
33
+ * [@webgpu/types.GPUBuffer.mapAsync]. K-1 decision: `mode` is the raw
34
+ * `GPUMapMode` bitmask (NOT a closed union 'read' | 'write') so the forgeax
35
+ * form mirrors `GPUMapMode.READ` / `GPUMapMode.WRITE` literals.
36
+ *
37
+ * D-P2 #6 (feat-20260511-rhi-spec-realign-aggressive): the success branch
38
+ * resolves to `MappedBuffer`, a brand on top of `Buffer`. AI users
39
+ * subsequently call `mapped.getMappedRange(...)` / `mapped.unmap()` on the
40
+ * branded handle; calling those methods on a plain `Buffer` is a TS2345
41
+ * compile-time signal (charter proposition 4 explicit failure encoded at
42
+ * the type layer).
43
+ *
44
+ * Failure paths (research §4.2 + plan-strategy §2 K-2):
45
+ * - mapState !== 'unmapped' (F-8 row 1) -> 'webgpu-runtime-error'.
46
+ * - offset % 8 != 0 (step 4) -> 'webgpu-runtime-error'.
47
+ * - rangeSize % 4 != 0 (step 5) -> 'webgpu-runtime-error'.
48
+ * - offset + rangeSize > size (step 6) -> 'webgpu-runtime-error'.
49
+ * - mode contains foreign bits (step 7) -> 'webgpu-runtime-error'.
50
+ * - mode is not exactly READ or WRITE (step 8) -> 'webgpu-runtime-error'.
51
+ * - mode-usage mismatch (step 9 / F-8 row 3) -> 'webgpu-runtime-error'.
52
+ *
53
+ * @example
54
+ * const r = await buffer.mapAsync(GPUMapMode.WRITE);
55
+ * if (!r.ok) {
56
+ * // route via switch (r.error.code)
57
+ * return;
58
+ * }
59
+ * const mapped: MappedBuffer = r.value;
60
+ * const range = mapped.getMappedRange();
61
+ * if (range.ok) new Uint32Array(range.value).set([1, 2, 3, 4]);
62
+ * mapped.unmap();
63
+ */
64
+ mapAsync(mode: GPUMapModeFlags, offset?: number | undefined, size?: number | undefined): Promise<Result<MappedBuffer, RhiError>>;
65
+ /**
66
+ * Current mapping state (read-only getter).
67
+ *
68
+ * Spec anchor: research §4.1 mapState 3-state enum; mirrors
69
+ * GPUBufferMapState. Transitions:
70
+ * - createBuffer({mappedAtCreation:true}) sets mapState='mapped'.
71
+ * - mapAsync moves 'unmapped' -> 'pending' -> 'mapped'.
72
+ * - unmap moves 'mapped' -> 'unmapped'.
73
+ */
74
+ readonly mapState: 'unmapped' | 'pending' | 'mapped';
75
+ }
76
+ /**
77
+ * Brand on top of `Buffer` indicating the mapping is currently open; only the
78
+ * `MappedBuffer` exposes `getMappedRange` / `unmap` method forms so AI users
79
+ * cannot accidentally call them on an unmapped `Buffer` (D-P2 #6).
80
+ *
81
+ * The brand is structural — runtime the `MappedBuffer` is the same JS object
82
+ * as the underlying `Buffer`; TypeScript narrows access through the
83
+ * `__mapped: void` private brand symbol.
84
+ *
85
+ * Spec anchor: W3C WebGPU §4 Buffers mapping lifecycle (research §4.1 /
86
+ * §4.4); plan-strategy §7.1 + D-P2 break-point #6 (brand + method form
87
+ * merged).
88
+ *
89
+ * @example
90
+ * const r = await buffer.mapAsync(GPUMapMode.WRITE);
91
+ * if (!r.ok) return;
92
+ * const mapped: MappedBuffer = r.value;
93
+ * mapped.getMappedRange(); // method form, this: MappedBuffer
94
+ * mapped.unmap(); // method form, this: MappedBuffer
95
+ */
96
+ export interface MappedBuffer extends Buffer {
97
+ readonly [RhiMappedBufferBrand]: void;
98
+ /**
99
+ * Return an ArrayBuffer view of the mapped range. Method form on
100
+ * `MappedBuffer` per D-P2 #6 — calling on a plain `Buffer` is TS2339.
101
+ *
102
+ * Spec anchor: W3C WebGPU §gpubuffer-getmappedrange /
103
+ * [@webgpu/types.GPUBuffer.getMappedRange].
104
+ *
105
+ * Failure paths:
106
+ * - mapState !== 'mapped' (incl after unmap, F-8 row 2 detach guard) ->
107
+ * 'webgpu-runtime-error'.
108
+ */
109
+ getMappedRange(offset?: number | undefined, size?: number | undefined): Result<ArrayBuffer, RhiError>;
110
+ /**
111
+ * Unmap the buffer, detaching all ArrayBuffer views obtained from
112
+ * getMappedRange. Method form on `MappedBuffer` per D-P2 #6.
113
+ *
114
+ * Spec anchor: W3C WebGPU §gpubuffer-unmap /
115
+ * [@webgpu/types.GPUBuffer.unmap]. unmap() returns void per spec normative
116
+ * silent no-op (research §4.4); calling unmap on an already-unmapped buffer
117
+ * does NOT error. This is the ONE Result-shape exception in the buffer
118
+ * surface (AI User Affordances explicit listing).
119
+ *
120
+ * After unmap, the JS object continues to exist but the brand narrows
121
+ * away at the TS layer: AI users who hold a `MappedBuffer` after the
122
+ * underlying state flipped should re-`mapAsync` to obtain a fresh branded
123
+ * instance (OQ-5 plan-decisions: unmap returns void; subsequent mapAsync
124
+ * returns a new MappedBuffer brand).
125
+ */
126
+ unmap(): void;
127
+ }
128
+ declare const RhiTextureBrand: unique symbol;
129
+ /** GPU texture opaque handle (2D / 3D / cube / array). */
130
+ export interface Texture {
131
+ readonly [RhiTextureBrand]: void;
132
+ }
133
+ /** RHI-owned destination for queue.writeTexture. The resource handle remains opaque. */
134
+ export interface TextureWriteDestination {
135
+ readonly texture: Texture;
136
+ readonly mipLevel?: number | undefined;
137
+ readonly origin?: GPUOrigin3D | undefined;
138
+ readonly aspect?: GPUTextureAspect | undefined;
139
+ }
140
+ /** RHI-owned destination for queue.copyExternalImageToTexture. */
141
+ export type ExternalImageTextureDestination = Omit<Pick<GPUCopyExternalImageDestInfo, 'texture' | 'mipLevel' | 'origin' | 'aspect' | 'colorSpace' | 'premultipliedAlpha'>, 'texture'> & {
142
+ readonly texture: Texture;
143
+ };
144
+ declare const RhiTextureViewBrand: unique symbol;
145
+ /** GPU texture view opaque handle. */
146
+ export interface TextureView {
147
+ readonly [RhiTextureViewBrand]: void;
148
+ }
149
+ declare const RhiSamplerBrand: unique symbol;
150
+ /** GPU sampler opaque handle. */
151
+ export interface Sampler {
152
+ readonly [RhiSamplerBrand]: void;
153
+ }
154
+ declare const RhiBindGroupBrand: unique symbol;
155
+ /** GPU bind group opaque handle (instantiated layout). */
156
+ export interface BindGroup {
157
+ readonly [RhiBindGroupBrand]: void;
158
+ }
159
+ declare const RhiBindGroupLayoutBrand: unique symbol;
160
+ /** GPU bind group layout opaque handle (declares binding shapes). */
161
+ export interface BindGroupLayout {
162
+ readonly [RhiBindGroupLayoutBrand]: void;
163
+ }
164
+ declare const RhiPipelineLayoutBrand: unique symbol;
165
+ /** GPU pipeline layout opaque handle (aggregates BindGroupLayouts). */
166
+ export interface PipelineLayout {
167
+ readonly [RhiPipelineLayoutBrand]: void;
168
+ }
169
+ declare const RhiRenderPipelineBrand: unique symbol;
170
+ /** GPU render pipeline opaque handle. */
171
+ export interface RenderPipeline {
172
+ readonly [RhiRenderPipelineBrand]: void;
173
+ }
174
+ declare const RhiComputePipelineBrand: unique symbol;
175
+ /** GPU compute pipeline opaque handle. */
176
+ export interface ComputePipeline {
177
+ readonly [RhiComputePipelineBrand]: void;
178
+ }
179
+ declare const RhiShaderModuleBrand: unique symbol;
180
+ /** GPU shader module opaque handle (WGSL / SPIR-V compile artifact). */
181
+ export interface ShaderModule {
182
+ readonly [RhiShaderModuleBrand]: void;
183
+ }
184
+ declare const RhiQuerySetBrand: unique symbol;
185
+ /** GPU query set opaque handle (occlusion / timestamp). */
186
+ export interface QuerySet {
187
+ readonly [RhiQuerySetBrand]: void;
188
+ }
189
+ declare const RhiFenceBrand: unique symbol;
190
+ /** GPU fence opaque handle (GPU/CPU sync barrier). */
191
+ export interface Fence {
192
+ readonly [RhiFenceBrand]: void;
193
+ }
194
+ declare const RhiCommandEncoderBrand: unique symbol;
195
+ /** GPU command encoder opaque handle (single-use). */
196
+ export interface CommandEncoder {
197
+ readonly [RhiCommandEncoderBrand]: void;
198
+ }
199
+ declare const RhiCommandBufferBrand: unique symbol;
200
+ /** GPU command buffer opaque handle (submitted to Queue). */
201
+ export interface CommandBuffer {
202
+ readonly [RhiCommandBufferBrand]: void;
203
+ }
204
+ /**
205
+ * Convert `?: T` optional fields to `?: T | undefined` (decision S-7).
206
+ *
207
+ * Compatible with exactOptionalPropertyTypes: writers may pass `undefined`
208
+ * explicitly or omit the field; the M2 shim distinguishes the two via
209
+ * `'x' in src` guards (research F-3 anti-pattern 2).
210
+ */
211
+ type ExplicitUndefined<T> = {
212
+ [K in keyof T]: T[K] | undefined;
213
+ };
214
+ /** GPU buffer descriptor. Field set strictly matches GPUBufferDescriptor;
215
+ * optional fields use `?: T | undefined`. */
216
+ export type BufferDescriptor = ExplicitUndefined<Pick<GPUBufferDescriptor, 'label' | 'size' | 'usage' | 'mappedAtCreation'>>;
217
+ /** GPU texture descriptor. Field set strictly matches GPUTextureDescriptor
218
+ * (incl R8 Compatibility Mode field). */
219
+ export type TextureDescriptor = ExplicitUndefined<Pick<GPUTextureDescriptor, 'label' | 'size' | 'mipLevelCount' | 'sampleCount' | 'dimension' | 'format' | 'usage' | 'viewFormats' | 'textureBindingViewDimension'>>;
220
+ /** GPU sampler descriptor. Field set strictly matches GPUSamplerDescriptor. */
221
+ export type SamplerDescriptor = ExplicitUndefined<Pick<GPUSamplerDescriptor, 'label' | 'addressModeU' | 'addressModeV' | 'addressModeW' | 'magFilter' | 'minFilter' | 'mipmapFilter' | 'lodMinClamp' | 'lodMaxClamp' | 'compare' | 'maxAnisotropy'>>;
222
+ /** GPU bind group layout descriptor. Field set strictly matches
223
+ * GPUBindGroupLayoutDescriptor. */
224
+ export type BindGroupLayoutDescriptor = ExplicitUndefined<Pick<GPUBindGroupLayoutDescriptor, 'label' | 'entries'>>;
225
+ /**
226
+ * GPU texture view descriptor (Pick<GPUTextureViewDescriptor, 9 fields>).
227
+ *
228
+ * Spec anchor: W3C WebGPU §texture-view-creation /
229
+ * [@webgpu/types.GPUTextureViewDescriptor]. Field NAMES align byte-for-byte;
230
+ * field set excludes the feature-gated `swizzle` field (research §1.1 OOS-MVP;
231
+ * a future closure can add it once `'texture-component-swizzle'` is enabled).
232
+ *
233
+ * Cross-resource validation (shim fast-path, research §1.1):
234
+ * - `format` must equal source.format OR be in source.viewFormats; otherwise
235
+ * the shim returns Result.err({ code: 'webgpu-runtime-error' }).
236
+ * - `usage` must be a subset of source.usage (bitmask); otherwise the shim
237
+ * returns the same code.
238
+ *
239
+ * @example
240
+ * const r = device.createTextureView(tex, { format: 'rgba8unorm', dimension: '2d' });
241
+ * if (!r.ok) {
242
+ * // route via switch (r.error.code)
243
+ * }
244
+ */
245
+ export type TextureViewDescriptor = ExplicitUndefined<Pick<GPUTextureViewDescriptor, 'label' | 'format' | 'dimension' | 'usage' | 'aspect' | 'baseMipLevel' | 'mipLevelCount' | 'baseArrayLayer' | 'arrayLayerCount'>>;
246
+ /**
247
+ * GPU compute pipeline descriptor (Pick<GPUComputePipelineDescriptor,
248
+ * 'label' | 'layout' | 'compute'>).
249
+ *
250
+ * Spec anchor: W3C WebGPU §compute-pipeline-creation /
251
+ * [@webgpu/types.GPUComputePipelineDescriptor]. Field NAMES align byte-for-byte
252
+ * with spec.
253
+ *
254
+ * `layout` is the spec union `'auto' | GPUPipelineLayout`; forgeax tightens
255
+ * the explicit form to the `PipelineLayout` opaque handle (D-S5 pattern):
256
+ * layout: 'auto' | PipelineLayout
257
+ *
258
+ * `compute` mirrors `GPUProgrammableStage` verbatim — `module` (required
259
+ * `ShaderModule` opaque handle), `entryPoint?` (optional string),
260
+ * `constants?` (optional `Record<string, number>`).
261
+ *
262
+ * Capability gate (research §1.2 NOTE; plan-strategy §4.3 boundary case row 1):
263
+ * - `caps.compute === false` -> shim returns Result.err({
264
+ * code: 'feature-not-enabled', expected: 'caps.compute === true',
265
+ * hint: 'check device.caps.compute before calling createComputePipeline'
266
+ * }). MVP WebGPU path always has caps.compute=true (spec mandate); the gate
267
+ * exists for potential future non-WebGPU backends.
268
+ *
269
+ * @example
270
+ * const r = device.createComputePipeline({
271
+ * label: 'cs',
272
+ * layout: 'auto',
273
+ * compute: { module: csModule, entryPoint: 'cs_main' },
274
+ * });
275
+ */
276
+ export type ComputePipelineDescriptor = ExplicitUndefined<Omit<Pick<GPUComputePipelineDescriptor, 'label' | 'layout' | 'compute'>, 'layout' | 'compute'>> & {
277
+ /**
278
+ * Either `'auto'` for user-agent BGL inference or a forgeax
279
+ * `PipelineLayout` opaque handle (D-S5 pattern: forgeax handle replaces
280
+ * spec polymorphism `(GPUAutoLayoutMode or GPUPipelineLayout)`).
281
+ */
282
+ layout: 'auto' | PipelineLayout;
283
+ /**
284
+ * The compute programmable stage. `module` is the forgeax `ShaderModule`
285
+ * opaque handle (replacing spec `GPUShaderModule`); `entryPoint?` defaults
286
+ * to the module's single compute entry; `constants?` is a record of
287
+ * pipeline-overridable constants.
288
+ */
289
+ compute: {
290
+ module: ShaderModule;
291
+ entryPoint?: string | undefined;
292
+ constants?: Record<string, number> | undefined;
293
+ };
294
+ };
295
+ /**
296
+ * GPU query set descriptor (Pick<GPUQuerySetDescriptor, 'label' | 'type' | 'count'>).
297
+ *
298
+ * Spec anchor: W3C WebGPU §queries / [@webgpu/types.GPUQuerySetDescriptor].
299
+ * Field NAMES align byte-for-byte.
300
+ *
301
+ * Hard constraints (research §1.3):
302
+ * - `count <= 4096` (spec normative). The shim fast-paths a violation to
303
+ * Result.err({ code: 'limit-exceeded',
304
+ * expected: 'count <= 4096 (spec normative)',
305
+ * hint: 'create multiple QuerySet instances if more than 4096 queries needed' }).
306
+ * - `type === 'timestamp'` requires `caps.timestampQuery === true` (the
307
+ * 'timestamp-query' feature). Otherwise the shim fast-paths to
308
+ * Result.err({ code: 'feature-not-enabled' }).
309
+ * - `count = 0` is legal (lower bound; dawn end2end test fixture).
310
+ *
311
+ * @example
312
+ * const r = device.createQuerySet({ type: 'occlusion', count: 4 });
313
+ * if (!r.ok) {
314
+ * // route via switch (r.error.code)
315
+ * }
316
+ */
317
+ export type QuerySetDescriptor = ExplicitUndefined<Pick<GPUQuerySetDescriptor, 'label' | 'type' | 'count'>>;
318
+ /**
319
+ * Discriminated union over the 4 BindGroup entry resource kinds (the spec
320
+ * polymorphic `GPUBindingResource` collapsed to a tagged union — charter
321
+ * proposition 4 closed-union exhaustive switch + proposition 5 consistent
322
+ * abstraction over duck-typing).
323
+ *
324
+ * Introduced in feat-20260511-rhi-spec-realign-aggressive w9 per requirements
325
+ * AC-10 + plan-strategy §7.1 + D-P2 break-point #5. AI users `switch
326
+ * (resource.kind)` is exhaustive without a default fallback; construction-side
327
+ * typos like `{ kind: 'samplre', ... }` trip TS2322 at the literal slot.
328
+ *
329
+ * Kind discriminator uses kebab-case for multi-word entries (`textureView` is
330
+ * already single-word camelCase by convention; `externalTexture` is multi-word
331
+ * camelCase for parity with the spec verb `GPUExternalTexture`).
332
+ *
333
+ * @example
334
+ * const e: RhiBindingResource = { kind: 'sampler', value: linearSampler };
335
+ * const e2: RhiBindingResource = {
336
+ * kind: 'buffer',
337
+ * value: { buffer: viewUniforms, offset: 0, size: 64 },
338
+ * };
339
+ */
340
+ export type RhiBindingResource = {
341
+ readonly kind: 'sampler';
342
+ readonly value: Sampler;
343
+ } | {
344
+ readonly kind: 'buffer';
345
+ readonly value: {
346
+ readonly buffer: Buffer;
347
+ readonly offset?: number;
348
+ readonly size?: number;
349
+ };
350
+ } | {
351
+ readonly kind: 'textureView';
352
+ readonly value: TextureView;
353
+ } | {
354
+ readonly kind: 'externalTexture';
355
+ readonly value: GPUExternalTexture;
356
+ };
357
+ /**
358
+ * BindGroup entry — one slot in a BindGroup, identified by `binding` (the
359
+ * shader binding number) and `resource` (the tagged-union `RhiBindingResource`
360
+ * — replaces the spec polymorphic `GPUBindingResource`).
361
+ *
362
+ * Field set strictly mirrors `Pick<GPUBindGroupEntry, 'binding'>`; the
363
+ * `resource` field is tightened to the forgeax `RhiBindingResource` tagged
364
+ * union per D-P2 break-point #5 (charter proposition 5 consistent abstraction:
365
+ * AI users see one canonical 4-kind switch rather than spec duck-typing).
366
+ */
367
+ export type BindGroupEntry = Pick<GPUBindGroupEntry, 'binding'> & {
368
+ resource: RhiBindingResource;
369
+ };
370
+ /**
371
+ * GPU bind group descriptor (Pick<GPUBindGroupDescriptor, 'label' | 'layout' | 'entries'>).
372
+ *
373
+ * Spec anchor: W3C WebGPU 10 Resource binding /
374
+ * [@webgpu/types.GPUBindGroupDescriptor].
375
+ *
376
+ * Introduced in feat-20260509-ecs-render-bridge-mvp (D-S1) so the RenderSystem
377
+ * can record `pass.setBindGroup(0/1/2, bg, ...)` through a single RHI surface
378
+ * (charter proposition 5 consistent abstraction; never via raw GPUDevice).
379
+ *
380
+ * The `layout` field references the forgeax `BindGroupLayout` opaque handle
381
+ * (created via `RhiDevice.createBindGroupLayout`); the `entries` array uses
382
+ * the forgeax `BindGroupEntry` shape (binding number + tagged-union
383
+ * `RhiBindingResource`) per feat-20260511-rhi-spec-realign-aggressive D-P2
384
+ * break-point #5 (was previously verbatim `GPUBindGroupEntry`).
385
+ *
386
+ * @example
387
+ * const desc: BindGroupDescriptor = {
388
+ * label: 'view-bg',
389
+ * layout: bgl,
390
+ * entries: [{ binding: 0, resource: { kind: 'buffer', value: { buffer: viewUniforms } } }],
391
+ * };
392
+ */
393
+ export type BindGroupDescriptor = ExplicitUndefined<Omit<Pick<GPUBindGroupDescriptor, 'label' | 'layout' | 'entries'>, 'layout' | 'entries'>> & {
394
+ /**
395
+ * BindGroupLayout opaque handle (forgeax tightening: `layout` is the
396
+ * already-shipped `BindGroupLayout` brand, not the spec
397
+ * `GPUBindGroupLayout`). Same D-S5 pattern as RenderPassColorAttachment.view
398
+ * — AI users receive a forgeax-creatable handle, never a phantom spec type.
399
+ */
400
+ layout: BindGroupLayout;
401
+ /**
402
+ * Iterable of forgeax `BindGroupEntry` (binding + tagged-union
403
+ * `RhiBindingResource`); replaces the spec polymorphic
404
+ * `iterable<GPUBindGroupEntry>` per D-P2 break-point #5.
405
+ */
406
+ entries: Iterable<BindGroupEntry>;
407
+ };
408
+ /**
409
+ * GPU pipeline layout descriptor (Pick<GPUPipelineLayoutDescriptor, 'label' | 'bindGroupLayouts'>).
410
+ *
411
+ * Spec anchor: W3C WebGPU 10.3 Pipeline layout /
412
+ * [@webgpu/types.GPUPipelineLayoutDescriptor].
413
+ *
414
+ * Introduced in feat-20260509-ecs-render-bridge-mvp (D-S1) so the
415
+ * `Renderer.ready` step 2 (PBR pipeline compile) can compose the 3
416
+ * BindGroupLayouts (view / material / mesh-array) into a single
417
+ * `PipelineLayout` for `RhiDevice.createRenderPipeline`.
418
+ *
419
+ * @example
420
+ * const desc: PipelineLayoutDescriptor = {
421
+ * label: 'pbr-pl',
422
+ * bindGroupLayouts: [viewBgl, materialBgl, meshArrayBgl],
423
+ * };
424
+ */
425
+ export type PipelineLayoutDescriptor = ExplicitUndefined<Omit<Pick<GPUPipelineLayoutDescriptor, 'label' | 'bindGroupLayouts'>, 'bindGroupLayouts'>> & {
426
+ /**
427
+ * Iterable of BindGroupLayout opaque handles (forgeax tightening: the
428
+ * iterable element type is the forgeax `BindGroupLayout` brand). Same D-S5
429
+ * pattern as BindGroupDescriptor.layout.
430
+ */
431
+ bindGroupLayouts: Iterable<BindGroupLayout>;
432
+ };
433
+ /** GPU render pipeline vertex stage with an opaque forgeax shader module. */
434
+ export type RenderPipelineVertexState = ExplicitUndefined<Omit<GPUVertexState, 'module' | 'buffers'>> & {
435
+ module: ShaderModule;
436
+ buffers: NonNullable<GPUVertexState['buffers']>;
437
+ };
438
+ /** GPU render pipeline fragment stage with an opaque forgeax shader module. */
439
+ export type RenderPipelineFragmentState = ExplicitUndefined<Omit<GPUFragmentState, 'module' | 'targets'>> & {
440
+ module: ShaderModule;
441
+ targets: NonNullable<GPUFragmentState['targets']>;
442
+ };
443
+ /** GPU render pipeline descriptor with opaque forgeax layout and shader handles. */
444
+ export type RenderPipelineDescriptor = ExplicitUndefined<Omit<Pick<GPURenderPipelineDescriptor, 'label' | 'layout' | 'vertex' | 'primitive' | 'depthStencil' | 'multisample' | 'fragment'>, 'layout' | 'vertex' | 'fragment'>> & {
445
+ layout: 'auto' | PipelineLayout;
446
+ vertex: RenderPipelineVertexState;
447
+ fragment?: RenderPipelineFragmentState | undefined;
448
+ };
449
+ /**
450
+ * Command encoder descriptor (Pick<GPUCommandEncoderDescriptor, 'label'>).
451
+ *
452
+ * Spec anchor: W3C WebGPU 22 GPUCommandEncoder /
453
+ * [@webgpu/types.GPUCommandEncoderDescriptor].
454
+ *
455
+ * @example
456
+ * const desc: CommandEncoderDescriptor = { label: 'frame-encoder' };
457
+ */
458
+ export type CommandEncoderDescriptor = ExplicitUndefined<Pick<GPUCommandEncoderDescriptor, 'label'>>;
459
+ /**
460
+ * Render-pass color attachment (Pick<GPURenderPassColorAttachment, ...> with
461
+ * `view` field aligned to the forgeax `TextureView` opaque handle, per
462
+ * feat-20260510-rhi-resource-creation M2 view narrow Path X / breakage point
463
+ * #1).
464
+ *
465
+ * Spec anchor: W3C WebGPU 22.7 Render pass /
466
+ * [@webgpu/types.GPURenderPassColorAttachment].
467
+ *
468
+ * v0.1.69 spec shape: `view: GPUTexture | GPUTextureView`. The forgeax RHI
469
+ * tightens the union to the single `TextureView` brand (the only branch the
470
+ * shim ever produces post-M1). Charter proposition 5 consistent abstraction
471
+ * (the field type matches what `RhiDevice.createTextureView` returns) +
472
+ * proposition 4 explicit failure (passing a `Texture` brand here is a tsc red
473
+ * signal; the AI user is steered to the spec idiom).
474
+ *
475
+ * **Migration**: see AGENTS.md break-point list 2026-05-10 #1
476
+ * "view: Texture -> TextureView narrow (major breaking)" for the call-site
477
+ * upgrade diff (3 narrowed fields: this `view` + `resolveTarget` +
478
+ * `RenderPassDepthStencilAttachment.view`).
479
+ *
480
+ * @example
481
+ * const view = device.createTextureView(tex, {}).unwrap();
482
+ * const att: RenderPassColorAttachment = {
483
+ * view,
484
+ * clearValue: { r: 0, g: 0, b: 0, a: 1 },
485
+ * loadOp: 'clear',
486
+ * storeOp: 'store',
487
+ * };
488
+ */
489
+ export type RenderPassColorAttachment = ExplicitUndefined<Omit<Pick<GPURenderPassColorAttachment, 'view' | 'depthSlice' | 'resolveTarget' | 'clearValue' | 'loadOp' | 'storeOp'>, 'view' | 'resolveTarget'>> & {
490
+ /**
491
+ * TextureView target of this color attachment (view narrow Path X: aligned
492
+ * to the forgeax `TextureView` brand returned by `createTextureView`; not
493
+ * the spec union `GPUTexture | GPUTextureView`).
494
+ */
495
+ view: TextureView;
496
+ /**
497
+ * Optional resolve target for multisample resolution. Same view narrow
498
+ * alignment as `view`.
499
+ */
500
+ resolveTarget?: TextureView | undefined;
501
+ };
502
+ /**
503
+ * Render-pass depth/stencil attachment (Pick<GPURenderPassDepthStencilAttachment,
504
+ * ...> with `view` field aligned to the forgeax `TextureView` opaque handle,
505
+ * per feat-20260510-rhi-resource-creation M2 view narrow Path X / breakage
506
+ * point #1).
507
+ *
508
+ * Spec anchor: W3C WebGPU 22.7 Render pass /
509
+ * [@webgpu/types.GPURenderPassDepthStencilAttachment].
510
+ *
511
+ * Same view narrow alignment as RenderPassColorAttachment.
512
+ *
513
+ * **Migration**: see AGENTS.md break-point list 2026-05-10 #1 for the
514
+ * call-site upgrade diff covering this `view` field plus the two
515
+ * `RenderPassColorAttachment` narrowed fields.
516
+ *
517
+ * @example
518
+ * const view = device.createTextureView(depthTex, {}).unwrap();
519
+ * const ds: RenderPassDepthStencilAttachment = {
520
+ * view,
521
+ * depthClearValue: 1,
522
+ * depthLoadOp: 'clear',
523
+ * depthStoreOp: 'store',
524
+ * };
525
+ */
526
+ export type RenderPassDepthStencilAttachment = ExplicitUndefined<Omit<Pick<GPURenderPassDepthStencilAttachment, 'view' | 'depthClearValue' | 'depthLoadOp' | 'depthStoreOp' | 'depthReadOnly' | 'stencilClearValue' | 'stencilLoadOp' | 'stencilStoreOp' | 'stencilReadOnly'>, 'view'>> & {
527
+ /**
528
+ * TextureView target of this depth/stencil attachment (view narrow Path X:
529
+ * aligned to the forgeax `TextureView` brand).
530
+ */
531
+ view: TextureView;
532
+ };
533
+ /**
534
+ * Render-pass descriptor (Pick<GPURenderPassDescriptor, ...>).
535
+ *
536
+ * Spec anchor: W3C WebGPU 22.7 Render pass /
537
+ * [@webgpu/types.GPURenderPassDescriptor].
538
+ *
539
+ * `colorAttachments` element type uses the forgeax narrow
540
+ * `RenderPassColorAttachment` (with `view: TextureView`);
541
+ * `depthStencilAttachment` uses `RenderPassDepthStencilAttachment`.
542
+ *
543
+ * @example
544
+ * const view = device.createTextureView(tex, {}).unwrap();
545
+ * const desc: RenderPassDescriptor = {
546
+ * label: 'frame',
547
+ * colorAttachments: [{ view, loadOp: 'clear', storeOp: 'store',
548
+ * clearValue: { r: 0, g: 0, b: 0, a: 1 } }],
549
+ * };
550
+ */
551
+ export type RenderPassDescriptor = ExplicitUndefined<Omit<Pick<GPURenderPassDescriptor, 'label' | 'colorAttachments' | 'depthStencilAttachment' | 'occlusionQuerySet' | 'timestampWrites' | 'maxDrawCount'>, 'colorAttachments' | 'depthStencilAttachment' | 'occlusionQuerySet' | 'timestampWrites'>> & {
552
+ /**
553
+ * Color attachments for this render pass (forgeax narrow element type per
554
+ * view narrow Path X: each entry's `view` field is `TextureView`, not the
555
+ * spec union `GPUTexture | GPUTextureView`).
556
+ */
557
+ colorAttachments: Iterable<RenderPassColorAttachment | null | undefined>;
558
+ /** Optional depth/stencil attachment with the same view narrow alignment. */
559
+ depthStencilAttachment?: RenderPassDepthStencilAttachment | undefined;
560
+ /** Occlusion query set (capability-gated). The shim accepts a
561
+ * `QuerySet` brand created via `device.createQuerySet({ type: 'occlusion',
562
+ * count })` and pairs it with `pass.beginOcclusionQuery(idx) /
563
+ * pass.endOcclusionQuery()`; both methods now have real implementations
564
+ * (see `RhiRenderPassEncoder` below). Capability gate: read
565
+ * `device.caps.timestampQuery` ahead of `'timestamp'` query sets;
566
+ * occlusion sets are unconditionally available. */
567
+ occlusionQuerySet?: QuerySet | undefined;
568
+ /** Optional timestamp query set and the pass boundary write slots. */
569
+ timestampWrites?: RenderPassTimestampWrites | undefined;
570
+ };
571
+ /** Timestamp writes attached to a render pass descriptor. */
572
+ export interface RenderPassTimestampWrites {
573
+ querySet: QuerySet;
574
+ beginningOfPassWriteIndex?: number | undefined;
575
+ endOfPassWriteIndex?: number | undefined;
576
+ }
577
+ /** Compute-pass descriptor with forgeax-owned timestamp query handles. */
578
+ export type ComputePassDescriptor = ExplicitUndefined<Omit<Pick<GPUComputePassDescriptor, 'label' | 'timestampWrites'>, 'timestampWrites'>> & {
579
+ timestampWrites?: ComputePassTimestampWrites | undefined;
580
+ };
581
+ /** Timestamp writes attached to a compute pass descriptor. */
582
+ export interface ComputePassTimestampWrites {
583
+ querySet: QuerySet;
584
+ beginningOfPassWriteIndex?: number | undefined;
585
+ endOfPassWriteIndex?: number | undefined;
586
+ }
587
+ /** Hardware-probe layer: readonly boolean capability flags. */
588
+ export interface RhiCaps {
589
+ /**
590
+ * The rendering backend kind — single source of truth for backend-aware
591
+ * logic (e.g. explicit barrier insertion vs. spec-managed / GL-implicit
592
+ * sync). Closed 4-member union: every backend reports exactly one.
593
+ *
594
+ * - `'webgpu'`: browser WebGPU — spec-managed barriers, no explicit
595
+ * barrier insertion needed.
596
+ * - `'wgpu-native'`: wgpu native-desktop runtime (Tauri / native) —
597
+ * requires explicit Vulkan/Metal/DX12 barrier commands.
598
+ * - `'wgpu-webgl2'`: wgpu GLES3/WebGL2 backend — GL implicit sync, no
599
+ * explicit barrier insertion needed (equivalence group with `'webgpu'`).
600
+ * - `'null'`: headless no-op backend (`@forgeax/engine-rhi-null`) for
601
+ * structural unit tests — no GPU / DOM; records command-stream shape into
602
+ * a ledger instead of executing it. No barrier insertion needed (the
603
+ * no-op backend executes nothing); falls into the same no-barrier branch
604
+ * as `'webgpu'` / `'wgpu-webgl2'`.
605
+ *
606
+ * @note `exactOptionalPropertyTypes` requires every backend fill this
607
+ * field; a backend that omits it produces a tsc compile error.
608
+ */
609
+ readonly backendKind: 'webgpu' | 'wgpu-native' | 'wgpu-webgl2' | 'null';
610
+ /** Whether compute pipelines are supported. */
611
+ readonly compute: boolean;
612
+ /** Whether timestamp queries are supported. */
613
+ readonly timestampQuery: boolean;
614
+ /** Backend-owned nanoseconds represented by one timestamp tick, or null when unavailable. */
615
+ readonly timestampPeriodNanoseconds: number | null;
616
+ /** Whether indirect drawing is supported. */
617
+ readonly indirectDrawing: boolean;
618
+ /**
619
+ * Whether BC texture compression (BC1-BC7) is available.
620
+ *
621
+ * Derived from `adapter.features.has('texture-compression-bc')`.
622
+ * On rhi-null this is always `false` (headless has no compression hardware,
623
+ * AC-06).
624
+ */
625
+ readonly textureCompressionBc: boolean;
626
+ /**
627
+ * Whether ETC2 texture compression is available.
628
+ *
629
+ * Derived from `adapter.features.has('texture-compression-etc2')`.
630
+ * On rhi-null this is always `false` (headless has no compression hardware,
631
+ * AC-06).
632
+ */
633
+ readonly textureCompressionEtc2: boolean;
634
+ /**
635
+ * Whether ASTC texture compression is available.
636
+ *
637
+ * Derived from `adapter.features.has('texture-compression-astc')`.
638
+ * On rhi-null this is always `false` (headless has no compression hardware,
639
+ * AC-06).
640
+ */
641
+ readonly textureCompressionAstc: boolean;
642
+ /**
643
+ * Whether multi-draw indirect is available (wgpu native extension).
644
+ *
645
+ * @reserved-for-wgpu-native-only always `false` on browser backends; only
646
+ * available when the forgeax renderer runs against a wgpu native runtime
647
+ * (Tauri / native runtime, not the wasm bundle). `caps.X = false` is an
648
+ * explicit signal, never an exception (charter proposition 4 / AGENTS.md
649
+ * `RHI / WebGPU` shape rule #2 capability-gated).
650
+ */
651
+ readonly multiDrawIndirect: boolean;
652
+ /**
653
+ * Whether push constants are available (wgpu native extension).
654
+ *
655
+ * @reserved-for-wgpu-native-only always `false` on browser backends; only
656
+ * available when the forgeax renderer runs against a wgpu native runtime
657
+ * (Tauri / native runtime, not the wasm bundle). `caps.X = false` is an
658
+ * explicit signal, never an exception (charter proposition 4 / AGENTS.md
659
+ * `RHI / WebGPU` shape rule #2 capability-gated).
660
+ */
661
+ readonly pushConstants: boolean;
662
+ /**
663
+ * Whether bindless texture array is available (wgpu native extension).
664
+ *
665
+ * @reserved-for-wgpu-native-only always `false` on browser backends; only
666
+ * available when the forgeax renderer runs against a wgpu native runtime
667
+ * (Tauri / native runtime, not the wasm bundle). `caps.X = false` is an
668
+ * explicit signal, never an exception (charter proposition 4 / AGENTS.md
669
+ * `RHI / WebGPU` shape rule #2 capability-gated).
670
+ */
671
+ readonly textureBindingArray: boolean;
672
+ /**
673
+ * Whether sampler binding aliasing is supported across pipelines.
674
+ *
675
+ * @spec-anchor W3C WebGPU §10.3 Bind group layout — spec mandates that a
676
+ * sampler may alias multiple binding slots; both navigator.gpu and the
677
+ * wgpu wasm bundle satisfy this so the field is always `true` on browser
678
+ * backends.
679
+ * @note Always `true` on shipped backends (WebGPU + wgpu wasm). The field
680
+ * exists for potential future backends that lack sampler aliasing.
681
+ * @hint AI users use `caps.samplerAliasing` to gate code that creates two
682
+ * `BindGroupEntry`s pointing at the same `Sampler` across different
683
+ * layouts; `caps.X = false` is an explicit signal, never an exception
684
+ * (charter proposition 4).
685
+ */
686
+ readonly samplerAliasing: boolean;
687
+ /**
688
+ * Whether the renderer can issue indirect draws with a non-zero
689
+ * `firstInstance`.
690
+ *
691
+ * @spec-anchor W3C WebGPU §22.4 drawIndirect — the `indirect-first-instance`
692
+ * feature on `GPUAdapter.features` gates non-zero `firstInstance` in
693
+ * indirect draws; rhi-webgpu maps this to `device.features.has(
694
+ * 'indirect-first-instance')`.
695
+ * @note `false` on backends without indirect drawing support.
696
+ * @hint Most AI users never need this; the field surfaces so a renderer
697
+ * author building instanced draw batchers can gate the fast path. With
698
+ * `caps.firstInstanceIndirect === false` the renderer must pre-rebase
699
+ * instance indices in the vertex shader (charter proposition 5
700
+ * consistent abstraction over a discoverable cap difference).
701
+ */
702
+ readonly firstInstanceIndirect: boolean;
703
+ /**
704
+ * Whether storage buffer bindings are available
705
+ * (`device.limits.maxStorageBuffersPerShaderStage > 0`).
706
+ *
707
+ * @spec-anchor W3C WebGPU §3.6.2 GPUSupportedLimits.
708
+ * maxStorageBuffersPerShaderStage; `> 0` means the device supports the
709
+ * `storage` / `read-only-storage` binding types.
710
+ * @note `false` on backends without storage buffer support.
711
+ * @hint AI users gate compute / large-buffer paths on
712
+ * `caps.storageBuffer`; the per-stage numeric limit lives on
713
+ * `device.limits.maxStorageBuffersPerShaderStage` for capacity planning
714
+ * (charter proposition 4: `caps.X = false` is an explicit signal).
715
+ */
716
+ readonly storageBuffer: boolean;
717
+ /**
718
+ * Whether storage texture bindings are available
719
+ * (`device.limits.maxStorageTexturesPerShaderStage > 0`).
720
+ *
721
+ * @spec-anchor W3C WebGPU §3.6.2 GPUSupportedLimits.
722
+ * maxStorageTexturesPerShaderStage; `> 0` means the device supports the
723
+ * `write-only` / `read-write` storage texture binding types.
724
+ * @note `false` on backends without storage texture support.
725
+ * @hint AI users gate image-effects compute / postprocess paths on
726
+ * `caps.storageTexture`; the per-stage numeric limit lives on
727
+ * `device.limits.maxStorageTexturesPerShaderStage` (charter proposition
728
+ * 4: `caps.X = false` is an explicit signal).
729
+ */
730
+ readonly storageTexture: boolean;
731
+ /**
732
+ * Whether the device can create `rgba16float` textures with `RENDER_ATTACHMENT`
733
+ * usage, enabling the HDR cubemap path for IBL irradiance / specular prefilter
734
+ * and downstream HDR render-target chains.
735
+ *
736
+ * @spec-anchor W3C WebGPU $25.1 GPUTextureFormat — `rgba16float` is an
737
+ * optional texture format whose `RENDER_ATTACHMENT` capability is probed by
738
+ * attempting `createTexture({ format: 'rgba16float', usage:
739
+ * GPUTextureUsage.RENDER_ATTACHMENT, size: [1, 1, 1] })` on the live
740
+ * device; failure maps the cap to `false`.
741
+ * @note Probed at device-creation time via a synchronous `createTexture`
742
+ * call, not via `GPUAdapter.features`. The `rgba16float` format is widely
743
+ * supported but `RENDER_ATTACHMENT` with float formats is optional per spec
744
+ * so the cap reflects the concrete device, not the adapter feature list.
745
+ * @hint AI users gate IBL / HDR post-processing paths on
746
+ * `caps.rgba16floatRenderable`; when `false` the internal equirect-to-cubemap
747
+ * IBL projection (driven by declaring `Skylight{equirect}`) degrades to the
748
+ * white-cube fallback and fires `{ code: 'equirect-projection-failed' }` with
749
+ * a machine-readable `expected` field naming this cap (charter P3 structured
750
+ * failure).
751
+ */
752
+ readonly rgba16floatRenderable: boolean;
753
+ /**
754
+ * Whether the device can create `rg11b10ufloat` textures with
755
+ * `RENDER_ATTACHMENT` usage, enabling the HDR swapchain / render-target path
756
+ * with reduced bit-depth precision versus `rgba16float`.
757
+ *
758
+ * @spec-anchor W3C WebGPU $25.1 GPUTextureFormat — `rg11b10ufloat` is
759
+ * `RENDER_ATTACHMENT`-capable only when the optional feature
760
+ * `rg11b10ufloat-renderable` is enabled (W3C WebGPU $4.2). Probed by gating
761
+ * on `device.features.has('rg11b10ufloat-renderable')` first; only then
762
+ * confirmed by `createTexture({ format: 'rg11b10ufloat', usage:
763
+ * GPUTextureUsage.RENDER_ATTACHMENT, size: [1, 1, 1] })`.
764
+ * @note The format packs 11+11+10 unsigned float bits into 32 bits per pixel;
765
+ * it is a popular HDR swapchain format for engines that trade precision
766
+ * for bandwidth but its `RENDER_ATTACHMENT` capability is not universal.
767
+ * The feature gate is the authoritative answer (avoiding fan-out via
768
+ * `device.onuncapturederror` when the optional feature is absent); the
769
+ * subsequent probe handles the rare case where the feature is reported
770
+ * but the concrete device still rejects.
771
+ * @hint AI users can select an HDR back-buffer format by reading
772
+ * `caps.rg11b10ufloatRenderable` before creating a
773
+ * `GPUTextureUsage.RENDER_ATTACHMENT` texture at that format; when `false`
774
+ * fall back to `rgba16float` (if `caps.rgba16floatRenderable` is true)
775
+ * or an SDR format.
776
+ */
777
+ readonly rg11b10ufloatRenderable: boolean;
778
+ /**
779
+ * Whether the device supports sampling `rgba32float` textures with a
780
+ * `filtering` sampler (linear / mipmap filtering), NOT just with a
781
+ * `non-filtering` sampler.
782
+ *
783
+ * @spec-anchor W3C WebGPU $10.3 Bind group layout — a bind group layout
784
+ * entry pairing a `filtering` sampler type with `sampleType: 'float'`
785
+ * (matching `rgba32float`) validates only when the
786
+ * `float32-filterable` feature is enabled. The cap probes this via
787
+ * `device.createBindGroupLayout({ entries: [{ sampler: { type:
788
+ * 'filtering' } }, { texture: { sampleType: 'float' } }] })` and
789
+ * `device.createSampler({ minFilter: 'linear', magFilter: 'linear' })`;
790
+ * failure maps the cap to `false`.
791
+ * @note Probed by gating on `device.features.has('float32-filterable')`
792
+ * first (the authoritative answer per spec $4.2; avoids fan-out via
793
+ * `device.onuncapturederror` when the optional feature is absent); only
794
+ * then confirmed by exercising the bind-group-layout. The subsequent
795
+ * probe handles the rare case where the feature is reported but the
796
+ * concrete device still rejects (spec ambiguity, driver quirks).
797
+ * @hint AI users gate float32-sampled compute / post-process paths on
798
+ * `caps.float32Filterable`; when `false` use `sampleType: 'unfilterable-
799
+ * float'` with a `non-filtering` sampler and compute the filter kernel
800
+ * manually in the shader, or fall back to `rgba16float` with filtering.
801
+ */
802
+ readonly float32Filterable: boolean;
803
+ /**
804
+ * Maximum number of color attachments per render pass.
805
+ *
806
+ * @spec-anchor W3C WebGPU $3.6.2 GPUSupportedLimits.maxColorAttachments;
807
+ * spec minimum = 4, defaults to 8 on mainstream backends.
808
+ * HDRP deferred pipeline requires >= 4 (3 g-buffer RT + 1 depth);
809
+ * installPipeline checks this cap at install time and throws
810
+ * `hdrp-deferred-caps-insufficient` on violation (charter P3).
811
+ * @note add-only minor (feat-20260612-hdrp-deferred-shading-learn-render-5-8
812
+ * M1 / w5); no existing field is modified.
813
+ */
814
+ readonly maxColorAttachments: number;
815
+ }
816
+ /**
817
+ * Enabled feature set, opaque iteration only via `has()`.
818
+ *
819
+ * Aligned with `GPUSupportedFeatures` shape; this empty interface intentionally
820
+ * adds no fields — the concrete enabled set is decided at `requestDevice` time
821
+ * and is then probed by AI users via `device.features.has('feature-name')`
822
+ * (charter proposition 5 consistent abstraction; no implementation-detail leak,
823
+ * no enumeration helper that would tie callers to a fixed feature list).
824
+ *
825
+ * @see {@link GPUSupportedFeatures}
826
+ */
827
+ export interface RhiFeatures extends ReadonlySet<GPUFeatureName> {
828
+ }
829
+ /** Numeric-limits layer aligned with GPUSupportedLimits (incl Compatibility
830
+ * Mode follow-on fields). */
831
+ export type RhiLimits = Readonly<GPUSupportedLimits>;
832
+ /**
833
+ * RhiAdapter request options.
834
+ *
835
+ * Spec anchor: W3C WebGPU §3.2 `GPURequestAdapterOptions` /
836
+ * [@webgpu/types.GPURequestAdapterOptions]. Fields pass through to the
837
+ * underlying `navigator.gpu.requestAdapter(opts)` call.
838
+ */
839
+ export type RequestAdapterOptions = ExplicitUndefined<Pick<GPURequestAdapterOptions, 'powerPreference' | 'forceFallbackAdapter'>>;
840
+ /**
841
+ * RhiAdapter.requestDevice options.
842
+ *
843
+ * Spec anchor: W3C WebGPU §3.4 `GPUDeviceDescriptor` /
844
+ * [@webgpu/types.GPUDeviceDescriptor]. Fields pass through to the underlying
845
+ * `adapter.requestDevice(opts)` call.
846
+ */
847
+ export type RequestDeviceOptions = ExplicitUndefined<Pick<GPUDeviceDescriptor, 'label' | 'requiredFeatures' | 'requiredLimits'>>;
848
+ /**
849
+ * RhiInstance — entry point for adapter discovery (K-6 strict two-step path).
850
+ *
851
+ * Spec anchor: W3C WebGPU §3.1 `GPU` interface / [@webgpu/types.GPU]; wgpu
852
+ * `Instance::request_adapter` (research §6.1) + Dawn
853
+ * `InstanceBase::APIRequestAdapter` (§6.2).
854
+ *
855
+ * Replaces the legacy top-level `rhi.requestDevice(opts)` factory (break-
856
+ * point #2). AI users follow the spec idiom:
857
+ * const a = (await rhi.requestAdapter()).unwrap();
858
+ * const d = (await a.requestDevice(opts)).unwrap();
859
+ *
860
+ * @example
861
+ * const adapterResult = await rhi.requestAdapter();
862
+ * if (!adapterResult.ok) {
863
+ * // route via switch (adapterResult.error.code)
864
+ * }
865
+ */
866
+ export interface RhiInstance {
867
+ /**
868
+ * Request a GPU adapter.
869
+ *
870
+ * Spec anchor: W3C WebGPU §3.1 `GPU.requestAdapter` /
871
+ * [@webgpu/types.GPU.requestAdapter].
872
+ *
873
+ * @param opts — W3C-spec request adapter options (powerPreference,
874
+ * forceFallbackAdapter).
875
+ * @param compatibleSurface — non-W3C extension required by the wgpu GL
876
+ * backend for adapter enumeration. Provided as a positional escape hatch
877
+ * so the first parameter stays spec-aligned (plan-strategy D-5).
878
+ * rhi-webgpu accepts and ignores this parameter (dual-impl symmetry);
879
+ * rhi-wgpu routes it to `requestAdapterWithCanvas`.
880
+ *
881
+ * Failure paths (research §F-5):
882
+ * - adapter null -> `Result.err({ code: 'adapter-unavailable' })`.
883
+ */
884
+ requestAdapter(opts?: RequestAdapterOptions | undefined, compatibleSurface?: HTMLCanvasElement | OffscreenCanvas | undefined): Promise<Result<RhiAdapter, RhiError>>;
885
+ }
886
+ /**
887
+ * RhiAdapter — capability-probe layer + device-creation entry (K-5 + K-6).
888
+ *
889
+ * Spec anchor: W3C WebGPU §3.2 `GPUAdapter` interface /
890
+ * [@webgpu/types.GPUAdapter]; wgpu `Adapter::request_device` (research §6.1)
891
+ * + Dawn `AdapterBase::APIRequestDevice` (§6.2).
892
+ *
893
+ * The `features` / `limits` fields let AI users **pre-screen** device
894
+ * capabilities before calling `requestDevice(opts)` (charter proposition 4
895
+ * forward-reachable: features mismatch becomes visible before spec
896
+ * validation surfaces it).
897
+ *
898
+ * @example
899
+ * if (!adapter.features.has('timestamp-query')) {
900
+ * // skip timestamp-related code paths
901
+ * }
902
+ * const deviceResult = await adapter.requestDevice({
903
+ * requiredFeatures: ['timestamp-query'],
904
+ * });
905
+ */
906
+ export interface RhiAdapter {
907
+ /**
908
+ * Read-only feature-name set (K-5).
909
+ *
910
+ * Aligned with `GPUAdapter.features` projection of `GPUSupportedFeatures`
911
+ * (a read-only Set) **and** with `RhiDevice.features` (Round 3 fix-up
912
+ * F-P1-2: cross-tier shape uniformity — AI users use `.has(name)` on
913
+ * both abstraction layers, no projection drift).
914
+ *
915
+ * F-1 ai-user-review: mutation of set entries (`features.add('x')` /
916
+ * deletion / clear) is rejected at compile time via `ReadonlySet`;
917
+ * charter proposition 4 explicit failure + proposition 5 consistent
918
+ * abstraction.
919
+ */
920
+ readonly features: ReadonlySet<GPUFeatureName>;
921
+ /**
922
+ * Read-only numeric-limits map (K-5).
923
+ *
924
+ * Aligned with `GPUAdapter.limits` projection of `GPUSupportedLimits`. The
925
+ * forgeax form flattens to a `Readonly<Record<string, number>>` so AI users
926
+ * can do `adapter.limits.maxTextureDimension2D` lookups without holding the
927
+ * spec object handle.
928
+ *
929
+ * F-1 ai-user-review: mutation of values (`limits.x = 0`) is rejected at
930
+ * compile time.
931
+ */
932
+ readonly limits: Readonly<Record<string, number>>;
933
+ /**
934
+ * Request a GPU device.
935
+ *
936
+ * Spec anchor: W3C WebGPU §3.2 `GPUAdapter.requestDevice` /
937
+ * [@webgpu/types.GPUAdapter.requestDevice].
938
+ *
939
+ * Returns `Result<RhiDevice, RhiError>` (K-6: NOT a `(Device, Queue)` tuple
940
+ * — queue is exposed via `RhiDevice.queue`).
941
+ *
942
+ * Failure paths (research §F-5):
943
+ * - feature not enabled -> `Result.err({ code: 'feature-not-enabled' })`.
944
+ * - limit exceeded -> `Result.err({ code: 'limit-exceeded' })`.
945
+ */
946
+ requestDevice(opts?: RequestDeviceOptions | undefined): Promise<Result<RhiDevice, RhiError>>;
947
+ }
948
+ /**
949
+ * Canvas configuration descriptor (Pick<GPUCanvasConfiguration, 7 fields>).
950
+ *
951
+ * Spec anchor: W3C WebGPU §3.3 `GPUCanvasConfiguration` /
952
+ * [@webgpu/types.GPUCanvasConfiguration].
953
+ *
954
+ * 7 fields (research §3.2):
955
+ * - `device` (required): the GPUDevice for the configured context.
956
+ * - `format` (required): one of `{'bgra8unorm', 'rgba8unorm', 'rgba16float'}`
957
+ * (the spec normative supported context formats).
958
+ * - `usage` (default `0x10` = RENDER_ATTACHMENT): bitmask of GPUTextureUsage
959
+ * for the swap-chain textures.
960
+ * - `viewFormats` (default `[]`): list of formats createView may yield;
961
+ * **the spec sRGB-render-target idiom** uses `format='bgra8unorm'` +
962
+ * `viewFormats=['bgra8unorm-srgb']` + `device.createTextureView` (research
963
+ * §3.2 normative).
964
+ * - `colorSpace` (default `'srgb'`): predefined color space for the canvas.
965
+ * - `toneMapping` (default `{}` ≅ `{ mode: 'standard' }`): HDR tone-mapping
966
+ * descriptor; the spec NOTE in research §3.2 says implementations
967
+ * without tone-mapping support **omit** this from `getConfiguration()`.
968
+ * - `alphaMode` (default `'opaque'`): canvas compositing mode.
969
+ *
970
+ * Field NAMES align byte-for-byte with the spec; the forgeax `?: T |
971
+ * undefined` shape (S-7 / hard-constraint 10) lets writers omit or pass
972
+ * `undefined` explicitly while the shim distinguishes via `'x' in src`.
973
+ */
974
+ export type CanvasConfiguration = ExplicitUndefined<Omit<Pick<GPUCanvasConfiguration, 'device' | 'format' | 'usage' | 'viewFormats' | 'colorSpace' | 'toneMapping' | 'alphaMode'>, 'device'>> & {
975
+ /**
976
+ * The forgeax RhiDevice the configured context binds to (D-S5 pattern: spec
977
+ * `device: GPUDevice` is replaced by the forgeax brand so AI users pass the
978
+ * device they got from `rhi.requestAdapter().requestDevice()`).
979
+ */
980
+ device: RhiDevice;
981
+ };
982
+ declare const RhiSurfaceBrand: unique symbol;
983
+ /**
984
+ * RhiSurface — opaque abstraction over a canvas surface
985
+ * (HTMLCanvasElement / OffscreenCanvas).
986
+ *
987
+ * Spec couples GPUCanvasContext to a canvas (research §3.1); the forgeax
988
+ * abstraction wraps the raw GPUCanvasContext in an opaque brand. AI users
989
+ * obtain the `RhiCanvasContext` via
990
+ * `rhi.acquireCanvasContext(canvas)` (returns `Result<RhiCanvasContext, RhiError>`).
991
+ *
992
+ * Charter proposition 5 consistent abstraction: the surface brand decouples
993
+ * AI-user code from the DOM canvas zoo (HTMLCanvasElement / OffscreenCanvas /
994
+ * native Window).
995
+ */
996
+ export interface RhiSurface {
997
+ readonly [RhiSurfaceBrand]: void;
998
+ }
999
+ /**
1000
+ * RhiCanvasContext — forgeax canvas-context abstraction (M3 / K-4).
1001
+ *
1002
+ * Spec anchor: W3C WebGPU §3.3 `GPUCanvasContext` /
1003
+ * [@webgpu/types.GPUCanvasContext]. 4 methods (research §3.1) match the spec
1004
+ * names; the return types differ:
1005
+ * - `configure` returns `Result<void, RhiError>` (the spec returns void; the
1006
+ * forgeax form surfaces `webgpu-runtime-error` on format-gate / device-
1007
+ * lost paths via Result, charter proposition 4 explicit failure).
1008
+ * - `unconfigure` returns void (spec literal alignment).
1009
+ * - `getConfiguration` returns `CanvasConfiguration | undefined` (the spec
1010
+ * returns `GPUCanvasConfiguration?`; forgeax uses `undefined`).
1011
+ * - `getCurrentTexture` returns `Result<Texture, RhiError>` (K-4: Texture
1012
+ * brand, NOT TextureView; AI users go two-step
1013
+ * `device.createTextureView(canvasContext.getCurrentTexture().unwrap(), {})`).
1014
+ *
1015
+ * Lifecycle (research §3.3 [[Expire the current texture]]): currentTexture
1016
+ * **must NOT be cached across frames** — every frame must call
1017
+ * `getCurrentTexture()` afresh.
1018
+ */
1019
+ export interface RhiCanvasContext {
1020
+ /**
1021
+ * Configure the canvas context with a forgeax CanvasConfiguration.
1022
+ *
1023
+ * Returns `Result<void, RhiError>` (charter proposition 4 explicit failure).
1024
+ *
1025
+ * Failure paths (research §3.3):
1026
+ * - `format` not in supported context formats (`{'bgra8unorm',
1027
+ * 'rgba8unorm', 'rgba16float'}`) -> `'webgpu-runtime-error'` with
1028
+ * `.expected = 'one of bgra8unorm/rgba8unorm/rgba16float'`.
1029
+ * - `device` invalid | lost -> `'rhi-not-available'`.
1030
+ *
1031
+ * @example
1032
+ * const out = canvasContext.configure({
1033
+ * device,
1034
+ * format: 'bgra8unorm',
1035
+ * usage: GPUTextureUsage.RENDER_ATTACHMENT,
1036
+ * viewFormats: ['rgba8unorm-srgb'],
1037
+ * });
1038
+ * if (!out.ok) {
1039
+ * // route via switch (out.error.code)
1040
+ * }
1041
+ */
1042
+ configure(desc: CanvasConfiguration): Result<void, RhiError>;
1043
+ /**
1044
+ * Unconfigure the canvas context (spec literal void return).
1045
+ *
1046
+ * Idempotent (already-unconfigured contexts continue to be unconfigured;
1047
+ * Operation is silent).
1048
+ */
1049
+ unconfigure(): void;
1050
+ /**
1051
+ * Return the current canvas configuration, or `undefined` if the context is
1052
+ * unconfigured.
1053
+ *
1054
+ * Feature-detection entry (research §3.2 spec NOTE): when an implementation
1055
+ * does not support a configuration field (e.g. tone-mapping), the field is
1056
+ * **omitted** from the returned record (NOT defaulted) so AI users can use
1057
+ * `'toneMapping' in conf` to detect support.
1058
+ */
1059
+ getConfiguration(): CanvasConfiguration | undefined;
1060
+ /**
1061
+ * Get the current swap-chain texture (K-4: returns Texture brand, NOT
1062
+ * TextureView).
1063
+ *
1064
+ * Spec anchor: W3C WebGPU §3.3 `GPUCanvasContext.getCurrentTexture` /
1065
+ * [@webgpu/types.GPUCanvasContext.getCurrentTexture].
1066
+ *
1067
+ * Failure paths (research §3.3):
1068
+ * - context unconfigured -> `'webgpu-runtime-error'` (spec
1069
+ * `InvalidStateError` mapping).
1070
+ *
1071
+ * AI users typically pair this with `device.createTextureView` to get the
1072
+ * render-pass attachment view (charter proposition 5 consistent abstraction):
1073
+ * const tex = canvasContext.getCurrentTexture().unwrap();
1074
+ * const view = device.createTextureView(tex, {}).unwrap();
1075
+ * pass.beginRenderPass({ colorAttachments: [{ view, ... }] });
1076
+ *
1077
+ * Lifecycle (research §3.3 [[Expire the current texture]]): each frame
1078
+ * **must call this fresh**; the forgeax shim does NOT cache across frames.
1079
+ */
1080
+ getCurrentTexture(): Result<Texture, RhiError>;
1081
+ }
1082
+ /** GPU device - sole entry point for resource creation + capability probing. */
1083
+ export interface RhiDevice {
1084
+ /** Hardware-probe layer (charter proposition 5). */
1085
+ readonly caps: RhiCaps;
1086
+ /** Enabled-features layer. */
1087
+ readonly features: RhiFeatures;
1088
+ /** Numeric-limits layer. */
1089
+ readonly limits: RhiLimits;
1090
+ /** Create GPU buffer. */
1091
+ createBuffer(desc: BufferDescriptor): Result<Buffer, RhiError>;
1092
+ /** Create GPU texture. */
1093
+ createTexture(desc: TextureDescriptor): Result<Texture, RhiError>;
1094
+ /**
1095
+ * Destroy a GPU buffer obtained from `createBuffer`.
1096
+ *
1097
+ * Spec anchor: W3C WebGPU §gpubuffer-destroy /
1098
+ * [@webgpu/types.GPUBuffer.destroy]; wgpu wasm
1099
+ * `RhiWgpuBuffer::destroy` (research §F-1; both surfaces are idempotent
1100
+ * void at the underlying GPU).
1101
+ *
1102
+ * The forgeax form prefers fail-fast over the spec idempotent void:
1103
+ * the shim layer (rhi-webgpu / rhi-wgpu) tracks per-handle
1104
+ * `destroyed: boolean` and surfaces a second destroy as
1105
+ * `Result.err({ code: 'destroy-after-destroy' })` rather than silently
1106
+ * succeeding. Double destroy is almost always a lifecycle bug — caching
1107
+ * a stale handle, a forgotten registry slot, a race between dispose
1108
+ * paths — and surfacing it early at the call site is more useful than
1109
+ * swallowing it (plan-strategy D-7 + architecture-principles §5 Fail
1110
+ * Fast). Charter proposition 4 explicit failure.
1111
+ *
1112
+ * Failure paths:
1113
+ * - second destroy on the same handle ->
1114
+ * `Result.err({ code: 'destroy-after-destroy' })`.
1115
+ *
1116
+ * @example
1117
+ * const r = device.destroyBuffer(buf);
1118
+ * if (!r.ok) {
1119
+ * // route via switch (r.error.code)
1120
+ * }
1121
+ */
1122
+ destroyBuffer(buf: Buffer): Result<void, RhiError>;
1123
+ /**
1124
+ * Destroy a GPU query set obtained from `createQuerySet`.
1125
+ *
1126
+ * Query sets are device-owned resources even though the WebGPU surface does
1127
+ * not expose them through the Buffer/Texture families. The explicit RHI
1128
+ * seam lets Render release timestamp query sets exactly once after queue
1129
+ * completion, including failure and disposal paths.
1130
+ */
1131
+ destroyQuerySet(querySet: QuerySet): Result<void, RhiError>;
1132
+ /**
1133
+ * Destroy a GPU texture obtained from `createTexture`.
1134
+ *
1135
+ * Spec anchor: W3C WebGPU §gputexture-destroy /
1136
+ * [@webgpu/types.GPUTexture.destroy]; wgpu wasm idempotent void at the
1137
+ * underlying GPU.
1138
+ *
1139
+ * Same fail-fast contract as `destroyBuffer`: the shim layer tracks
1140
+ * per-handle `destroyed: boolean` and surfaces a second destroy as
1141
+ * `Result.err({ code: 'destroy-after-destroy' })`.
1142
+ *
1143
+ * @example
1144
+ * const r = device.destroyTexture(tex);
1145
+ * if (!r.ok) {
1146
+ * // route via switch (r.error.code)
1147
+ * }
1148
+ */
1149
+ destroyTexture(tex: Texture): Result<void, RhiError>;
1150
+ /**
1151
+ * Create a GPU texture view of an existing texture.
1152
+ *
1153
+ * Spec anchor: W3C WebGPU §texture-view-creation /
1154
+ * [@webgpu/types.GPUTexture.createView].
1155
+ *
1156
+ * Introduced in feat-20260510-rhi-resource-creation (M1). Cross-resource
1157
+ * validation is performed fast-path by the shim before forwarding to raw
1158
+ * GPUTexture.createView (research §1.1):
1159
+ * - `format` must be in `source.format ∪ source.viewFormats`; violation
1160
+ * returns Result.err({ code: 'webgpu-runtime-error' }).
1161
+ * - `usage` must be a subset of source.usage (bitmask); violation returns
1162
+ * the same code.
1163
+ *
1164
+ * @example
1165
+ * const r = device.createTextureView(tex, { format: 'rgba8unorm', dimension: '2d' });
1166
+ * if (!r.ok) {
1167
+ * // route via switch (r.error.code)
1168
+ * }
1169
+ */
1170
+ createTextureView(texture: Texture, desc: TextureViewDescriptor): Result<TextureView, RhiError>;
1171
+ /** Create sampler (spec defaults are applied by the shim). */
1172
+ createSampler(desc?: SamplerDescriptor | undefined): Result<Sampler, RhiError>;
1173
+ /** Create bind group layout. */
1174
+ createBindGroupLayout(desc: BindGroupLayoutDescriptor): Result<BindGroupLayout, RhiError>;
1175
+ /**
1176
+ * Create a bind group (instantiated layout + resource bindings).
1177
+ *
1178
+ * Spec anchor: W3C WebGPU 10 Resource binding /
1179
+ * [@webgpu/types.GPUDevice.createBindGroup].
1180
+ *
1181
+ * Introduced in feat-20260509-ecs-render-bridge-mvp (D-S1) — additive
1182
+ * extension; reuses the existing 17-member `RhiErrorCode` union
1183
+ * ('feature-not-enabled' / 'limit-exceeded' / 'webgpu-runtime-error').
1184
+ * No new error code is introduced (AGENTS.md evolution contract no-op,
1185
+ * breakage list stays empty).
1186
+ *
1187
+ * @example
1188
+ * const out = device.createBindGroup({ label: 'view-bg', layout: bgl, entries: [...] });
1189
+ * if (!out.ok) {
1190
+ * // route via switch (out.error.code)
1191
+ * }
1192
+ */
1193
+ createBindGroup(desc: BindGroupDescriptor): Result<BindGroup, RhiError>;
1194
+ /**
1195
+ * Create a pipeline layout (aggregates BindGroupLayouts).
1196
+ *
1197
+ * Spec anchor: W3C WebGPU 10.3 Pipeline layout /
1198
+ * [@webgpu/types.GPUDevice.createPipelineLayout].
1199
+ *
1200
+ * Introduced in feat-20260509-ecs-render-bridge-mvp (D-S1) — additive
1201
+ * extension; reuses the existing 17-member `RhiErrorCode` union.
1202
+ *
1203
+ * @example
1204
+ * const out = device.createPipelineLayout({ label: 'pbr-pl', bindGroupLayouts: [viewBgl, materialBgl, meshArrayBgl] });
1205
+ * if (!out.ok) {
1206
+ * // route via switch (out.error.code)
1207
+ * }
1208
+ */
1209
+ createPipelineLayout(desc: PipelineLayoutDescriptor): Result<PipelineLayout, RhiError>;
1210
+ /** Create render pipeline (synchronous path). */
1211
+ createRenderPipeline(desc: RenderPipelineDescriptor): Result<RenderPipeline, RhiError>;
1212
+ /**
1213
+ * Create a compute pipeline (synchronous path).
1214
+ *
1215
+ * Spec anchor: W3C WebGPU §compute-pipeline-creation /
1216
+ * [@webgpu/types.GPUDevice.createComputePipeline].
1217
+ *
1218
+ * Introduced in feat-20260510-rhi-resource-creation (M1). Capability gate
1219
+ * (research §1.2 + plan-strategy §4.3 boundary case row 1):
1220
+ * `caps.compute === false` -> Result.err({ code: 'feature-not-enabled' }).
1221
+ * The MVP WebGPU path always has caps.compute=true; the gate exists for
1222
+ * potential future backends that lack compute.
1223
+ *
1224
+ * @example
1225
+ * const r = device.createComputePipeline({
1226
+ * layout: 'auto',
1227
+ * compute: { module, entryPoint: 'cs_main' },
1228
+ * });
1229
+ * if (!r.ok) {
1230
+ * // route via switch (r.error.code)
1231
+ * }
1232
+ */
1233
+ createComputePipeline(desc: ComputePipelineDescriptor): Result<ComputePipeline, RhiError>;
1234
+ /**
1235
+ * Create a query set (occlusion or timestamp).
1236
+ *
1237
+ * Spec anchor: W3C WebGPU §queries / [@webgpu/types.GPUDevice.createQuerySet].
1238
+ *
1239
+ * Introduced in feat-20260510-rhi-resource-creation (M1). Hard constraints
1240
+ * (research §1.3):
1241
+ * - `count <= 4096` (spec normative); violation -> 'limit-exceeded'.
1242
+ * - `type === 'timestamp'` requires caps.timestampQuery; otherwise ->
1243
+ * 'feature-not-enabled'.
1244
+ * - `count = 0` is legal.
1245
+ *
1246
+ * @example
1247
+ * const r = device.createQuerySet({ type: 'occlusion', count: 4 });
1248
+ * if (!r.ok) {
1249
+ * // route via switch (r.error.code)
1250
+ * }
1251
+ */
1252
+ createQuerySet(desc: QuerySetDescriptor): Result<QuerySet, RhiError>;
1253
+ /**
1254
+ * Create a command encoder.
1255
+ *
1256
+ * Spec anchor: W3C WebGPU 21.2 createCommandEncoder /
1257
+ * [@webgpu/types.GPUDevice.createCommandEncoder].
1258
+ *
1259
+ * @example
1260
+ * const encResult = device.createCommandEncoder({ label: 'frame' });
1261
+ * if (!encResult.ok) {
1262
+ * // route via switch (encResult.error.code)
1263
+ * } else {
1264
+ * const enc = encResult.value;
1265
+ * // ... record commands ...
1266
+ * }
1267
+ */
1268
+ createCommandEncoder(desc?: CommandEncoderDescriptor | undefined): Result<RhiCommandEncoder, RhiError>;
1269
+ /** Queue for command submission. */
1270
+ readonly queue: RhiQueue;
1271
+ /**
1272
+ * Spec-style device.lost Promise (research F-4 / R2 mitigation). The engine
1273
+ * layer performs single-source subscription + dual-form fan-out without a
1274
+ * second cache. `reason` is a binary union ('destroyed' / 'unknown').
1275
+ */
1276
+ readonly lost: Promise<{
1277
+ readonly reason: 'destroyed' | 'unknown';
1278
+ readonly message: string;
1279
+ }>;
1280
+ }
1281
+ /** GPU command queue - writeBuffer / submit + M5 writeTexture /
1282
+ * copyExternalImageToTexture / onSubmittedWorkDone. */
1283
+ export interface RhiQueue {
1284
+ /** Direct write to a buffer (POD + ArrayBufferView, math-free). */
1285
+ writeBuffer(buffer: Buffer, bufferOffset: number, data: ArrayBufferView | ArrayBuffer, dataOffset?: number | undefined, size?: number | undefined): Result<void, RhiError>;
1286
+ /**
1287
+ * Direct write to a texture region.
1288
+ *
1289
+ * Spec anchor: W3C WebGPU §queue-writetexture /
1290
+ * [@webgpu/types.GPUQueue.writeTexture]. Field NAMES align byte-for-byte
1291
+ * with the spec; the forgeax form returns Result<void, RhiError> instead
1292
+ * of void so AI users can route alignment failures (research §1.3 +
1293
+ * plan-strategy 2 K-2: bytesPerRow % 256 != 0 maps to
1294
+ * 'queue-write-buffer-out-of-bounds').
1295
+ *
1296
+ * @example
1297
+ * const out = device.queue.writeTexture(
1298
+ * { texture: tex, mipLevel: 0, origin: [0, 0, 0] },
1299
+ * pixels,
1300
+ * { offset: 0, bytesPerRow: 256, rowsPerImage: H },
1301
+ * { width: W, height: H, depthOrArrayLayers: 1 },
1302
+ * );
1303
+ * if (!out.ok) {
1304
+ * // route via switch (out.error.code)
1305
+ * }
1306
+ */
1307
+ writeTexture(destination: TextureWriteDestination, data: ArrayBufferView | ArrayBuffer, dataLayout: Pick<GPUTexelCopyBufferLayout, 'offset' | 'bytesPerRow' | 'rowsPerImage'>, size: GPUExtent3DStrict): Result<void, RhiError>;
1308
+ /**
1309
+ * Copy an external image source (ImageBitmap / canvas / video) into a
1310
+ * GPUTexture region.
1311
+ *
1312
+ * Spec anchor: W3C WebGPU §queue-copyexternalimagetotexture /
1313
+ * [@webgpu/types.GPUQueue.copyExternalImageToTexture]. The forgeax form
1314
+ * returns Result<void, RhiError>.
1315
+ *
1316
+ * dawn-node note: dawn-node lacks HTMLCanvasElement / VideoFrame /
1317
+ * HTMLImageElement; only the ImageBitmap subset reachable from
1318
+ * createImageBitmap is exercised in dawn tests (research §7.1).
1319
+ */
1320
+ copyExternalImageToTexture(source: Pick<GPUCopyExternalImageSourceInfo, 'source' | 'origin' | 'flipY'>, destination: ExternalImageTextureDestination, copySize: GPUExtent3DStrict): Result<void, RhiError>;
1321
+ /** Submit command buffers (single-use). */
1322
+ submit(commandBuffers: readonly CommandBuffer[]): Result<void, RhiError>;
1323
+ /**
1324
+ * Resolve when all currently-enqueued operations have completed.
1325
+ *
1326
+ * Spec anchor: W3C WebGPU §queue-onsubmittedworkdone /
1327
+ * [@webgpu/types.GPUQueue.onSubmittedWorkDone]. Returns
1328
+ * `Promise<undefined>` per spec normative (research §5.1: no reject path;
1329
+ * device-lost flows through `RhiDevice.lost` instead). Ordering
1330
+ * constraints (research §5.2):
1331
+ * - constraint #1 (FIFO): if p1 = q.onSubmittedWorkDone() is called
1332
+ * before p2 = q.onSubmittedWorkDone(), p1 must settle before p2.
1333
+ * - constraint #2 (mapAsync vs onSubmittedWorkDone): if p1 =
1334
+ * b.mapAsync() is called before p2 = q.onSubmittedWorkDone(), p1 must
1335
+ * settle before p2.
1336
+ *
1337
+ * @example Pattern A read-back idiom:
1338
+ * const cb = enc.finish().value;
1339
+ * queue.submit([cb]);
1340
+ * await queue.onSubmittedWorkDone();
1341
+ * await readBuf.mapAsync(GPUMapMode.READ);
1342
+ * const range = readBuf.getMappedRange().value;
1343
+ */
1344
+ onSubmittedWorkDone(): Promise<undefined>;
1345
+ }
1346
+ /** GPU command encoder - records render / compute passes + resource copies.
1347
+ *
1348
+ * Method NAMES align byte-for-byte with `@webgpu/types.GPUCommandEncoder` +
1349
+ * `GPUDebugCommandsMixin` (research F-1 / D-S4). 12 methods total:
1350
+ * - 9 direct: beginRenderPass / beginComputePass / copyBufferToBuffer /
1351
+ * copyBufferToTexture / copyTextureToBuffer / copyTextureToTexture /
1352
+ * clearBuffer / resolveQuerySet / finish
1353
+ * - 3 mixin (GPUDebugCommandsMixin): pushDebugGroup / popDebugGroup /
1354
+ * insertDebugMarker
1355
+ *
1356
+ * Lifecycle: after `finish()`, all subsequent recording calls return
1357
+ * Result.err({ code: 'command-encoder-finished' }) per D-S3 template 1
1358
+ * (where the method returns Result; void-returning methods throw the
1359
+ * structured error so AI users observe the failure consistently).
1360
+ */
1361
+ export interface RhiCommandEncoder {
1362
+ /** Begin render pass (auto-closes on end()).
1363
+ *
1364
+ * Spec anchor: [@webgpu/types.GPUCommandEncoder.beginRenderPass].
1365
+ *
1366
+ * @throws RhiError code === 'command-encoder-finished' when invoked on a finished encoder (dual-channel: spec-aligned void return throws on finished state; AI users wrap call sites with `try / catch (e: unknown) { if (e instanceof RhiError && e.code === 'command-encoder-finished') ... }`).
1367
+ * @example
1368
+ * const pass = encoder.beginRenderPass({ colorAttachments: [{ ... }] });
1369
+ */
1370
+ beginRenderPass(desc: RenderPassDescriptor): RhiRenderPassEncoder;
1371
+ /** Begin compute pass.
1372
+ *
1373
+ * Spec anchor: [@webgpu/types.GPUCommandEncoder.beginComputePass].
1374
+ *
1375
+ * @throws RhiError code === 'command-encoder-finished' when invoked on a finished encoder (dual-channel: see beginRenderPass for the recovery pattern).
1376
+ * @example
1377
+ * const pass = encoder.beginComputePass();
1378
+ */
1379
+ beginComputePass(desc?: ComputePassDescriptor | undefined): RhiComputePassEncoder;
1380
+ /** Copy a sub-region of a Buffer to another Buffer (5-arg full form).
1381
+ *
1382
+ * Spec anchor: [@webgpu/types.GPUCommandEncoder.copyBufferToBuffer].
1383
+ *
1384
+ * @throws RhiError code === 'command-encoder-finished' when invoked on a finished encoder (dual-channel; see beginRenderPass JSDoc for the recovery pattern). Both overloads share the same throw contract.
1385
+ * @example
1386
+ * encoder.copyBufferToBuffer(src, 0, dst, 0, 256);
1387
+ */
1388
+ copyBufferToBuffer(source: Buffer, sourceOffset: number, destination: Buffer, destinationOffset: number, size: number): void;
1389
+ /** Copy a Buffer to another Buffer (3-arg shorthand).
1390
+ *
1391
+ * Spec anchor: [@webgpu/types.GPUCommandEncoder.copyBufferToBuffer].
1392
+ *
1393
+ * @throws RhiError code === 'command-encoder-finished' when invoked on a finished encoder (dual-channel; see beginRenderPass JSDoc for the recovery pattern). Both overloads share the same throw contract.
1394
+ * @example
1395
+ * encoder.copyBufferToBuffer(src, dst, 256);
1396
+ */
1397
+ copyBufferToBuffer(source: Buffer, destination: Buffer, size?: number | undefined): void;
1398
+ /** Copy a Buffer sub-region to a Texture sub-region.
1399
+ *
1400
+ * Spec anchor: [@webgpu/types.GPUCommandEncoder.copyBufferToTexture].
1401
+ *
1402
+ * @throws RhiError code === 'command-encoder-finished' when invoked on a finished encoder (dual-channel; see beginRenderPass JSDoc for the recovery pattern).
1403
+ * @example
1404
+ * encoder.copyBufferToTexture(srcInfo, dstInfo, [w, h, 1]);
1405
+ */
1406
+ copyBufferToTexture(source: GPUTexelCopyBufferInfo, destination: GPUTexelCopyTextureInfo, copySize: GPUExtent3DStrict): void;
1407
+ /** Copy a Texture sub-region to a Buffer sub-region.
1408
+ *
1409
+ * Spec anchor: [@webgpu/types.GPUCommandEncoder.copyTextureToBuffer].
1410
+ *
1411
+ * @throws RhiError code === 'command-encoder-finished' when invoked on a finished encoder (dual-channel; see beginRenderPass JSDoc for the recovery pattern).
1412
+ * @example
1413
+ * encoder.copyTextureToBuffer(srcInfo, dstInfo, [w, h, 1]);
1414
+ */
1415
+ copyTextureToBuffer(source: GPUTexelCopyTextureInfo, destination: GPUTexelCopyBufferInfo, copySize: GPUExtent3DStrict): void;
1416
+ /** Copy a Texture sub-region to a Texture sub-region.
1417
+ *
1418
+ * Spec anchor: [@webgpu/types.GPUCommandEncoder.copyTextureToTexture].
1419
+ *
1420
+ * @throws RhiError code === 'command-encoder-finished' when invoked on a finished encoder (dual-channel; see beginRenderPass JSDoc for the recovery pattern).
1421
+ * @example
1422
+ * encoder.copyTextureToTexture(srcInfo, dstInfo, [w, h, 1]);
1423
+ */
1424
+ copyTextureToTexture(source: GPUTexelCopyTextureInfo, destination: GPUTexelCopyTextureInfo, copySize: GPUExtent3DStrict): void;
1425
+ /** Fill a Buffer sub-region with zeros.
1426
+ *
1427
+ * Spec anchor: [@webgpu/types.GPUCommandEncoder.clearBuffer].
1428
+ *
1429
+ * @throws RhiError code === 'command-encoder-finished' when invoked on a finished encoder (dual-channel; see beginRenderPass JSDoc for the recovery pattern).
1430
+ * @example
1431
+ * encoder.clearBuffer(buf, 0, 256);
1432
+ */
1433
+ clearBuffer(buffer: Buffer, offset?: number | undefined, size?: number | undefined): void;
1434
+ /** Resolve query results from a QuerySet to a Buffer.
1435
+ *
1436
+ * Real implementation (M3 / w26): writes 8-byte query results in 256-byte
1437
+ * aligned strides into `destination` starting at `destinationOffset`.
1438
+ * Validates `destination.usage & GPUBufferUsage.QUERY_RESOLVE` and the
1439
+ * 256-byte alignment up front; on misuse returns
1440
+ * `Result.err({ code: 'webgpu-runtime-error', ... })` with a structured
1441
+ * .expected / .hint pair.
1442
+ *
1443
+ * Spec anchor: [@webgpu/types.GPUCommandEncoder.resolveQuerySet].
1444
+ *
1445
+ * @example
1446
+ * const out = encoder.resolveQuerySet(qs, 0, 4, dstBuf, 0);
1447
+ * if (!out.ok) {
1448
+ * // switch (out.error.code) { case 'webgpu-runtime-error': ... }
1449
+ * }
1450
+ */
1451
+ resolveQuerySet(querySet: QuerySet, firstQuery: number, queryCount: number, destination: Buffer, destinationOffset: number): Result<void, RhiError>;
1452
+ /**
1453
+ * Write a u64 GPU timestamp into a timestamp QuerySet at queryIndex.
1454
+ *
1455
+ * Spec anchor: W3C WebGPU §queries / [@webgpu/types.GPUCommandEncoder].
1456
+ * dawn `TimestampOnCommandEncoder` reference (research §2.4): the entry is
1457
+ * gated on the `'timestamp-query'` device feature. K-3 decision
1458
+ * (plan-strategy §2): the forgeax form ships ONLY this CommandEncoder
1459
+ * entry; CPE/RPE inside-pass timestamp writes are deferred to
1460
+ * `feat-future-rhi-perf-timestamp-pass` (additive minor evolution).
1461
+ *
1462
+ * Return shape: `void` per spec literal alignment. When
1463
+ * `caps.timestampQuery === false` the shim fans out a structured
1464
+ * `'feature-not-enabled'` RhiError through the engine `onError` channel
1465
+ * (the forgeax form keeps the spec void return; AI users probe the gate
1466
+ * via `device.caps.timestampQuery` BEFORE calling this entry, charter
1467
+ * proposition 4 explicit failure forward-reachable).
1468
+ *
1469
+ * @throws RhiError code === 'command-encoder-finished' when invoked on a finished encoder (dual-channel; see beginRenderPass JSDoc for the recovery pattern).
1470
+ * @example
1471
+ * if (device.caps.timestampQuery) {
1472
+ * encoder.writeTimestamp(qs, 0);
1473
+ * // ... draw / dispatch ...
1474
+ * encoder.writeTimestamp(qs, 1);
1475
+ * }
1476
+ */
1477
+ writeTimestamp(querySet: QuerySet, queryIndex: number): void;
1478
+ /** Push a labelled debug group (GPUDebugCommandsMixin).
1479
+ *
1480
+ * Spec anchor: [@webgpu/types.GPUDebugCommandsMixin.pushDebugGroup].
1481
+ *
1482
+ * @note silent delegate to raw GPU encoder on finished encoder; matches W3C spec lenience (the debug-commands mixin permits a no-op pass-through after finish() so the signal stays real instead of forging a `@throws` contract; charter proposition 4).
1483
+ * @example
1484
+ * encoder.pushDebugGroup('frame-setup');
1485
+ */
1486
+ pushDebugGroup(groupLabel: string): void;
1487
+ /** Pop the most recent debug group (GPUDebugCommandsMixin).
1488
+ *
1489
+ * Spec anchor: [@webgpu/types.GPUDebugCommandsMixin.popDebugGroup].
1490
+ *
1491
+ * @note silent delegate to raw GPU encoder on finished encoder; matches W3C spec lenience (see pushDebugGroup JSDoc for the dual-channel rationale).
1492
+ * @example
1493
+ * encoder.popDebugGroup();
1494
+ */
1495
+ popDebugGroup(): void;
1496
+ /** Insert a labelled debug marker (GPUDebugCommandsMixin).
1497
+ *
1498
+ * Spec anchor: [@webgpu/types.GPUDebugCommandsMixin.insertDebugMarker].
1499
+ *
1500
+ * @note silent delegate to raw GPU encoder on finished encoder; matches W3C spec lenience (see pushDebugGroup JSDoc for the dual-channel rationale).
1501
+ * @example
1502
+ * encoder.insertDebugMarker('post-resolve');
1503
+ */
1504
+ insertDebugMarker(markerLabel: string): void;
1505
+ /** Finish recording -> CommandBuffer. After finish() any further recording
1506
+ * call returns Result.err({ code: 'command-encoder-finished' }).
1507
+ *
1508
+ * Spec anchor: [@webgpu/types.GPUCommandEncoder.finish].
1509
+ *
1510
+ * @example
1511
+ * const cb = encoder.finish();
1512
+ * if (cb.ok) device.queue.submit([cb.value]);
1513
+ */
1514
+ finish(): Result<CommandBuffer, RhiError>;
1515
+ }
1516
+ /** GPU render pass encoder - records draw calls + state changes.
1517
+ *
1518
+ * Method NAMES align byte-for-byte with @webgpu/types.GPURenderPassEncoder +
1519
+ * GPURenderCommandsMixin + GPUBindingCommandsMixin + GPUDebugCommandsMixin
1520
+ * (research F-2 / D-S4): 17 spec stable + 1 setBindGroup overload + 1
1521
+ * remaining capability-gated placeholder (`executeBundles` returns
1522
+ * Result.err({ code: 'rhi-not-available', hint: 'see
1523
+ * feat-future-rhi-render-bundle' })). The 2 occlusion-query methods
1524
+ * (`beginOcclusionQuery` / `endOcclusionQuery`) shipped real implementations
1525
+ * in M3 (w23) backed by `RenderPassDescriptor.occlusionQuerySet`.
1526
+ *
1527
+ * `setImmediates` (PROPOSED) is intentionally NOT exposed (charter
1528
+ * proposition 4: untested features hide behind caps, not surfaces).
1529
+ *
1530
+ * Lifecycle: encoder.finish() while a pass is unfinished returns
1531
+ * Result.err({ code: 'render-pass-not-ended' }) per D-S3 template 2.
1532
+ */
1533
+ export interface RhiRenderPassEncoder {
1534
+ /** Set the bound render pipeline.
1535
+ *
1536
+ * Spec anchor: [@webgpu/types.GPURenderCommandsMixin.setPipeline].
1537
+ *
1538
+ * @example pass.setPipeline(pipeline);
1539
+ */
1540
+ setPipeline(pipeline: RenderPipeline): void;
1541
+ /** Set a vertex buffer.
1542
+ *
1543
+ * Spec anchor: [@webgpu/types.GPURenderCommandsMixin.setVertexBuffer].
1544
+ *
1545
+ * @example pass.setVertexBuffer(0, vbo);
1546
+ */
1547
+ setVertexBuffer(slot: number, buffer: Buffer, offset?: number | undefined, size?: number | undefined): void;
1548
+ /** Set the index buffer.
1549
+ *
1550
+ * Spec anchor: [@webgpu/types.GPURenderCommandsMixin.setIndexBuffer].
1551
+ *
1552
+ * @example pass.setIndexBuffer(ibo, 'uint32');
1553
+ */
1554
+ setIndexBuffer(buffer: Buffer, format: 'uint16' | 'uint32', offset?: number | undefined, size?: number | undefined): void;
1555
+ /** Set a bind group with optional dynamic offsets array (overload (a)).
1556
+ *
1557
+ * Spec anchor: [@webgpu/types.GPUBindingCommandsMixin.setBindGroup].
1558
+ *
1559
+ * @example pass.setBindGroup(0, bg, [0, 256]);
1560
+ */
1561
+ setBindGroup(index: number, bindGroup: BindGroup, dynamicOffsets?: readonly number[] | undefined): void;
1562
+ /** Set a bind group with a Uint32Array slice for dynamic offsets (overload (b)).
1563
+ *
1564
+ * Spec anchor: [@webgpu/types.GPUBindingCommandsMixin.setBindGroup].
1565
+ *
1566
+ * @example
1567
+ * pass.setBindGroup(0, bg, dynamicOffsetsData, dynamicOffsetsDataStart, dynamicOffsetsDataLength);
1568
+ */
1569
+ setBindGroup(index: number, bindGroup: BindGroup, dynamicOffsetsData: Uint32Array, dynamicOffsetsDataStart: number, dynamicOffsetsDataLength: number): void;
1570
+ /** Issue a draw.
1571
+ *
1572
+ * Spec anchor: [@webgpu/types.GPURenderCommandsMixin.draw].
1573
+ *
1574
+ * @example pass.draw(3);
1575
+ */
1576
+ draw(vertexCount: number, instanceCount?: number | undefined, firstVertex?: number | undefined, firstInstance?: number | undefined): void;
1577
+ /** Issue an indexed draw.
1578
+ *
1579
+ * Spec anchor: [@webgpu/types.GPURenderCommandsMixin.drawIndexed].
1580
+ *
1581
+ * @example pass.drawIndexed(36);
1582
+ */
1583
+ drawIndexed(indexCount: number, instanceCount?: number | undefined, firstIndex?: number | undefined, baseVertex?: number | undefined, firstInstance?: number | undefined): void;
1584
+ /** End the render pass.
1585
+ *
1586
+ * Spec anchor: [@webgpu/types.GPURenderPassEncoder.end].
1587
+ *
1588
+ * @example pass.end();
1589
+ */
1590
+ end(): void;
1591
+ /** Set the viewport.
1592
+ *
1593
+ * Spec anchor: [@webgpu/types.GPURenderPassEncoder.setViewport].
1594
+ *
1595
+ * @example pass.setViewport(0, 0, 800, 600, 0, 1);
1596
+ */
1597
+ setViewport(x: number, y: number, w: number, h: number, minDepth: number, maxDepth: number): void;
1598
+ /** Set the scissor rect.
1599
+ *
1600
+ * Spec anchor: [@webgpu/types.GPURenderPassEncoder.setScissorRect].
1601
+ *
1602
+ * @example pass.setScissorRect(0, 0, 800, 600);
1603
+ */
1604
+ setScissorRect(x: number, y: number, w: number, h: number): void;
1605
+ /** Set the blend constant color.
1606
+ *
1607
+ * Spec anchor: [@webgpu/types.GPURenderPassEncoder.setBlendConstant].
1608
+ *
1609
+ * @example pass.setBlendConstant({ r: 1, g: 0, b: 0, a: 1 });
1610
+ */
1611
+ setBlendConstant(color: GPUColor): void;
1612
+ /** Set the stencil reference value.
1613
+ *
1614
+ * Spec anchor: [@webgpu/types.GPURenderPassEncoder.setStencilReference].
1615
+ *
1616
+ * @example pass.setStencilReference(0xff);
1617
+ */
1618
+ setStencilReference(reference: number): void;
1619
+ /** Issue an indirect draw.
1620
+ *
1621
+ * Spec anchor: [@webgpu/types.GPURenderCommandsMixin.drawIndirect].
1622
+ *
1623
+ * @example pass.drawIndirect(indirectBuf, 0);
1624
+ */
1625
+ drawIndirect(indirectBuffer: Buffer, indirectOffset: number): void;
1626
+ /** Issue an indexed indirect draw.
1627
+ *
1628
+ * Spec anchor: [@webgpu/types.GPURenderCommandsMixin.drawIndexedIndirect].
1629
+ *
1630
+ * @example pass.drawIndexedIndirect(indirectBuf, 0);
1631
+ */
1632
+ drawIndexedIndirect(indirectBuffer: Buffer, indirectOffset: number): void;
1633
+ /** Push a labelled debug group (GPUDebugCommandsMixin).
1634
+ *
1635
+ * Spec anchor: [@webgpu/types.GPUDebugCommandsMixin.pushDebugGroup].
1636
+ *
1637
+ * @note silent delegate to raw GPU encoder on finished encoder; matches W3C spec lenience (the debug-commands mixin permits a no-op pass-through after finish() so the signal stays real instead of forging a `@throws` contract; charter proposition 4).
1638
+ * @example pass.pushDebugGroup('lighting');
1639
+ */
1640
+ pushDebugGroup(groupLabel: string): void;
1641
+ /** Pop the most recent debug group (GPUDebugCommandsMixin).
1642
+ *
1643
+ * Spec anchor: [@webgpu/types.GPUDebugCommandsMixin.popDebugGroup].
1644
+ *
1645
+ * @note silent delegate to raw GPU encoder on finished encoder; matches W3C spec lenience (see pushDebugGroup JSDoc for the dual-channel rationale).
1646
+ * @example pass.popDebugGroup();
1647
+ */
1648
+ popDebugGroup(): void;
1649
+ /** Insert a labelled debug marker (GPUDebugCommandsMixin).
1650
+ *
1651
+ * Spec anchor: [@webgpu/types.GPUDebugCommandsMixin.insertDebugMarker].
1652
+ *
1653
+ * @note silent delegate to raw GPU encoder on finished encoder; matches W3C spec lenience (see pushDebugGroup JSDoc for the dual-channel rationale).
1654
+ * @example pass.insertDebugMarker('post-shadow');
1655
+ */
1656
+ insertDebugMarker(markerLabel: string): void;
1657
+ /** Execute render bundles. Capability-gated placeholder per D-S4: returns
1658
+ * Result.err({ code: 'rhi-not-available', hint: 'see feat-future-rhi-render-bundle' })
1659
+ * until that closure lands RenderBundle creation.
1660
+ *
1661
+ * Spec anchor: [@webgpu/types.GPURenderPassEncoder.executeBundles].
1662
+ *
1663
+ * @example
1664
+ * const out = pass.executeBundles([bundle]);
1665
+ * if (!out.ok) { ... route via switch (out.error.code) ... }
1666
+ */
1667
+ executeBundles(bundles: Iterable<unknown>): Result<void, RhiError>;
1668
+ /** Begin an occlusion query. Real implementation (M3 / w23): pairs with
1669
+ * `endOcclusionQuery()` against the `RenderPassDescriptor.occlusionQuerySet`
1670
+ * and validates the spec [[occlusion_query_active]] state machine
1671
+ * (queries cannot nest; missing occlusionQuerySet returns
1672
+ * Result.err({ code: 'webgpu-runtime-error' }) with structured
1673
+ * .expected / .hint fields).
1674
+ *
1675
+ * Spec anchor: [@webgpu/types.GPURenderPassEncoder.beginOcclusionQuery].
1676
+ *
1677
+ * @example
1678
+ * const out = pass.beginOcclusionQuery(0);
1679
+ * if (!out.ok) { ... route via switch (out.error.code) ... }
1680
+ */
1681
+ beginOcclusionQuery(queryIndex: number): Result<void, RhiError>;
1682
+ /** End an occlusion query. Real implementation (M3 / w23): finalizes the
1683
+ * matching `beginOcclusionQuery(idx)` slot; emits
1684
+ * Result.err({ code: 'render-pass-not-ended', ... }) if no active begin
1685
+ * is pending.
1686
+ *
1687
+ * Spec anchor: [@webgpu/types.GPURenderPassEncoder.endOcclusionQuery].
1688
+ *
1689
+ * @example
1690
+ * const out = pass.endOcclusionQuery();
1691
+ * if (!out.ok) { ... route via switch (out.error.code) ... }
1692
+ */
1693
+ endOcclusionQuery(): Result<void, RhiError>;
1694
+ }
1695
+ /** GPU compute pass encoder - records dispatch calls. */
1696
+ export interface RhiComputePassEncoder {
1697
+ setPipeline(pipeline: ComputePipeline): void;
1698
+ setBindGroup(index: number, bindGroup: BindGroup, dynamicOffsets?: readonly number[] | undefined): void;
1699
+ dispatchWorkgroups(x: number, y?: number | undefined, z?: number | undefined): void;
1700
+ /** Dispatch dimensions read from three consecutive u32 values in an indirect buffer. */
1701
+ dispatchWorkgroupsIndirect(indirectBuffer: Buffer, indirectOffset: number): void;
1702
+ end(): void;
1703
+ }
1704
+ /** GPU render pipeline operations - returned by RhiDevice.createRenderPipeline. */
1705
+ export interface RhiRenderPipelineOps {
1706
+ /** Get bind group layout (used for dynamic bind-group creation). */
1707
+ getBindGroupLayout(index: number): BindGroupLayout;
1708
+ }
1709
+ /** GPU compute pipeline operations. */
1710
+ export interface RhiComputePipelineOps {
1711
+ /** Get bind group layout. */
1712
+ getBindGroupLayout(index: number): BindGroupLayout;
1713
+ }
1714
+ export type { DrawOwnerSplit, LimitExceededDetail, Result, ResultErr, ResultOk, RhiAssetNotRegisteredDetail, RhiErrorCode, RhiErrorDetail, RhiOwnerOutOfRangeDetail, RhiShaderCompileDetail, RhiWebgpuRuntimeDetail, } from './errors';
1715
+ export { err, ok, RhiError, validateDrawArgs } from './errors';
1716
+ export type { AddressMode, CompareFunction, FilterMode, TextureFormat };
1717
+ /**
1718
+ * Build a TextureDescriptor for a cube-array depth texture suitable for
1719
+ * point-light shadow atlas (texture_depth_cube_array).
1720
+ *
1721
+ * Usage: `device.createTexture(cubeArrayDepthDescriptor(512, 4))`
1722
+ *
1723
+ * @param faceSize - width and height of each cube face in pixels (default 512)
1724
+ * @param layers - number of cube layers (= max shadow-casting point lights, default 4)
1725
+ * @param usage - texture usage flags (default RENDER_ATTACHMENT | TEXTURE_BINDING)
1726
+ */
1727
+ export declare function cubeArrayDepthDescriptor(faceSize?: number, layers?: number, usage?: number): TextureDescriptor;
1728
+ /**
1729
+ * Build a SamplerDescriptor for a depth-comparison sampler used with
1730
+ * texture_depth_2d or texture_depth_cube_array.
1731
+ *
1732
+ * The returned descriptor uses clamp-to-edge addressing, linear filtering
1733
+ * (required for comparison sampling on some backends), and `compare: 'less'`.
1734
+ *
1735
+ * Usage: `device.createSampler(comparisonSamplerDescriptor())`
1736
+ */
1737
+ export declare function comparisonSamplerDescriptor(): SamplerDescriptor;
1738
+ /**
1739
+ * Build a TextureViewDescriptor to view a single cube layer + face as a 2D
1740
+ * depth attachment during shadow-caster rendering.
1741
+ *
1742
+ * Each shadow-caster pass renders to one face of one cube layer. This helper
1743
+ * produces the view descriptor that selects `baseArrayLayer = layerIndex * 6 + faceIndex`
1744
+ * with `arrayLayerCount = 1` and `dimension = '2d'`, which satisfies WebGPU's
1745
+ * requirement that render pass attachments are 2D views (cube views cannot be
1746
+ * bound as render targets).
1747
+ *
1748
+ * Usage:
1749
+ * ```
1750
+ * const viewDesc = cubeArrayDepthFaceView(lightIndex, faceIndex);
1751
+ * const view = device.createTextureView(atlas, viewDesc).unwrap();
1752
+ * ```
1753
+ *
1754
+ * @param layerIndex - 0-based shadow-casting light index (0..3)
1755
+ * @param faceIndex - 0-based cube face index (0..5, +X/-X/+Y/-Y/+Z/-Z per §5.3)
1756
+ */
1757
+ export declare function cubeArrayDepthFaceView(layerIndex: number, faceIndex: number): TextureViewDescriptor;
1758
+ //# sourceMappingURL=index.d.ts.map