@forgeax/engine-render-graph 0.1.7 → 0.1.20
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/dist/.tsbuildinfo +1 -1
- package/dist/__tests__/texture-dimensions-lifecycle.unit.test.d.ts +2 -0
- package/dist/__tests__/texture-dimensions-lifecycle.unit.test.d.ts.map +1 -0
- package/dist/__tests__/texture-dimensions.unit.test.d.ts +2 -0
- package/dist/__tests__/texture-dimensions.unit.test.d.ts.map +1 -0
- package/dist/builder.d.ts.map +1 -1
- package/dist/compiled-graph.d.ts +7 -2
- package/dist/compiled-graph.d.ts.map +1 -1
- package/dist/errors.d.ts +14 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +250 -110
- package/dist/index.mjs.map +1 -1
- package/dist/resource-registry.d.ts +2 -0
- package/dist/resource-registry.d.ts.map +1 -1
- package/dist/types.d.ts +39 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/render-graph-builder.unit.test.ts +32 -1
- package/src/__tests__/render-graph.unit.test.ts +15 -0
- package/src/__tests__/texture-dimensions-lifecycle.unit.test.ts +95 -0
- package/src/__tests__/texture-dimensions.unit.test.ts +99 -0
- package/src/builder.ts +191 -10
- package/src/compiled-graph.ts +21 -0
- package/src/errors.ts +22 -0
- package/src/index.ts +4 -0
- package/src/pipeline/__tests__/color-value-domain.test.ts +8 -0
- package/src/resource-registry.ts +10 -0
- package/src/types.ts +40 -0
package/src/builder.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Buffer, RhiCaps, Texture, TextureView } from '@forgeax/engine-rhi';
|
|
2
2
|
import { CompiledRenderGraphImpl } from './compiled-graph.js';
|
|
3
3
|
import { err, ok, RenderGraphError, type Result } from './errors.js';
|
|
4
|
+
import type { ResolvedColorTargetDescriptor } from './graph.js';
|
|
4
5
|
import {
|
|
5
6
|
accessResourceId,
|
|
6
7
|
bufferHandle,
|
|
@@ -14,9 +15,11 @@ import {
|
|
|
14
15
|
textureHandle,
|
|
15
16
|
textureViewHandle,
|
|
16
17
|
} from './kernel-internal.js';
|
|
18
|
+
import { isTextureViewDimensionCompatible } from './resource-registry.js';
|
|
17
19
|
import type {
|
|
18
20
|
CompiledRenderGraph,
|
|
19
21
|
CompiledRenderGraphInfo,
|
|
22
|
+
CompiledResourceDescriptor,
|
|
20
23
|
ComputeGraphPass,
|
|
21
24
|
CopyGraphPass,
|
|
22
25
|
GraphAccess,
|
|
@@ -58,6 +61,60 @@ const TEXTURE_USAGE = {
|
|
|
58
61
|
|
|
59
62
|
let nextGeneration = 1;
|
|
60
63
|
|
|
64
|
+
function textureByteSize(
|
|
65
|
+
format: GPUTextureFormat,
|
|
66
|
+
extent: { readonly width: number; readonly height: number; readonly depthOrArrayLayers: number },
|
|
67
|
+
mipLevelCount: number,
|
|
68
|
+
): number | undefined {
|
|
69
|
+
const bytesPerTexel =
|
|
70
|
+
format === 'r8unorm' || format === 'r8snorm' || format === 'r8uint' || format === 'r8sint'
|
|
71
|
+
? 1
|
|
72
|
+
: format === 'rg8unorm' ||
|
|
73
|
+
format === 'rg8snorm' ||
|
|
74
|
+
format === 'rg8uint' ||
|
|
75
|
+
format === 'rg8sint'
|
|
76
|
+
? 2
|
|
77
|
+
: format === 'rgba8unorm' ||
|
|
78
|
+
format === 'rgba8unorm-srgb' ||
|
|
79
|
+
format === 'rgba8snorm' ||
|
|
80
|
+
format === 'rgba8uint' ||
|
|
81
|
+
format === 'rgba8sint' ||
|
|
82
|
+
format === 'r32float' ||
|
|
83
|
+
format === 'r32uint' ||
|
|
84
|
+
format === 'r32sint'
|
|
85
|
+
? 4
|
|
86
|
+
: format === 'rg16float' ||
|
|
87
|
+
format === 'rg16uint' ||
|
|
88
|
+
format === 'rg16sint' ||
|
|
89
|
+
format === 'rg16snorm' ||
|
|
90
|
+
format === 'rg16unorm'
|
|
91
|
+
? 4
|
|
92
|
+
: format === 'rgba16float' ||
|
|
93
|
+
format === 'rgba16uint' ||
|
|
94
|
+
format === 'rgba16sint' ||
|
|
95
|
+
format === 'rgba16snorm' ||
|
|
96
|
+
format === 'rgba16unorm' ||
|
|
97
|
+
format === 'rg32float' ||
|
|
98
|
+
format === 'rg32uint' ||
|
|
99
|
+
format === 'rg32sint'
|
|
100
|
+
? 8
|
|
101
|
+
: format === 'rgba32float' || format === 'rgba32uint' || format === 'rgba32sint'
|
|
102
|
+
? 16
|
|
103
|
+
: undefined;
|
|
104
|
+
if (bytesPerTexel === undefined) return undefined;
|
|
105
|
+
let bytes = 0;
|
|
106
|
+
let width = extent.width;
|
|
107
|
+
let height = extent.height;
|
|
108
|
+
let depth = extent.depthOrArrayLayers;
|
|
109
|
+
for (let level = 0; level < mipLevelCount; level += 1) {
|
|
110
|
+
bytes += width * height * depth * bytesPerTexel;
|
|
111
|
+
width = Math.max(1, Math.floor(width / 2));
|
|
112
|
+
height = Math.max(1, Math.floor(height / 2));
|
|
113
|
+
depth = Math.max(1, Math.floor(depth / 2));
|
|
114
|
+
}
|
|
115
|
+
return bytes;
|
|
116
|
+
}
|
|
117
|
+
|
|
61
118
|
interface NormalizedRange {
|
|
62
119
|
readonly mipStart: number;
|
|
63
120
|
readonly mipEnd: number;
|
|
@@ -106,6 +163,8 @@ function textureUsage(access: GraphTextureAccess): number {
|
|
|
106
163
|
case 'storage-write':
|
|
107
164
|
case 'storage-read-write':
|
|
108
165
|
return TEXTURE_USAGE.storageBinding;
|
|
166
|
+
case 'sampled-storage-read-write':
|
|
167
|
+
return TEXTURE_USAGE.storageBinding | TEXTURE_USAGE.textureBinding;
|
|
109
168
|
case 'color-attachment':
|
|
110
169
|
case 'depth-stencil-read':
|
|
111
170
|
case 'depth-stencil-write':
|
|
@@ -123,6 +182,7 @@ function accessMode(access: GraphBufferAccess | GraphTextureAccess): {
|
|
|
123
182
|
} {
|
|
124
183
|
switch (access) {
|
|
125
184
|
case 'storage-read-write':
|
|
185
|
+
case 'sampled-storage-read-write':
|
|
126
186
|
return { read: true, write: true };
|
|
127
187
|
case 'storage-write':
|
|
128
188
|
case 'color-attachment':
|
|
@@ -151,7 +211,14 @@ function rangesOverlap(
|
|
|
151
211
|
}
|
|
152
212
|
|
|
153
213
|
function freezeInfo(info: CompiledRenderGraphInfo): CompiledRenderGraphInfo {
|
|
214
|
+
const freezeDescriptor = (descriptor: CompiledResourceDescriptor) => {
|
|
215
|
+
if (descriptor.kind !== 'texture') return Object.freeze({ ...descriptor });
|
|
216
|
+
const size =
|
|
217
|
+
typeof descriptor.size === 'string' ? descriptor.size : Object.freeze({ ...descriptor.size });
|
|
218
|
+
return Object.freeze({ ...descriptor, size });
|
|
219
|
+
};
|
|
154
220
|
return Object.freeze({
|
|
221
|
+
generation: info.generation,
|
|
155
222
|
passes: Object.freeze(
|
|
156
223
|
info.passes.map((pass) =>
|
|
157
224
|
Object.freeze({
|
|
@@ -161,7 +228,14 @@ function freezeInfo(info: CompiledRenderGraphInfo): CompiledRenderGraphInfo {
|
|
|
161
228
|
}),
|
|
162
229
|
),
|
|
163
230
|
),
|
|
164
|
-
resources: Object.freeze(
|
|
231
|
+
resources: Object.freeze(
|
|
232
|
+
info.resources.map((resource) =>
|
|
233
|
+
Object.freeze({
|
|
234
|
+
...resource,
|
|
235
|
+
descriptor: freezeDescriptor(resource.descriptor),
|
|
236
|
+
}),
|
|
237
|
+
),
|
|
238
|
+
),
|
|
165
239
|
});
|
|
166
240
|
}
|
|
167
241
|
|
|
@@ -336,7 +410,20 @@ export class RenderGraphBuilder<FrameCtx extends RenderGraphFrame> {
|
|
|
336
410
|
);
|
|
337
411
|
if (!allocated.ok) return allocated;
|
|
338
412
|
|
|
413
|
+
const generation = nextGeneration;
|
|
414
|
+
const physicalAllocationKeys = new WeakMap<object, string>();
|
|
415
|
+
let nextPhysicalAllocationKey = 1;
|
|
416
|
+
const physicalKey = (resource: CompiledResource<FrameCtx>): string | undefined => {
|
|
417
|
+
const handle = resource.texture ?? resource.buffer;
|
|
418
|
+
if (handle === undefined) return undefined;
|
|
419
|
+
const existing = physicalAllocationKeys.get(handle);
|
|
420
|
+
if (existing !== undefined) return existing;
|
|
421
|
+
const key = `allocation-${nextPhysicalAllocationKey++}`;
|
|
422
|
+
physicalAllocationKeys.set(handle, key);
|
|
423
|
+
return key;
|
|
424
|
+
};
|
|
339
425
|
const info = freezeInfo({
|
|
426
|
+
generation,
|
|
340
427
|
passes: analyzed.value.passes.map((pass, executionIndex) => ({
|
|
341
428
|
name: pass.name,
|
|
342
429
|
kind: pass.pass.kind,
|
|
@@ -352,16 +439,78 @@ export class RenderGraphBuilder<FrameCtx extends RenderGraphFrame> {
|
|
|
352
439
|
(dependency) => analyzed.value.passes[dependency]?.name ?? 'unknown',
|
|
353
440
|
),
|
|
354
441
|
})),
|
|
355
|
-
resources: [...allocated.value.resources.values()].map((resource) =>
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
442
|
+
resources: [...allocated.value.resources.values()].map((resource) => {
|
|
443
|
+
const texture =
|
|
444
|
+
resource.record.kind === 'texture'
|
|
445
|
+
? (resource.record.descriptor as GraphTextureDescriptor)
|
|
446
|
+
: undefined;
|
|
447
|
+
const buffer =
|
|
448
|
+
resource.record.kind === 'buffer'
|
|
449
|
+
? (resource.record.descriptor as GraphBufferDescriptor)
|
|
450
|
+
: undefined;
|
|
451
|
+
const allocationKey = physicalKey(resource);
|
|
452
|
+
const extent =
|
|
453
|
+
texture === undefined ? undefined : this.resolveExtent(texture.size, options.surfaceSize);
|
|
454
|
+
return {
|
|
455
|
+
label: resource.record.label,
|
|
456
|
+
kind: resource.record.kind,
|
|
457
|
+
origin: resource.record.origin,
|
|
458
|
+
descriptor:
|
|
459
|
+
texture === undefined
|
|
460
|
+
? {
|
|
461
|
+
kind: 'buffer' as const,
|
|
462
|
+
size: buffer?.size ?? 0,
|
|
463
|
+
}
|
|
464
|
+
: {
|
|
465
|
+
kind: 'texture' as const,
|
|
466
|
+
format: texture.format,
|
|
467
|
+
...(texture.domain === undefined ? {} : { domain: texture.domain }),
|
|
468
|
+
size: texture.size,
|
|
469
|
+
width: extent?.width ?? 1,
|
|
470
|
+
height: extent?.height ?? 1,
|
|
471
|
+
depthOrArrayLayers: extent?.depthOrArrayLayers ?? 1,
|
|
472
|
+
mipLevelCount: texture.mipLevelCount ?? 1,
|
|
473
|
+
sampleCount: texture.sampleCount ?? 1,
|
|
474
|
+
},
|
|
475
|
+
firstUse: resource.firstUse,
|
|
476
|
+
lastUse: resource.lastUse,
|
|
477
|
+
derivedUsage: resource.usage,
|
|
478
|
+
...(allocationKey === undefined ? {} : { physicalAllocationKey: allocationKey }),
|
|
479
|
+
...(texture === undefined ? {} : { format: texture.format }),
|
|
480
|
+
...(texture === undefined
|
|
481
|
+
? buffer === undefined
|
|
482
|
+
? {}
|
|
483
|
+
: { byteSize: buffer.size }
|
|
484
|
+
: {
|
|
485
|
+
byteSize: textureByteSize(
|
|
486
|
+
texture.format,
|
|
487
|
+
this.resolveExtent(texture.size, options.surfaceSize),
|
|
488
|
+
texture.mipLevelCount ?? 1,
|
|
489
|
+
),
|
|
490
|
+
}),
|
|
491
|
+
...(texture === undefined
|
|
492
|
+
? {}
|
|
493
|
+
: {
|
|
494
|
+
dimension: texture.dimension ?? '2d',
|
|
495
|
+
extent,
|
|
496
|
+
}),
|
|
497
|
+
};
|
|
498
|
+
}),
|
|
363
499
|
});
|
|
364
500
|
|
|
501
|
+
const colorTargetDescriptors = new Map<string, ResolvedColorTargetDescriptor>();
|
|
502
|
+
for (const resource of allocated.value.resources.values()) {
|
|
503
|
+
if (resource.record.kind !== 'texture' || resource.texture === undefined) continue;
|
|
504
|
+
const extent = this.resolveExtent(resource.record.descriptor.size, options.surfaceSize);
|
|
505
|
+
colorTargetDescriptors.set(resource.record.label, {
|
|
506
|
+
texture: resource.texture,
|
|
507
|
+
format: resource.record.descriptor.format,
|
|
508
|
+
size: { width: extent.width, height: extent.height },
|
|
509
|
+
usage: resource.usage,
|
|
510
|
+
sample: resource.record.descriptor.sampleCount ?? 1,
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
|
|
365
514
|
return ok(
|
|
366
515
|
new CompiledRenderGraphImpl(
|
|
367
516
|
nextGeneration++,
|
|
@@ -371,6 +520,7 @@ export class RenderGraphBuilder<FrameCtx extends RenderGraphFrame> {
|
|
|
371
520
|
allocated.value.views,
|
|
372
521
|
Object.freeze(analyzed.value.passes),
|
|
373
522
|
info,
|
|
523
|
+
colorTargetDescriptors,
|
|
374
524
|
),
|
|
375
525
|
);
|
|
376
526
|
}
|
|
@@ -431,6 +581,12 @@ export class RenderGraphBuilder<FrameCtx extends RenderGraphFrame> {
|
|
|
431
581
|
const compiledPasses: CompiledPass<FrameCtx>[] = [];
|
|
432
582
|
const history: NormalizedAccess[] = [];
|
|
433
583
|
|
|
584
|
+
for (const resource of this.resources.values()) {
|
|
585
|
+
if (resource.kind !== 'texture' || resource.origin !== 'created') continue;
|
|
586
|
+
const usage = resource.descriptor.usage ?? 0;
|
|
587
|
+
if (usage !== 0) usageByResource.set(resource.id, usage);
|
|
588
|
+
}
|
|
589
|
+
|
|
434
590
|
for (let passIndex = 0; passIndex < this.passes.length; passIndex++) {
|
|
435
591
|
const pass = this.passes[passIndex];
|
|
436
592
|
if (pass === undefined) continue;
|
|
@@ -665,7 +821,11 @@ export class RenderGraphBuilder<FrameCtx extends RenderGraphFrame> {
|
|
|
665
821
|
continue;
|
|
666
822
|
}
|
|
667
823
|
if (!left.write && !right.write) continue;
|
|
668
|
-
if (
|
|
824
|
+
if (
|
|
825
|
+
left.usage === right.usage &&
|
|
826
|
+
(left.usage === 'storage-read-write' || left.usage === 'sampled-storage-read-write')
|
|
827
|
+
)
|
|
828
|
+
continue;
|
|
669
829
|
const label = this.resources.get(left.resourceId)?.label;
|
|
670
830
|
return err(
|
|
671
831
|
new RenderGraphError({
|
|
@@ -839,6 +999,27 @@ export class RenderGraphBuilder<FrameCtx extends RenderGraphFrame> {
|
|
|
839
999
|
}
|
|
840
1000
|
}
|
|
841
1001
|
}
|
|
1002
|
+
for (const view of this.views.values()) {
|
|
1003
|
+
const texture = this.resources.get(view.textureId);
|
|
1004
|
+
if (texture?.kind !== 'texture') continue;
|
|
1005
|
+
const allocationDimension = texture.descriptor.dimension ?? '2d';
|
|
1006
|
+
const viewDimension = view.descriptor.dimension;
|
|
1007
|
+
if (!isTextureViewDimensionCompatible(allocationDimension, viewDimension)) {
|
|
1008
|
+
return err(
|
|
1009
|
+
new RenderGraphError({
|
|
1010
|
+
code: 'resource-descriptor-invalid',
|
|
1011
|
+
expected: `texture '${texture.label}' view dimension matches allocation dimension`,
|
|
1012
|
+
hint: 'use a 3d view only for a 3d allocation and preserve array views on 2d allocations',
|
|
1013
|
+
detail: {
|
|
1014
|
+
resourceLabel: texture.label,
|
|
1015
|
+
field: 'dimension',
|
|
1016
|
+
expected: allocationDimension,
|
|
1017
|
+
actual: viewDimension ?? '2d',
|
|
1018
|
+
},
|
|
1019
|
+
}),
|
|
1020
|
+
);
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
842
1023
|
return ok(undefined);
|
|
843
1024
|
}
|
|
844
1025
|
|
package/src/compiled-graph.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
TextureView,
|
|
9
9
|
} from '@forgeax/engine-rhi';
|
|
10
10
|
import { err, ok, RenderGraphError, type Result } from './errors.js';
|
|
11
|
+
import type { ResolvedColorTargetDescriptor } from './graph.js';
|
|
11
12
|
import type {
|
|
12
13
|
CompiledPass,
|
|
13
14
|
CompiledResource,
|
|
@@ -55,12 +56,32 @@ export class CompiledRenderGraphImpl<FrameCtx extends RenderGraphFrame>
|
|
|
55
56
|
private readonly views: ReadonlyMap<number, CompiledView<FrameCtx>>,
|
|
56
57
|
private readonly passes: readonly CompiledPass<FrameCtx>[],
|
|
57
58
|
private readonly info: CompiledRenderGraphInfo,
|
|
59
|
+
private readonly colorTargetDescriptors: ReadonlyMap<string, ResolvedColorTargetDescriptor>,
|
|
58
60
|
) {}
|
|
59
61
|
|
|
60
62
|
inspect(): CompiledRenderGraphInfo {
|
|
61
63
|
return this.info;
|
|
62
64
|
}
|
|
63
65
|
|
|
66
|
+
getColorTargetView(name: string): TextureView | undefined {
|
|
67
|
+
for (const view of this.views.values()) {
|
|
68
|
+
const resource = this.resources.get(view.record.textureId);
|
|
69
|
+
if (resource?.record.label === name) return view.view;
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
getColorTargetTexture(name: string): Texture | undefined {
|
|
75
|
+
for (const resource of this.resources.values()) {
|
|
76
|
+
if (resource.record.label === name) return resource.texture;
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
getColorTargetDescriptor(name: string): ResolvedColorTargetDescriptor | undefined {
|
|
82
|
+
return this.colorTargetDescriptors.get(name);
|
|
83
|
+
}
|
|
84
|
+
|
|
64
85
|
execute(frame: FrameCtx, runPass?: RenderGraphPassRunner): Result<void, RenderGraphError> {
|
|
65
86
|
if (this.retired) {
|
|
66
87
|
return err(
|
package/src/errors.ts
CHANGED
|
@@ -66,6 +66,28 @@ export type RenderGraphErrorCode = keyof RenderGraphErrorDetailByCode;
|
|
|
66
66
|
/** Detail union projected from the private code-to-detail map. */
|
|
67
67
|
export type RenderGraphErrorDetail = RenderGraphErrorDetailByCode[RenderGraphErrorCode];
|
|
68
68
|
|
|
69
|
+
/** Failure stages for the shared float/output surface contract. */
|
|
70
|
+
export type SurfaceFailureKind =
|
|
71
|
+
| 'allocation'
|
|
72
|
+
| 'attachment'
|
|
73
|
+
| 'sampled-read'
|
|
74
|
+
| 'view-domain'
|
|
75
|
+
| 'raw-endpoint';
|
|
76
|
+
|
|
77
|
+
/** Closed error codes derived from the surface failure kinds. */
|
|
78
|
+
export type SurfaceFailureCode = `surface-${SurfaceFailureKind}-failed`;
|
|
79
|
+
|
|
80
|
+
/** Shared detail for graph and render-surface recovery. */
|
|
81
|
+
export interface RenderSurfaceFailureDetail {
|
|
82
|
+
readonly lane: string;
|
|
83
|
+
readonly stage: string;
|
|
84
|
+
readonly target: string;
|
|
85
|
+
readonly format: string;
|
|
86
|
+
readonly domain: string;
|
|
87
|
+
readonly endpoint: string;
|
|
88
|
+
readonly capability: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
69
91
|
/**
|
|
70
92
|
* Constructor arguments correlated by code. `detail` remains optional to
|
|
71
93
|
* preserve the existing envelope behavior for callers that only need the
|
package/src/index.ts
CHANGED
|
@@ -14,10 +14,13 @@ export {
|
|
|
14
14
|
RenderGraphError,
|
|
15
15
|
type RenderGraphErrorCode,
|
|
16
16
|
type RenderGraphErrorDetail,
|
|
17
|
+
type RenderSurfaceFailureDetail,
|
|
17
18
|
type ResourceAllocFailedDetail,
|
|
18
19
|
type Result,
|
|
19
20
|
type ResultErr,
|
|
20
21
|
type ResultOk,
|
|
22
|
+
type SurfaceFailureCode,
|
|
23
|
+
type SurfaceFailureKind,
|
|
21
24
|
} from './errors.js';
|
|
22
25
|
export type {
|
|
23
26
|
BufferRole,
|
|
@@ -65,6 +68,7 @@ export {
|
|
|
65
68
|
export type {
|
|
66
69
|
CompiledRenderGraph,
|
|
67
70
|
CompiledRenderGraphInfo,
|
|
71
|
+
CompiledResourceDescriptor,
|
|
68
72
|
ComputeGraphPass,
|
|
69
73
|
CopyGraphPass,
|
|
70
74
|
GraphAccess,
|
|
@@ -32,6 +32,14 @@ describe('ColorValueDomain', () => {
|
|
|
32
32
|
expect(JSON.parse(serializeColorResourceDescriptor(descriptor))).toEqual(descriptor);
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
+
it('keeps display-encoded as an explicit domain for float storage', () => {
|
|
36
|
+
const descriptor: ColorResourceDescriptor = {
|
|
37
|
+
domain: 'display-encoded',
|
|
38
|
+
format: 'rgba16float',
|
|
39
|
+
};
|
|
40
|
+
expect(JSON.parse(serializeColorResourceDescriptor(descriptor))).toEqual(descriptor);
|
|
41
|
+
});
|
|
42
|
+
|
|
35
43
|
it.each([undefined, 'gamma-magic'])('rejects a missing or unknown domain: %s', (value) => {
|
|
36
44
|
const result = deserializeColorValueDomain(value);
|
|
37
45
|
expect(result.ok).toBe(false);
|
package/src/resource-registry.ts
CHANGED
|
@@ -26,6 +26,16 @@ import type {
|
|
|
26
26
|
} from './graph.js';
|
|
27
27
|
import type { ColorValueDomain } from './pipeline/color-value-domain.js';
|
|
28
28
|
|
|
29
|
+
/** Check the only allocation/view dimension combinations supported by WebGPU. */
|
|
30
|
+
export function isTextureViewDimensionCompatible(
|
|
31
|
+
allocationDimension: GPUTextureDimension,
|
|
32
|
+
viewDimension: GPUTextureViewDimension | undefined,
|
|
33
|
+
): boolean {
|
|
34
|
+
if (viewDimension === undefined) return true;
|
|
35
|
+
if (allocationDimension === '3d') return viewDimension === '3d';
|
|
36
|
+
return viewDimension !== '3d';
|
|
37
|
+
}
|
|
38
|
+
|
|
29
39
|
/**
|
|
30
40
|
* Per-resource GPU allocation metadata carried through compile.
|
|
31
41
|
* When the resource was registered via addColorTarget, colorTarget
|
package/src/types.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
TextureView,
|
|
11
11
|
} from '@forgeax/engine-rhi';
|
|
12
12
|
import type { RenderGraphError, Result } from './errors.js';
|
|
13
|
+
import type { ColorValueDomain } from './pipeline/color-value-domain.js';
|
|
13
14
|
|
|
14
15
|
declare const graphTextureBrand: unique symbol;
|
|
15
16
|
declare const graphTextureViewBrand: unique symbol;
|
|
@@ -44,10 +45,14 @@ export type GraphExtent =
|
|
|
44
45
|
export interface GraphTextureDescriptor {
|
|
45
46
|
readonly format: TextureFormat;
|
|
46
47
|
readonly size: GraphExtent;
|
|
48
|
+
/** Additional RHI usage required by an external diagnostic consumer. */
|
|
49
|
+
readonly usage?: number | undefined;
|
|
47
50
|
readonly mipLevelCount?: number | undefined;
|
|
48
51
|
readonly sampleCount?: number | undefined;
|
|
49
52
|
readonly dimension?: GPUTextureDimension | undefined;
|
|
50
53
|
readonly viewFormats?: readonly TextureFormat[] | undefined;
|
|
54
|
+
/** Explicit semantic color domain; never inferred from the attachment format. */
|
|
55
|
+
readonly domain?: ColorValueDomain | undefined;
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
export interface ImportedTextureDescriptor extends GraphTextureDescriptor {
|
|
@@ -92,6 +97,7 @@ export type GraphTextureAccess =
|
|
|
92
97
|
| 'storage-read'
|
|
93
98
|
| 'storage-write'
|
|
94
99
|
| 'storage-read-write'
|
|
100
|
+
| 'sampled-storage-read-write'
|
|
95
101
|
| 'color-attachment'
|
|
96
102
|
| 'depth-stencil-read'
|
|
97
103
|
| 'depth-stencil-write'
|
|
@@ -174,6 +180,26 @@ export interface GraphAccessInfo {
|
|
|
174
180
|
readonly usage: GraphBufferAccess | GraphTextureAccess;
|
|
175
181
|
}
|
|
176
182
|
|
|
183
|
+
/** Detached physical allocation facts for one compiled graph resource. */
|
|
184
|
+
export type CompiledResourceDescriptor =
|
|
185
|
+
| {
|
|
186
|
+
readonly kind: 'texture';
|
|
187
|
+
readonly format: TextureFormat;
|
|
188
|
+
/** Explicit semantic color domain retained in detached graph facts. */
|
|
189
|
+
readonly domain?: ColorValueDomain | undefined;
|
|
190
|
+
/** Authored extent retained so consumers can validate the allocation contract. */
|
|
191
|
+
readonly size: GraphExtent;
|
|
192
|
+
readonly width: number;
|
|
193
|
+
readonly height: number;
|
|
194
|
+
readonly depthOrArrayLayers: number;
|
|
195
|
+
readonly mipLevelCount: number;
|
|
196
|
+
readonly sampleCount: number;
|
|
197
|
+
}
|
|
198
|
+
| {
|
|
199
|
+
readonly kind: 'buffer';
|
|
200
|
+
readonly size: number;
|
|
201
|
+
};
|
|
202
|
+
|
|
177
203
|
export interface CompiledRenderGraphInfo {
|
|
178
204
|
readonly passes: readonly {
|
|
179
205
|
readonly name: string;
|
|
@@ -186,10 +212,24 @@ export interface CompiledRenderGraphInfo {
|
|
|
186
212
|
readonly label: string;
|
|
187
213
|
readonly kind: GraphResourceKind;
|
|
188
214
|
readonly origin: GraphResourceOrigin;
|
|
215
|
+
readonly descriptor: CompiledResourceDescriptor;
|
|
189
216
|
readonly firstUse: number | null;
|
|
190
217
|
readonly lastUse: number | null;
|
|
191
218
|
readonly derivedUsage: number;
|
|
219
|
+
/** Stable compile-local identity of the physical RHI allocation. */
|
|
220
|
+
readonly physicalAllocationKey?: string | undefined;
|
|
221
|
+
/** Descriptor-derived byte size when the format is uncompressed and known. */
|
|
222
|
+
readonly byteSize?: number | undefined;
|
|
223
|
+
/** Texture format retained for producer-owned resource inspection. */
|
|
224
|
+
readonly format?: TextureFormat | undefined;
|
|
225
|
+
/** Texture allocation facts retained for capability/lifetime inspection. */
|
|
226
|
+
readonly dimension?: GPUTextureDimension | undefined;
|
|
227
|
+
readonly extent?:
|
|
228
|
+
| { readonly width: number; readonly height: number; readonly depthOrArrayLayers: number }
|
|
229
|
+
| undefined;
|
|
192
230
|
}[];
|
|
231
|
+
/** Monotonic graph generation assigned when this graph was compiled. */
|
|
232
|
+
readonly generation: number;
|
|
193
233
|
}
|
|
194
234
|
|
|
195
235
|
export interface RenderGraphFrame {
|