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