@forgeax/engine-rhi 0.0.0-dev.8d955ade1c79
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +234 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/__tests__/async-form.test-d.d.ts +2 -0
- package/dist/__tests__/async-form.test-d.d.ts.map +1 -0
- package/dist/__tests__/binding-resource.test-d.d.ts +2 -0
- package/dist/__tests__/binding-resource.test-d.d.ts.map +1 -0
- package/dist/__tests__/buffer-mapping.test-d.d.ts +2 -0
- package/dist/__tests__/buffer-mapping.test-d.d.ts.map +1 -0
- package/dist/__tests__/canvasContext.test-d.d.ts +2 -0
- package/dist/__tests__/canvasContext.test-d.d.ts.map +1 -0
- package/dist/__tests__/caps-three-way.unit.test.d.ts +2 -0
- package/dist/__tests__/caps-three-way.unit.test.d.ts.map +1 -0
- package/dist/__tests__/caps.test-d.d.ts +2 -0
- package/dist/__tests__/caps.test-d.d.ts.map +1 -0
- package/dist/__tests__/command-encoder-spec-alignment.test-d.d.ts +2 -0
- package/dist/__tests__/command-encoder-spec-alignment.test-d.d.ts.map +1 -0
- package/dist/__tests__/createComputePipeline.test-d.d.ts +2 -0
- package/dist/__tests__/createComputePipeline.test-d.d.ts.map +1 -0
- package/dist/__tests__/createQuerySet.test-d.d.ts +2 -0
- package/dist/__tests__/createQuerySet.test-d.d.ts.map +1 -0
- package/dist/__tests__/createTextureView.test-d.d.ts +2 -0
- package/dist/__tests__/createTextureView.test-d.d.ts.map +1 -0
- package/dist/__tests__/descriptor-mirror.test-d.d.ts +2 -0
- package/dist/__tests__/descriptor-mirror.test-d.d.ts.map +1 -0
- package/dist/__tests__/draw-validation-errors.test.d.ts +2 -0
- package/dist/__tests__/draw-validation-errors.test.d.ts.map +1 -0
- package/dist/__tests__/errors-instancing.unit.test.d.ts +2 -0
- package/dist/__tests__/errors-instancing.unit.test.d.ts.map +1 -0
- package/dist/__tests__/errors.test-d.d.ts +2 -0
- package/dist/__tests__/errors.test-d.d.ts.map +1 -0
- package/dist/__tests__/limit-exceeded-detail.test-d.d.ts +2 -0
- package/dist/__tests__/limit-exceeded-detail.test-d.d.ts.map +1 -0
- package/dist/__tests__/mapped-buffer.test-d.d.ts +2 -0
- package/dist/__tests__/mapped-buffer.test-d.d.ts.map +1 -0
- package/dist/__tests__/opaque-handle.test-d.d.ts +2 -0
- package/dist/__tests__/opaque-handle.test-d.d.ts.map +1 -0
- package/dist/__tests__/owner-out-of-range-role.test.d.ts +2 -0
- package/dist/__tests__/owner-out-of-range-role.test.d.ts.map +1 -0
- package/dist/__tests__/queue-writes.test-d.d.ts +2 -0
- package/dist/__tests__/queue-writes.test-d.d.ts.map +1 -0
- package/dist/__tests__/render-pass-encoder-spec-alignment.test-d.d.ts +2 -0
- package/dist/__tests__/render-pass-encoder-spec-alignment.test-d.d.ts.map +1 -0
- package/dist/__tests__/requestAdapter.test-d.d.ts +2 -0
- package/dist/__tests__/requestAdapter.test-d.d.ts.map +1 -0
- package/dist/errors.d.ts +315 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.mjs +56 -0
- package/dist/errors.mjs.map +1 -0
- package/dist/index.d.ts +1758 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +87 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +61 -0
- package/src/__tests__/async-form.test-d.ts +40 -0
- package/src/__tests__/binding-resource.test-d.ts +66 -0
- package/src/__tests__/buffer-mapping.test-d.ts +84 -0
- package/src/__tests__/canvasContext.test-d.ts +106 -0
- package/src/__tests__/caps-three-way.unit.test.ts +144 -0
- package/src/__tests__/caps.test-d.ts +83 -0
- package/src/__tests__/command-encoder-spec-alignment.test-d.ts +117 -0
- package/src/__tests__/createComputePipeline.test-d.ts +149 -0
- package/src/__tests__/createQuerySet.test-d.ts +81 -0
- package/src/__tests__/createTextureView.test-d.ts +127 -0
- package/src/__tests__/descriptor-mirror.test-d.ts +284 -0
- package/src/__tests__/draw-validation-errors.test.ts +155 -0
- package/src/__tests__/errors-instancing.unit.test.ts +167 -0
- package/src/__tests__/errors.test-d.ts +199 -0
- package/src/__tests__/limit-exceeded-detail.test-d.ts +33 -0
- package/src/__tests__/mapped-buffer.test-d.ts +65 -0
- package/src/__tests__/opaque-handle.test-d.ts +121 -0
- package/src/__tests__/owner-out-of-range-role.test.ts +84 -0
- package/src/__tests__/queue-writes.test-d.ts +108 -0
- package/src/__tests__/render-pass-encoder-spec-alignment.test-d.ts +202 -0
- package/src/__tests__/requestAdapter.test-d.ts +123 -0
- package/src/errors.ts +504 -0
- package/src/index.ts +2276 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// w07 - RhiDevice.createComputePipeline type-level contract test (TDD red).
|
|
2
|
+
//
|
|
3
|
+
// Locks M1 createComputePipeline surface (plan-tasks w07 / requirements IN-1):
|
|
4
|
+
// RhiDevice.createComputePipeline(desc: ComputePipelineDescriptor)
|
|
5
|
+
// -> Result<ComputePipeline, RhiError>
|
|
6
|
+
//
|
|
7
|
+
// Field shape (research §1.2 IDL):
|
|
8
|
+
// ComputePipelineDescriptor = ExplicitUndefined<
|
|
9
|
+
// Pick<GPUComputePipelineDescriptor, 'label' | 'layout' | 'compute'>
|
|
10
|
+
// > with the spec polymorphism `(GPUAutoLayoutMode or GPUPipelineLayout)`
|
|
11
|
+
// tightened to the forgeax union `'auto' | PipelineLayout` (D-S5 pattern),
|
|
12
|
+
// and `compute.module` tightened to the forgeax `ShaderModule` opaque handle.
|
|
13
|
+
//
|
|
14
|
+
// Red expected: tsc -b fails with TS2724 (missing ComputePipelineDescriptor)
|
|
15
|
+
// + TS2339 (createComputePipeline missing on RhiDevice). Turns green after
|
|
16
|
+
// w09 ships interface + shim.
|
|
17
|
+
//
|
|
18
|
+
// Anchors: requirements §IN-1 / §AC-01; research §1.2 IDL + 'auto' vs
|
|
19
|
+
// explicit PipelineLayout double form; plan-strategy §4.2 + K-10.
|
|
20
|
+
|
|
21
|
+
import { describe, expectTypeOf, it } from 'vitest';
|
|
22
|
+
import type {
|
|
23
|
+
ComputePipeline,
|
|
24
|
+
ComputePipelineDescriptor,
|
|
25
|
+
PipelineLayout,
|
|
26
|
+
Result,
|
|
27
|
+
RhiDevice,
|
|
28
|
+
RhiError,
|
|
29
|
+
ShaderModule,
|
|
30
|
+
} from '../index';
|
|
31
|
+
|
|
32
|
+
/** Strip undefined from an optional field; bridges forgeax `?: T | undefined`
|
|
33
|
+
* and spec `?: T` while comparing value types. */
|
|
34
|
+
type ValueOf<T, K extends keyof T> = NonNullable<T[K]>;
|
|
35
|
+
|
|
36
|
+
describe('w07 type-level - ComputePipelineDescriptor field set === Pick<GPUComputePipelineDescriptor, ...>', () => {
|
|
37
|
+
it('has exactly the keys label / layout / compute', () => {
|
|
38
|
+
type ExpectedKeys = 'label' | 'layout' | 'compute';
|
|
39
|
+
expectTypeOf<keyof ComputePipelineDescriptor>().toEqualTypeOf<ExpectedKeys>();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('label field type aligns with spec (string | undefined)', () => {
|
|
43
|
+
type LabelForgeaX = NonNullable<ComputePipelineDescriptor['label']>;
|
|
44
|
+
type LabelSpec = NonNullable<GPUComputePipelineDescriptor['label']>;
|
|
45
|
+
expectTypeOf<LabelForgeaX>().toEqualTypeOf<LabelSpec>();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe("w07 type-level - layout union 'auto' | PipelineLayout", () => {
|
|
50
|
+
it("layout: 'auto' literal is a legal assignment", () => {
|
|
51
|
+
const psm = {} as ShaderModule;
|
|
52
|
+
const _auto: ComputePipelineDescriptor = {
|
|
53
|
+
layout: 'auto',
|
|
54
|
+
compute: { module: psm },
|
|
55
|
+
};
|
|
56
|
+
void _auto;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('layout: PipelineLayout brand is a legal assignment', () => {
|
|
60
|
+
const psm = {} as ShaderModule;
|
|
61
|
+
const pl = {} as PipelineLayout;
|
|
62
|
+
const _explicit: ComputePipelineDescriptor = {
|
|
63
|
+
layout: pl,
|
|
64
|
+
compute: { module: psm },
|
|
65
|
+
};
|
|
66
|
+
void _explicit;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("layout type is exactly 'auto' | PipelineLayout (no spec polymorphism leak)", () => {
|
|
70
|
+
type Layout = ComputePipelineDescriptor['layout'];
|
|
71
|
+
expectTypeOf<Layout>().toEqualTypeOf<'auto' | PipelineLayout>();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('w07 type-level - compute nested dictionary mirrors GPUProgrammableStage', () => {
|
|
76
|
+
it('compute.module is the forgeax ShaderModule opaque handle (not GPUShaderModule)', () => {
|
|
77
|
+
type ModuleForgeaX = ComputePipelineDescriptor['compute']['module'];
|
|
78
|
+
expectTypeOf<ModuleForgeaX>().toEqualTypeOf<ShaderModule>();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('compute.entryPoint is optional string | undefined', () => {
|
|
82
|
+
const psm = {} as ShaderModule;
|
|
83
|
+
const _withEntry: ComputePipelineDescriptor = {
|
|
84
|
+
layout: 'auto',
|
|
85
|
+
compute: { module: psm, entryPoint: 'cs_main' },
|
|
86
|
+
};
|
|
87
|
+
const _withoutEntry: ComputePipelineDescriptor = {
|
|
88
|
+
layout: 'auto',
|
|
89
|
+
compute: { module: psm },
|
|
90
|
+
};
|
|
91
|
+
const _explicitUndef: ComputePipelineDescriptor = {
|
|
92
|
+
layout: 'auto',
|
|
93
|
+
compute: { module: psm, entryPoint: undefined },
|
|
94
|
+
};
|
|
95
|
+
void _withEntry;
|
|
96
|
+
void _withoutEntry;
|
|
97
|
+
void _explicitUndef;
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('compute.constants is optional Record<string, number>', () => {
|
|
101
|
+
const psm = {} as ShaderModule;
|
|
102
|
+
const _withConsts: ComputePipelineDescriptor = {
|
|
103
|
+
layout: 'auto',
|
|
104
|
+
compute: { module: psm, constants: { foo: 1.0, bar: 2.5 } },
|
|
105
|
+
};
|
|
106
|
+
void _withConsts;
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('w07 type-level - RhiDevice.createComputePipeline signature', () => {
|
|
111
|
+
it('returns Result<ComputePipeline, RhiError>', () => {
|
|
112
|
+
type Sig = RhiDevice['createComputePipeline'];
|
|
113
|
+
type Ret = ReturnType<Sig>;
|
|
114
|
+
expectTypeOf<Ret>().toEqualTypeOf<Result<ComputePipeline, RhiError>>();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('takes a ComputePipelineDescriptor as the sole parameter', () => {
|
|
118
|
+
type Sig = RhiDevice['createComputePipeline'];
|
|
119
|
+
type Params = Parameters<Sig>;
|
|
120
|
+
expectTypeOf<Params>().toEqualTypeOf<[ComputePipelineDescriptor]>();
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('w07 type-level - ComputePipeline opaque handle does not expose raw GPU fields', () => {
|
|
125
|
+
it('ComputePipeline is brand-only (no .gpuComputePipeline access)', () => {
|
|
126
|
+
const h = {} as ComputePipeline;
|
|
127
|
+
// @ts-expect-error MVP-1.3: ComputePipeline is opaque; raw fields not exposed.
|
|
128
|
+
h.gpuComputePipeline;
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('S-7 optional shape: label uses `?: T | undefined`', () => {
|
|
132
|
+
const psm = {} as ShaderModule;
|
|
133
|
+
const _omitted: ComputePipelineDescriptor = {
|
|
134
|
+
layout: 'auto',
|
|
135
|
+
compute: { module: psm },
|
|
136
|
+
};
|
|
137
|
+
const _explicit: ComputePipelineDescriptor = {
|
|
138
|
+
label: undefined,
|
|
139
|
+
layout: 'auto',
|
|
140
|
+
compute: { module: psm },
|
|
141
|
+
};
|
|
142
|
+
void _omitted;
|
|
143
|
+
void _explicit;
|
|
144
|
+
type LabelType = ComputePipelineDescriptor['label'];
|
|
145
|
+
expectTypeOf<NonNullable<LabelType>>().toEqualTypeOf<
|
|
146
|
+
ValueOf<GPUComputePipelineDescriptor, 'label'>
|
|
147
|
+
>();
|
|
148
|
+
});
|
|
149
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// w10 - RhiDevice.createQuerySet type-level contract test (TDD red).
|
|
2
|
+
//
|
|
3
|
+
// Locks M1 createQuerySet surface (plan-tasks w10 / requirements IN-1):
|
|
4
|
+
// RhiDevice.createQuerySet(desc: QuerySetDescriptor)
|
|
5
|
+
// -> Result<QuerySet, RhiError>
|
|
6
|
+
//
|
|
7
|
+
// Field shape (research §1.3 IDL):
|
|
8
|
+
// QuerySetDescriptor = ExplicitUndefined<
|
|
9
|
+
// Pick<GPUQuerySetDescriptor, 'label' | 'type' | 'count'>
|
|
10
|
+
// >
|
|
11
|
+
// type: 'occlusion' | 'timestamp' (closed union, GPUQueryType).
|
|
12
|
+
//
|
|
13
|
+
// Red expected: tsc -b fails with TS2305 (missing QuerySetDescriptor) +
|
|
14
|
+
// TS2339 (createQuerySet missing on RhiDevice). Turns green after w12 ships.
|
|
15
|
+
//
|
|
16
|
+
// Anchors: requirements §IN-1 / §AC-01; research §1.3 IDL + Pick set;
|
|
17
|
+
// plan-strategy §4.2 + K-10.
|
|
18
|
+
|
|
19
|
+
import { describe, expectTypeOf, it } from 'vitest';
|
|
20
|
+
import type { QuerySet, QuerySetDescriptor, Result, RhiDevice, RhiError } from '../index';
|
|
21
|
+
|
|
22
|
+
/** Strip undefined from an optional field; bridges forgeax `?: T | undefined`
|
|
23
|
+
* and spec `?: T` while comparing value types. */
|
|
24
|
+
type ValueOf<T, K extends keyof T> = NonNullable<T[K]>;
|
|
25
|
+
|
|
26
|
+
describe('w10 type-level - QuerySetDescriptor field set === Pick<GPUQuerySetDescriptor, ...>', () => {
|
|
27
|
+
it('has exactly the keys label / type / count', () => {
|
|
28
|
+
type ExpectedKeys = 'label' | 'type' | 'count';
|
|
29
|
+
expectTypeOf<keyof QuerySetDescriptor>().toEqualTypeOf<ExpectedKeys>();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('label field type aligns with spec (string | undefined)', () => {
|
|
33
|
+
type LabelForgeaX = NonNullable<QuerySetDescriptor['label']>;
|
|
34
|
+
type LabelSpec = NonNullable<GPUQuerySetDescriptor['label']>;
|
|
35
|
+
expectTypeOf<LabelForgeaX>().toEqualTypeOf<LabelSpec>();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("type field is a closed union 'occlusion' | 'timestamp' (GPUQueryType)", () => {
|
|
39
|
+
type TypeForgeaX = ValueOf<QuerySetDescriptor, 'type'>;
|
|
40
|
+
expectTypeOf<TypeForgeaX>().toEqualTypeOf<ValueOf<GPUQuerySetDescriptor, 'type'>>();
|
|
41
|
+
// Sentinel assignment: both literal members are accepted.
|
|
42
|
+
const _occ: QuerySetDescriptor = { type: 'occlusion', count: 4 };
|
|
43
|
+
const _ts: QuerySetDescriptor = { type: 'timestamp', count: 4 };
|
|
44
|
+
void _occ;
|
|
45
|
+
void _ts;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('count field is GPUSize32 (number)', () => {
|
|
49
|
+
type CountForgeaX = ValueOf<QuerySetDescriptor, 'count'>;
|
|
50
|
+
expectTypeOf<CountForgeaX>().toEqualTypeOf<ValueOf<GPUQuerySetDescriptor, 'count'>>();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('S-7 optional shape: label uses `?: T | undefined`', () => {
|
|
54
|
+
const _omitted: QuerySetDescriptor = { type: 'occlusion', count: 0 };
|
|
55
|
+
const _explicit: QuerySetDescriptor = { label: undefined, type: 'occlusion', count: 0 };
|
|
56
|
+
void _omitted;
|
|
57
|
+
void _explicit;
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('w10 type-level - RhiDevice.createQuerySet signature', () => {
|
|
62
|
+
it('returns Result<QuerySet, RhiError>', () => {
|
|
63
|
+
type Sig = RhiDevice['createQuerySet'];
|
|
64
|
+
type Ret = ReturnType<Sig>;
|
|
65
|
+
expectTypeOf<Ret>().toEqualTypeOf<Result<QuerySet, RhiError>>();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('takes a QuerySetDescriptor as the sole parameter', () => {
|
|
69
|
+
type Sig = RhiDevice['createQuerySet'];
|
|
70
|
+
type Params = Parameters<Sig>;
|
|
71
|
+
expectTypeOf<Params>().toEqualTypeOf<[QuerySetDescriptor]>();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('w10 type-level - QuerySet opaque handle does not expose raw GPU fields', () => {
|
|
76
|
+
it('QuerySet is brand-only (no .gpuQuerySet access)', () => {
|
|
77
|
+
const h = {} as QuerySet;
|
|
78
|
+
// @ts-expect-error MVP-1.3: QuerySet is opaque; raw fields not exposed.
|
|
79
|
+
h.gpuQuerySet;
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// w03 - RhiDevice.createTextureView type-level contract test (TDD red).
|
|
2
|
+
//
|
|
3
|
+
// Locks M1 createTextureView surface (plan-tasks w03 / requirements IN-1):
|
|
4
|
+
// RhiDevice.createTextureView(texture: Texture, desc: TextureViewDescriptor)
|
|
5
|
+
// -> Result<TextureView, RhiError>
|
|
6
|
+
//
|
|
7
|
+
// Field shape: TextureViewDescriptor = ExplicitUndefined<
|
|
8
|
+
// Pick<GPUTextureViewDescriptor,
|
|
9
|
+
// 'format' | 'dimension' | 'usage' | 'aspect' | 'baseMipLevel' |
|
|
10
|
+
// 'mipLevelCount' | 'baseArrayLayer' | 'arrayLayerCount' | 'label'
|
|
11
|
+
// >
|
|
12
|
+
// >
|
|
13
|
+
//
|
|
14
|
+
// Red expected state: this file fails tsc -b until w06 ships
|
|
15
|
+
// (1) the `TextureViewDescriptor` type alias on `@forgeax/engine-rhi`,
|
|
16
|
+
// (2) the `RhiDevice.createTextureView` method signature.
|
|
17
|
+
// Green expected state (after w06 commit): all expectTypeOf assertions pass.
|
|
18
|
+
//
|
|
19
|
+
// Anchors: requirements §IN-1 / §AC-01 / §AC-07(a); research §1.1 9 fields
|
|
20
|
+
// (excl swizzle feature-gate); plan-strategy §4.2 type layer + K-10;
|
|
21
|
+
// AI User Charter proposition 1 progressive disclosure +
|
|
22
|
+
// proposition 5 consistent abstraction (single createX entry).
|
|
23
|
+
|
|
24
|
+
import { describe, expectTypeOf, it } from 'vitest';
|
|
25
|
+
import type {
|
|
26
|
+
Result,
|
|
27
|
+
RhiDevice,
|
|
28
|
+
RhiError,
|
|
29
|
+
Texture,
|
|
30
|
+
TextureView,
|
|
31
|
+
TextureViewDescriptor,
|
|
32
|
+
} from '../index';
|
|
33
|
+
|
|
34
|
+
/** Strip undefined from an optional field; bridges forgeax `?: T | undefined`
|
|
35
|
+
* and spec `?: T` while comparing value types. */
|
|
36
|
+
type ValueOf<T, K extends keyof T> = NonNullable<T[K]>;
|
|
37
|
+
|
|
38
|
+
describe('w03 type-level - TextureViewDescriptor field set === Pick<GPUTextureViewDescriptor, ...> (research §1.1 9 fields, swizzle excluded)', () => {
|
|
39
|
+
it('has exactly the 9 spec keys (label / format / dimension / usage / aspect / baseMipLevel / mipLevelCount / baseArrayLayer / arrayLayerCount)', () => {
|
|
40
|
+
type ExpectedKeys =
|
|
41
|
+
| 'label'
|
|
42
|
+
| 'format'
|
|
43
|
+
| 'dimension'
|
|
44
|
+
| 'usage'
|
|
45
|
+
| 'aspect'
|
|
46
|
+
| 'baseMipLevel'
|
|
47
|
+
| 'mipLevelCount'
|
|
48
|
+
| 'baseArrayLayer'
|
|
49
|
+
| 'arrayLayerCount';
|
|
50
|
+
expectTypeOf<keyof TextureViewDescriptor>().toEqualTypeOf<ExpectedKeys>();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('format / dimension / usage value types align with spec', () => {
|
|
54
|
+
expectTypeOf<ValueOf<TextureViewDescriptor, 'format'>>().toEqualTypeOf<
|
|
55
|
+
ValueOf<GPUTextureViewDescriptor, 'format'>
|
|
56
|
+
>();
|
|
57
|
+
expectTypeOf<ValueOf<TextureViewDescriptor, 'dimension'>>().toEqualTypeOf<
|
|
58
|
+
ValueOf<GPUTextureViewDescriptor, 'dimension'>
|
|
59
|
+
>();
|
|
60
|
+
expectTypeOf<ValueOf<TextureViewDescriptor, 'usage'>>().toEqualTypeOf<
|
|
61
|
+
ValueOf<GPUTextureViewDescriptor, 'usage'>
|
|
62
|
+
>();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('aspect / baseMipLevel / mipLevelCount / baseArrayLayer / arrayLayerCount value types align with spec', () => {
|
|
66
|
+
expectTypeOf<ValueOf<TextureViewDescriptor, 'aspect'>>().toEqualTypeOf<
|
|
67
|
+
ValueOf<GPUTextureViewDescriptor, 'aspect'>
|
|
68
|
+
>();
|
|
69
|
+
expectTypeOf<ValueOf<TextureViewDescriptor, 'baseMipLevel'>>().toEqualTypeOf<
|
|
70
|
+
ValueOf<GPUTextureViewDescriptor, 'baseMipLevel'>
|
|
71
|
+
>();
|
|
72
|
+
expectTypeOf<ValueOf<TextureViewDescriptor, 'mipLevelCount'>>().toEqualTypeOf<
|
|
73
|
+
ValueOf<GPUTextureViewDescriptor, 'mipLevelCount'>
|
|
74
|
+
>();
|
|
75
|
+
expectTypeOf<ValueOf<TextureViewDescriptor, 'baseArrayLayer'>>().toEqualTypeOf<
|
|
76
|
+
ValueOf<GPUTextureViewDescriptor, 'baseArrayLayer'>
|
|
77
|
+
>();
|
|
78
|
+
expectTypeOf<ValueOf<TextureViewDescriptor, 'arrayLayerCount'>>().toEqualTypeOf<
|
|
79
|
+
ValueOf<GPUTextureViewDescriptor, 'arrayLayerCount'>
|
|
80
|
+
>();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('S-7 optional shape: every field uses `?: T | undefined` (omitted vs explicit-undefined both legal)', () => {
|
|
84
|
+
const _empty: TextureViewDescriptor = {};
|
|
85
|
+
const _explicit: TextureViewDescriptor = {
|
|
86
|
+
label: undefined,
|
|
87
|
+
format: undefined,
|
|
88
|
+
dimension: undefined,
|
|
89
|
+
usage: undefined,
|
|
90
|
+
aspect: undefined,
|
|
91
|
+
baseMipLevel: undefined,
|
|
92
|
+
mipLevelCount: undefined,
|
|
93
|
+
baseArrayLayer: undefined,
|
|
94
|
+
arrayLayerCount: undefined,
|
|
95
|
+
};
|
|
96
|
+
void _empty;
|
|
97
|
+
void _explicit;
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('swizzle is NOT exposed (research §1.1 OOS-MVP feature-gated)', () => {
|
|
101
|
+
type Keys = keyof TextureViewDescriptor;
|
|
102
|
+
type HasSwizzle = 'swizzle' extends Keys ? true : false;
|
|
103
|
+
expectTypeOf<HasSwizzle>().toEqualTypeOf<false>();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('w03 type-level - RhiDevice.createTextureView signature', () => {
|
|
108
|
+
it('returns Result<TextureView, RhiError>', () => {
|
|
109
|
+
type Sig = RhiDevice['createTextureView'];
|
|
110
|
+
type Ret = ReturnType<Sig>;
|
|
111
|
+
expectTypeOf<Ret>().toEqualTypeOf<Result<TextureView, RhiError>>();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('takes (texture: Texture, desc: TextureViewDescriptor) as parameters', () => {
|
|
115
|
+
type Sig = RhiDevice['createTextureView'];
|
|
116
|
+
type Params = Parameters<Sig>;
|
|
117
|
+
expectTypeOf<Params>().toEqualTypeOf<[Texture, TextureViewDescriptor]>();
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe('w03 type-level - TextureView opaque handle does not expose raw GPU fields', () => {
|
|
122
|
+
it('TextureView is brand-only (no .gpuTextureView access)', () => {
|
|
123
|
+
const h = {} as TextureView;
|
|
124
|
+
// @ts-expect-error MVP-1.3: TextureView is opaque; gpuTextureView is not exposed.
|
|
125
|
+
h.gpuTextureView;
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
// MVP-1.1 - 5 core descriptors mirror @webgpu/types (type-level red -> green).
|
|
2
|
+
// Extended in feat-20260508-rhi-surface-completion w8 (D-S5) with 4 new
|
|
3
|
+
// descriptors: CommandEncoderDescriptor / RenderPassColorAttachment /
|
|
4
|
+
// RenderPassDepthStencilAttachment / RenderPassDescriptor.
|
|
5
|
+
//
|
|
6
|
+
// Mirror policy (decision S-7 + research F-3):
|
|
7
|
+
// - Field NAMES align byte-for-byte with spec: keyof forgeax === locked key
|
|
8
|
+
// list === keyof Pick<spec, ...>.
|
|
9
|
+
// - Field VALUE types (NonNullable) align with spec: forgeax[K] minus undefined
|
|
10
|
+
// === spec[K] minus undefined.
|
|
11
|
+
// - Optional shape: forgeax writes `?: T | undefined` (decision S-7); spec
|
|
12
|
+
// v0.1.69 writes `?: T` (ecosystem upstream pending migration); the
|
|
13
|
+
// ExplicitUndefined mapped type bridges them, allowing
|
|
14
|
+
// `{ label: undefined }` writes (charter proposition 4: explicit failure /
|
|
15
|
+
// distinguish missing vs explicit-undefined).
|
|
16
|
+
//
|
|
17
|
+
// view narrow Path X (feat-20260510-rhi-resource-creation M2 / breakage point
|
|
18
|
+
// #1): the `view` field on RenderPassColorAttachment /
|
|
19
|
+
// RenderPassDepthStencilAttachment is NARROWED to the forgeax `TextureView`
|
|
20
|
+
// brand (instead of the spec polymorphism `GPUTexture | GPUTextureView`).
|
|
21
|
+
// This breaks field-type alignment but preserves field-name alignment (R12
|
|
22
|
+
// lint scope). Charter proposition 5 (consistent abstraction) over
|
|
23
|
+
// proposition 1 (strict spec shape) so AI users land on the single
|
|
24
|
+
// constructable brand: `device.createTextureView(tex, {}).unwrap()`. The
|
|
25
|
+
// earlier D-S5 tightening to `Texture` was retired with M1 of this closure.
|
|
26
|
+
//
|
|
27
|
+
// Related: requirements AC MVP-1.1 (5 descriptors + field-name byte-for-byte) +
|
|
28
|
+
// AC-RSC-08 (4 new descriptors + view tightening); plan-strategy 4.3
|
|
29
|
+
// key tests #1; 7 propositions 4 / 5; research F-1 / F-3 / F-7;
|
|
30
|
+
// plan-strategy 2 D-S3 / D-S5 / D-S7.
|
|
31
|
+
|
|
32
|
+
import { describe, expectTypeOf, it } from 'vitest';
|
|
33
|
+
import type {
|
|
34
|
+
BindGroupLayoutDescriptor,
|
|
35
|
+
BufferDescriptor,
|
|
36
|
+
CommandEncoderDescriptor,
|
|
37
|
+
PipelineLayout,
|
|
38
|
+
RenderPassColorAttachment,
|
|
39
|
+
RenderPassDepthStencilAttachment,
|
|
40
|
+
RenderPassDescriptor,
|
|
41
|
+
RenderPassTimestampWrites,
|
|
42
|
+
RenderPipelineDescriptor,
|
|
43
|
+
RenderPipelineFragmentState,
|
|
44
|
+
RenderPipelineVertexState,
|
|
45
|
+
SamplerDescriptor,
|
|
46
|
+
TextureDescriptor,
|
|
47
|
+
TextureFormat,
|
|
48
|
+
TextureView,
|
|
49
|
+
} from '../index';
|
|
50
|
+
|
|
51
|
+
/** Strip undefined from an optional field; bridges forgeax `?: T | undefined`
|
|
52
|
+
* and spec `?: T` while comparing value types. */
|
|
53
|
+
type ValueOf<T, K extends keyof T> = NonNullable<T[K]>;
|
|
54
|
+
|
|
55
|
+
describe('MVP-1.1 - 5 descriptors mirror @webgpu/types', () => {
|
|
56
|
+
it('BufferDescriptor field set === Pick<GPUBufferDescriptor, ...>', () => {
|
|
57
|
+
type BufferKeys = 'label' | 'size' | 'usage' | 'mappedAtCreation';
|
|
58
|
+
expectTypeOf<keyof BufferDescriptor>().toEqualTypeOf<BufferKeys>();
|
|
59
|
+
expectTypeOf<ValueOf<BufferDescriptor, 'label'>>().toEqualTypeOf<
|
|
60
|
+
ValueOf<GPUBufferDescriptor, 'label'>
|
|
61
|
+
>();
|
|
62
|
+
expectTypeOf<ValueOf<BufferDescriptor, 'size'>>().toEqualTypeOf<
|
|
63
|
+
ValueOf<GPUBufferDescriptor, 'size'>
|
|
64
|
+
>();
|
|
65
|
+
expectTypeOf<ValueOf<BufferDescriptor, 'usage'>>().toEqualTypeOf<
|
|
66
|
+
ValueOf<GPUBufferDescriptor, 'usage'>
|
|
67
|
+
>();
|
|
68
|
+
expectTypeOf<ValueOf<BufferDescriptor, 'mappedAtCreation'>>().toEqualTypeOf<
|
|
69
|
+
ValueOf<GPUBufferDescriptor, 'mappedAtCreation'>
|
|
70
|
+
>();
|
|
71
|
+
// S-7: writing `{ label: undefined }` is legal under exactOptionalPropertyTypes.
|
|
72
|
+
const b: BufferDescriptor = { size: 16, usage: 0, label: undefined };
|
|
73
|
+
expectTypeOf(b).toMatchTypeOf<BufferDescriptor>();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('TextureDescriptor field set === Pick<GPUTextureDescriptor, ...> (incl R8 Compatibility Mode)', () => {
|
|
77
|
+
type TextureKeys =
|
|
78
|
+
| 'label'
|
|
79
|
+
| 'size'
|
|
80
|
+
| 'mipLevelCount'
|
|
81
|
+
| 'sampleCount'
|
|
82
|
+
| 'dimension'
|
|
83
|
+
| 'format'
|
|
84
|
+
| 'usage'
|
|
85
|
+
| 'viewFormats'
|
|
86
|
+
| 'textureBindingViewDimension';
|
|
87
|
+
expectTypeOf<keyof TextureDescriptor>().toEqualTypeOf<TextureKeys>();
|
|
88
|
+
expectTypeOf<ValueOf<TextureDescriptor, 'format'>>().toEqualTypeOf<
|
|
89
|
+
ValueOf<GPUTextureDescriptor, 'format'>
|
|
90
|
+
>();
|
|
91
|
+
expectTypeOf<ValueOf<TextureDescriptor, 'usage'>>().toEqualTypeOf<
|
|
92
|
+
ValueOf<GPUTextureDescriptor, 'usage'>
|
|
93
|
+
>();
|
|
94
|
+
expectTypeOf<ValueOf<TextureDescriptor, 'textureBindingViewDimension'>>().toEqualTypeOf<
|
|
95
|
+
ValueOf<GPUTextureDescriptor, 'textureBindingViewDimension'>
|
|
96
|
+
>();
|
|
97
|
+
// Explicit `undefined` writes are legal (R8 Compatibility Mode follow-on).
|
|
98
|
+
const t: TextureDescriptor = {
|
|
99
|
+
size: [128, 128],
|
|
100
|
+
format: 'rgba8unorm',
|
|
101
|
+
usage: 0,
|
|
102
|
+
textureBindingViewDimension: undefined,
|
|
103
|
+
};
|
|
104
|
+
expectTypeOf(t).toMatchTypeOf<TextureDescriptor>();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('SamplerDescriptor field set === Pick<GPUSamplerDescriptor, ...>', () => {
|
|
108
|
+
type SamplerKeys =
|
|
109
|
+
| 'label'
|
|
110
|
+
| 'addressModeU'
|
|
111
|
+
| 'addressModeV'
|
|
112
|
+
| 'addressModeW'
|
|
113
|
+
| 'magFilter'
|
|
114
|
+
| 'minFilter'
|
|
115
|
+
| 'mipmapFilter'
|
|
116
|
+
| 'lodMinClamp'
|
|
117
|
+
| 'lodMaxClamp'
|
|
118
|
+
| 'compare'
|
|
119
|
+
| 'maxAnisotropy';
|
|
120
|
+
expectTypeOf<keyof SamplerDescriptor>().toEqualTypeOf<SamplerKeys>();
|
|
121
|
+
expectTypeOf<ValueOf<SamplerDescriptor, 'compare'>>().toEqualTypeOf<
|
|
122
|
+
ValueOf<GPUSamplerDescriptor, 'compare'>
|
|
123
|
+
>();
|
|
124
|
+
expectTypeOf<ValueOf<SamplerDescriptor, 'magFilter'>>().toEqualTypeOf<
|
|
125
|
+
ValueOf<GPUSamplerDescriptor, 'magFilter'>
|
|
126
|
+
>();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('BindGroupLayoutDescriptor field set === Pick<GPUBindGroupLayoutDescriptor, ...>', () => {
|
|
130
|
+
type BglKeys = 'label' | 'entries';
|
|
131
|
+
expectTypeOf<keyof BindGroupLayoutDescriptor>().toEqualTypeOf<BglKeys>();
|
|
132
|
+
expectTypeOf<ValueOf<BindGroupLayoutDescriptor, 'entries'>>().toEqualTypeOf<
|
|
133
|
+
ValueOf<GPUBindGroupLayoutDescriptor, 'entries'>
|
|
134
|
+
>();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('RenderPipelineDescriptor field set === Pick<GPURenderPipelineDescriptor, ...>', () => {
|
|
138
|
+
type RppKeys =
|
|
139
|
+
| 'label'
|
|
140
|
+
| 'layout'
|
|
141
|
+
| 'vertex'
|
|
142
|
+
| 'primitive'
|
|
143
|
+
| 'depthStencil'
|
|
144
|
+
| 'multisample'
|
|
145
|
+
| 'fragment';
|
|
146
|
+
expectTypeOf<keyof RenderPipelineDescriptor>().toEqualTypeOf<RppKeys>();
|
|
147
|
+
expectTypeOf<
|
|
148
|
+
ValueOf<RenderPipelineDescriptor, 'vertex'>
|
|
149
|
+
>().toEqualTypeOf<RenderPipelineVertexState>();
|
|
150
|
+
expectTypeOf<
|
|
151
|
+
ValueOf<RenderPipelineDescriptor, 'fragment'>
|
|
152
|
+
>().toEqualTypeOf<RenderPipelineFragmentState>();
|
|
153
|
+
expectTypeOf<ValueOf<RenderPipelineDescriptor, 'layout'>>().toEqualTypeOf<
|
|
154
|
+
'auto' | PipelineLayout
|
|
155
|
+
>();
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe('M4 RHI descriptor mirror', () => {
|
|
160
|
+
it('uses the spec TextureFormat union for texture descriptors', () => {
|
|
161
|
+
expectTypeOf<NonNullable<TextureDescriptor['format']>>().toEqualTypeOf<TextureFormat>();
|
|
162
|
+
const invalid: TextureDescriptor = {
|
|
163
|
+
// @ts-expect-error -- RHI descriptors reject arbitrary format strings.
|
|
164
|
+
format: 'not-a-texture-format',
|
|
165
|
+
size: { width: 1, height: 1 },
|
|
166
|
+
usage: 0,
|
|
167
|
+
};
|
|
168
|
+
void invalid;
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// w8: 4 new descriptors (D-S5) - field NAMES align byte-for-byte with spec;
|
|
173
|
+
// the `view` field TYPE is tightened to `Texture` (spec has
|
|
174
|
+
// `GPUTexture | GPUTextureView`).
|
|
175
|
+
describe('w8 - 4 new descriptors mirror @webgpu/types (D-S5)', () => {
|
|
176
|
+
it('CommandEncoderDescriptor field set === Pick<GPUCommandEncoderDescriptor, label>', () => {
|
|
177
|
+
type CmdKeys = 'label';
|
|
178
|
+
expectTypeOf<keyof CommandEncoderDescriptor>().toEqualTypeOf<CmdKeys>();
|
|
179
|
+
expectTypeOf<ValueOf<CommandEncoderDescriptor, 'label'>>().toEqualTypeOf<
|
|
180
|
+
ValueOf<GPUCommandEncoderDescriptor, 'label'>
|
|
181
|
+
>();
|
|
182
|
+
const desc: CommandEncoderDescriptor = { label: 'frame-encoder' };
|
|
183
|
+
expectTypeOf(desc).toMatchTypeOf<CommandEncoderDescriptor>();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('RenderPassColorAttachment field set === Pick + view aligned to TextureView (view narrow Path X)', () => {
|
|
187
|
+
type AttKeys = 'view' | 'depthSlice' | 'resolveTarget' | 'clearValue' | 'loadOp' | 'storeOp';
|
|
188
|
+
expectTypeOf<keyof RenderPassColorAttachment>().toEqualTypeOf<AttKeys>();
|
|
189
|
+
// view is the forgeax TextureView handle, NOT spec polymorphism.
|
|
190
|
+
// (feat-20260510-rhi-resource-creation M2 view narrow Path X / breakage
|
|
191
|
+
// point #1: the earlier D-S5 temporary tightening to Texture was
|
|
192
|
+
// retired once M1 shipped RhiDevice.createTextureView.)
|
|
193
|
+
expectTypeOf<RenderPassColorAttachment['view']>().toEqualTypeOf<TextureView>();
|
|
194
|
+
// resolveTarget shares the same view narrow alignment.
|
|
195
|
+
expectTypeOf<
|
|
196
|
+
NonNullable<RenderPassColorAttachment['resolveTarget']>
|
|
197
|
+
>().toEqualTypeOf<TextureView>();
|
|
198
|
+
// Other fields keep spec types.
|
|
199
|
+
expectTypeOf<ValueOf<RenderPassColorAttachment, 'loadOp'>>().toEqualTypeOf<
|
|
200
|
+
ValueOf<GPURenderPassColorAttachment, 'loadOp'>
|
|
201
|
+
>();
|
|
202
|
+
expectTypeOf<ValueOf<RenderPassColorAttachment, 'storeOp'>>().toEqualTypeOf<
|
|
203
|
+
ValueOf<GPURenderPassColorAttachment, 'storeOp'>
|
|
204
|
+
>();
|
|
205
|
+
expectTypeOf<ValueOf<RenderPassColorAttachment, 'clearValue'>>().toEqualTypeOf<
|
|
206
|
+
ValueOf<GPURenderPassColorAttachment, 'clearValue'>
|
|
207
|
+
>();
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('RenderPassDepthStencilAttachment field set === Pick + view aligned to TextureView (view narrow Path X)', () => {
|
|
211
|
+
type DsKeys =
|
|
212
|
+
| 'view'
|
|
213
|
+
| 'depthClearValue'
|
|
214
|
+
| 'depthLoadOp'
|
|
215
|
+
| 'depthStoreOp'
|
|
216
|
+
| 'depthReadOnly'
|
|
217
|
+
| 'stencilClearValue'
|
|
218
|
+
| 'stencilLoadOp'
|
|
219
|
+
| 'stencilStoreOp'
|
|
220
|
+
| 'stencilReadOnly';
|
|
221
|
+
expectTypeOf<keyof RenderPassDepthStencilAttachment>().toEqualTypeOf<DsKeys>();
|
|
222
|
+
expectTypeOf<RenderPassDepthStencilAttachment['view']>().toEqualTypeOf<TextureView>();
|
|
223
|
+
expectTypeOf<ValueOf<RenderPassDepthStencilAttachment, 'depthClearValue'>>().toEqualTypeOf<
|
|
224
|
+
ValueOf<GPURenderPassDepthStencilAttachment, 'depthClearValue'>
|
|
225
|
+
>();
|
|
226
|
+
expectTypeOf<ValueOf<RenderPassDepthStencilAttachment, 'depthLoadOp'>>().toEqualTypeOf<
|
|
227
|
+
ValueOf<GPURenderPassDepthStencilAttachment, 'depthLoadOp'>
|
|
228
|
+
>();
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// w40 (M5) - BufferDescriptor.mappedAtCreation mirror extension. Pairs
|
|
232
|
+
// with K-7 (mappedAtCreation passthrough fix) by locking the type-level
|
|
233
|
+
// contract: forgeax field is `boolean | undefined` (forgeax
|
|
234
|
+
// ExplicitUndefined<...>) and the underlying value type matches
|
|
235
|
+
// @webgpu/types.GPUBufferDescriptor.mappedAtCreation byte-for-byte.
|
|
236
|
+
// Anchors: requirements §IN-5 F-4 / AC-05; research §4.3 +
|
|
237
|
+
// plan-strategy §4.2 type layer + K-10.
|
|
238
|
+
it('BufferDescriptor.mappedAtCreation field is `boolean | undefined` and matches GPUBufferDescriptor.mappedAtCreation', () => {
|
|
239
|
+
// Field is part of the Pick<> set (already covered above; re-anchor here
|
|
240
|
+
// so a future Pick<> edit that drops the field is caught by w40 too).
|
|
241
|
+
type HasMappedAtCreation = 'mappedAtCreation' extends keyof BufferDescriptor ? true : false;
|
|
242
|
+
expectTypeOf<HasMappedAtCreation>().toEqualTypeOf<true>();
|
|
243
|
+
// Underlying value type aligns with the spec.
|
|
244
|
+
expectTypeOf<ValueOf<BufferDescriptor, 'mappedAtCreation'>>().toEqualTypeOf<
|
|
245
|
+
ValueOf<GPUBufferDescriptor, 'mappedAtCreation'>
|
|
246
|
+
>();
|
|
247
|
+
// ExplicitUndefined<...> shape: the forgeax form accepts `boolean |
|
|
248
|
+
// undefined` writes (S-7 distinguishes missing vs explicit-undefined).
|
|
249
|
+
const m1: BufferDescriptor = { size: 16, usage: 0, mappedAtCreation: true };
|
|
250
|
+
expectTypeOf(m1).toMatchTypeOf<BufferDescriptor>();
|
|
251
|
+
const m2: BufferDescriptor = { size: 16, usage: 0, mappedAtCreation: false };
|
|
252
|
+
expectTypeOf(m2).toMatchTypeOf<BufferDescriptor>();
|
|
253
|
+
const m3: BufferDescriptor = { size: 16, usage: 0, mappedAtCreation: undefined };
|
|
254
|
+
expectTypeOf(m3).toMatchTypeOf<BufferDescriptor>();
|
|
255
|
+
const m4: BufferDescriptor = { size: 16, usage: 0 };
|
|
256
|
+
expectTypeOf(m4).toMatchTypeOf<BufferDescriptor>();
|
|
257
|
+
// Anti-form: passing a non-boolean must NOT type-check (commented out;
|
|
258
|
+
// uncommenting must turn tsc red, not throw at runtime):
|
|
259
|
+
// const bad: BufferDescriptor = { size: 16, usage: 0, mappedAtCreation: 1 as never };
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('RenderPassDescriptor field set === Pick<GPURenderPassDescriptor, ...> (label inherited)', () => {
|
|
263
|
+
type RpKeys =
|
|
264
|
+
| 'label'
|
|
265
|
+
| 'colorAttachments'
|
|
266
|
+
| 'depthStencilAttachment'
|
|
267
|
+
| 'occlusionQuerySet'
|
|
268
|
+
| 'timestampWrites'
|
|
269
|
+
| 'maxDrawCount';
|
|
270
|
+
expectTypeOf<keyof RenderPassDescriptor>().toEqualTypeOf<RpKeys>();
|
|
271
|
+
// depthStencilAttachment uses the tightened type (not spec).
|
|
272
|
+
expectTypeOf<
|
|
273
|
+
NonNullable<RenderPassDescriptor['depthStencilAttachment']>
|
|
274
|
+
>().toEqualTypeOf<RenderPassDepthStencilAttachment>();
|
|
275
|
+
// timestampWrites uses the forgeax QuerySet brand; maxDrawCount keeps the
|
|
276
|
+
// spec type.
|
|
277
|
+
expectTypeOf<
|
|
278
|
+
ValueOf<RenderPassDescriptor, 'timestampWrites'>
|
|
279
|
+
>().toEqualTypeOf<RenderPassTimestampWrites>();
|
|
280
|
+
expectTypeOf<ValueOf<RenderPassDescriptor, 'maxDrawCount'>>().toEqualTypeOf<
|
|
281
|
+
ValueOf<GPURenderPassDescriptor, 'maxDrawCount'>
|
|
282
|
+
>();
|
|
283
|
+
});
|
|
284
|
+
});
|