@forgeax/engine-rhi-null 0.1.21 → 0.1.23

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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=r32float-capability-generation.unit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"r32float-capability-generation.unit.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/r32float-capability-generation.unit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=r32float-capability.unit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"r32float-capability.unit.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/r32float-capability.unit.test.ts"],"names":[],"mappings":""}
package/dist/device.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { BindGroup, BindGroupDescriptor, BindGroupLayout, BindGroupLayoutDescriptor, Buffer, BufferDescriptor, CommandEncoderDescriptor, ComputePipeline, ComputePipelineDescriptor, PipelineLayout, PipelineLayoutDescriptor, QuerySet, QuerySetDescriptor, RenderPipeline, RenderPipelineDescriptor, Result, RhiCaps, RhiCommandEncoder, RhiDevice, RhiError as RhiErrorType, RhiFeatures, RhiLimits, RhiQueue, Sampler, SamplerDescriptor, Texture, TextureDescriptor, TextureView, TextureViewDescriptor } from '@forgeax/engine-rhi';
2
2
  import { Bookkeeper } from './bookkeeping';
3
+ import { probeR32FloatCapability } from './internal/r32float-capability';
3
4
  /**
4
5
  * Factory that builds a command encoder bound to a device's ledger. Injected at
5
6
  * device construction (rather than imported here) so device.ts carries no
@@ -18,6 +19,8 @@ export declare class RhiNullDevice implements RhiDevice {
18
19
  private readonly internalBookkeeper;
19
20
  private readonly nullQueue;
20
21
  private readonly encoderFactory;
22
+ private readonly deviceGeneration;
23
+ private r32FloatProbe;
21
24
  /** Per-frame total draw count across all pass encoders executed this frame
22
25
  * (aggregated by the command encoder on finish, then reset). M3 unit tests
23
26
  * (w17) read this to assert draw count >= 1 (AC-06). */
@@ -32,6 +35,7 @@ export declare class RhiNullDevice implements RhiDevice {
32
35
  * pairing and BGL/PSO shape counts (AC-05/06/07). */
33
36
  get bookkeeper(): Bookkeeper;
34
37
  constructor(queue: RhiQueue, encoderFactory: CommandEncoderFactory);
38
+ probeTextureFormatCapability(): ReturnType<typeof probeR32FloatCapability>;
35
39
  get caps(): RhiCaps;
36
40
  get features(): RhiFeatures;
37
41
  get limits(): RhiLimits;
@@ -1 +1 @@
1
- {"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../src/device.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../src/device.ts"],"names":[],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ok, err } from '@forgeax/engine-types';
2
- import { RhiError } from '@forgeax/engine-rhi';
2
+ import { RhiError, ok as ok$1, R32FLOAT_PROBE_STAGES } from '@forgeax/engine-rhi';
3
3
 
4
4
  // src/index.ts
5
5
  var RhiNullRenderPassEncoder = class {
@@ -264,6 +264,24 @@ function isHandleDestroyed(handle) {
264
264
  }
265
265
  return ok(void 0);
266
266
  }
267
+ function probeR32FloatCapability(deviceGeneration) {
268
+ return Promise.resolve(
269
+ ok$1({
270
+ profile: "r32float-mip-sampled-storage",
271
+ verdict: "structural-only",
272
+ evidence: "structural",
273
+ deviceGeneration,
274
+ stages: R32FLOAT_PROBE_STAGES.map((stage) => ({
275
+ stage,
276
+ verdict: "structural-only",
277
+ evidence: "structural"
278
+ })),
279
+ sampleType: "unfilterable-float",
280
+ usages: ["texture-binding", "storage-binding", "copy-src"],
281
+ probeExecutions: 1
282
+ })
283
+ );
284
+ }
267
285
 
268
286
  // src/device.ts
269
287
  var nextDeviceId = 0;
@@ -271,6 +289,8 @@ var RhiNullDevice = class {
271
289
  internalBookkeeper;
272
290
  nullQueue;
273
291
  encoderFactory;
292
+ deviceGeneration;
293
+ r32FloatProbe;
274
294
  /** Per-frame total draw count across all pass encoders executed this frame
275
295
  * (aggregated by the command encoder on finish, then reset). M3 unit tests
276
296
  * (w17) read this to assert draw count >= 1 (AC-06). */
@@ -287,10 +307,15 @@ var RhiNullDevice = class {
287
307
  return this.internalBookkeeper;
288
308
  }
289
309
  constructor(queue, encoderFactory) {
310
+ this.deviceGeneration = nextDeviceId;
290
311
  this.internalBookkeeper = new Bookkeeper(nextDeviceId++);
291
312
  this.nullQueue = queue;
292
313
  this.encoderFactory = encoderFactory;
293
314
  }
315
+ probeTextureFormatCapability() {
316
+ this.r32FloatProbe ??= probeR32FloatCapability(this.deviceGeneration);
317
+ return this.r32FloatProbe;
318
+ }
294
319
  get caps() {
295
320
  return {
296
321
  backendKind: "null",
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/pass-encoders.ts","../src/command-encoder.ts","../src/bookkeeping.ts","../src/device.ts","../src/queue.ts","../src/adapter.ts","../src/canvas-context.ts","../src/shader.ts","../src/index.ts"],"names":["ok","err","RhiErrorClass"],"mappings":";;;;AA+CO,IAAM,2BAAN,MAA+D;AAAA;AAAA,EAEpE,SAAA,GAAY,CAAA;AAAA,EACZ,cAAA,GAAiB,CAAA;AAAA;AAAA;AAAA,EAGjB,cAAA,GAAgD,GAAG,MAAS,CAAA;AAAA,EAE3C,UAAA;AAAA,EACA,OAAA;AAAA,EACR,QAAA;AAAA,EAET,WAAA,CAAY,UAAA,EAAwB,OAAA,EAA6B,QAAA,EAAkB;AACjF,IAAA,IAAA,CAAK,UAAA,GAAa,UAAA;AAClB,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AACf,IAAA,IAAA,CAAK,QAAA,GAAW,QAAA;AAAA,EAClB;AAAA,EAEA,YAAY,SAAA,EAAiC;AAAA,EAAC;AAAA,EAE9C,eAAA,CACE,KAAA,EACA,MAAA,EACA,OAAA,EACA,KAAA,EACM;AACN,IAAA,IAAA,CAAK,cAAA,GAAiB,IAAA,CAAK,UAAA,CAAW,iBAAA,CAAkB,MAAM,CAAA;AAAA,EAChE;AAAA,EAEA,cAAA,CACE,OAAA,EACA,OAAA,EACA,OAAA,EACA,KAAA,EACM;AAAA,EAAC;AAAA,EAET,YAAA,CACE,MAAA,EACA,SAAA,EACA,mBAAA,EACA,0BACA,yBAAA,EACM;AACN,IAAA,IAAA,CAAK,cAAA,EAAA;AACL,IAAA,IAAA,CAAK,SAAS,eAAA,EAAgB;AAC9B,IAAA,IAAA,CAAK,cAAA,GAAiB,IAAA,CAAK,UAAA,CAAW,iBAAA,CAAkB,SAAS,CAAA;AAAA,EACnE;AAAA,EAEA,IAAA,CACE,YAAA,EACA,cAAA,EACA,YAAA,EACA,cAAA,EACM;AACN,IAAA,IAAA,CAAK,SAAA,EAAA;AACL,IAAA,IAAA,CAAK,SAAS,UAAA,EAAW;AAAA,EAC3B;AAAA,EAEA,WAAA,CACE,WAAA,EACA,cAAA,EACA,WAAA,EACA,aACA,cAAA,EACM;AACN,IAAA,IAAA,CAAK,SAAA,EAAA;AACL,IAAA,IAAA,CAAK,SAAS,UAAA,EAAW;AAAA,EAC3B;AAAA,EAEA,GAAA,GAAY;AACV,IAAA,IAAA,CAAK,OAAA,EAAS,cAAA,CAAe,IAAA,CAAK,QAAQ,CAAA;AAAA,EAC5C;AAAA,EAEA,YACE,EAAA,EACA,EAAA,EACA,EAAA,EACA,EAAA,EACA,WACA,SAAA,EACM;AAAA,EAAC;AAAA,EAET,cAAA,CAAe,EAAA,EAAY,EAAA,EAAY,EAAA,EAAY,EAAA,EAAkB;AAAA,EAAC;AAAA,EAEtE,iBAAiB,MAAA,EAAwB;AAAA,EAAC;AAAA,EAE1C,oBAAoB,UAAA,EAA0B;AAAA,EAAC;AAAA,EAE/C,YAAA,CAAa,iBAAyB,eAAA,EAA+B;AACnE,IAAA,IAAA,CAAK,SAAA,EAAA;AACL,IAAA,IAAA,CAAK,SAAS,UAAA,EAAW;AAAA,EAC3B;AAAA,EAEA,mBAAA,CAAoB,iBAAyB,eAAA,EAA+B;AAC1E,IAAA,IAAA,CAAK,SAAA,EAAA;AACL,IAAA,IAAA,CAAK,SAAS,UAAA,EAAW;AAAA,EAC3B;AAAA,EAEA,eAAe,WAAA,EAA2B;AAAA,EAAC;AAAA,EAE3C,aAAA,GAAsB;AAAA,EAAC;AAAA,EAEvB,kBAAkB,YAAA,EAA4B;AAAA,EAAC;AAAA,EAE/C,eAAe,QAAA,EAAyD;AAGtE,IAAA,OAAO,GAAG,MAAS,CAAA;AAAA,EACrB;AAAA,EAEA,oBAAoB,WAAA,EAAiD;AACnE,IAAA,OAAO,GAAG,MAAS,CAAA;AAAA,EACrB;AAAA,EAEA,iBAAA,GAAgD;AAC9C,IAAA,OAAO,GAAG,MAAS,CAAA;AAAA,EACrB;AACF;AAMO,IAAM,4BAAN,MAAiE;AAAA;AAAA,EAEtE,aAAA,GAAgB,CAAA;AAAA;AAAA,EAEhB,cAAA,GAAgD,GAAG,MAAS,CAAA;AAAA,EAE3C,UAAA;AAAA,EACA,OAAA;AAAA,EACR,QAAA;AAAA,EAET,WAAA,CAAY,UAAA,EAAwB,OAAA,EAA6B,QAAA,EAAkB;AACjF,IAAA,IAAA,CAAK,UAAA,GAAa,UAAA;AAClB,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AACf,IAAA,IAAA,CAAK,QAAA,GAAW,QAAA;AAAA,EAClB;AAAA,EAEA,YAAY,SAAA,EAAkC;AAAA,EAAC;AAAA,EAE/C,YAAA,CACE,MAAA,EACA,SAAA,EACA,eAAA,EACM;AACN,IAAA,IAAA,CAAK,cAAA,GAAiB,IAAA,CAAK,UAAA,CAAW,iBAAA,CAAkB,SAAS,CAAA;AAAA,EACnE;AAAA,EAEA,kBAAA,CAAmB,EAAA,EAAY,EAAA,EAAyB,EAAA,EAA+B;AACrF,IAAA,IAAA,CAAK,aAAA,EAAA;AACL,IAAA,IAAA,CAAK,SAAS,cAAA,EAAe;AAAA,EAC/B;AAAA,EAEA,0BAAA,CAA2B,gBAAwB,eAAA,EAA+B;AAChF,IAAA,IAAA,CAAK,cAAA,GAAiB,IAAA,CAAK,UAAA,CAAW,iBAAA,CAAkB,cAAc,CAAA;AACtE,IAAA,IAAA,CAAK,aAAA,EAAA;AACL,IAAA,IAAA,CAAK,SAAS,cAAA,EAAe;AAAA,EAC/B;AAAA,EAEA,GAAA,GAAY;AACV,IAAA,IAAA,CAAK,OAAA,EAAS,cAAA,CAAe,IAAA,CAAK,QAAQ,CAAA;AAAA,EAC5C;AACF;;;ACzKA,IAAM,gBAAN,MAA2C;AAAA,EACzC,YAA6B,MAAA,EAAuB;AAAvB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAAwB;AAAA,EAAxB,MAAA;AAAA,EAE7B,UAAA,GAAmB;AACjB,IAAA,IAAA,CAAK,MAAA,CAAO,cAAA,EAAA;AAAA,EACd;AAAA,EAEA,cAAA,GAAuB;AACrB,IAAA,IAAA,CAAK,MAAA,CAAO,kBAAA,EAAA;AAAA,EACd;AAAA,EAEA,eAAA,GAAwB;AACtB,IAAA,IAAA,CAAK,MAAA,CAAO,mBAAA,EAAA;AAAA,EACd;AAAA,EAEA,eAAe,IAAA,EAAoB;AACjC,IAAA,IAAA,CAAK,MAAA,CAAO,cAAA,CAAe,IAAA,CAAK,IAAI,CAAA;AAAA,EACtC;AACF,CAAA;AAQA,SAAS,cAAc,IAAA,EAAmE;AACxF,EAAA,IAAI,IAAA,IAAQ,OAAO,IAAA,CAAK,KAAA,KAAU,YAAY,IAAA,CAAK,KAAA,CAAM,SAAS,CAAA,EAAG;AACnE,IAAA,OAAO,IAAA,CAAK,KAAA;AAAA,EACd;AACA,EAAA,OAAO,WAAA;AACT;AAOO,IAAM,wBAAN,MAAyD;AAAA,EAC7C,UAAA;AAAA,EACA,OAAA;AAAA,EAEjB,WAAA,CAAY,YAAwB,MAAA,EAAuB;AACzD,IAAA,IAAA,CAAK,UAAA,GAAa,UAAA;AAClB,IAAA,IAAA,CAAK,OAAA,GAAU,IAAI,aAAA,CAAc,MAAM,CAAA;AAAA,EACzC;AAAA,EAEA,gBAAgB,IAAA,EAAkD;AAChE,IAAA,MAAM,KAAA,GAAQ,cAAc,IAAI,CAAA;AAChC,IAAA,OAAO,IAAI,wBAAA,CAAyB,IAAA,CAAK,UAAA,EAAY,IAAA,CAAK,SAAS,KAAK,CAAA;AAAA,EAC1E;AAAA,EAEA,iBAAiB,IAAA,EAAiE;AAChF,IAAA,MAAM,KAAA,GAAQ,cAAc,IAAI,CAAA;AAChC,IAAA,OAAO,IAAI,yBAAA,CAA0B,IAAA,CAAK,UAAA,EAAY,IAAA,CAAK,SAAS,KAAK,CAAA;AAAA,EAC3E;AAAA,EAEA,uBAAuB,IAAA,EAAmC;AACxD,IAAA,IAAA,CAAK,OAAA,CAAQ,cAAA,CAAe,aAAA,CAAc,IAAI,CAAC,CAAA;AAAA,EACjD;AAAA,EAEA,kBAAA,CACE,OAAA,EACA,0BAAA,EACA,kBAAA,EACA,oBACA,KAAA,EACM;AAAA,EAAC;AAAA,EAET,mBAAA,CACE,OAAA,EACA,YAAA,EACA,SAAA,EACM;AAAA,EAAC;AAAA,EAET,mBAAA,CACE,OAAA,EACA,YAAA,EACA,SAAA,EACM;AAAA,EAAC;AAAA,EAET,oBAAA,CACE,OAAA,EACA,YAAA,EACA,SAAA,EACM;AAAA,EAAC;AAAA,EAET,WAAA,CAAY,OAAA,EAAiB,OAAA,EAA8B,KAAA,EAAkC;AAAA,EAAC;AAAA,EAE9F,eAAA,CACE,SAAA,EACA,WAAA,EACA,WAAA,EACA,cACA,kBAAA,EAC4B;AAC5B,IAAA,OAAOA,GAAG,MAAS,CAAA;AAAA,EACrB;AAAA,EAEA,eAAe,WAAA,EAA2B;AAAA,EAAC;AAAA,EAE3C,aAAA,GAAsB;AAAA,EAAC;AAAA,EAEvB,kBAAkB,YAAA,EAA4B;AAAA,EAAC;AAAA,EAE/C,MAAA,GAA8C;AAC5C,IAAA,OAAOA,EAAAA,CAAG,IAAA,CAAK,UAAA,CAAW,QAAA,CAAS,eAAe,CAA6B,CAAA;AAAA,EACjF;AACF;AC7FO,IAAM,eAAA,0BAAyB,8BAA8B,CAAA;AAc7D,IAAM,aAAN,MAAiB;AAAA,EACb,QAAA;AAAA,EACD,YAAA,GAAe,CAAA;AAAA,EACN,OAAA,uBAAc,GAAA,EAA0B;AAAA,EAEzD,YAAY,QAAA,EAAkB;AAC5B,IAAA,IAAA,CAAK,QAAA,GAAW,QAAA;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAS,IAAA,EAA8B;AACrC,IAAA,MAAM,KAAK,IAAA,CAAK,YAAA,EAAA;AAChB,IAAA,MAAM,MAAA,GAAuB;AAAA,MAC3B,EAAA;AAAA,MACA,IAAA;AAAA,MACA,SAAA,EAAW,KAAA;AAAA,MACX,gBAAgB,IAAA,CAAK;AAAA,KACvB;AACA,IAAA,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,EAAA,EAAI,MAAM,CAAA;AAC3B,IAAA,OAAO,EAAE,CAAC,eAAe,GAAG,MAAA,EAAO;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAQ,MAAA,EAA6C;AACnD,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,iBAAA,CAAkB,MAAM,CAAA;AAC/C,IAAA,IAAI,CAAC,SAAA,CAAU,EAAA,EAAI,OAAO,SAAA;AAC1B,IAAA,MAAM,cAAA,GAAiB,kBAAkB,MAAM,CAAA;AAC/C,IAAA,IAAI,CAAC,cAAA,CAAe,EAAA,EAAI,OAAO,cAAA;AAC/B,IAAA,SAAA,CAAU,MAAM,SAAA,GAAY,IAAA;AAC5B,IAAA,OAAOA,GAAG,MAAS,CAAA;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,YAAY,MAAA,EAA0B;AACpC,IAAA,OAAO,UAAA,CAAW,MAAM,CAAA,EAAG,SAAA,KAAc,IAAA;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,kBAAkB,MAAA,EAAqD;AACrE,IAAA,MAAM,MAAA,GAAS,WAAW,MAAM,CAAA;AAChC,IAAA,IAAI,MAAA,KAAW,MAAA,IAAa,MAAA,CAAO,cAAA,KAAmB,KAAK,QAAA,EAAU;AACnE,MAAA,OAAO,GAAA;AAAA,QACL,IAAI,QAAA,CAAS;AAAA,UACX,IAAA,EAAM,mBAAA;AAAA,UACN,QAAA,EAAU,0CAAA;AAAA,UACV,IAAA,EAAM;AAAA,SACP;AAAA,OACH;AAAA,IACF;AACA,IAAA,OAAOA,GAAG,MAAM,CAAA;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,UAAA,GAAsC;AACpC,IAAA,OAAO,CAAC,GAAG,IAAA,CAAK,OAAA,CAAQ,QAAQ,CAAA;AAAA,EAClC;AAAA;AAAA,EAGA,WAAA,GAAsB;AACpB,IAAA,OAAO,KAAK,OAAA,CAAQ,IAAA;AAAA,EACtB;AACF,CAAA;AAIA,SAAS,WAAW,MAAA,EAA2C;AAC7D,EAAA,IAAI,MAAA,KAAW,IAAA,IAAQ,OAAO,MAAA,KAAW,UAAU,OAAO,MAAA;AAC1D,EAAA,MAAM,MAAA,GAAS,MAAA;AACf,EAAA,OAAO,OAAO,eAAe,CAAA;AAC/B;AAOO,SAAS,kBAAkB,MAAA,EAA6C;AAC7E,EAAA,IAAI,UAAA,CAAW,MAAM,CAAA,EAAG,SAAA,KAAc,IAAA,EAAM;AAC1C,IAAA,OAAO,GAAA;AAAA,MACL,IAAI,QAAA,CAAS;AAAA,QACX,IAAA,EAAM,uBAAA;AAAA,QACN,QAAA,EAAU,sDAAA;AAAA,QACV,IAAA,EAAM;AAAA,OACP;AAAA,KACH;AAAA,EACF;AACA,EAAA,OAAOA,GAAG,MAAS,CAAA;AACrB;;;ACtHA,IAAI,YAAA,GAAe,CAAA;AAyBZ,IAAM,gBAAN,MAAyC;AAAA,EAC7B,kBAAA;AAAA,EACA,SAAA;AAAA,EACA,cAAA;AAAA;AAAA;AAAA;AAAA,EAKjB,cAAA,GAAiB,CAAA;AAAA;AAAA,EAEjB,kBAAA,GAAqB,CAAA;AAAA;AAAA,EAErB,mBAAA,GAAsB,CAAA;AAAA;AAAA,EAEtB,iBAA2B,EAAC;AAAA;AAAA;AAAA,EAI5B,IAAI,UAAA,GAAyB;AAC3B,IAAA,OAAO,IAAA,CAAK,kBAAA;AAAA,EACd;AAAA,EAEA,WAAA,CAAY,OAAiB,cAAA,EAAuC;AAClE,IAAA,IAAA,CAAK,kBAAA,GAAqB,IAAI,UAAA,CAAW,YAAA,EAAc,CAAA;AACvD,IAAA,IAAA,CAAK,SAAA,GAAY,KAAA;AACjB,IAAA,IAAA,CAAK,cAAA,GAAiB,cAAA;AAAA,EACxB;AAAA,EAEA,IAAI,IAAA,GAAgB;AAClB,IAAA,OAAO;AAAA,MACL,WAAA,EAAa,MAAA;AAAA,MACb,OAAA,EAAS,IAAA;AAAA,MACT,cAAA,EAAgB,KAAA;AAAA,MAChB,0BAAA,EAA4B,IAAA;AAAA,MAC5B,eAAA,EAAiB,IAAA;AAAA,MACjB,oBAAA,EAAsB,KAAA;AAAA,MACtB,sBAAA,EAAwB,KAAA;AAAA,MACxB,sBAAA,EAAwB,KAAA;AAAA;AAAA;AAAA,MAGxB,iBAAA,EAAmB,KAAA;AAAA,MACnB,aAAA,EAAe,KAAA;AAAA,MACf,mBAAA,EAAqB,KAAA;AAAA,MACrB,eAAA,EAAiB,IAAA;AAAA,MACjB,qBAAA,EAAuB,IAAA;AAAA,MACvB,aAAA,EAAe,IAAA;AAAA,MACf,cAAA,EAAgB,IAAA;AAAA,MAChB,qBAAA,EAAuB,IAAA;AAAA,MACvB,uBAAA,EAAyB,IAAA;AAAA,MACzB,iBAAA,EAAmB,IAAA;AAAA,MACnB,mBAAA,EAAqB;AAAA,KACvB;AAAA,EACF;AAAA,EAEA,IAAI,QAAA,GAAwB;AAC1B,IAAA,OAAO,cAAA;AAAA,EACT;AAAA,EAEA,IAAI,MAAA,GAAoB;AACtB,IAAA,OAAO,YAAA;AAAA,EACT;AAAA,EAEA,IAAI,KAAA,GAAkB;AACpB,IAAA,OAAO,IAAA,CAAK,SAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,IAAA,GAAwF;AAC1F,IAAA,OAAO,KAAA;AAAA,EACT;AAAA,EAEA,aAAa,KAAA,EAAuD;AAClE,IAAA,OAAOA,EAAAA,CAAG,IAAA,CAAK,kBAAA,CAAmB,QAAA,CAAS,QAAQ,CAAsB,CAAA;AAAA,EAC3E;AAAA,EAEA,cAAc,KAAA,EAAyD;AACrE,IAAA,OAAOA,EAAAA,CAAG,IAAA,CAAK,kBAAA,CAAmB,QAAA,CAAS,SAAS,CAAuB,CAAA;AAAA,EAC7E;AAAA,EAEA,cAAc,GAAA,EAAyC;AACrD,IAAA,OAAO,IAAA,CAAK,kBAAA,CAAmB,OAAA,CAAQ,GAAG,CAAA;AAAA,EAC5C;AAAA,EAEA,gBAAgB,QAAA,EAAgD;AAC9D,IAAA,OAAO,IAAA,CAAK,kBAAA,CAAmB,OAAA,CAAQ,QAAQ,CAAA;AAAA,EACjD;AAAA,EAEA,eAAe,GAAA,EAA0C;AACvD,IAAA,OAAO,IAAA,CAAK,kBAAA,CAAmB,OAAA,CAAQ,GAAG,CAAA;AAAA,EAC5C;AAAA,EAEA,iBAAA,CACE,UACA,KAAA,EACmC;AACnC,IAAA,OAAOA,EAAAA,CAAG,IAAA,CAAK,kBAAA,CAAmB,QAAA,CAAS,aAAa,CAA2B,CAAA;AAAA,EACrF;AAAA,EAEA,cAAc,KAAA,EAAsE;AAClF,IAAA,OAAOA,EAAAA,CAAG,IAAA,CAAK,kBAAA,CAAmB,QAAA,CAAS,SAAS,CAAuB,CAAA;AAAA,EAC7E;AAAA,EAEA,sBAAsB,KAAA,EAAyE;AAC7F,IAAA,OAAOA,EAAAA,CAAG,IAAA,CAAK,kBAAA,CAAmB,QAAA,CAAS,iBAAiB,CAA+B,CAAA;AAAA,EAC7F;AAAA,EAEA,gBAAgB,KAAA,EAA6D;AAC3E,IAAA,OAAOA,EAAAA,CAAG,IAAA,CAAK,kBAAA,CAAmB,QAAA,CAAS,WAAW,CAAyB,CAAA;AAAA,EACjF;AAAA,EAEA,qBAAqB,KAAA,EAAuE;AAC1F,IAAA,OAAOA,EAAAA,CAAG,IAAA,CAAK,kBAAA,CAAmB,QAAA,CAAS,gBAAgB,CAA8B,CAAA;AAAA,EAC3F;AAAA,EAEA,qBAAqB,KAAA,EAAuE;AAC1F,IAAA,OAAOA,EAAAA,CAAG,IAAA,CAAK,YAAA,CAA6B,gBAAgB,CAAC,CAAA;AAAA,EAC/D;AAAA,EAEA,sBAAsB,KAAA,EAAyE;AAC7F,IAAA,OAAOA,EAAAA,CAAG,IAAA,CAAK,YAAA,CAA8B,iBAAiB,CAAC,CAAA;AAAA,EACjE;AAAA,EAEA,eAAe,IAAA,EAA0D;AACvE,IAAA,IAAI,IAAA,CAAK,SAAS,WAAA,EAAa;AAC7B,MAAA,OAAOC,GAAAA;AAAA,QACL,IAAIC,QAAA,CAAc;AAAA,UAChB,IAAA,EAAM,qBAAA;AAAA,UACN,QAAA,EAAU,wDAAA;AAAA,UACV,IAAA,EAAM;AAAA,SACP;AAAA,OACH;AAAA,IACF;AACA,IAAA,OAAOF,EAAAA,CAAG,IAAA,CAAK,kBAAA,CAAmB,QAAA,CAAS,UAAU,CAAwB,CAAA;AAAA,EAC/E;AAAA,EAEA,qBACE,KAAA,EACyC;AACzC,IAAA,OAAOA,GAAG,IAAA,CAAK,cAAA,CAAe,IAAA,CAAK,kBAAA,EAAoB,IAAI,CAAC,CAAA;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,aAAoB,IAAA,EAAqC;AAC/D,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,kBAAA,CAAmB,QAAA,CAAS,IAAI,CAAA;AACpD,IAAA,MAAM,qBAAqB,CAAC,MAAA,KAC1B,IAAA,CAAK,kBAAA,CAAmB,SAAS,iBAAiB,CAAA;AACpD,IAAA,OAAO,MAAA,CAAO,MAAA,CAAO,MAAA,EAAQ,EAAE,oBAAoB,CAAA;AAAA,EACrD;AACF;AAKA,IAAM,cAAA,uBAAkC,GAAA,EAAI;AAI5C,IAAM,eAA0B,EAAC;AAIjC,IAAM,KAAA,GACJ,IAAI,OAAA,CAAQ,MAAM;AAAC,CAAC,CAAA;AC3Of,IAAM,eAAN,MAAuC;AAAA,EAC5C,WAAA,CACE,OAAA,EACA,aAAA,EACA,KAAA,EACA,aACA,KAAA,EAC4B;AAC5B,IAAA,OAAOA,GAAG,MAAS,CAAA;AAAA,EACrB;AAAA,EAEA,YAAA,CACE,YAAA,EACA,KAAA,EACA,WAAA,EACA,KAAA,EAC4B;AAC5B,IAAA,OAAOA,GAAG,MAAS,CAAA;AAAA,EACrB;AAAA,EAEA,0BAAA,CACE,OAAA,EACA,YAAA,EACA,SAAA,EAC4B;AAC5B,IAAA,OAAOA,GAAG,MAAS,CAAA;AAAA,EACrB;AAAA,EAEA,OAAO,eAAA,EAAuE;AAC5E,IAAA,OAAOA,GAAG,MAAS,CAAA;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAA,GAA0C;AACxC,IAAA,OAAO,OAAA,CAAQ,QAAQ,MAAS,CAAA;AAAA,EAClC;AACF;;;ACpCO,IAAM,iBAAN,MAA2C;AAAA,EACvC,QAAA,uBAA4C,GAAA,EAAI;AAAA,EAChD,SAA2C,EAAC;AAAA;AAAA;AAAA,EAIrD,cACE,KAAA,EAC0C;AAC1C,IAAA,MAAM,SAAS,IAAI,aAAA;AAAA,MACjB,IAAI,YAAA,EAAa;AAAA,MACjB,CAAC,UAAA,EAAY,GAAA,KAAQ,IAAI,qBAAA,CAAsB,YAAY,GAAG;AAAA,KAChE;AACA,IAAA,OAAO,OAAA,CAAQ,OAAA,CAAQA,EAAAA,CAAG,MAAM,CAAC,CAAA;AAAA,EACnC;AACF;ACAO,IAAM,uBAAN,MAAuD;AAAA,EAC5D,UAAU,KAAA,EAAwD;AAChE,IAAA,OAAOA,GAAG,MAAS,CAAA;AAAA,EACrB;AAAA,EAEA,WAAA,GAAoB;AAAA,EAAC;AAAA,EAErB,gBAAA,GAAoD;AAClD,IAAA,OAAO,MAAA;AAAA,EACT;AAAA,EAEA,iBAAA,GAAmD;AAIjD,IAAA,OAAOA,EAAAA,CAAG,EAAwB,CAAA;AAAA,EACpC;AACF;AAUO,SAAS,qBACd,OAAA,EACwC;AACxC,EAAA,OAAOA,EAAAA,CAAG,IAAI,oBAAA,EAAsB,CAAA;AACtC;ACvCO,SAAS,kBAAA,CACd,SACA,KAAA,EAC6C;AAC7C,EAAA,OAAO,OAAA,CAAQ,OAAA,CAAQA,EAAAA,CAAG,EAA6B,CAAC,CAAA;AAC1D;;;ACFA,SAAS,cAAA,CACP,OACA,kBAAA,EAC2C;AAC3C,EAAA,OAAO,QAAQ,OAAA,CAAQA,EAAAA,CAAG,IAAI,cAAA,EAAgB,CAAC,CAAA;AACjD;AAQO,IAAM,GAAA,GAGT;AAAA,EACF,cAAA;AAAA,EACA,oBAAA;AAAA,EACA;AACF","file":"index.mjs","sourcesContent":["// @forgeax/engine-rhi-null/src/pass-encoders - headless render / compute pass\n// encoders.\n//\n// Both encoders are no-ops that thread state changes through nothing real; the\n// only side effects are command-stream bookkeeping the M3 unit tests read back:\n// - draw / drawIndexed / drawIndirect / drawIndexedIndirect bump a draw\n// counter (AC-06);\n// - dispatchWorkgroups bumps a dispatch counter;\n// - setVertexBuffer / setBindGroup validate handle ownership against the\n// issuing device's ledger (AC-09 handle-chain consistency) and record the\n// outcome so an assertion can read the most recent validation result\n// without the call site throwing (the spec method form is void).\n//\n// Counters + last-validation live on the encoder instance (public readonly) so\n// a test holding the pass encoder reads them directly; the per-device ledger\n// (Bookkeeper) supplies cross-device validation.\n//\n// Related: requirements AC-04 (pass sequence) + AC-06 (draw count + binding\n// assembly) + AC-09 (handle-chain consistency); research Finding A1 rows 6/7;\n// plan-strategy §3.1 (pass encoder bookkeeping design) + §2 D-1.\n\nimport type {\n BindGroup,\n Buffer,\n ComputePipeline,\n RenderPipeline,\n Result,\n RhiComputePassEncoder,\n RhiError as RhiErrorType,\n RhiRenderPassEncoder,\n} from '@forgeax/engine-rhi';\nimport { ok } from '@forgeax/engine-types';\nimport type { Bookkeeper } from './bookkeeping';\n\n/** Shared counter interface that pass encoders bump so the device can aggregate\n * per-frame stats for M3 unit-test readback. */\nexport interface PassCounter {\n recordDraw(): void;\n recordDispatch(): void;\n recordBindGroup(): void;\n recordPassName(name: string): void;\n}\n\n/**\n * Headless render pass encoder. Records draw counts + the most recent handle\n * validation outcome; all state-setting methods are no-ops.\n */\nexport class RhiNullRenderPassEncoder implements RhiRenderPassEncoder {\n /** Number of draw* calls issued on this pass (AC-06 readback). */\n drawCount = 0;\n bindGroupCount = 0;\n /** Most recent setVertexBuffer / setBindGroup ownership validation; ok unless\n * a foreign handle was passed (AC-09 readback). */\n lastValidation: Result<unknown, RhiErrorType> = ok(undefined);\n\n private readonly bookkeeper: Bookkeeper;\n private readonly counter: PassCounter | null;\n readonly passName: string;\n\n constructor(bookkeeper: Bookkeeper, counter: PassCounter | null, passName: string) {\n this.bookkeeper = bookkeeper;\n this.counter = counter;\n this.passName = passName;\n }\n\n setPipeline(_pipeline: RenderPipeline): void {}\n\n setVertexBuffer(\n _slot: number,\n buffer: Buffer,\n _offset?: number | undefined,\n _size?: number | undefined,\n ): void {\n this.lastValidation = this.bookkeeper.validateOwnership(buffer);\n }\n\n setIndexBuffer(\n _buffer: Buffer,\n _format: 'uint16' | 'uint32',\n _offset?: number | undefined,\n _size?: number | undefined,\n ): void {}\n\n setBindGroup(\n _index: number,\n bindGroup: BindGroup,\n _dynamicOffsetsData?: readonly number[] | Uint32Array | undefined,\n _dynamicOffsetsDataStart?: number | undefined,\n _dynamicOffsetsDataLength?: number | undefined,\n ): void {\n this.bindGroupCount++;\n this.counter?.recordBindGroup();\n this.lastValidation = this.bookkeeper.validateOwnership(bindGroup);\n }\n\n draw(\n _vertexCount: number,\n _instanceCount?: number | undefined,\n _firstVertex?: number | undefined,\n _firstInstance?: number | undefined,\n ): void {\n this.drawCount++;\n this.counter?.recordDraw();\n }\n\n drawIndexed(\n _indexCount: number,\n _instanceCount?: number | undefined,\n _firstIndex?: number | undefined,\n _baseVertex?: number | undefined,\n _firstInstance?: number | undefined,\n ): void {\n this.drawCount++;\n this.counter?.recordDraw();\n }\n\n end(): void {\n this.counter?.recordPassName(this.passName);\n }\n\n setViewport(\n _x: number,\n _y: number,\n _w: number,\n _h: number,\n _minDepth: number,\n _maxDepth: number,\n ): void {}\n\n setScissorRect(_x: number, _y: number, _w: number, _h: number): void {}\n\n setBlendConstant(_color: GPUColor): void {}\n\n setStencilReference(_reference: number): void {}\n\n drawIndirect(_indirectBuffer: Buffer, _indirectOffset: number): void {\n this.drawCount++;\n this.counter?.recordDraw();\n }\n\n drawIndexedIndirect(_indirectBuffer: Buffer, _indirectOffset: number): void {\n this.drawCount++;\n this.counter?.recordDraw();\n }\n\n pushDebugGroup(_groupLabel: string): void {}\n\n popDebugGroup(): void {}\n\n insertDebugMarker(_markerLabel: string): void {}\n\n executeBundles(_bundles: Iterable<unknown>): Result<void, RhiErrorType> {\n // Headless no-op: executing zero bundles against no GPU succeeds vacuously\n // (plan-strategy §3.1 — pass-encoder Result methods return ok(void)).\n return ok(undefined);\n }\n\n beginOcclusionQuery(_queryIndex: number): Result<void, RhiErrorType> {\n return ok(undefined);\n }\n\n endOcclusionQuery(): Result<void, RhiErrorType> {\n return ok(undefined);\n }\n}\n\n/**\n * Headless compute pass encoder. Records dispatch counts; all state-setting\n * methods are no-ops.\n */\nexport class RhiNullComputePassEncoder implements RhiComputePassEncoder {\n /** Number of dispatchWorkgroups calls issued on this pass (readback). */\n dispatchCount = 0;\n /** Most recent setBindGroup ownership validation (AC-09 readback). */\n lastValidation: Result<unknown, RhiErrorType> = ok(undefined);\n\n private readonly bookkeeper: Bookkeeper;\n private readonly counter: PassCounter | null;\n readonly passName: string;\n\n constructor(bookkeeper: Bookkeeper, counter: PassCounter | null, passName: string) {\n this.bookkeeper = bookkeeper;\n this.counter = counter;\n this.passName = passName;\n }\n\n setPipeline(_pipeline: ComputePipeline): void {}\n\n setBindGroup(\n _index: number,\n bindGroup: BindGroup,\n _dynamicOffsets?: readonly number[] | undefined,\n ): void {\n this.lastValidation = this.bookkeeper.validateOwnership(bindGroup);\n }\n\n dispatchWorkgroups(_x: number, _y?: number | undefined, _z?: number | undefined): void {\n this.dispatchCount++;\n this.counter?.recordDispatch();\n }\n\n dispatchWorkgroupsIndirect(indirectBuffer: Buffer, _indirectOffset: number): void {\n this.lastValidation = this.bookkeeper.validateOwnership(indirectBuffer);\n this.dispatchCount++;\n this.counter?.recordDispatch();\n }\n\n end(): void {\n this.counter?.recordPassName(this.passName);\n }\n}\n","// @forgeax/engine-rhi-null/src/command-encoder - headless command encoder.\n//\n// RhiNullCommandEncoder is a no-op recorder: begin*Pass returns a fresh\n// pass-encoder bound to the same per-device ledger; copy* / clear* / debug*\n// are no-ops; resolveQuerySet / finish return ok. finish() mints\n// a legal CommandBuffer brand through the ledger so submit-side bookkeeping\n// (AC-12) can read it back.\n//\n// Each beginRenderPass / beginComputePass reads the `label` from the descriptor\n// and threads it as the pass name to the per-device counters so M3 unit tests\n// (w17 AC-04) can assert per-frame pass scheduling order.\n//\n// Related: requirements AC-04 (pass sequence) + AC-06 (draw count via pass\n// encoders); research Finding A1 row 5; plan-strategy §3.1.\n\nimport type {\n Buffer,\n BufferCopyDestination,\n CommandBuffer,\n ComputePassDescriptor,\n QuerySet,\n RenderPassDescriptor,\n Result,\n RhiCommandEncoder,\n RhiComputePassEncoder,\n RhiError as RhiErrorType,\n RhiRenderPassEncoder,\n TextureCopySource,\n} from '@forgeax/engine-rhi';\nimport { ok } from '@forgeax/engine-types';\nimport type { Bookkeeper } from './bookkeeping';\nimport type { RhiNullDevice } from './device';\nimport {\n type PassCounter,\n RhiNullComputePassEncoder,\n RhiNullRenderPassEncoder,\n} from './pass-encoders';\n\n/**\n * Internal counter that writes per-frame stats back to the owning device.\n */\nclass DeviceCounter implements PassCounter {\n constructor(private readonly device: RhiNullDevice) {}\n\n recordDraw(): void {\n this.device.totalDrawCount++;\n }\n\n recordDispatch(): void {\n this.device.totalDispatchCount++;\n }\n\n recordBindGroup(): void {\n this.device.totalBindGroupCount++;\n }\n\n recordPassName(name: string): void {\n this.device.framePassNames.push(name);\n }\n}\n\n/**\n * Read the `label` field from a GPURenderPassDescriptor (or undefined if not\n * set). The engine sets `label` on the render pass descriptor; the graph's\n * compile path doesn't always thread a per-pass label, so we fall back to the\n * RenderPassDescriptor's generic label (or '<unnamed>').\n */\nfunction readPassLabel(desc: { readonly label?: string | undefined } | undefined): string {\n if (desc && typeof desc.label === 'string' && desc.label.length > 0) {\n return desc.label as string;\n }\n return '<unnamed>';\n}\n\n/**\n * Headless command encoder. begin*Pass returns a no-op pass encoder bound to\n * the issuing device's ledger; recording methods are no-ops; finish mints a\n * CommandBuffer brand.\n */\nexport class RhiNullCommandEncoder implements RhiCommandEncoder {\n private readonly bookkeeper: Bookkeeper;\n private readonly counter: PassCounter;\n\n constructor(bookkeeper: Bookkeeper, device: RhiNullDevice) {\n this.bookkeeper = bookkeeper;\n this.counter = new DeviceCounter(device);\n }\n\n beginRenderPass(desc: RenderPassDescriptor): RhiRenderPassEncoder {\n const label = readPassLabel(desc);\n return new RhiNullRenderPassEncoder(this.bookkeeper, this.counter, label);\n }\n\n beginComputePass(desc?: ComputePassDescriptor | undefined): RhiComputePassEncoder {\n const label = readPassLabel(desc);\n return new RhiNullComputePassEncoder(this.bookkeeper, this.counter, label);\n }\n\n encodeEmptyComputePass(desc: ComputePassDescriptor): void {\n this.counter.recordPassName(readPassLabel(desc));\n }\n\n copyBufferToBuffer(\n _source: Buffer,\n _sourceOffsetOrDestination: number | Buffer,\n _destinationOrSize?: Buffer | number | undefined,\n _destinationOffset?: number | undefined,\n _size?: number | undefined,\n ): void {}\n\n copyBufferToTexture(\n _source: GPUTexelCopyBufferInfo,\n _destination: GPUTexelCopyTextureInfo,\n _copySize: GPUExtent3DStrict,\n ): void {}\n\n copyTextureToBuffer(\n _source: GPUTexelCopyTextureInfo | TextureCopySource,\n _destination: GPUTexelCopyBufferInfo | BufferCopyDestination,\n _copySize: GPUExtent3DStrict,\n ): void {}\n\n copyTextureToTexture(\n _source: GPUTexelCopyTextureInfo,\n _destination: GPUTexelCopyTextureInfo,\n _copySize: GPUExtent3DStrict,\n ): void {}\n\n clearBuffer(_buffer: Buffer, _offset?: number | undefined, _size?: number | undefined): void {}\n\n resolveQuerySet(\n _querySet: QuerySet,\n _firstQuery: number,\n _queryCount: number,\n _destination: Buffer,\n _destinationOffset: number,\n ): Result<void, RhiErrorType> {\n return ok(undefined);\n }\n\n pushDebugGroup(_groupLabel: string): void {}\n\n popDebugGroup(): void {}\n\n insertDebugMarker(_markerLabel: string): void {}\n\n finish(): Result<CommandBuffer, RhiErrorType> {\n return ok(this.bookkeeper.register('CommandBuffer') as unknown as CommandBuffer);\n }\n}\n","// @forgeax/engine-rhi-null/src/bookkeeping - method-A handle ledger for the\n// headless no-op backend.\n//\n// RhiNull issues legal opaque-handle brands (each RHI brand is a compile-time\n// `unique symbol` with no runtime field, so any plain object cast via\n// `as unknown as Buffer` satisfies the type contract; research Finding A6). To\n// let M3 command-stream assertions read back create/destroy pairing, draw /\n// dispatch counts and binding assembly, RhiNull threads each issued handle\n// through a per-device Bookkeeper that records a numeric id + kind + destroyed\n// flag + the issuing device id.\n//\n// Error contract (plan-strategy D-1): handle-chain inconsistencies reuse the\n// existing closed RhiErrorCode union with ZERO new members:\n// - a handle observed on a device other than its issuer -> 'rhi-not-available'\n// - a destroy / use observed on an already-destroyed handle ->\n// 'destroy-after-destroy'\n// Every RhiError carries the required code / expected / hint triple\n// (charter proposition 3 structured failure).\n//\n// Related: requirements AC-09 (foreign handle returns structured err, no silent\n// pass); plan-strategy §2 D-1; research Finding A3 (RhiErrorCode 20 members,\n// 'rhi-not-available' at errors.ts:113, 'destroy-after-destroy' at errors.ts:127)\n// + Finding A6 (brand construction); charter P3.\n\nimport type { Result, RhiError as RhiErrorType } from '@forgeax/engine-rhi';\nimport { RhiError } from '@forgeax/engine-rhi';\nimport { err, ok } from '@forgeax/engine-types';\n\n/**\n * One ledger row for an issued opaque-handle brand.\n *\n * - `id` — per-device monotonic numeric id assigned at register time; the value\n * M3 assertions read back to pair create / destroy and count draws.\n * - `kind` — the handle category (`'Buffer'` / `'Texture'` / `'BindGroupLayout'`\n * etc.); free-form so every create* entry can tag what it minted.\n * - `destroyed` — flipped to `true` by the first destroy; a second destroy\n * fail-fasts via `'destroy-after-destroy'`.\n * - `sourceDeviceId` — the id of the device that issued the handle; cross-device\n * use fail-fasts via `'rhi-not-available'`.\n */\nexport interface HandleRecord {\n readonly id: number;\n readonly kind: string;\n destroyed: boolean;\n readonly sourceDeviceId: number;\n}\n\n/**\n * The brand attachment RhiNull stamps onto every issued handle object so the\n * ledger can be recovered from the handle alone (handle-chain validation reads\n * `handle[BOOKKEEPING_KEY]` rather than holding a side Map keyed on identity,\n * which would leak across device instances).\n *\n * Lives behind a module-private symbol so it never collides with a real RHI\n * brand symbol and never appears in the public `.d.ts` surface.\n */\nexport const BOOKKEEPING_KEY = Symbol('forgeax-rhi-null-bookkeeping');\n\n/** A handle object as RhiNull mints it: a plain object carrying its ledger row\n * under the private bookkeeping symbol, cast to the relevant brand at the\n * create* call site. */\nexport interface BookkeptHandle {\n readonly [BOOKKEEPING_KEY]: HandleRecord;\n}\n\n/**\n * Per-device handle ledger. One Bookkeeper instance lives on each\n * RhiNullDevice; it assigns the device id at construction and mints monotonic\n * handle ids thereafter.\n */\nexport class Bookkeeper {\n readonly deviceId: number;\n private nextHandleId = 0;\n private readonly records = new Map<number, HandleRecord>();\n\n constructor(deviceId: number) {\n this.deviceId = deviceId;\n }\n\n /**\n * Register a freshly-minted handle of the given kind, returning a plain\n * object that carries its ledger row. The caller casts the return value to\n * the concrete brand (`as unknown as Buffer` etc.).\n */\n register(kind: string): BookkeptHandle {\n const id = this.nextHandleId++;\n const record: HandleRecord = {\n id,\n kind,\n destroyed: false,\n sourceDeviceId: this.deviceId,\n };\n this.records.set(id, record);\n return { [BOOKKEEPING_KEY]: record };\n }\n\n /**\n * Mark a handle destroyed. Fail-fasts on a second destroy\n * ('destroy-after-destroy') or on a handle issued by a different device\n * ('rhi-not-available'); otherwise flips the destroyed flag and returns ok.\n */\n destroy(handle: unknown): Result<void, RhiErrorType> {\n const ownership = this.validateOwnership(handle);\n if (!ownership.ok) return ownership;\n const destroyedCheck = isHandleDestroyed(handle);\n if (!destroyedCheck.ok) return destroyedCheck;\n ownership.value.destroyed = true;\n return ok(undefined);\n }\n\n /**\n * Report whether a handle has already been destroyed (true once destroy()\n * has flipped its flag). Used by command-stream methods that must not\n * consume a stale handle.\n */\n isDestroyed(handle: unknown): boolean {\n return readRecord(handle)?.destroyed === true;\n }\n\n /**\n * Validate that a handle was issued by THIS device. Returns the ledger row on\n * success so callers can mutate it (e.g. flip destroyed); returns\n * 'rhi-not-available' for a foreign handle (AC-09 — no silent pass).\n */\n validateOwnership(handle: unknown): Result<HandleRecord, RhiErrorType> {\n const record = readRecord(handle);\n if (record === undefined || record.sourceDeviceId !== this.deviceId) {\n return err(\n new RhiError({\n code: 'rhi-not-available',\n expected: 'handle was issued by this RhiNull device',\n hint: 'do not pass a handle created on a different RhiNull device into this device; create resources on the device they are used with',\n }),\n );\n }\n return ok(record);\n }\n\n /**\n * Return all ledger rows as a readonly array. M3 unit tests (w17) read this\n * to assert create/destroy pairing, BGL/PSO shape counts, and resource\n * lifecycle coverage (AC-05/06/07). Returns a snapshot of the current Map\n * so callers can iterate without a stale reference after further mutations.\n */\n allRecords(): readonly HandleRecord[] {\n return [...this.records.values()];\n }\n\n /** Report the total number of records in the ledger. */\n recordCount(): number {\n return this.records.size;\n }\n}\n\n/** Recover the ledger row from a handle object, or undefined if the object\n * carries no RhiNull bookkeeping (foreign / non-RhiNull handle). */\nfunction readRecord(handle: unknown): HandleRecord | undefined {\n if (handle === null || typeof handle !== 'object') return undefined;\n const tagged = handle as Partial<BookkeptHandle>;\n return tagged[BOOKKEEPING_KEY];\n}\n\n/**\n * Validation function: has this handle already been destroyed? Returns ok(void)\n * for a live handle; returns 'destroy-after-destroy' for a handle whose ledger\n * row is flagged destroyed (charter P3 fail-fast over silent idempotency).\n */\nexport function isHandleDestroyed(handle: unknown): Result<void, RhiErrorType> {\n if (readRecord(handle)?.destroyed === true) {\n return err(\n new RhiError({\n code: 'destroy-after-destroy',\n expected: 'GPU buffer/texture handle has not been destroyed yet',\n hint: 'object already destroyed; track lifecycle in caller or check isDestroyed before re-destroy',\n }),\n );\n }\n return ok(undefined);\n}\n","// @forgeax/engine-rhi-null/src/device - RhiNullDevice + caps for the headless\n// no-op backend.\n//\n// RhiNullDevice implements the full RhiDevice surface (research Finding A1\n// row 3) as no-ops that mint legal opaque-handle brands and thread them through\n// the per-device Bookkeeper (method A; research Finding A6). The `implements\n// RhiDevice` clause is the completeness guard: a missing method is a tsc -b\n// red, satisfying AC-01 without a hand-maintained member checklist.\n//\n// caps (D-5): backendKind 'null'; the 3 wgpu-native-only reserved flags\n// (multiDrawIndirect / pushConstants / textureBindingArray) are false; every\n// other boolean cap is true and maxColorAttachments is 8, so the headless\n// backend maximizes structural coverage of capability-gated paths.\n//\n// The queue is supplied at construction (the adapter's requestDevice mints the\n// RhiNullQueue, w10) so device.ts carries no dependency on queue.ts.\n//\n// Related: requirements scope row 2 (8 interfaces full method set) + AC-01\n// (implements compiles) + AC-08 (backendKind === 'null'); plan-strategy §2 D-5\n// (caps field-level) + §4 R-3; research Finding A1 + A6.\n\nimport type {\n BindGroup,\n BindGroupDescriptor,\n BindGroupLayout,\n BindGroupLayoutDescriptor,\n Buffer,\n BufferDescriptor,\n CommandEncoderDescriptor,\n ComputePipeline,\n ComputePipelineDescriptor,\n PipelineLayout,\n PipelineLayoutDescriptor,\n QuerySet,\n QuerySetDescriptor,\n RenderPipeline,\n RenderPipelineDescriptor,\n Result,\n RhiCaps,\n RhiCommandEncoder,\n RhiComputePipelineOps,\n RhiDevice,\n RhiError as RhiErrorType,\n RhiFeatures,\n RhiLimits,\n RhiQueue,\n RhiRenderPipelineOps,\n Sampler,\n SamplerDescriptor,\n Texture,\n TextureDescriptor,\n TextureView,\n TextureViewDescriptor,\n} from '@forgeax/engine-rhi';\nimport { RhiError as RhiErrorClass } from '@forgeax/engine-rhi';\nimport { err, ok } from '@forgeax/engine-types';\nimport { Bookkeeper } from './bookkeeping';\n\n/** Monotonic device-id source so each RhiNullDevice owns a distinct id; the id\n * threads into the Bookkeeper for cross-device handle-chain validation. */\nlet nextDeviceId = 0;\n\n/**\n * Factory that builds a command encoder bound to a device's ledger. Injected at\n * device construction (rather than imported here) so device.ts carries no\n * dependency on command-encoder.ts; the singleton assembly (index.ts) supplies\n * the real factory. The Bookkeeper and RhiNullDevice are passed so the encoder\n * threads draw / dispatch counts + binding validation through the same per-device\n * ledger AND writes aggregated frame stats to the device for M3 unit-test readback.\n */\nexport type CommandEncoderFactory = (\n bookkeeper: Bookkeeper,\n device: RhiNullDevice,\n) => RhiCommandEncoder;\n\n/** A pipeline brand augmented with its no-op `getBindGroupLayout` ops method\n * (D-2). createRenderPipeline / createComputePipeline return objects of this\n * shape so the auto-layout consumers can call getBindGroupLayout. */\ntype PipelineHandle<Brand> = Brand & RhiRenderPipelineOps & RhiComputePipelineOps;\n\n/**\n * Headless no-op RhiDevice. Every create* mints a legal brand and records it;\n * every destroy* fail-fasts a double-destroy; caps reports the all-true-except-\n * reserved profile (D-5).\n */\nexport class RhiNullDevice implements RhiDevice {\n private readonly internalBookkeeper: Bookkeeper;\n private readonly nullQueue: RhiQueue;\n private readonly encoderFactory: CommandEncoderFactory;\n\n /** Per-frame total draw count across all pass encoders executed this frame\n * (aggregated by the command encoder on finish, then reset). M3 unit tests\n * (w17) read this to assert draw count >= 1 (AC-06). */\n totalDrawCount = 0;\n /** Per-frame total direct and indirect compute dispatch count. */\n totalDispatchCount = 0;\n /** Per-frame total bind group set count (AC-06 / AC-05 readback). */\n totalBindGroupCount = 0;\n /** Per-frame pass names executed this frame, in schedule order (AC-04). */\n framePassNames: string[] = [];\n\n /** The per-device handle ledger — exposed so M3 tests can assert create/destroy\n * pairing and BGL/PSO shape counts (AC-05/06/07). */\n get bookkeeper(): Bookkeeper {\n return this.internalBookkeeper;\n }\n\n constructor(queue: RhiQueue, encoderFactory: CommandEncoderFactory) {\n this.internalBookkeeper = new Bookkeeper(nextDeviceId++);\n this.nullQueue = queue;\n this.encoderFactory = encoderFactory;\n }\n\n get caps(): RhiCaps {\n return {\n backendKind: 'null',\n compute: true,\n timestampQuery: false,\n timestampPeriodNanoseconds: null,\n indirectDrawing: true,\n textureCompressionBc: false,\n textureCompressionEtc2: false,\n textureCompressionAstc: false,\n // 3 wgpu-native-only reserved flags stay false on non-native backends\n // (D-5); the headless backend is not a native runtime.\n multiDrawIndirect: false,\n pushConstants: false,\n textureBindingArray: false,\n samplerAliasing: true,\n firstInstanceIndirect: true,\n storageBuffer: true,\n storageTexture: true,\n rgba16floatRenderable: true,\n rg11b10ufloatRenderable: true,\n float32Filterable: true,\n maxColorAttachments: 8,\n };\n }\n\n get features(): RhiFeatures {\n return EMPTY_FEATURES;\n }\n\n get limits(): RhiLimits {\n return EMPTY_LIMITS;\n }\n\n get queue(): RhiQueue {\n return this.nullQueue;\n }\n\n // forgeax-async-whitelist: dom-native — spec `GPUDevice.lost` Promise\n // passthrough. The headless backend never loses a device (no GPU), so the\n // Promise stays unsettled for the lifetime of the device, mirroring a live\n // device that never transitions to the lost state.\n get lost(): Promise<{ readonly reason: 'destroyed' | 'unknown'; readonly message: string }> {\n return NEVER;\n }\n\n createBuffer(_desc: BufferDescriptor): Result<Buffer, RhiErrorType> {\n return ok(this.internalBookkeeper.register('Buffer') as unknown as Buffer);\n }\n\n createTexture(_desc: TextureDescriptor): Result<Texture, RhiErrorType> {\n return ok(this.internalBookkeeper.register('Texture') as unknown as Texture);\n }\n\n destroyBuffer(buf: Buffer): Result<void, RhiErrorType> {\n return this.internalBookkeeper.destroy(buf);\n }\n\n destroyQuerySet(querySet: QuerySet): Result<void, RhiErrorType> {\n return this.internalBookkeeper.destroy(querySet);\n }\n\n destroyTexture(tex: Texture): Result<void, RhiErrorType> {\n return this.internalBookkeeper.destroy(tex);\n }\n\n createTextureView(\n _texture: Texture,\n _desc: TextureViewDescriptor,\n ): Result<TextureView, RhiErrorType> {\n return ok(this.internalBookkeeper.register('TextureView') as unknown as TextureView);\n }\n\n createSampler(_desc?: SamplerDescriptor | undefined): Result<Sampler, RhiErrorType> {\n return ok(this.internalBookkeeper.register('Sampler') as unknown as Sampler);\n }\n\n createBindGroupLayout(_desc: BindGroupLayoutDescriptor): Result<BindGroupLayout, RhiErrorType> {\n return ok(this.internalBookkeeper.register('BindGroupLayout') as unknown as BindGroupLayout);\n }\n\n createBindGroup(_desc: BindGroupDescriptor): Result<BindGroup, RhiErrorType> {\n return ok(this.internalBookkeeper.register('BindGroup') as unknown as BindGroup);\n }\n\n createPipelineLayout(_desc: PipelineLayoutDescriptor): Result<PipelineLayout, RhiErrorType> {\n return ok(this.internalBookkeeper.register('PipelineLayout') as unknown as PipelineLayout);\n }\n\n createRenderPipeline(_desc: RenderPipelineDescriptor): Result<RenderPipeline, RhiErrorType> {\n return ok(this.makePipeline<RenderPipeline>('RenderPipeline'));\n }\n\n createComputePipeline(_desc: ComputePipelineDescriptor): Result<ComputePipeline, RhiErrorType> {\n return ok(this.makePipeline<ComputePipeline>('ComputePipeline'));\n }\n\n createQuerySet(desc: QuerySetDescriptor): Result<QuerySet, RhiErrorType> {\n if (desc.type === 'timestamp') {\n return err(\n new RhiErrorClass({\n code: 'feature-not-enabled',\n expected: 'caps.timestampQuery === true (timestamp-query feature)',\n hint: 'RhiNull is structural-only and cannot produce GPU timestamp ticks',\n }),\n );\n }\n return ok(this.internalBookkeeper.register('QuerySet') as unknown as QuerySet);\n }\n\n createCommandEncoder(\n _desc?: CommandEncoderDescriptor | undefined,\n ): Result<RhiCommandEncoder, RhiErrorType> {\n return ok(this.encoderFactory(this.internalBookkeeper, this));\n }\n\n /**\n * Mint a pipeline handle whose object also carries the no-op\n * `getBindGroupLayout(index)` ops method (D-2). The prod auto-layout path\n * (debug-draw.ts) and the existing mock unit tests both call\n * `pipeline.getBindGroupLayout(n)`; returning a legal BindGroupLayout brand\n * (recorded in the ledger) keeps those consumers from crashing on a missing\n * method.\n */\n private makePipeline<Brand>(kind: string): PipelineHandle<Brand> {\n const handle = this.internalBookkeeper.register(kind);\n const getBindGroupLayout = (_index: number): BindGroupLayout =>\n this.internalBookkeeper.register('BindGroupLayout') as unknown as BindGroupLayout;\n return Object.assign(handle, { getBindGroupLayout }) as unknown as PipelineHandle<Brand>;\n }\n}\n\n/** Empty enabled-feature set — headless backend enables nothing beyond the\n * always-true caps profile (research Finding A1: features getter returns an\n * empty ReadonlySet). */\nconst EMPTY_FEATURES: RhiFeatures = new Set() as RhiFeatures;\n\n/** Empty numeric-limits map. The headless backend reports no concrete numeric\n * limits; capability planning reads caps booleans instead. */\nconst EMPTY_LIMITS: RhiLimits = {} as RhiLimits;\n\n/** A Promise that never settles, mirroring a live GPUDevice.lost that stays\n * unsettled while the device is healthy. */\nconst NEVER: Promise<{ readonly reason: 'destroyed' | 'unknown'; readonly message: string }> =\n new Promise(() => {});\n","// @forgeax/engine-rhi-null/src/queue - headless command queue.\n//\n// All write* methods are no-ops returning ok; submit records nothing and\n// returns ok (AC-12). onSubmittedWorkDone resolves immediately so headless\n// read-back idioms never hang (the real GPU resolves after work completes; with\n// no pending work the headless backend resolves synchronously).\n//\n// Related: requirements AC-12 (submit ok + onSubmittedWorkDone resolves);\n// research Finding A1 row 4.\n\nimport type {\n Buffer,\n CommandBuffer,\n ExternalImageTextureDestination,\n Result,\n RhiError as RhiErrorType,\n RhiQueue,\n TextureWriteDestination,\n} from '@forgeax/engine-rhi';\nimport { ok } from '@forgeax/engine-types';\n\n/** Headless no-op queue. */\nexport class RhiNullQueue implements RhiQueue {\n writeBuffer(\n _buffer: Buffer,\n _bufferOffset: number,\n _data: ArrayBufferView | ArrayBuffer,\n _dataOffset?: number | undefined,\n _size?: number | undefined,\n ): Result<void, RhiErrorType> {\n return ok(undefined);\n }\n\n writeTexture(\n _destination: TextureWriteDestination,\n _data: ArrayBufferView | ArrayBuffer,\n _dataLayout: Pick<GPUTexelCopyBufferLayout, 'offset' | 'bytesPerRow' | 'rowsPerImage'>,\n _size: GPUExtent3DStrict,\n ): Result<void, RhiErrorType> {\n return ok(undefined);\n }\n\n copyExternalImageToTexture(\n _source: Pick<GPUCopyExternalImageSourceInfo, 'source' | 'origin' | 'flipY'>,\n _destination: ExternalImageTextureDestination,\n _copySize: GPUExtent3DStrict,\n ): Result<void, RhiErrorType> {\n return ok(undefined);\n }\n\n submit(_commandBuffers: readonly CommandBuffer[]): Result<void, RhiErrorType> {\n return ok(undefined);\n }\n\n // forgeax-async-whitelist: dom-native — spec `GPUQueue.onSubmittedWorkDone`\n // never rejects. The headless backend has no pending GPU work, so it resolves\n // immediately (AC-12: read-back idioms must not hang).\n onSubmittedWorkDone(): Promise<undefined> {\n return Promise.resolve(undefined);\n }\n}\n","// @forgeax/engine-rhi-null/src/adapter - headless adapter.\n//\n// requestDevice mints a fresh RhiNullDevice, wiring its queue + command-encoder\n// factory. features / limits are empty (the headless backend enables nothing\n// beyond the always-true caps profile); the two-step requestAdapter ->\n// requestDevice path mirrors the spec idiom (research Finding A1 row 2).\n//\n// Related: requirements AC-02 (createRenderer ready chain needs adapter ->\n// device) + AC-12; research Finding A1 row 2.\n\nimport type {\n RequestDeviceOptions,\n Result,\n RhiAdapter,\n RhiDevice,\n RhiError as RhiErrorType,\n} from '@forgeax/engine-rhi';\nimport { ok } from '@forgeax/engine-types';\nimport { RhiNullCommandEncoder } from './command-encoder';\nimport { RhiNullDevice } from './device';\nimport { RhiNullQueue } from './queue';\n\n/** Headless adapter. Holds an empty feature set + limits map; requestDevice\n * builds a fully-wired RhiNullDevice. */\nexport class RhiNullAdapter implements RhiAdapter {\n readonly features: ReadonlySet<GPUFeatureName> = new Set();\n readonly limits: Readonly<Record<string, number>> = {};\n\n // forgeax-async-whitelist is not needed: this returns Promise<Result<...>>\n // per the spec contract; never rejects.\n requestDevice(\n _opts?: RequestDeviceOptions | undefined,\n ): Promise<Result<RhiDevice, RhiErrorType>> {\n const device = new RhiNullDevice(\n new RhiNullQueue(),\n (bookkeeper, dev) => new RhiNullCommandEncoder(bookkeeper, dev),\n );\n return Promise.resolve(ok(device));\n }\n}\n","// @forgeax/engine-rhi-null/src/canvas-context - headless canvas context (R-1).\n//\n// The headless backend has no real canvas. RhiNullCanvasContext satisfies the\n// RhiCanvasContext interface as no-ops: configure returns ok (a headless\n// configure is vacuously successful), unconfigure is void, getConfiguration\n// returns undefined (no real configuration).\n//\n// getCurrentTexture returns a legal Texture brand so the swap-chain\n// acquisition path succeeds — without this the entire render-frame path fails\n// at the first swap-chain access, blocking all dogfood testing of\n// render-graph-to-RhiNull command-flow. The returned Texture carries no real\n// pixels; it is opaque-handle syntactically valid and structurally sound for\n// CI structural unit tests.\n//\n// acquireCanvasContext (mounted on the rhi singleton, R-1) accepts any\n// canvas / null / undefined and returns ok(canvasContext) so the facade\n// never crashes and canvas-dependent paths exercise through to the\n// render-graph level.\n//\n// Related: requirements AC-11 (canvas ctx structured behaviour) — the\n// context itself succeeds; AC-11 verify tests confirm the getCurrentTexture\n// brand is a valid opaque handle and downstream consumption doesn't crash.\n// Research Finding A4 (acquireCanvasContext on singleton) + A3\n// (rhi-not-available legal); plan-strategy §4 R-1; charter P3.\n\nimport type {\n CanvasConfiguration,\n Result,\n RhiCanvasContext,\n RhiError as RhiErrorType,\n Texture,\n} from '@forgeax/engine-rhi';\nimport { ok } from '@forgeax/engine-types';\n\n/**\n * Headless canvas context. configure succeeds vacuously; getCurrentTexture\n * mints a legal opaque-handle Texture brand so the swap-chain acquisition path\n * never blocks frame rendering in headless CI.\n */\nexport class RhiNullCanvasContext implements RhiCanvasContext {\n configure(_desc: CanvasConfiguration): Result<void, RhiErrorType> {\n return ok(undefined);\n }\n\n unconfigure(): void {}\n\n getConfiguration(): CanvasConfiguration | undefined {\n return undefined;\n }\n\n getCurrentTexture(): Result<Texture, RhiErrorType> {\n // A brand is a compile-time unique symbol with no runtime field, so a plain\n // object cast is a structurally valid Texture (research Finding A6). No real\n // pixels; the command-stream path only needs a legal handle to thread on.\n return ok({} as unknown as Texture);\n }\n}\n\n/**\n * Acquire a canvas context. The headless backend returns ok(context) so the\n * createRenderer context-acquisition step succeeds. The context's\n * getCurrentTexture mints a legal Texture brand once a bookkeeper is attached.\n *\n * R-1: this is mounted on the rhi singleton so Channel 1's facade never\n * crashes on a missing acquireCanvasContext.\n */\nexport function acquireCanvasContext(\n _canvas?: HTMLCanvasElement | OffscreenCanvas | null | undefined,\n): Result<RhiCanvasContext, RhiErrorType> {\n return ok(new RhiNullCanvasContext());\n}\n","// @forgeax/engine-rhi-null/src/shader - headless shader module factory (R-2).\n//\n// createShaderModule is the highest-risk hidden contract: the createRenderer\n// ready chain resolves a shader module through a three-tier fallback\n// (RhiBackendPack.createShaderModule -> RhiDevice.createShaderModule duck-typed\n// -> err('rhi-not-available')). If RhiNull provides neither, createRenderer ->\n// ready REJECTS rhi-not-available and AC-02 / AC-10 / AC-13 all fail. RhiNull\n// exposes the top-level async factory (symmetric with rhi-webgpu) that skips\n// real WGSL compilation and returns a legal ShaderModule brand.\n//\n// Related: requirements AC-10 (shader brand skips compile) + AC-02\n// (createRenderer ready does not reject); research Finding A5 (three-tier\n// fallback, top-level factory recommended) + A6 (brand construction);\n// plan-strategy §4 R-2.\n\nimport type {\n Result,\n RhiDevice,\n RhiError as RhiErrorType,\n ShaderModule,\n} from '@forgeax/engine-rhi';\nimport { ok } from '@forgeax/engine-types';\n\n/**\n * Build a shader module for the headless backend. No WGSL is compiled — the\n * code is ignored and a legal ShaderModule brand is returned immediately\n * (<1ms), so the ready chain's shader step always resolves ok (AC-10). Mirrors\n * the rhi-webgpu top-level `createShaderModule(device, desc)` signature so\n * Channel 1's RhiBackendPack picks it up via the same `'createShaderModule' in\n * mod` probe.\n */\nexport function createShaderModule(\n _device: RhiDevice,\n _desc: { label?: string | undefined; code: string },\n): Promise<Result<ShaderModule, RhiErrorType>> {\n return Promise.resolve(ok({} as unknown as ShaderModule));\n}\n","// @forgeax/engine-rhi-null - headless no-op RHI backend.\n//\n// Single entry for `import { rhi } from '@forgeax/engine-rhi-null'`. The\n// exported `rhi` singleton has the RhiBackendPack-mandated shape\n// `RhiInstance & { acquireCanvasContext }` (research Finding A4 — Channel 1\n// injects this verbatim and calls acquireCanvasContext on the facade, so the\n// method MUST exist on the singleton even though the public renderer options.rhi\n// type does not require it). createShaderModule is exposed at the top level\n// (R-2) for symmetry with rhi-webgpu: createRenderer's ready chain resolves the\n// shader step through `RhiBackendPack.createShaderModule`, otherwise it rejects\n// rhi-not-available.\n//\n// Strict two-step path: rhi.requestAdapter() -> adapter.requestDevice().\n//\n// Related: requirements AC-02 + AC-10 + AC-12 + scope row 4; research Finding\n// A1 row 1 + A4 + A5; plan-strategy §4 R-1 + R-2.\n\nimport type {\n RequestAdapterOptions,\n Result,\n RhiAdapter,\n RhiError as RhiErrorType,\n RhiInstance,\n} from '@forgeax/engine-rhi';\nimport { ok } from '@forgeax/engine-types';\nimport { RhiNullAdapter } from './adapter';\nimport { acquireCanvasContext } from './canvas-context';\nimport { createShaderModule } from './shader';\n\n/**\n * Request a headless adapter. The two-positional-arg signature mirrors the spec\n * RhiInstance.requestAdapter (the second compatibleSurface arg is accepted and\n * ignored — there is no GL backend to route it to).\n */\nfunction requestAdapter(\n _opts?: RequestAdapterOptions | undefined,\n _compatibleSurface?: HTMLCanvasElement | OffscreenCanvas | undefined,\n): Promise<Result<RhiAdapter, RhiErrorType>> {\n return Promise.resolve(ok(new RhiNullAdapter()));\n}\n\n/**\n * The `rhi` singleton — RhiBackendPack-shaped entry for Channel 1 injection\n * (`createRenderer(canvas, { rhi })`). Carries acquireCanvasContext (R-1) so\n * the facade never crashes on a missing method, and createShaderModule (R-2) so\n * the ready chain's shader step resolves rather than rejecting.\n */\nexport const rhi: RhiInstance & {\n acquireCanvasContext: typeof acquireCanvasContext;\n createShaderModule: typeof createShaderModule;\n} = {\n requestAdapter,\n acquireCanvasContext,\n createShaderModule,\n};\n\nexport { RhiNullAdapter } from './adapter';\nexport { acquireCanvasContext, RhiNullCanvasContext } from './canvas-context';\nexport { RhiNullCommandEncoder } from './command-encoder';\nexport { RhiNullDevice } from './device';\nexport { RhiNullComputePassEncoder, RhiNullRenderPassEncoder } from './pass-encoders';\nexport { RhiNullQueue } from './queue';\nexport { createShaderModule } from './shader';\n"]}
1
+ {"version":3,"sources":["../src/pass-encoders.ts","../src/command-encoder.ts","../src/bookkeeping.ts","../src/internal/r32float-capability.ts","../src/device.ts","../src/queue.ts","../src/adapter.ts","../src/canvas-context.ts","../src/shader.ts","../src/index.ts"],"names":["ok","err","RhiErrorClass"],"mappings":"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","file":"index.mjs","sourcesContent":["// @forgeax/engine-rhi-null/src/pass-encoders - headless render / compute pass\n// encoders.\n//\n// Both encoders are no-ops that thread state changes through nothing real; the\n// only side effects are command-stream bookkeeping the M3 unit tests read back:\n// - draw / drawIndexed / drawIndirect / drawIndexedIndirect bump a draw\n// counter (AC-06);\n// - dispatchWorkgroups bumps a dispatch counter;\n// - setVertexBuffer / setBindGroup validate handle ownership against the\n// issuing device's ledger (AC-09 handle-chain consistency) and record the\n// outcome so an assertion can read the most recent validation result\n// without the call site throwing (the spec method form is void).\n//\n// Counters + last-validation live on the encoder instance (public readonly) so\n// a test holding the pass encoder reads them directly; the per-device ledger\n// (Bookkeeper) supplies cross-device validation.\n//\n// Related: requirements AC-04 (pass sequence) + AC-06 (draw count + binding\n// assembly) + AC-09 (handle-chain consistency); research Finding A1 rows 6/7;\n// plan-strategy §3.1 (pass encoder bookkeeping design) + §2 D-1.\n\nimport type {\n BindGroup,\n Buffer,\n ComputePipeline,\n RenderPipeline,\n Result,\n RhiComputePassEncoder,\n RhiError as RhiErrorType,\n RhiRenderPassEncoder,\n} from '@forgeax/engine-rhi';\nimport { ok } from '@forgeax/engine-types';\nimport type { Bookkeeper } from './bookkeeping';\n\n/** Shared counter interface that pass encoders bump so the device can aggregate\n * per-frame stats for M3 unit-test readback. */\nexport interface PassCounter {\n recordDraw(): void;\n recordDispatch(): void;\n recordBindGroup(): void;\n recordPassName(name: string): void;\n}\n\n/**\n * Headless render pass encoder. Records draw counts + the most recent handle\n * validation outcome; all state-setting methods are no-ops.\n */\nexport class RhiNullRenderPassEncoder implements RhiRenderPassEncoder {\n /** Number of draw* calls issued on this pass (AC-06 readback). */\n drawCount = 0;\n bindGroupCount = 0;\n /** Most recent setVertexBuffer / setBindGroup ownership validation; ok unless\n * a foreign handle was passed (AC-09 readback). */\n lastValidation: Result<unknown, RhiErrorType> = ok(undefined);\n\n private readonly bookkeeper: Bookkeeper;\n private readonly counter: PassCounter | null;\n readonly passName: string;\n\n constructor(bookkeeper: Bookkeeper, counter: PassCounter | null, passName: string) {\n this.bookkeeper = bookkeeper;\n this.counter = counter;\n this.passName = passName;\n }\n\n setPipeline(_pipeline: RenderPipeline): void {}\n\n setVertexBuffer(\n _slot: number,\n buffer: Buffer,\n _offset?: number | undefined,\n _size?: number | undefined,\n ): void {\n this.lastValidation = this.bookkeeper.validateOwnership(buffer);\n }\n\n setIndexBuffer(\n _buffer: Buffer,\n _format: 'uint16' | 'uint32',\n _offset?: number | undefined,\n _size?: number | undefined,\n ): void {}\n\n setBindGroup(\n _index: number,\n bindGroup: BindGroup,\n _dynamicOffsetsData?: readonly number[] | Uint32Array | undefined,\n _dynamicOffsetsDataStart?: number | undefined,\n _dynamicOffsetsDataLength?: number | undefined,\n ): void {\n this.bindGroupCount++;\n this.counter?.recordBindGroup();\n this.lastValidation = this.bookkeeper.validateOwnership(bindGroup);\n }\n\n draw(\n _vertexCount: number,\n _instanceCount?: number | undefined,\n _firstVertex?: number | undefined,\n _firstInstance?: number | undefined,\n ): void {\n this.drawCount++;\n this.counter?.recordDraw();\n }\n\n drawIndexed(\n _indexCount: number,\n _instanceCount?: number | undefined,\n _firstIndex?: number | undefined,\n _baseVertex?: number | undefined,\n _firstInstance?: number | undefined,\n ): void {\n this.drawCount++;\n this.counter?.recordDraw();\n }\n\n end(): void {\n this.counter?.recordPassName(this.passName);\n }\n\n setViewport(\n _x: number,\n _y: number,\n _w: number,\n _h: number,\n _minDepth: number,\n _maxDepth: number,\n ): void {}\n\n setScissorRect(_x: number, _y: number, _w: number, _h: number): void {}\n\n setBlendConstant(_color: GPUColor): void {}\n\n setStencilReference(_reference: number): void {}\n\n drawIndirect(_indirectBuffer: Buffer, _indirectOffset: number): void {\n this.drawCount++;\n this.counter?.recordDraw();\n }\n\n drawIndexedIndirect(_indirectBuffer: Buffer, _indirectOffset: number): void {\n this.drawCount++;\n this.counter?.recordDraw();\n }\n\n pushDebugGroup(_groupLabel: string): void {}\n\n popDebugGroup(): void {}\n\n insertDebugMarker(_markerLabel: string): void {}\n\n executeBundles(_bundles: Iterable<unknown>): Result<void, RhiErrorType> {\n // Headless no-op: executing zero bundles against no GPU succeeds vacuously\n // (plan-strategy §3.1 — pass-encoder Result methods return ok(void)).\n return ok(undefined);\n }\n\n beginOcclusionQuery(_queryIndex: number): Result<void, RhiErrorType> {\n return ok(undefined);\n }\n\n endOcclusionQuery(): Result<void, RhiErrorType> {\n return ok(undefined);\n }\n}\n\n/**\n * Headless compute pass encoder. Records dispatch counts; all state-setting\n * methods are no-ops.\n */\nexport class RhiNullComputePassEncoder implements RhiComputePassEncoder {\n /** Number of dispatchWorkgroups calls issued on this pass (readback). */\n dispatchCount = 0;\n /** Most recent setBindGroup ownership validation (AC-09 readback). */\n lastValidation: Result<unknown, RhiErrorType> = ok(undefined);\n\n private readonly bookkeeper: Bookkeeper;\n private readonly counter: PassCounter | null;\n readonly passName: string;\n\n constructor(bookkeeper: Bookkeeper, counter: PassCounter | null, passName: string) {\n this.bookkeeper = bookkeeper;\n this.counter = counter;\n this.passName = passName;\n }\n\n setPipeline(_pipeline: ComputePipeline): void {}\n\n setBindGroup(\n _index: number,\n bindGroup: BindGroup,\n _dynamicOffsets?: readonly number[] | undefined,\n ): void {\n this.lastValidation = this.bookkeeper.validateOwnership(bindGroup);\n }\n\n dispatchWorkgroups(_x: number, _y?: number | undefined, _z?: number | undefined): void {\n this.dispatchCount++;\n this.counter?.recordDispatch();\n }\n\n dispatchWorkgroupsIndirect(indirectBuffer: Buffer, _indirectOffset: number): void {\n this.lastValidation = this.bookkeeper.validateOwnership(indirectBuffer);\n this.dispatchCount++;\n this.counter?.recordDispatch();\n }\n\n end(): void {\n this.counter?.recordPassName(this.passName);\n }\n}\n","// @forgeax/engine-rhi-null/src/command-encoder - headless command encoder.\n//\n// RhiNullCommandEncoder is a no-op recorder: begin*Pass returns a fresh\n// pass-encoder bound to the same per-device ledger; copy* / clear* / debug*\n// are no-ops; resolveQuerySet / finish return ok. finish() mints\n// a legal CommandBuffer brand through the ledger so submit-side bookkeeping\n// (AC-12) can read it back.\n//\n// Each beginRenderPass / beginComputePass reads the `label` from the descriptor\n// and threads it as the pass name to the per-device counters so M3 unit tests\n// (w17 AC-04) can assert per-frame pass scheduling order.\n//\n// Related: requirements AC-04 (pass sequence) + AC-06 (draw count via pass\n// encoders); research Finding A1 row 5; plan-strategy §3.1.\n\nimport type {\n Buffer,\n BufferCopyDestination,\n CommandBuffer,\n ComputePassDescriptor,\n QuerySet,\n RenderPassDescriptor,\n Result,\n RhiCommandEncoder,\n RhiComputePassEncoder,\n RhiError as RhiErrorType,\n RhiRenderPassEncoder,\n TextureCopySource,\n} from '@forgeax/engine-rhi';\nimport { ok } from '@forgeax/engine-types';\nimport type { Bookkeeper } from './bookkeeping';\nimport type { RhiNullDevice } from './device';\nimport {\n type PassCounter,\n RhiNullComputePassEncoder,\n RhiNullRenderPassEncoder,\n} from './pass-encoders';\n\n/**\n * Internal counter that writes per-frame stats back to the owning device.\n */\nclass DeviceCounter implements PassCounter {\n constructor(private readonly device: RhiNullDevice) {}\n\n recordDraw(): void {\n this.device.totalDrawCount++;\n }\n\n recordDispatch(): void {\n this.device.totalDispatchCount++;\n }\n\n recordBindGroup(): void {\n this.device.totalBindGroupCount++;\n }\n\n recordPassName(name: string): void {\n this.device.framePassNames.push(name);\n }\n}\n\n/**\n * Read the `label` field from a GPURenderPassDescriptor (or undefined if not\n * set). The engine sets `label` on the render pass descriptor; the graph's\n * compile path doesn't always thread a per-pass label, so we fall back to the\n * RenderPassDescriptor's generic label (or '<unnamed>').\n */\nfunction readPassLabel(desc: { readonly label?: string | undefined } | undefined): string {\n if (desc && typeof desc.label === 'string' && desc.label.length > 0) {\n return desc.label as string;\n }\n return '<unnamed>';\n}\n\n/**\n * Headless command encoder. begin*Pass returns a no-op pass encoder bound to\n * the issuing device's ledger; recording methods are no-ops; finish mints a\n * CommandBuffer brand.\n */\nexport class RhiNullCommandEncoder implements RhiCommandEncoder {\n private readonly bookkeeper: Bookkeeper;\n private readonly counter: PassCounter;\n\n constructor(bookkeeper: Bookkeeper, device: RhiNullDevice) {\n this.bookkeeper = bookkeeper;\n this.counter = new DeviceCounter(device);\n }\n\n beginRenderPass(desc: RenderPassDescriptor): RhiRenderPassEncoder {\n const label = readPassLabel(desc);\n return new RhiNullRenderPassEncoder(this.bookkeeper, this.counter, label);\n }\n\n beginComputePass(desc?: ComputePassDescriptor | undefined): RhiComputePassEncoder {\n const label = readPassLabel(desc);\n return new RhiNullComputePassEncoder(this.bookkeeper, this.counter, label);\n }\n\n encodeEmptyComputePass(desc: ComputePassDescriptor): void {\n this.counter.recordPassName(readPassLabel(desc));\n }\n\n copyBufferToBuffer(\n _source: Buffer,\n _sourceOffsetOrDestination: number | Buffer,\n _destinationOrSize?: Buffer | number | undefined,\n _destinationOffset?: number | undefined,\n _size?: number | undefined,\n ): void {}\n\n copyBufferToTexture(\n _source: GPUTexelCopyBufferInfo,\n _destination: GPUTexelCopyTextureInfo,\n _copySize: GPUExtent3DStrict,\n ): void {}\n\n copyTextureToBuffer(\n _source: GPUTexelCopyTextureInfo | TextureCopySource,\n _destination: GPUTexelCopyBufferInfo | BufferCopyDestination,\n _copySize: GPUExtent3DStrict,\n ): void {}\n\n copyTextureToTexture(\n _source: GPUTexelCopyTextureInfo,\n _destination: GPUTexelCopyTextureInfo,\n _copySize: GPUExtent3DStrict,\n ): void {}\n\n clearBuffer(_buffer: Buffer, _offset?: number | undefined, _size?: number | undefined): void {}\n\n resolveQuerySet(\n _querySet: QuerySet,\n _firstQuery: number,\n _queryCount: number,\n _destination: Buffer,\n _destinationOffset: number,\n ): Result<void, RhiErrorType> {\n return ok(undefined);\n }\n\n pushDebugGroup(_groupLabel: string): void {}\n\n popDebugGroup(): void {}\n\n insertDebugMarker(_markerLabel: string): void {}\n\n finish(): Result<CommandBuffer, RhiErrorType> {\n return ok(this.bookkeeper.register('CommandBuffer') as unknown as CommandBuffer);\n }\n}\n","// @forgeax/engine-rhi-null/src/bookkeeping - method-A handle ledger for the\n// headless no-op backend.\n//\n// RhiNull issues legal opaque-handle brands (each RHI brand is a compile-time\n// `unique symbol` with no runtime field, so any plain object cast via\n// `as unknown as Buffer` satisfies the type contract; research Finding A6). To\n// let M3 command-stream assertions read back create/destroy pairing, draw /\n// dispatch counts and binding assembly, RhiNull threads each issued handle\n// through a per-device Bookkeeper that records a numeric id + kind + destroyed\n// flag + the issuing device id.\n//\n// Error contract (plan-strategy D-1): handle-chain inconsistencies reuse the\n// existing closed RhiErrorCode union with ZERO new members:\n// - a handle observed on a device other than its issuer -> 'rhi-not-available'\n// - a destroy / use observed on an already-destroyed handle ->\n// 'destroy-after-destroy'\n// Every RhiError carries the required code / expected / hint triple\n// (charter proposition 3 structured failure).\n//\n// Related: requirements AC-09 (foreign handle returns structured err, no silent\n// pass); plan-strategy §2 D-1; research Finding A3 (RhiErrorCode 20 members,\n// 'rhi-not-available' at errors.ts:113, 'destroy-after-destroy' at errors.ts:127)\n// + Finding A6 (brand construction); charter P3.\n\nimport type { Result, RhiError as RhiErrorType } from '@forgeax/engine-rhi';\nimport { RhiError } from '@forgeax/engine-rhi';\nimport { err, ok } from '@forgeax/engine-types';\n\n/**\n * One ledger row for an issued opaque-handle brand.\n *\n * - `id` — per-device monotonic numeric id assigned at register time; the value\n * M3 assertions read back to pair create / destroy and count draws.\n * - `kind` — the handle category (`'Buffer'` / `'Texture'` / `'BindGroupLayout'`\n * etc.); free-form so every create* entry can tag what it minted.\n * - `destroyed` — flipped to `true` by the first destroy; a second destroy\n * fail-fasts via `'destroy-after-destroy'`.\n * - `sourceDeviceId` — the id of the device that issued the handle; cross-device\n * use fail-fasts via `'rhi-not-available'`.\n */\nexport interface HandleRecord {\n readonly id: number;\n readonly kind: string;\n destroyed: boolean;\n readonly sourceDeviceId: number;\n}\n\n/**\n * The brand attachment RhiNull stamps onto every issued handle object so the\n * ledger can be recovered from the handle alone (handle-chain validation reads\n * `handle[BOOKKEEPING_KEY]` rather than holding a side Map keyed on identity,\n * which would leak across device instances).\n *\n * Lives behind a module-private symbol so it never collides with a real RHI\n * brand symbol and never appears in the public `.d.ts` surface.\n */\nexport const BOOKKEEPING_KEY = Symbol('forgeax-rhi-null-bookkeeping');\n\n/** A handle object as RhiNull mints it: a plain object carrying its ledger row\n * under the private bookkeeping symbol, cast to the relevant brand at the\n * create* call site. */\nexport interface BookkeptHandle {\n readonly [BOOKKEEPING_KEY]: HandleRecord;\n}\n\n/**\n * Per-device handle ledger. One Bookkeeper instance lives on each\n * RhiNullDevice; it assigns the device id at construction and mints monotonic\n * handle ids thereafter.\n */\nexport class Bookkeeper {\n readonly deviceId: number;\n private nextHandleId = 0;\n private readonly records = new Map<number, HandleRecord>();\n\n constructor(deviceId: number) {\n this.deviceId = deviceId;\n }\n\n /**\n * Register a freshly-minted handle of the given kind, returning a plain\n * object that carries its ledger row. The caller casts the return value to\n * the concrete brand (`as unknown as Buffer` etc.).\n */\n register(kind: string): BookkeptHandle {\n const id = this.nextHandleId++;\n const record: HandleRecord = {\n id,\n kind,\n destroyed: false,\n sourceDeviceId: this.deviceId,\n };\n this.records.set(id, record);\n return { [BOOKKEEPING_KEY]: record };\n }\n\n /**\n * Mark a handle destroyed. Fail-fasts on a second destroy\n * ('destroy-after-destroy') or on a handle issued by a different device\n * ('rhi-not-available'); otherwise flips the destroyed flag and returns ok.\n */\n destroy(handle: unknown): Result<void, RhiErrorType> {\n const ownership = this.validateOwnership(handle);\n if (!ownership.ok) return ownership;\n const destroyedCheck = isHandleDestroyed(handle);\n if (!destroyedCheck.ok) return destroyedCheck;\n ownership.value.destroyed = true;\n return ok(undefined);\n }\n\n /**\n * Report whether a handle has already been destroyed (true once destroy()\n * has flipped its flag). Used by command-stream methods that must not\n * consume a stale handle.\n */\n isDestroyed(handle: unknown): boolean {\n return readRecord(handle)?.destroyed === true;\n }\n\n /**\n * Validate that a handle was issued by THIS device. Returns the ledger row on\n * success so callers can mutate it (e.g. flip destroyed); returns\n * 'rhi-not-available' for a foreign handle (AC-09 — no silent pass).\n */\n validateOwnership(handle: unknown): Result<HandleRecord, RhiErrorType> {\n const record = readRecord(handle);\n if (record === undefined || record.sourceDeviceId !== this.deviceId) {\n return err(\n new RhiError({\n code: 'rhi-not-available',\n expected: 'handle was issued by this RhiNull device',\n hint: 'do not pass a handle created on a different RhiNull device into this device; create resources on the device they are used with',\n }),\n );\n }\n return ok(record);\n }\n\n /**\n * Return all ledger rows as a readonly array. M3 unit tests (w17) read this\n * to assert create/destroy pairing, BGL/PSO shape counts, and resource\n * lifecycle coverage (AC-05/06/07). Returns a snapshot of the current Map\n * so callers can iterate without a stale reference after further mutations.\n */\n allRecords(): readonly HandleRecord[] {\n return [...this.records.values()];\n }\n\n /** Report the total number of records in the ledger. */\n recordCount(): number {\n return this.records.size;\n }\n}\n\n/** Recover the ledger row from a handle object, or undefined if the object\n * carries no RhiNull bookkeeping (foreign / non-RhiNull handle). */\nfunction readRecord(handle: unknown): HandleRecord | undefined {\n if (handle === null || typeof handle !== 'object') return undefined;\n const tagged = handle as Partial<BookkeptHandle>;\n return tagged[BOOKKEEPING_KEY];\n}\n\n/**\n * Validation function: has this handle already been destroyed? Returns ok(void)\n * for a live handle; returns 'destroy-after-destroy' for a handle whose ledger\n * row is flagged destroyed (charter P3 fail-fast over silent idempotency).\n */\nexport function isHandleDestroyed(handle: unknown): Result<void, RhiErrorType> {\n if (readRecord(handle)?.destroyed === true) {\n return err(\n new RhiError({\n code: 'destroy-after-destroy',\n expected: 'GPU buffer/texture handle has not been destroyed yet',\n hint: 'object already destroyed; track lifecycle in caller or check isDestroyed before re-destroy',\n }),\n );\n }\n return ok(undefined);\n}\n","import {\n ok,\n R32FLOAT_PROBE_STAGES,\n type Result,\n type RhiError,\n type RhiTextureFormatCapabilityReceipt,\n} from '@forgeax/engine-rhi';\n\n/** Structural command-shape evidence; no RhiNull pixel admission is possible. */\nexport function probeR32FloatCapability(\n deviceGeneration: number,\n): Promise<Result<RhiTextureFormatCapabilityReceipt, RhiError>> {\n return Promise.resolve(\n ok({\n profile: 'r32float-mip-sampled-storage',\n verdict: 'structural-only',\n evidence: 'structural',\n deviceGeneration,\n stages: R32FLOAT_PROBE_STAGES.map((stage) => ({\n stage,\n verdict: 'structural-only' as const,\n evidence: 'structural' as const,\n })),\n sampleType: 'unfilterable-float' as const,\n usages: ['texture-binding', 'storage-binding', 'copy-src'] as const,\n probeExecutions: 1,\n }),\n );\n}\n","// @forgeax/engine-rhi-null/src/device - RhiNullDevice + caps for the headless\n// no-op backend.\n//\n// RhiNullDevice implements the full RhiDevice surface (research Finding A1\n// row 3) as no-ops that mint legal opaque-handle brands and thread them through\n// the per-device Bookkeeper (method A; research Finding A6). The `implements\n// RhiDevice` clause is the completeness guard: a missing method is a tsc -b\n// red, satisfying AC-01 without a hand-maintained member checklist.\n//\n// caps (D-5): backendKind 'null'; the 3 wgpu-native-only reserved flags\n// (multiDrawIndirect / pushConstants / textureBindingArray) are false; every\n// other boolean cap is true and maxColorAttachments is 8, so the headless\n// backend maximizes structural coverage of capability-gated paths.\n//\n// The queue is supplied at construction (the adapter's requestDevice mints the\n// RhiNullQueue, w10) so device.ts carries no dependency on queue.ts.\n//\n// Related: requirements scope row 2 (8 interfaces full method set) + AC-01\n// (implements compiles) + AC-08 (backendKind === 'null'); plan-strategy §2 D-5\n// (caps field-level) + §4 R-3; research Finding A1 + A6.\n\nimport type {\n BindGroup,\n BindGroupDescriptor,\n BindGroupLayout,\n BindGroupLayoutDescriptor,\n Buffer,\n BufferDescriptor,\n CommandEncoderDescriptor,\n ComputePipeline,\n ComputePipelineDescriptor,\n PipelineLayout,\n PipelineLayoutDescriptor,\n QuerySet,\n QuerySetDescriptor,\n RenderPipeline,\n RenderPipelineDescriptor,\n Result,\n RhiCaps,\n RhiCommandEncoder,\n RhiComputePipelineOps,\n RhiDevice,\n RhiError as RhiErrorType,\n RhiFeatures,\n RhiLimits,\n RhiQueue,\n RhiRenderPipelineOps,\n Sampler,\n SamplerDescriptor,\n Texture,\n TextureDescriptor,\n TextureView,\n TextureViewDescriptor,\n} from '@forgeax/engine-rhi';\nimport { RhiError as RhiErrorClass } from '@forgeax/engine-rhi';\nimport { err, ok } from '@forgeax/engine-types';\nimport { Bookkeeper } from './bookkeeping';\nimport { probeR32FloatCapability } from './internal/r32float-capability';\n\n/** Monotonic device-id source so each RhiNullDevice owns a distinct id; the id\n * threads into the Bookkeeper for cross-device handle-chain validation. */\nlet nextDeviceId = 0;\n\n/**\n * Factory that builds a command encoder bound to a device's ledger. Injected at\n * device construction (rather than imported here) so device.ts carries no\n * dependency on command-encoder.ts; the singleton assembly (index.ts) supplies\n * the real factory. The Bookkeeper and RhiNullDevice are passed so the encoder\n * threads draw / dispatch counts + binding validation through the same per-device\n * ledger AND writes aggregated frame stats to the device for M3 unit-test readback.\n */\nexport type CommandEncoderFactory = (\n bookkeeper: Bookkeeper,\n device: RhiNullDevice,\n) => RhiCommandEncoder;\n\n/** A pipeline brand augmented with its no-op `getBindGroupLayout` ops method\n * (D-2). createRenderPipeline / createComputePipeline return objects of this\n * shape so the auto-layout consumers can call getBindGroupLayout. */\ntype PipelineHandle<Brand> = Brand & RhiRenderPipelineOps & RhiComputePipelineOps;\n\n/**\n * Headless no-op RhiDevice. Every create* mints a legal brand and records it;\n * every destroy* fail-fasts a double-destroy; caps reports the all-true-except-\n * reserved profile (D-5).\n */\nexport class RhiNullDevice implements RhiDevice {\n private readonly internalBookkeeper: Bookkeeper;\n private readonly nullQueue: RhiQueue;\n private readonly encoderFactory: CommandEncoderFactory;\n private readonly deviceGeneration: number;\n private r32FloatProbe: ReturnType<typeof probeR32FloatCapability> | undefined;\n\n /** Per-frame total draw count across all pass encoders executed this frame\n * (aggregated by the command encoder on finish, then reset). M3 unit tests\n * (w17) read this to assert draw count >= 1 (AC-06). */\n totalDrawCount = 0;\n /** Per-frame total direct and indirect compute dispatch count. */\n totalDispatchCount = 0;\n /** Per-frame total bind group set count (AC-06 / AC-05 readback). */\n totalBindGroupCount = 0;\n /** Per-frame pass names executed this frame, in schedule order (AC-04). */\n framePassNames: string[] = [];\n\n /** The per-device handle ledger — exposed so M3 tests can assert create/destroy\n * pairing and BGL/PSO shape counts (AC-05/06/07). */\n get bookkeeper(): Bookkeeper {\n return this.internalBookkeeper;\n }\n\n constructor(queue: RhiQueue, encoderFactory: CommandEncoderFactory) {\n this.deviceGeneration = nextDeviceId;\n this.internalBookkeeper = new Bookkeeper(nextDeviceId++);\n this.nullQueue = queue;\n this.encoderFactory = encoderFactory;\n }\n\n probeTextureFormatCapability(): ReturnType<typeof probeR32FloatCapability> {\n this.r32FloatProbe ??= probeR32FloatCapability(this.deviceGeneration);\n return this.r32FloatProbe;\n }\n\n get caps(): RhiCaps {\n return {\n backendKind: 'null',\n compute: true,\n timestampQuery: false,\n timestampPeriodNanoseconds: null,\n indirectDrawing: true,\n textureCompressionBc: false,\n textureCompressionEtc2: false,\n textureCompressionAstc: false,\n // 3 wgpu-native-only reserved flags stay false on non-native backends\n // (D-5); the headless backend is not a native runtime.\n multiDrawIndirect: false,\n pushConstants: false,\n textureBindingArray: false,\n samplerAliasing: true,\n firstInstanceIndirect: true,\n storageBuffer: true,\n storageTexture: true,\n rgba16floatRenderable: true,\n rg11b10ufloatRenderable: true,\n float32Filterable: true,\n maxColorAttachments: 8,\n };\n }\n\n get features(): RhiFeatures {\n return EMPTY_FEATURES;\n }\n\n get limits(): RhiLimits {\n return EMPTY_LIMITS;\n }\n\n get queue(): RhiQueue {\n return this.nullQueue;\n }\n\n // forgeax-async-whitelist: dom-native — spec `GPUDevice.lost` Promise\n // passthrough. The headless backend never loses a device (no GPU), so the\n // Promise stays unsettled for the lifetime of the device, mirroring a live\n // device that never transitions to the lost state.\n get lost(): Promise<{ readonly reason: 'destroyed' | 'unknown'; readonly message: string }> {\n return NEVER;\n }\n\n createBuffer(_desc: BufferDescriptor): Result<Buffer, RhiErrorType> {\n return ok(this.internalBookkeeper.register('Buffer') as unknown as Buffer);\n }\n\n createTexture(_desc: TextureDescriptor): Result<Texture, RhiErrorType> {\n return ok(this.internalBookkeeper.register('Texture') as unknown as Texture);\n }\n\n destroyBuffer(buf: Buffer): Result<void, RhiErrorType> {\n return this.internalBookkeeper.destroy(buf);\n }\n\n destroyQuerySet(querySet: QuerySet): Result<void, RhiErrorType> {\n return this.internalBookkeeper.destroy(querySet);\n }\n\n destroyTexture(tex: Texture): Result<void, RhiErrorType> {\n return this.internalBookkeeper.destroy(tex);\n }\n\n createTextureView(\n _texture: Texture,\n _desc: TextureViewDescriptor,\n ): Result<TextureView, RhiErrorType> {\n return ok(this.internalBookkeeper.register('TextureView') as unknown as TextureView);\n }\n\n createSampler(_desc?: SamplerDescriptor | undefined): Result<Sampler, RhiErrorType> {\n return ok(this.internalBookkeeper.register('Sampler') as unknown as Sampler);\n }\n\n createBindGroupLayout(_desc: BindGroupLayoutDescriptor): Result<BindGroupLayout, RhiErrorType> {\n return ok(this.internalBookkeeper.register('BindGroupLayout') as unknown as BindGroupLayout);\n }\n\n createBindGroup(_desc: BindGroupDescriptor): Result<BindGroup, RhiErrorType> {\n return ok(this.internalBookkeeper.register('BindGroup') as unknown as BindGroup);\n }\n\n createPipelineLayout(_desc: PipelineLayoutDescriptor): Result<PipelineLayout, RhiErrorType> {\n return ok(this.internalBookkeeper.register('PipelineLayout') as unknown as PipelineLayout);\n }\n\n createRenderPipeline(_desc: RenderPipelineDescriptor): Result<RenderPipeline, RhiErrorType> {\n return ok(this.makePipeline<RenderPipeline>('RenderPipeline'));\n }\n\n createComputePipeline(_desc: ComputePipelineDescriptor): Result<ComputePipeline, RhiErrorType> {\n return ok(this.makePipeline<ComputePipeline>('ComputePipeline'));\n }\n\n createQuerySet(desc: QuerySetDescriptor): Result<QuerySet, RhiErrorType> {\n if (desc.type === 'timestamp') {\n return err(\n new RhiErrorClass({\n code: 'feature-not-enabled',\n expected: 'caps.timestampQuery === true (timestamp-query feature)',\n hint: 'RhiNull is structural-only and cannot produce GPU timestamp ticks',\n }),\n );\n }\n return ok(this.internalBookkeeper.register('QuerySet') as unknown as QuerySet);\n }\n\n createCommandEncoder(\n _desc?: CommandEncoderDescriptor | undefined,\n ): Result<RhiCommandEncoder, RhiErrorType> {\n return ok(this.encoderFactory(this.internalBookkeeper, this));\n }\n\n /**\n * Mint a pipeline handle whose object also carries the no-op\n * `getBindGroupLayout(index)` ops method (D-2). The prod auto-layout path\n * (debug-draw.ts) and the existing mock unit tests both call\n * `pipeline.getBindGroupLayout(n)`; returning a legal BindGroupLayout brand\n * (recorded in the ledger) keeps those consumers from crashing on a missing\n * method.\n */\n private makePipeline<Brand>(kind: string): PipelineHandle<Brand> {\n const handle = this.internalBookkeeper.register(kind);\n const getBindGroupLayout = (_index: number): BindGroupLayout =>\n this.internalBookkeeper.register('BindGroupLayout') as unknown as BindGroupLayout;\n return Object.assign(handle, { getBindGroupLayout }) as unknown as PipelineHandle<Brand>;\n }\n}\n\n/** Empty enabled-feature set — headless backend enables nothing beyond the\n * always-true caps profile (research Finding A1: features getter returns an\n * empty ReadonlySet). */\nconst EMPTY_FEATURES: RhiFeatures = new Set() as RhiFeatures;\n\n/** Empty numeric-limits map. The headless backend reports no concrete numeric\n * limits; capability planning reads caps booleans instead. */\nconst EMPTY_LIMITS: RhiLimits = {} as RhiLimits;\n\n/** A Promise that never settles, mirroring a live GPUDevice.lost that stays\n * unsettled while the device is healthy. */\nconst NEVER: Promise<{ readonly reason: 'destroyed' | 'unknown'; readonly message: string }> =\n new Promise(() => {});\n","// @forgeax/engine-rhi-null/src/queue - headless command queue.\n//\n// All write* methods are no-ops returning ok; submit records nothing and\n// returns ok (AC-12). onSubmittedWorkDone resolves immediately so headless\n// read-back idioms never hang (the real GPU resolves after work completes; with\n// no pending work the headless backend resolves synchronously).\n//\n// Related: requirements AC-12 (submit ok + onSubmittedWorkDone resolves);\n// research Finding A1 row 4.\n\nimport type {\n Buffer,\n CommandBuffer,\n ExternalImageTextureDestination,\n Result,\n RhiError as RhiErrorType,\n RhiQueue,\n TextureWriteDestination,\n} from '@forgeax/engine-rhi';\nimport { ok } from '@forgeax/engine-types';\n\n/** Headless no-op queue. */\nexport class RhiNullQueue implements RhiQueue {\n writeBuffer(\n _buffer: Buffer,\n _bufferOffset: number,\n _data: ArrayBufferView | ArrayBuffer,\n _dataOffset?: number | undefined,\n _size?: number | undefined,\n ): Result<void, RhiErrorType> {\n return ok(undefined);\n }\n\n writeTexture(\n _destination: TextureWriteDestination,\n _data: ArrayBufferView | ArrayBuffer,\n _dataLayout: Pick<GPUTexelCopyBufferLayout, 'offset' | 'bytesPerRow' | 'rowsPerImage'>,\n _size: GPUExtent3DStrict,\n ): Result<void, RhiErrorType> {\n return ok(undefined);\n }\n\n copyExternalImageToTexture(\n _source: Pick<GPUCopyExternalImageSourceInfo, 'source' | 'origin' | 'flipY'>,\n _destination: ExternalImageTextureDestination,\n _copySize: GPUExtent3DStrict,\n ): Result<void, RhiErrorType> {\n return ok(undefined);\n }\n\n submit(_commandBuffers: readonly CommandBuffer[]): Result<void, RhiErrorType> {\n return ok(undefined);\n }\n\n // forgeax-async-whitelist: dom-native — spec `GPUQueue.onSubmittedWorkDone`\n // never rejects. The headless backend has no pending GPU work, so it resolves\n // immediately (AC-12: read-back idioms must not hang).\n onSubmittedWorkDone(): Promise<undefined> {\n return Promise.resolve(undefined);\n }\n}\n","// @forgeax/engine-rhi-null/src/adapter - headless adapter.\n//\n// requestDevice mints a fresh RhiNullDevice, wiring its queue + command-encoder\n// factory. features / limits are empty (the headless backend enables nothing\n// beyond the always-true caps profile); the two-step requestAdapter ->\n// requestDevice path mirrors the spec idiom (research Finding A1 row 2).\n//\n// Related: requirements AC-02 (createRenderer ready chain needs adapter ->\n// device) + AC-12; research Finding A1 row 2.\n\nimport type {\n RequestDeviceOptions,\n Result,\n RhiAdapter,\n RhiDevice,\n RhiError as RhiErrorType,\n} from '@forgeax/engine-rhi';\nimport { ok } from '@forgeax/engine-types';\nimport { RhiNullCommandEncoder } from './command-encoder';\nimport { RhiNullDevice } from './device';\nimport { RhiNullQueue } from './queue';\n\n/** Headless adapter. Holds an empty feature set + limits map; requestDevice\n * builds a fully-wired RhiNullDevice. */\nexport class RhiNullAdapter implements RhiAdapter {\n readonly features: ReadonlySet<GPUFeatureName> = new Set();\n readonly limits: Readonly<Record<string, number>> = {};\n\n // forgeax-async-whitelist is not needed: this returns Promise<Result<...>>\n // per the spec contract; never rejects.\n requestDevice(\n _opts?: RequestDeviceOptions | undefined,\n ): Promise<Result<RhiDevice, RhiErrorType>> {\n const device = new RhiNullDevice(\n new RhiNullQueue(),\n (bookkeeper, dev) => new RhiNullCommandEncoder(bookkeeper, dev),\n );\n return Promise.resolve(ok(device));\n }\n}\n","// @forgeax/engine-rhi-null/src/canvas-context - headless canvas context (R-1).\n//\n// The headless backend has no real canvas. RhiNullCanvasContext satisfies the\n// RhiCanvasContext interface as no-ops: configure returns ok (a headless\n// configure is vacuously successful), unconfigure is void, getConfiguration\n// returns undefined (no real configuration).\n//\n// getCurrentTexture returns a legal Texture brand so the swap-chain\n// acquisition path succeeds — without this the entire render-frame path fails\n// at the first swap-chain access, blocking all dogfood testing of\n// render-graph-to-RhiNull command-flow. The returned Texture carries no real\n// pixels; it is opaque-handle syntactically valid and structurally sound for\n// CI structural unit tests.\n//\n// acquireCanvasContext (mounted on the rhi singleton, R-1) accepts any\n// canvas / null / undefined and returns ok(canvasContext) so the facade\n// never crashes and canvas-dependent paths exercise through to the\n// render-graph level.\n//\n// Related: requirements AC-11 (canvas ctx structured behaviour) — the\n// context itself succeeds; AC-11 verify tests confirm the getCurrentTexture\n// brand is a valid opaque handle and downstream consumption doesn't crash.\n// Research Finding A4 (acquireCanvasContext on singleton) + A3\n// (rhi-not-available legal); plan-strategy §4 R-1; charter P3.\n\nimport type {\n CanvasConfiguration,\n Result,\n RhiCanvasContext,\n RhiError as RhiErrorType,\n Texture,\n} from '@forgeax/engine-rhi';\nimport { ok } from '@forgeax/engine-types';\n\n/**\n * Headless canvas context. configure succeeds vacuously; getCurrentTexture\n * mints a legal opaque-handle Texture brand so the swap-chain acquisition path\n * never blocks frame rendering in headless CI.\n */\nexport class RhiNullCanvasContext implements RhiCanvasContext {\n configure(_desc: CanvasConfiguration): Result<void, RhiErrorType> {\n return ok(undefined);\n }\n\n unconfigure(): void {}\n\n getConfiguration(): CanvasConfiguration | undefined {\n return undefined;\n }\n\n getCurrentTexture(): Result<Texture, RhiErrorType> {\n // A brand is a compile-time unique symbol with no runtime field, so a plain\n // object cast is a structurally valid Texture (research Finding A6). No real\n // pixels; the command-stream path only needs a legal handle to thread on.\n return ok({} as unknown as Texture);\n }\n}\n\n/**\n * Acquire a canvas context. The headless backend returns ok(context) so the\n * createRenderer context-acquisition step succeeds. The context's\n * getCurrentTexture mints a legal Texture brand once a bookkeeper is attached.\n *\n * R-1: this is mounted on the rhi singleton so Channel 1's facade never\n * crashes on a missing acquireCanvasContext.\n */\nexport function acquireCanvasContext(\n _canvas?: HTMLCanvasElement | OffscreenCanvas | null | undefined,\n): Result<RhiCanvasContext, RhiErrorType> {\n return ok(new RhiNullCanvasContext());\n}\n","// @forgeax/engine-rhi-null/src/shader - headless shader module factory (R-2).\n//\n// createShaderModule is the highest-risk hidden contract: the createRenderer\n// ready chain resolves a shader module through a three-tier fallback\n// (RhiBackendPack.createShaderModule -> RhiDevice.createShaderModule duck-typed\n// -> err('rhi-not-available')). If RhiNull provides neither, createRenderer ->\n// ready REJECTS rhi-not-available and AC-02 / AC-10 / AC-13 all fail. RhiNull\n// exposes the top-level async factory (symmetric with rhi-webgpu) that skips\n// real WGSL compilation and returns a legal ShaderModule brand.\n//\n// Related: requirements AC-10 (shader brand skips compile) + AC-02\n// (createRenderer ready does not reject); research Finding A5 (three-tier\n// fallback, top-level factory recommended) + A6 (brand construction);\n// plan-strategy §4 R-2.\n\nimport type {\n Result,\n RhiDevice,\n RhiError as RhiErrorType,\n ShaderModule,\n} from '@forgeax/engine-rhi';\nimport { ok } from '@forgeax/engine-types';\n\n/**\n * Build a shader module for the headless backend. No WGSL is compiled — the\n * code is ignored and a legal ShaderModule brand is returned immediately\n * (<1ms), so the ready chain's shader step always resolves ok (AC-10). Mirrors\n * the rhi-webgpu top-level `createShaderModule(device, desc)` signature so\n * Channel 1's RhiBackendPack picks it up via the same `'createShaderModule' in\n * mod` probe.\n */\nexport function createShaderModule(\n _device: RhiDevice,\n _desc: { label?: string | undefined; code: string },\n): Promise<Result<ShaderModule, RhiErrorType>> {\n return Promise.resolve(ok({} as unknown as ShaderModule));\n}\n","// @forgeax/engine-rhi-null - headless no-op RHI backend.\n//\n// Single entry for `import { rhi } from '@forgeax/engine-rhi-null'`. The\n// exported `rhi` singleton has the RhiBackendPack-mandated shape\n// `RhiInstance & { acquireCanvasContext }` (research Finding A4 — Channel 1\n// injects this verbatim and calls acquireCanvasContext on the facade, so the\n// method MUST exist on the singleton even though the public renderer options.rhi\n// type does not require it). createShaderModule is exposed at the top level\n// (R-2) for symmetry with rhi-webgpu: createRenderer's ready chain resolves the\n// shader step through `RhiBackendPack.createShaderModule`, otherwise it rejects\n// rhi-not-available.\n//\n// Strict two-step path: rhi.requestAdapter() -> adapter.requestDevice().\n//\n// Related: requirements AC-02 + AC-10 + AC-12 + scope row 4; research Finding\n// A1 row 1 + A4 + A5; plan-strategy §4 R-1 + R-2.\n\nimport type {\n RequestAdapterOptions,\n Result,\n RhiAdapter,\n RhiError as RhiErrorType,\n RhiInstance,\n} from '@forgeax/engine-rhi';\nimport { ok } from '@forgeax/engine-types';\nimport { RhiNullAdapter } from './adapter';\nimport { acquireCanvasContext } from './canvas-context';\nimport { createShaderModule } from './shader';\n\n/**\n * Request a headless adapter. The two-positional-arg signature mirrors the spec\n * RhiInstance.requestAdapter (the second compatibleSurface arg is accepted and\n * ignored — there is no GL backend to route it to).\n */\nfunction requestAdapter(\n _opts?: RequestAdapterOptions | undefined,\n _compatibleSurface?: HTMLCanvasElement | OffscreenCanvas | undefined,\n): Promise<Result<RhiAdapter, RhiErrorType>> {\n return Promise.resolve(ok(new RhiNullAdapter()));\n}\n\n/**\n * The `rhi` singleton — RhiBackendPack-shaped entry for Channel 1 injection\n * (`createRenderer(canvas, { rhi })`). Carries acquireCanvasContext (R-1) so\n * the facade never crashes on a missing method, and createShaderModule (R-2) so\n * the ready chain's shader step resolves rather than rejecting.\n */\nexport const rhi: RhiInstance & {\n acquireCanvasContext: typeof acquireCanvasContext;\n createShaderModule: typeof createShaderModule;\n} = {\n requestAdapter,\n acquireCanvasContext,\n createShaderModule,\n};\n\nexport { RhiNullAdapter } from './adapter';\nexport { acquireCanvasContext, RhiNullCanvasContext } from './canvas-context';\nexport { RhiNullCommandEncoder } from './command-encoder';\nexport { RhiNullDevice } from './device';\nexport { RhiNullComputePassEncoder, RhiNullRenderPassEncoder } from './pass-encoders';\nexport { RhiNullQueue } from './queue';\nexport { createShaderModule } from './shader';\n"]}
@@ -0,0 +1,4 @@
1
+ import { type Result, type RhiError, type RhiTextureFormatCapabilityReceipt } from '@forgeax/engine-rhi';
2
+ /** Structural command-shape evidence; no RhiNull pixel admission is possible. */
3
+ export declare function probeR32FloatCapability(deviceGeneration: number): Promise<Result<RhiTextureFormatCapabilityReceipt, RhiError>>;
4
+ //# sourceMappingURL=r32float-capability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"r32float-capability.d.ts","sourceRoot":"","sources":["../../src/internal/r32float-capability.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,MAAM,EACX,KAAK,QAAQ,EACb,KAAK,iCAAiC,EACvC,MAAM,qBAAqB,CAAC;AAE7B,iFAAiF;AACjF,wBAAgB,uBAAuB,CACrC,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,MAAM,CAAC,iCAAiC,EAAE,QAAQ,CAAC,CAAC,CAiB9D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgeax/engine-rhi-null",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -22,8 +22,8 @@
22
22
  "LICENSE"
23
23
  ],
24
24
  "dependencies": {
25
- "@forgeax/engine-rhi": "0.1.21",
26
- "@forgeax/engine-types": "0.1.21",
25
+ "@forgeax/engine-rhi": "0.1.23",
26
+ "@forgeax/engine-types": "0.1.23",
27
27
  "@webgpu/types": "^0.1.71"
28
28
  },
29
29
  "forgeax": {
@@ -0,0 +1,21 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { RhiNullAdapter } from '../adapter';
3
+
4
+ describe('RhiNull r32float probe generation budget', () => {
5
+ it('reports one structural probe per replacement device generation', async () => {
6
+ const firstResult = await new RhiNullAdapter().requestDevice();
7
+ const replacementResult = await new RhiNullAdapter().requestDevice();
8
+ expect(firstResult.ok).toBe(true);
9
+ expect(replacementResult.ok).toBe(true);
10
+ if (!firstResult.ok || !replacementResult.ok) return;
11
+
12
+ const first = await firstResult.value.probeTextureFormatCapability();
13
+ const replacement = await replacementResult.value.probeTextureFormatCapability();
14
+ expect(first.ok).toBe(true);
15
+ expect(replacement.ok).toBe(true);
16
+ if (!first.ok || !replacement.ok) return;
17
+ expect(first.value.verdict).toBe('structural-only');
18
+ expect(first.value.probeExecutions).toBe(1);
19
+ expect(replacement.value.deviceGeneration).not.toBe(first.value.deviceGeneration);
20
+ });
21
+ });
@@ -0,0 +1,18 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { RhiNullAdapter } from '../adapter';
3
+
4
+ describe('RhiNull r32float profile matrix', () => {
5
+ it('returns structural-only evidence and never pixel admission', async () => {
6
+ const adapter = new RhiNullAdapter();
7
+ const deviceResult = await adapter.requestDevice();
8
+ expect(deviceResult.ok).toBe(true);
9
+ if (!deviceResult.ok) return;
10
+
11
+ const receiptResult = await deviceResult.value.probeTextureFormatCapability();
12
+ expect(receiptResult.ok).toBe(true);
13
+ if (!receiptResult.ok) return;
14
+ expect(receiptResult.value.verdict).toBe('structural-only');
15
+ expect(receiptResult.value.evidence).toBe('structural');
16
+ expect(receiptResult.value.readback).toBeUndefined();
17
+ });
18
+ });
package/src/device.ts CHANGED
@@ -55,6 +55,7 @@ import type {
55
55
  import { RhiError as RhiErrorClass } from '@forgeax/engine-rhi';
56
56
  import { err, ok } from '@forgeax/engine-types';
57
57
  import { Bookkeeper } from './bookkeeping';
58
+ import { probeR32FloatCapability } from './internal/r32float-capability';
58
59
 
59
60
  /** Monotonic device-id source so each RhiNullDevice owns a distinct id; the id
60
61
  * threads into the Bookkeeper for cross-device handle-chain validation. */
@@ -87,6 +88,8 @@ export class RhiNullDevice implements RhiDevice {
87
88
  private readonly internalBookkeeper: Bookkeeper;
88
89
  private readonly nullQueue: RhiQueue;
89
90
  private readonly encoderFactory: CommandEncoderFactory;
91
+ private readonly deviceGeneration: number;
92
+ private r32FloatProbe: ReturnType<typeof probeR32FloatCapability> | undefined;
90
93
 
91
94
  /** Per-frame total draw count across all pass encoders executed this frame
92
95
  * (aggregated by the command encoder on finish, then reset). M3 unit tests
@@ -106,11 +109,17 @@ export class RhiNullDevice implements RhiDevice {
106
109
  }
107
110
 
108
111
  constructor(queue: RhiQueue, encoderFactory: CommandEncoderFactory) {
112
+ this.deviceGeneration = nextDeviceId;
109
113
  this.internalBookkeeper = new Bookkeeper(nextDeviceId++);
110
114
  this.nullQueue = queue;
111
115
  this.encoderFactory = encoderFactory;
112
116
  }
113
117
 
118
+ probeTextureFormatCapability(): ReturnType<typeof probeR32FloatCapability> {
119
+ this.r32FloatProbe ??= probeR32FloatCapability(this.deviceGeneration);
120
+ return this.r32FloatProbe;
121
+ }
122
+
114
123
  get caps(): RhiCaps {
115
124
  return {
116
125
  backendKind: 'null',
@@ -0,0 +1,29 @@
1
+ import {
2
+ ok,
3
+ R32FLOAT_PROBE_STAGES,
4
+ type Result,
5
+ type RhiError,
6
+ type RhiTextureFormatCapabilityReceipt,
7
+ } from '@forgeax/engine-rhi';
8
+
9
+ /** Structural command-shape evidence; no RhiNull pixel admission is possible. */
10
+ export function probeR32FloatCapability(
11
+ deviceGeneration: number,
12
+ ): Promise<Result<RhiTextureFormatCapabilityReceipt, RhiError>> {
13
+ return Promise.resolve(
14
+ ok({
15
+ profile: 'r32float-mip-sampled-storage',
16
+ verdict: 'structural-only',
17
+ evidence: 'structural',
18
+ deviceGeneration,
19
+ stages: R32FLOAT_PROBE_STAGES.map((stage) => ({
20
+ stage,
21
+ verdict: 'structural-only' as const,
22
+ evidence: 'structural' as const,
23
+ })),
24
+ sampleType: 'unfilterable-float' as const,
25
+ usages: ['texture-binding', 'storage-binding', 'copy-src'] as const,
26
+ probeExecutions: 1,
27
+ }),
28
+ );
29
+ }
package/dist/.tsbuildinfo DELETED
@@ -1 +0,0 @@
1
- {"fileNames":["../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2015.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2016.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2017.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2018.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2019.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2020.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2021.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.dom.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.dom.iterable.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2015.core.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2015.collection.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2015.generator.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2015.promise.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2016.intl.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2017.date.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2017.object.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2017.string.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2017.intl.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2018.intl.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2018.promise.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2019.array.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2019.object.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2019.string.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2019.intl.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2020.date.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2020.promise.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2020.string.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2020.intl.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2020.number.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2021.promise.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2021.string.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2021.intl.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.array.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.error.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.intl.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.object.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.string.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.decorators.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../types/dist/animation-target.d.ts","../../types/dist/asset-errors.d.ts","../../types/dist/result.d.ts","../../types/dist/asset-evidence.d.ts","../../types/dist/asset.d.ts","../../types/dist/material/color-space.d.ts","../../types/dist/material/asset.d.ts","../../types/dist/vfx.d.ts","../../types/dist/asset-runtime.d.ts","../../types/dist/handle.d.ts","../../types/dist/material/errors.d.ts","../../types/dist/material/resolve.d.ts","../../types/dist/material/index.d.ts","../../types/dist/asset-producer.d.ts","../../types/dist/catalog.d.ts","../../types/dist/runtime-scope.d.ts","../../types/dist/texture/asset.d.ts","../../types/dist/texture/errors.d.ts","../../types/dist/texture/layout.d.ts","../../types/dist/texture/index.d.ts","../../types/dist/derive-paramschema.d.ts","../../types/dist/import.d.ts","../../types/dist/index.d.ts","../../rhi/dist/errors.d.ts","../../rhi/dist/index.d.ts","../src/bookkeeping.ts","../src/device.ts","../src/pass-encoders.ts","../src/command-encoder.ts","../src/queue.ts","../src/adapter.ts","../src/canvas-context.ts","../src/shader.ts","../src/index.ts","../../../node_modules/.pnpm/@webgpu+types@0.1.71/node_modules/@webgpu/types/dist/index.d.ts"],"fileIdsList":[[82,84,86,88,89],[82,84],[82,84,85,86,87],[82,84,85],[82,84,86,87,88,89,90,91,92],[82],[82,83],[61,62,64],[61,62,64,82],[63],[62,64,73],[61,73,74,75,82],[60,61,62,64,66,67,68,69,72,73,74,75,76,79,80,81],[65,82],[66],[65,66,70,71],[62,66,70],[73,74],[62,76],[76,77,78],[62,76,77]],"fileInfos":[{"version":"bcd24271a113971ba9eb71ff8cb01bc6b0f872a85c23fdbe5d93065b375933cd","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f88bedbeb09c6f5a6645cb24c7c55f1aa22d19ae96c8e6959cbd8b85a707bc6","impliedFormat":1},{"version":"7fe93b39b810eadd916be8db880dd7f0f7012a5cc6ffb62de8f62a2117fa6f1f","impliedFormat":1},{"version":"bb0074cc08b84a2374af33d8bf044b80851ccc9e719a5e202eacf40db2c31600","impliedFormat":1},{"version":"1a7daebe4f45fb03d9ec53d60008fbf9ac45a697fdc89e4ce218bc94b94f94d6","impliedFormat":1},{"version":"f94b133a3cb14a288803be545ac2683e0d0ff6661bcd37e31aaaec54fc382aed","impliedFormat":1},{"version":"f59d0650799f8782fd74cf73c19223730c6d1b9198671b1c5b3a38e1188b5953","impliedFormat":1},{"version":"8a15b4607d9a499e2dbeed9ec0d3c0d7372c850b2d5f1fb259e8f6d41d468a84","impliedFormat":1},{"version":"26e0fe14baee4e127f4365d1ae0b276f400562e45e19e35fd2d4c296684715e6","impliedFormat":1},{"version":"d6b1eba8496bdd0eed6fc8a685768fe01b2da4a0388b5fe7df558290bffcf32f","affectsGlobalScope":true,"impliedFormat":1},{"version":"7f57fc4404ff020bc45b9c620aff2b40f700b95fe31164024c453a5e3c163c54","impliedFormat":1},{"version":"eadcffda2aa84802c73938e589b9e58248d74c59cb7fcbca6474e3435ac15504","affectsGlobalScope":true,"impliedFormat":1},{"version":"105ba8ff7ba746404fe1a2e189d1d3d2e0eb29a08c18dded791af02f29fb4711","affectsGlobalScope":true,"impliedFormat":1},{"version":"00343ca5b2e3d48fa5df1db6e32ea2a59afab09590274a6cccb1dbae82e60c7c","affectsGlobalScope":true,"impliedFormat":1},{"version":"ebd9f816d4002697cb2864bea1f0b70a103124e18a8cd9645eeccc09bdf80ab4","affectsGlobalScope":true,"impliedFormat":1},{"version":"2c1afac30a01772cd2a9a298a7ce7706b5892e447bb46bdbeef720f7b5da77ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"7b0225f483e4fa685625ebe43dd584bb7973bbd84e66a6ba7bbe175ee1048b4f","affectsGlobalScope":true,"impliedFormat":1},{"version":"c0a4b8ac6ce74679c1da2b3795296f5896e31c38e888469a8e0f99dc3305de60","affectsGlobalScope":true,"impliedFormat":1},{"version":"3084a7b5f569088e0146533a00830e206565de65cae2239509168b11434cd84f","affectsGlobalScope":true,"impliedFormat":1},{"version":"c5079c53f0f141a0698faa903e76cb41cd664e3efb01cc17a5c46ec2eb0bef42","affectsGlobalScope":true,"impliedFormat":1},{"version":"32cafbc484dea6b0ab62cf8473182bbcb23020d70845b406f80b7526f38ae862","affectsGlobalScope":true,"impliedFormat":1},{"version":"fca4cdcb6d6c5ef18a869003d02c9f0fd95df8cfaf6eb431cd3376bc034cad36","affectsGlobalScope":true,"impliedFormat":1},{"version":"b93ec88115de9a9dc1b602291b85baf825c85666bf25985cc5f698073892b467","affectsGlobalScope":true,"impliedFormat":1},{"version":"f5c06dcc3fe849fcb297c247865a161f995cc29de7aa823afdd75aaaddc1419b","affectsGlobalScope":true,"impliedFormat":1},{"version":"b77e16112127a4b169ef0b8c3a4d730edf459c5f25fe52d5e436a6919206c4d7","affectsGlobalScope":true,"impliedFormat":1},{"version":"fbffd9337146eff822c7c00acbb78b01ea7ea23987f6c961eba689349e744f8c","affectsGlobalScope":true,"impliedFormat":1},{"version":"a995c0e49b721312f74fdfb89e4ba29bd9824c770bbb4021d74d2bf560e4c6bd","affectsGlobalScope":true,"impliedFormat":1},{"version":"c7b3542146734342e440a84b213384bfa188835537ddbda50d30766f0593aff9","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce6180fa19b1cccd07ee7f7dbb9a367ac19c0ed160573e4686425060b6df7f57","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f02e2476bccb9dbe21280d6090f0df17d2f66b74711489415a8aa4df73c9675","affectsGlobalScope":true,"impliedFormat":1},{"version":"45e3ab34c1c013c8ab2dc1ba4c80c780744b13b5676800ae2e3be27ae862c40c","affectsGlobalScope":true,"impliedFormat":1},{"version":"805c86f6cca8d7702a62a844856dbaa2a3fd2abef0536e65d48732441dde5b5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e42e397f1a5a77994f0185fd1466520691456c772d06bf843e5084ceb879a0ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"f4c2b41f90c95b1c532ecc874bd3c111865793b23aebcc1c3cbbabcd5d76ffb0","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab26191cfad5b66afa11b8bf935ef1cd88fabfcb28d30b2dfa6fad877d050332","affectsGlobalScope":true,"impliedFormat":1},{"version":"2088bc26531e38fb05eedac2951480db5309f6be3fa4a08d2221abb0f5b4200d","affectsGlobalScope":true,"impliedFormat":1},{"version":"cb9d366c425fea79716a8fb3af0d78e6b22ebbab3bd64d25063b42dc9f531c1e","affectsGlobalScope":true,"impliedFormat":1},{"version":"500934a8089c26d57ebdb688fc9757389bb6207a3c8f0674d68efa900d2abb34","affectsGlobalScope":true,"impliedFormat":1},{"version":"689da16f46e647cef0d64b0def88910e818a5877ca5379ede156ca3afb780ac3","affectsGlobalScope":true,"impliedFormat":1},{"version":"bc21cc8b6fee4f4c2440d08035b7ea3c06b3511314c8bab6bef7a92de58a2593","affectsGlobalScope":true,"impliedFormat":1},{"version":"7ca53d13d2957003abb47922a71866ba7cb2068f8d154877c596d63c359fed25","affectsGlobalScope":true,"impliedFormat":1},{"version":"54725f8c4df3d900cb4dac84b64689ce29548da0b4e9b7c2de61d41c79293611","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5594bc3076ac29e6c1ebda77939bc4c8833de72f654b6e376862c0473199323","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f3eb332c2d73e729f3364fcc0c2b375e72a121e8157d25a82d67a138c83a95c","affectsGlobalScope":true,"impliedFormat":1},{"version":"6f4427f9642ce8d500970e4e69d1397f64072ab73b97e476b4002a646ac743b1","affectsGlobalScope":true,"impliedFormat":1},{"version":"48915f327cd1dea4d7bd358d9dc7732f58f9e1626a29cc0c05c8c692419d9bb7","affectsGlobalScope":true,"impliedFormat":1},{"version":"b7bf9377723203b5a6a4b920164df22d56a43f593269ba6ae1fdc97774b68855","affectsGlobalScope":true,"impliedFormat":1},{"version":"db9709688f82c9e5f65a119c64d835f906efe5f559d08b11642d56eb85b79357","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b25b8c874acd1a4cf8444c3617e037d444d19080ac9f634b405583fd10ce1f7","affectsGlobalScope":true,"impliedFormat":1},{"version":"37be57d7c90cf1f8112ee2636a068d8fd181289f82b744160ec56a7dc158a9f5","affectsGlobalScope":true,"impliedFormat":1},{"version":"a917a49ac94cd26b754ab84e113369a75d1a47a710661d7cd25e961cc797065f","affectsGlobalScope":true,"impliedFormat":1},{"version":"6d3261badeb7843d157ef3e6f5d1427d0eeb0af0cf9df84a62cfd29fd47ac86e","affectsGlobalScope":true,"impliedFormat":1},{"version":"195daca651dde22f2167ac0d0a05e215308119a3100f5e6268e8317d05a92526","affectsGlobalScope":true,"impliedFormat":1},{"version":"8b11e4285cd2bb164a4dc09248bdec69e9842517db4ca47c1ba913011e44ff2f","affectsGlobalScope":true,"impliedFormat":1},{"version":"0508571a52475e245b02bc50fa1394065a0a3d05277fbf5120c3784b85651799","affectsGlobalScope":true,"impliedFormat":1},{"version":"8f9af488f510c3015af3cc8c267a9e9d96c4dd38a1fdff0e11dc5a544711415b","affectsGlobalScope":true,"impliedFormat":1},{"version":"fc611fea8d30ea72c6bbfb599c9b4d393ce22e2f5bfef2172534781e7d138104","affectsGlobalScope":true,"impliedFormat":1},{"version":"1ce14b81c5cc821994aa8ec1d42b220dd41b27fcc06373bce3958af7421b77d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3a048b3e9302ef9a34ef4ebb9aecfb28b66abb3bce577206a79fee559c230da","affectsGlobalScope":true,"impliedFormat":1},"89fe170d9a59f81cd9a9d95de3278f108ff91491810b2529cc0ffb502a57b6c0","0b4928041c807c01b33b1fa028236df635c52f7acff5a40f2c2d0d677b53428b","5ad2e5b8b17893d0eb1f11a1b6e8c03b17344be03a6a72a624cd38ab05fe4d5c","6a1e553079e654a1771463e9eeb98ea4f7bb4b6417a5237d4fdf9b546d49647f","7f11be86b9ac5c2cd5127cd9d40ccc7694c58868a3157692a153d452fc171525","6e3488ee1183ea915eb43f08aebc5f75e30be25aef1f678d1965324bdd7506e1","08b2edc86cfd71fe5a832912df79e79040c90c9df60ed2af1dc50f7baef36aeb","a318d8734523d9e750681e7b8e685ef817d4049c5612451f4e849da8b2b572a9","8b52dd8be86f44f871db89a6c1f77f7585688d53141ff55b6c5fac3eb67b1241","beae2d03867dde9fdf8da461c7e7e84da71612842422d4b482ab1c2a15fe6b12","2aa457dd6699d3015c3c98457ac72a2dd47349f75a1edddbace0ee86dd7177c9","51342c51a9090c0bc8bae60c5ec1a0f46d6d44db84392f12a8cd46e6d8a9f617","ae777307a0baeb9b5d7ba18e6a029a54eb0b612b1491591dcedb020cc8d36f97","d7ba738e316b7baed10ca49cc156b57b8c973b747bec6612e62a925e9f7f33a4","1d5292578fcf0329a43ef19fe2481d7d1cde75e75610019a7897c70dcfff0b31","459b3765e622008ed590474ae6b61ab55107a7b9529a28d05087a6fef51d7248","d4acac107ca8b949e32947d9b5afa27721584f4ffd7ad7a4008fae892a4ec913","b52569fa5c5793a182d033b2fc7443e27205625dc67782a03d2e2e972a2918f3","097ebdc98e0de1205b1104df5038026ca5830b80faee3707b78e7c28ce6dde55","1ae177260d032e05e62f0432227af19a857b327fa604a9b34baa535f44db130e","e1f11ad703121f97a74001059d054aba25c9c4662af44da05ead0a247fe18742","d9c64d02dfe3a9c74da35fdbcc645cf4e8d4ca0f4162ec312de51474103ac3a7","ef30959e03d8e4ede695c8b400a9b9004a49ed6448214f087271d4f1e56352ba","8d6101c60d99ac57af23b947db35bedaa5580f2e7ae126c5b39841aa423cd242","d4885389038cb270bba16efef1d9e3aa19808f924435602ccc5ac0c9d4c76bce",{"version":"e6ab1909f187622ae669cf63724c9ea605a205b6722963f0c3c8128a1e6d6604","signature":"09dc81335309c1cbd4e76e059081d600202d1ee94f28f472f7e268bbe3038f3c"},{"version":"6a379a45f68b6512c078edc38dbe923bfa4602e913dde8a9ba2a82e2ea99b4b8","signature":"144f48de4a0e8e5edfa2401d214040eda94438dd1259a0df1a330cb5a50cedc9"},{"version":"50785ac8e17f7830df875aed608bcd406b617477c1a59f240405e0b113bacb94","signature":"e8238980485ff78002802ba1459cc9c3d535b4780d9e5fd93275b431ce22b976"},{"version":"2c401df6f4cce13ac11dfb5f7f587333142dd771787368d362be55e94f9b8e3a","signature":"7a51db072367efbe8eabe28d658ff14860ff3442081f2615902ff9a8208227b4"},{"version":"4494b76dd2de2fe3496eff1c73f011b5b90f702ca50c89a584117686f306bc7b","signature":"fc304265d07f6f2f6375b9940342f00ac07310d129744314cc4e498fbde8a2cd"},{"version":"5c922107890068582021093ebb548c2c9c95e3fb76c68f02bdf4293410652828","signature":"fcc13b35d46376913754604f7b81009164b5622798324ca14a0faaeb88b302d9"},{"version":"827972af1602afdf6980f358beabe2434441e3b09783db6795a6a0b199d30aa7","signature":"d4dba6009e05b0c51e668d54ba8756d76a5c80c342a8e18f7adcde962c130a21"},{"version":"daaf595617f9e5d7315f958ba0f35127e94400033cf13564f7ed7fc467e667cc","signature":"a3a9d29a44d494ebcd48e3745a0cfb086ea2ec547372e8994ecd869f9b37c203"},{"version":"84cfde571cec4735a9f768e9f74cf9ff728fd8be13017c7acd127aa2a97904a8","signature":"853323635a5dab6bb009411a2af0c65a7126d18b3aafdb65591b1034311b0ad4"},{"version":"d2e5cfb2397ec8cacfd30de0e6f7992eb7db7b02cc83b7c43ef58bcd5aa88bc3","affectsGlobalScope":true,"impliedFormat":1}],"root":[[85,93]],"options":{"composite":true,"declaration":true,"declarationMap":true,"emitDeclarationOnly":true,"exactOptionalPropertyTypes":true,"module":99,"noFallthroughCasesInSwitch":true,"noImplicitOverride":true,"noImplicitReturns":true,"noUncheckedIndexedAccess":true,"noUnusedLocals":true,"noUnusedParameters":true,"outDir":"./","rootDir":"../src","skipLibCheck":true,"strict":true,"target":9,"tsBuildInfoFile":"./.tsbuildinfo","useDefineForClassFields":true,"verbatimModuleSyntax":true},"referencedMap":[[90,1],[85,2],[91,2],[88,3],[86,4],[93,5],[87,4],[89,2],[92,2],[83,6],[84,7],[63,8],[68,9],[64,10],[74,11],[80,6],[69,6],[81,12],[82,13],[66,14],[65,15],[72,16],[71,17],[75,18],[77,19],[79,20],[78,21]],"latestChangedDtsFile":"./index.d.ts","version":"6.0.3"}