@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/dist/index.mjs
CHANGED
|
@@ -57,7 +57,7 @@ function resolutionError(label, cause) {
|
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
var CompiledRenderGraphImpl = class {
|
|
60
|
-
constructor(generation, owner, device, resources, views, passes, info) {
|
|
60
|
+
constructor(generation, owner, device, resources, views, passes, info, colorTargetDescriptors) {
|
|
61
61
|
this.generation = generation;
|
|
62
62
|
this.owner = owner;
|
|
63
63
|
this.device = device;
|
|
@@ -65,6 +65,7 @@ var CompiledRenderGraphImpl = class {
|
|
|
65
65
|
this.views = views;
|
|
66
66
|
this.passes = passes;
|
|
67
67
|
this.info = info;
|
|
68
|
+
this.colorTargetDescriptors = colorTargetDescriptors;
|
|
68
69
|
}
|
|
69
70
|
generation;
|
|
70
71
|
owner;
|
|
@@ -73,11 +74,28 @@ var CompiledRenderGraphImpl = class {
|
|
|
73
74
|
views;
|
|
74
75
|
passes;
|
|
75
76
|
info;
|
|
77
|
+
colorTargetDescriptors;
|
|
76
78
|
retired = false;
|
|
77
79
|
retireResult;
|
|
78
80
|
inspect() {
|
|
79
81
|
return this.info;
|
|
80
82
|
}
|
|
83
|
+
getColorTargetView(name) {
|
|
84
|
+
for (const view of this.views.values()) {
|
|
85
|
+
const resource = this.resources.get(view.record.textureId);
|
|
86
|
+
if (resource?.record.label === name) return view.view;
|
|
87
|
+
}
|
|
88
|
+
return void 0;
|
|
89
|
+
}
|
|
90
|
+
getColorTargetTexture(name) {
|
|
91
|
+
for (const resource of this.resources.values()) {
|
|
92
|
+
if (resource.record.label === name) return resource.texture;
|
|
93
|
+
}
|
|
94
|
+
return void 0;
|
|
95
|
+
}
|
|
96
|
+
getColorTargetDescriptor(name) {
|
|
97
|
+
return this.colorTargetDescriptors.get(name);
|
|
98
|
+
}
|
|
81
99
|
execute(frame, runPass) {
|
|
82
100
|
if (this.retired) {
|
|
83
101
|
return err(
|
|
@@ -346,6 +364,109 @@ var CompiledRenderGraphImpl = class {
|
|
|
346
364
|
}
|
|
347
365
|
};
|
|
348
366
|
|
|
367
|
+
// src/resource-registry.ts
|
|
368
|
+
function isTextureViewDimensionCompatible(allocationDimension, viewDimension) {
|
|
369
|
+
if (viewDimension === void 0) return true;
|
|
370
|
+
if (allocationDimension === "3d") return viewDimension === "3d";
|
|
371
|
+
return viewDimension !== "3d";
|
|
372
|
+
}
|
|
373
|
+
var ResourceRegistry = class {
|
|
374
|
+
resources = /* @__PURE__ */ new Map();
|
|
375
|
+
add(key, descriptor) {
|
|
376
|
+
const entry = {
|
|
377
|
+
key,
|
|
378
|
+
descriptor,
|
|
379
|
+
lifetime: descriptor.lifetime
|
|
380
|
+
};
|
|
381
|
+
return this.register(entry);
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Register a color target resource (D-8).
|
|
385
|
+
* Same semantics as addResource with kind:'texture' plus GPU texture
|
|
386
|
+
* allocation metadata. Existing callers default to a transient target.
|
|
387
|
+
*/
|
|
388
|
+
addColorTarget(name, desc) {
|
|
389
|
+
const lifetime = desc.lifetime ?? "transient";
|
|
390
|
+
const colorTargetMeta = {
|
|
391
|
+
format: desc.format,
|
|
392
|
+
size: desc.size,
|
|
393
|
+
sample: desc.sample ?? 1,
|
|
394
|
+
usage: desc.usage ?? 16 | 4,
|
|
395
|
+
// RENDER_ATTACHMENT | TEXTURE_BINDING
|
|
396
|
+
...desc.domain !== void 0 ? { domain: desc.domain } : {},
|
|
397
|
+
...desc.viewFormats !== void 0 ? { viewFormats: desc.viewFormats } : {}
|
|
398
|
+
};
|
|
399
|
+
const entry = {
|
|
400
|
+
key: name,
|
|
401
|
+
descriptor: { kind: "texture", lifetime },
|
|
402
|
+
lifetime,
|
|
403
|
+
colorTarget: colorTargetMeta
|
|
404
|
+
};
|
|
405
|
+
return this.register(entry);
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Register a color target alias that folds into the source's physical
|
|
409
|
+
* texture at compile time (KB-1 MoveNode pattern, D-2).
|
|
410
|
+
* The source must already be registered via addColorTarget.
|
|
411
|
+
*/
|
|
412
|
+
addColorTargetAlias(name, source) {
|
|
413
|
+
if (this.resources.has(name)) return this.duplicateResource(name);
|
|
414
|
+
const sourceMeta = this.resources.get(source)?.colorTarget;
|
|
415
|
+
if (sourceMeta === void 0) {
|
|
416
|
+
return err(
|
|
417
|
+
new RenderGraphError({
|
|
418
|
+
code: "alias-source-missing",
|
|
419
|
+
expected: `alias '${name}' source '${source}' must be a registered color target`,
|
|
420
|
+
hint: `call addColorTarget('${source}', ...) before retrying alias '${name}'`,
|
|
421
|
+
detail: { aliasKey: name, sourceKey: source }
|
|
422
|
+
})
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
const entry = {
|
|
426
|
+
key: name,
|
|
427
|
+
descriptor: { kind: "texture", lifetime: "transient" },
|
|
428
|
+
lifetime: "transient",
|
|
429
|
+
colorTarget: {
|
|
430
|
+
format: sourceMeta.format,
|
|
431
|
+
size: sourceMeta.size,
|
|
432
|
+
sample: sourceMeta.sample,
|
|
433
|
+
usage: sourceMeta.usage,
|
|
434
|
+
...sourceMeta.domain !== void 0 ? { domain: sourceMeta.domain } : {},
|
|
435
|
+
...sourceMeta.viewFormats !== void 0 ? { viewFormats: sourceMeta.viewFormats } : {},
|
|
436
|
+
aliasedFrom: source
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
return this.register(entry);
|
|
440
|
+
}
|
|
441
|
+
duplicateResource(key) {
|
|
442
|
+
return err(
|
|
443
|
+
new RenderGraphError({
|
|
444
|
+
code: "duplicate-resource",
|
|
445
|
+
expected: `resource key '${key}' registered exactly once`,
|
|
446
|
+
hint: `remove the duplicate resource declaration for '${key}' or use a different key`,
|
|
447
|
+
detail: { resourceKey: key }
|
|
448
|
+
})
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
register(entry) {
|
|
452
|
+
if (this.resources.has(entry.key)) return this.duplicateResource(entry.key);
|
|
453
|
+
this.resources.set(entry.key, entry);
|
|
454
|
+
return ok(entry);
|
|
455
|
+
}
|
|
456
|
+
get(key) {
|
|
457
|
+
return this.resources.get(key);
|
|
458
|
+
}
|
|
459
|
+
getColorTargetMeta(key) {
|
|
460
|
+
return this.resources.get(key)?.colorTarget;
|
|
461
|
+
}
|
|
462
|
+
has(key) {
|
|
463
|
+
return this.resources.has(key);
|
|
464
|
+
}
|
|
465
|
+
entries() {
|
|
466
|
+
return this.resources.values();
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
|
|
349
470
|
// src/builder.ts
|
|
350
471
|
var BUFFER_USAGE = {
|
|
351
472
|
copySrc: 4,
|
|
@@ -364,6 +485,21 @@ var TEXTURE_USAGE = {
|
|
|
364
485
|
renderAttachment: 16
|
|
365
486
|
};
|
|
366
487
|
var nextGeneration = 1;
|
|
488
|
+
function textureByteSize(format, extent, mipLevelCount) {
|
|
489
|
+
const bytesPerTexel = format === "r8unorm" || format === "r8snorm" || format === "r8uint" || format === "r8sint" ? 1 : format === "rg8unorm" || format === "rg8snorm" || format === "rg8uint" || format === "rg8sint" ? 2 : format === "rgba8unorm" || format === "rgba8unorm-srgb" || format === "rgba8snorm" || format === "rgba8uint" || format === "rgba8sint" || format === "r32float" || format === "r32uint" || format === "r32sint" ? 4 : format === "rg16float" || format === "rg16uint" || format === "rg16sint" || format === "rg16snorm" || format === "rg16unorm" ? 4 : format === "rgba16float" || format === "rgba16uint" || format === "rgba16sint" || format === "rgba16snorm" || format === "rgba16unorm" || format === "rg32float" || format === "rg32uint" || format === "rg32sint" ? 8 : format === "rgba32float" || format === "rgba32uint" || format === "rgba32sint" ? 16 : void 0;
|
|
490
|
+
if (bytesPerTexel === void 0) return void 0;
|
|
491
|
+
let bytes = 0;
|
|
492
|
+
let width = extent.width;
|
|
493
|
+
let height = extent.height;
|
|
494
|
+
let depth = extent.depthOrArrayLayers;
|
|
495
|
+
for (let level = 0; level < mipLevelCount; level += 1) {
|
|
496
|
+
bytes += width * height * depth * bytesPerTexel;
|
|
497
|
+
width = Math.max(1, Math.floor(width / 2));
|
|
498
|
+
height = Math.max(1, Math.floor(height / 2));
|
|
499
|
+
depth = Math.max(1, Math.floor(depth / 2));
|
|
500
|
+
}
|
|
501
|
+
return bytes;
|
|
502
|
+
}
|
|
367
503
|
function bufferUsage(access) {
|
|
368
504
|
switch (access) {
|
|
369
505
|
case "uniform-read":
|
|
@@ -392,6 +528,8 @@ function textureUsage(access) {
|
|
|
392
528
|
case "storage-write":
|
|
393
529
|
case "storage-read-write":
|
|
394
530
|
return TEXTURE_USAGE.storageBinding;
|
|
531
|
+
case "sampled-storage-read-write":
|
|
532
|
+
return TEXTURE_USAGE.storageBinding | TEXTURE_USAGE.textureBinding;
|
|
395
533
|
case "color-attachment":
|
|
396
534
|
case "depth-stencil-read":
|
|
397
535
|
case "depth-stencil-write":
|
|
@@ -405,6 +543,7 @@ function textureUsage(access) {
|
|
|
405
543
|
function accessMode(access) {
|
|
406
544
|
switch (access) {
|
|
407
545
|
case "storage-read-write":
|
|
546
|
+
case "sampled-storage-read-write":
|
|
408
547
|
return { read: true, write: true };
|
|
409
548
|
case "storage-write":
|
|
410
549
|
case "color-attachment":
|
|
@@ -421,7 +560,13 @@ function rangesOverlap(left, right) {
|
|
|
421
560
|
return aspectOverlap && left.mipStart < right.mipEnd && right.mipStart < left.mipEnd && left.layerStart < right.layerEnd && right.layerStart < left.layerEnd;
|
|
422
561
|
}
|
|
423
562
|
function freezeInfo(info) {
|
|
563
|
+
const freezeDescriptor = (descriptor) => {
|
|
564
|
+
if (descriptor.kind !== "texture") return Object.freeze({ ...descriptor });
|
|
565
|
+
const size = typeof descriptor.size === "string" ? descriptor.size : Object.freeze({ ...descriptor.size });
|
|
566
|
+
return Object.freeze({ ...descriptor, size });
|
|
567
|
+
};
|
|
424
568
|
return Object.freeze({
|
|
569
|
+
generation: info.generation,
|
|
425
570
|
passes: Object.freeze(
|
|
426
571
|
info.passes.map(
|
|
427
572
|
(pass) => Object.freeze({
|
|
@@ -431,7 +576,14 @@ function freezeInfo(info) {
|
|
|
431
576
|
})
|
|
432
577
|
)
|
|
433
578
|
),
|
|
434
|
-
resources: Object.freeze(
|
|
579
|
+
resources: Object.freeze(
|
|
580
|
+
info.resources.map(
|
|
581
|
+
(resource) => Object.freeze({
|
|
582
|
+
...resource,
|
|
583
|
+
descriptor: freezeDescriptor(resource.descriptor)
|
|
584
|
+
})
|
|
585
|
+
)
|
|
586
|
+
)
|
|
435
587
|
});
|
|
436
588
|
}
|
|
437
589
|
var RenderGraphBuilder = class {
|
|
@@ -563,7 +715,20 @@ var RenderGraphBuilder = class {
|
|
|
563
715
|
analyzed.value.lastUseByResource
|
|
564
716
|
);
|
|
565
717
|
if (!allocated.ok) return allocated;
|
|
718
|
+
const generation = nextGeneration;
|
|
719
|
+
const physicalAllocationKeys = /* @__PURE__ */ new WeakMap();
|
|
720
|
+
let nextPhysicalAllocationKey = 1;
|
|
721
|
+
const physicalKey = (resource) => {
|
|
722
|
+
const handle = resource.texture ?? resource.buffer;
|
|
723
|
+
if (handle === void 0) return void 0;
|
|
724
|
+
const existing = physicalAllocationKeys.get(handle);
|
|
725
|
+
if (existing !== void 0) return existing;
|
|
726
|
+
const key = `allocation-${nextPhysicalAllocationKey++}`;
|
|
727
|
+
physicalAllocationKeys.set(handle, key);
|
|
728
|
+
return key;
|
|
729
|
+
};
|
|
566
730
|
const info = freezeInfo({
|
|
731
|
+
generation,
|
|
567
732
|
passes: analyzed.value.passes.map((pass, executionIndex) => ({
|
|
568
733
|
name: pass.name,
|
|
569
734
|
kind: pass.pass.kind,
|
|
@@ -579,15 +744,60 @@ var RenderGraphBuilder = class {
|
|
|
579
744
|
(dependency) => analyzed.value.passes[dependency]?.name ?? "unknown"
|
|
580
745
|
)
|
|
581
746
|
})),
|
|
582
|
-
resources: [...allocated.value.resources.values()].map((resource) =>
|
|
583
|
-
|
|
584
|
-
kind
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
747
|
+
resources: [...allocated.value.resources.values()].map((resource) => {
|
|
748
|
+
const texture = resource.record.kind === "texture" ? resource.record.descriptor : void 0;
|
|
749
|
+
const buffer = resource.record.kind === "buffer" ? resource.record.descriptor : void 0;
|
|
750
|
+
const allocationKey = physicalKey(resource);
|
|
751
|
+
const extent = texture === void 0 ? void 0 : this.resolveExtent(texture.size, options.surfaceSize);
|
|
752
|
+
return {
|
|
753
|
+
label: resource.record.label,
|
|
754
|
+
kind: resource.record.kind,
|
|
755
|
+
origin: resource.record.origin,
|
|
756
|
+
descriptor: texture === void 0 ? {
|
|
757
|
+
kind: "buffer",
|
|
758
|
+
size: buffer?.size ?? 0
|
|
759
|
+
} : {
|
|
760
|
+
kind: "texture",
|
|
761
|
+
format: texture.format,
|
|
762
|
+
...texture.domain === void 0 ? {} : { domain: texture.domain },
|
|
763
|
+
size: texture.size,
|
|
764
|
+
width: extent?.width ?? 1,
|
|
765
|
+
height: extent?.height ?? 1,
|
|
766
|
+
depthOrArrayLayers: extent?.depthOrArrayLayers ?? 1,
|
|
767
|
+
mipLevelCount: texture.mipLevelCount ?? 1,
|
|
768
|
+
sampleCount: texture.sampleCount ?? 1
|
|
769
|
+
},
|
|
770
|
+
firstUse: resource.firstUse,
|
|
771
|
+
lastUse: resource.lastUse,
|
|
772
|
+
derivedUsage: resource.usage,
|
|
773
|
+
...allocationKey === void 0 ? {} : { physicalAllocationKey: allocationKey },
|
|
774
|
+
...texture === void 0 ? {} : { format: texture.format },
|
|
775
|
+
...texture === void 0 ? buffer === void 0 ? {} : { byteSize: buffer.size } : {
|
|
776
|
+
byteSize: textureByteSize(
|
|
777
|
+
texture.format,
|
|
778
|
+
this.resolveExtent(texture.size, options.surfaceSize),
|
|
779
|
+
texture.mipLevelCount ?? 1
|
|
780
|
+
)
|
|
781
|
+
},
|
|
782
|
+
...texture === void 0 ? {} : {
|
|
783
|
+
dimension: texture.dimension ?? "2d",
|
|
784
|
+
extent
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
})
|
|
590
788
|
});
|
|
789
|
+
const colorTargetDescriptors = /* @__PURE__ */ new Map();
|
|
790
|
+
for (const resource of allocated.value.resources.values()) {
|
|
791
|
+
if (resource.record.kind !== "texture" || resource.texture === void 0) continue;
|
|
792
|
+
const extent = this.resolveExtent(resource.record.descriptor.size, options.surfaceSize);
|
|
793
|
+
colorTargetDescriptors.set(resource.record.label, {
|
|
794
|
+
texture: resource.texture,
|
|
795
|
+
format: resource.record.descriptor.format,
|
|
796
|
+
size: { width: extent.width, height: extent.height },
|
|
797
|
+
usage: resource.usage,
|
|
798
|
+
sample: resource.record.descriptor.sampleCount ?? 1
|
|
799
|
+
});
|
|
800
|
+
}
|
|
591
801
|
return ok(
|
|
592
802
|
new CompiledRenderGraphImpl(
|
|
593
803
|
nextGeneration++,
|
|
@@ -596,7 +806,8 @@ var RenderGraphBuilder = class {
|
|
|
596
806
|
allocated.value.resources,
|
|
597
807
|
allocated.value.views,
|
|
598
808
|
Object.freeze(analyzed.value.passes),
|
|
599
|
-
info
|
|
809
|
+
info,
|
|
810
|
+
colorTargetDescriptors
|
|
600
811
|
)
|
|
601
812
|
);
|
|
602
813
|
}
|
|
@@ -641,6 +852,11 @@ var RenderGraphBuilder = class {
|
|
|
641
852
|
const lastUseByResource = /* @__PURE__ */ new Map();
|
|
642
853
|
const compiledPasses = [];
|
|
643
854
|
const history = [];
|
|
855
|
+
for (const resource of this.resources.values()) {
|
|
856
|
+
if (resource.kind !== "texture" || resource.origin !== "created") continue;
|
|
857
|
+
const usage = resource.descriptor.usage ?? 0;
|
|
858
|
+
if (usage !== 0) usageByResource.set(resource.id, usage);
|
|
859
|
+
}
|
|
644
860
|
for (let passIndex = 0; passIndex < this.passes.length; passIndex++) {
|
|
645
861
|
const pass = this.passes[passIndex];
|
|
646
862
|
if (pass === void 0) continue;
|
|
@@ -822,7 +1038,8 @@ var RenderGraphBuilder = class {
|
|
|
822
1038
|
continue;
|
|
823
1039
|
}
|
|
824
1040
|
if (!left.write && !right.write) continue;
|
|
825
|
-
if (left.usage === right.usage && left.usage === "storage-read-write")
|
|
1041
|
+
if (left.usage === right.usage && (left.usage === "storage-read-write" || left.usage === "sampled-storage-read-write"))
|
|
1042
|
+
continue;
|
|
826
1043
|
const label = this.resources.get(left.resourceId)?.label;
|
|
827
1044
|
return err(
|
|
828
1045
|
new RenderGraphError({
|
|
@@ -966,6 +1183,27 @@ var RenderGraphBuilder = class {
|
|
|
966
1183
|
}
|
|
967
1184
|
}
|
|
968
1185
|
}
|
|
1186
|
+
for (const view of this.views.values()) {
|
|
1187
|
+
const texture = this.resources.get(view.textureId);
|
|
1188
|
+
if (texture?.kind !== "texture") continue;
|
|
1189
|
+
const allocationDimension = texture.descriptor.dimension ?? "2d";
|
|
1190
|
+
const viewDimension = view.descriptor.dimension;
|
|
1191
|
+
if (!isTextureViewDimensionCompatible(allocationDimension, viewDimension)) {
|
|
1192
|
+
return err(
|
|
1193
|
+
new RenderGraphError({
|
|
1194
|
+
code: "resource-descriptor-invalid",
|
|
1195
|
+
expected: `texture '${texture.label}' view dimension matches allocation dimension`,
|
|
1196
|
+
hint: "use a 3d view only for a 3d allocation and preserve array views on 2d allocations",
|
|
1197
|
+
detail: {
|
|
1198
|
+
resourceLabel: texture.label,
|
|
1199
|
+
field: "dimension",
|
|
1200
|
+
expected: allocationDimension,
|
|
1201
|
+
actual: viewDimension ?? "2d"
|
|
1202
|
+
}
|
|
1203
|
+
})
|
|
1204
|
+
);
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
969
1207
|
return ok(void 0);
|
|
970
1208
|
}
|
|
971
1209
|
allocate(options, usageByResource, firstUseByResource, lastUseByResource) {
|
|
@@ -1299,104 +1537,6 @@ function validateColorDomainConnection(source, destination, conversion) {
|
|
|
1299
1537
|
};
|
|
1300
1538
|
}
|
|
1301
1539
|
|
|
1302
|
-
// src/resource-registry.ts
|
|
1303
|
-
var ResourceRegistry = class {
|
|
1304
|
-
resources = /* @__PURE__ */ new Map();
|
|
1305
|
-
add(key, descriptor) {
|
|
1306
|
-
const entry = {
|
|
1307
|
-
key,
|
|
1308
|
-
descriptor,
|
|
1309
|
-
lifetime: descriptor.lifetime
|
|
1310
|
-
};
|
|
1311
|
-
return this.register(entry);
|
|
1312
|
-
}
|
|
1313
|
-
/**
|
|
1314
|
-
* Register a color target resource (D-8).
|
|
1315
|
-
* Same semantics as addResource with kind:'texture' plus GPU texture
|
|
1316
|
-
* allocation metadata. Existing callers default to a transient target.
|
|
1317
|
-
*/
|
|
1318
|
-
addColorTarget(name, desc) {
|
|
1319
|
-
const lifetime = desc.lifetime ?? "transient";
|
|
1320
|
-
const colorTargetMeta = {
|
|
1321
|
-
format: desc.format,
|
|
1322
|
-
size: desc.size,
|
|
1323
|
-
sample: desc.sample ?? 1,
|
|
1324
|
-
usage: desc.usage ?? 16 | 4,
|
|
1325
|
-
// RENDER_ATTACHMENT | TEXTURE_BINDING
|
|
1326
|
-
...desc.domain !== void 0 ? { domain: desc.domain } : {},
|
|
1327
|
-
...desc.viewFormats !== void 0 ? { viewFormats: desc.viewFormats } : {}
|
|
1328
|
-
};
|
|
1329
|
-
const entry = {
|
|
1330
|
-
key: name,
|
|
1331
|
-
descriptor: { kind: "texture", lifetime },
|
|
1332
|
-
lifetime,
|
|
1333
|
-
colorTarget: colorTargetMeta
|
|
1334
|
-
};
|
|
1335
|
-
return this.register(entry);
|
|
1336
|
-
}
|
|
1337
|
-
/**
|
|
1338
|
-
* Register a color target alias that folds into the source's physical
|
|
1339
|
-
* texture at compile time (KB-1 MoveNode pattern, D-2).
|
|
1340
|
-
* The source must already be registered via addColorTarget.
|
|
1341
|
-
*/
|
|
1342
|
-
addColorTargetAlias(name, source) {
|
|
1343
|
-
if (this.resources.has(name)) return this.duplicateResource(name);
|
|
1344
|
-
const sourceMeta = this.resources.get(source)?.colorTarget;
|
|
1345
|
-
if (sourceMeta === void 0) {
|
|
1346
|
-
return err(
|
|
1347
|
-
new RenderGraphError({
|
|
1348
|
-
code: "alias-source-missing",
|
|
1349
|
-
expected: `alias '${name}' source '${source}' must be a registered color target`,
|
|
1350
|
-
hint: `call addColorTarget('${source}', ...) before retrying alias '${name}'`,
|
|
1351
|
-
detail: { aliasKey: name, sourceKey: source }
|
|
1352
|
-
})
|
|
1353
|
-
);
|
|
1354
|
-
}
|
|
1355
|
-
const entry = {
|
|
1356
|
-
key: name,
|
|
1357
|
-
descriptor: { kind: "texture", lifetime: "transient" },
|
|
1358
|
-
lifetime: "transient",
|
|
1359
|
-
colorTarget: {
|
|
1360
|
-
format: sourceMeta.format,
|
|
1361
|
-
size: sourceMeta.size,
|
|
1362
|
-
sample: sourceMeta.sample,
|
|
1363
|
-
usage: sourceMeta.usage,
|
|
1364
|
-
...sourceMeta.domain !== void 0 ? { domain: sourceMeta.domain } : {},
|
|
1365
|
-
...sourceMeta.viewFormats !== void 0 ? { viewFormats: sourceMeta.viewFormats } : {},
|
|
1366
|
-
aliasedFrom: source
|
|
1367
|
-
}
|
|
1368
|
-
};
|
|
1369
|
-
return this.register(entry);
|
|
1370
|
-
}
|
|
1371
|
-
duplicateResource(key) {
|
|
1372
|
-
return err(
|
|
1373
|
-
new RenderGraphError({
|
|
1374
|
-
code: "duplicate-resource",
|
|
1375
|
-
expected: `resource key '${key}' registered exactly once`,
|
|
1376
|
-
hint: `remove the duplicate resource declaration for '${key}' or use a different key`,
|
|
1377
|
-
detail: { resourceKey: key }
|
|
1378
|
-
})
|
|
1379
|
-
);
|
|
1380
|
-
}
|
|
1381
|
-
register(entry) {
|
|
1382
|
-
if (this.resources.has(entry.key)) return this.duplicateResource(entry.key);
|
|
1383
|
-
this.resources.set(entry.key, entry);
|
|
1384
|
-
return ok(entry);
|
|
1385
|
-
}
|
|
1386
|
-
get(key) {
|
|
1387
|
-
return this.resources.get(key);
|
|
1388
|
-
}
|
|
1389
|
-
getColorTargetMeta(key) {
|
|
1390
|
-
return this.resources.get(key)?.colorTarget;
|
|
1391
|
-
}
|
|
1392
|
-
has(key) {
|
|
1393
|
-
return this.resources.has(key);
|
|
1394
|
-
}
|
|
1395
|
-
entries() {
|
|
1396
|
-
return this.resources.values();
|
|
1397
|
-
}
|
|
1398
|
-
};
|
|
1399
|
-
|
|
1400
1540
|
// src/graph.ts
|
|
1401
1541
|
function poolKey(meta) {
|
|
1402
1542
|
return `${meta.format}:${meta.width}x${meta.height}:${meta.usage}:${meta.sample}:${JSON.stringify(meta.viewFormats)}`;
|