@plasius/gpu-renderer 0.1.7 → 0.1.8
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/CHANGELOG.md +22 -0
- package/README.md +23 -0
- package/dist/index.cjs +192 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +188 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.d.ts +96 -0
- package/src/index.js +218 -0
package/src/index.js
CHANGED
|
@@ -3,6 +3,106 @@ const DEFAULT_CANVAS_SELECTOR = "canvas[data-plasius-gpu-renderer]";
|
|
|
3
3
|
export const rendererDebugOwner = "renderer";
|
|
4
4
|
export const rendererWorkerQueueClass = "render";
|
|
5
5
|
export const defaultRendererWorkerProfile = "realtime";
|
|
6
|
+
export const rendererRepresentationBands = Object.freeze([
|
|
7
|
+
"near",
|
|
8
|
+
"mid",
|
|
9
|
+
"far",
|
|
10
|
+
"horizon",
|
|
11
|
+
]);
|
|
12
|
+
export const rendererAccelerationStructureUpdateClasses = Object.freeze([
|
|
13
|
+
"static",
|
|
14
|
+
"rigid-dynamic",
|
|
15
|
+
"deforming",
|
|
16
|
+
"proxy",
|
|
17
|
+
]);
|
|
18
|
+
export const rendererRayTracingStageOrder = Object.freeze([
|
|
19
|
+
"primaryVisibility",
|
|
20
|
+
"shadowAssist",
|
|
21
|
+
"opaqueFoundation",
|
|
22
|
+
"rtDirectLighting",
|
|
23
|
+
"rtReflections",
|
|
24
|
+
"rtGlobalIllumination",
|
|
25
|
+
"denoiseTemporal",
|
|
26
|
+
"transparents",
|
|
27
|
+
"composition",
|
|
28
|
+
"present",
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
const rendererRayTracingStageDefinitions = Object.freeze(
|
|
32
|
+
rendererRayTracingStageOrder.map((key, index) =>
|
|
33
|
+
Object.freeze({
|
|
34
|
+
key,
|
|
35
|
+
order: index + 1,
|
|
36
|
+
required: true,
|
|
37
|
+
description:
|
|
38
|
+
{
|
|
39
|
+
primaryVisibility: "Primary visibility and depth preparation.",
|
|
40
|
+
shadowAssist: "Shadow assist passes and regional shadow preparation.",
|
|
41
|
+
opaqueFoundation: "Main opaque foundation for shading and tracing inputs.",
|
|
42
|
+
rtDirectLighting: "Ray-traced direct lighting and premium shadows.",
|
|
43
|
+
rtReflections: "Ray-traced reflections for important surfaces.",
|
|
44
|
+
rtGlobalIllumination: "Selective ray-traced indirect lighting and GI.",
|
|
45
|
+
denoiseTemporal: "Required denoise and temporal accumulation stage.",
|
|
46
|
+
transparents: "Transparents, particles, and volumetrics composition.",
|
|
47
|
+
composition: "Final world composition and color resolve.",
|
|
48
|
+
present: "Presentation to the active surface.",
|
|
49
|
+
}[key],
|
|
50
|
+
})
|
|
51
|
+
)
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const rendererRepresentationBandPolicies = Object.freeze({
|
|
55
|
+
near: Object.freeze({
|
|
56
|
+
band: "near",
|
|
57
|
+
rasterMode: "full-live",
|
|
58
|
+
rtParticipation: "premium",
|
|
59
|
+
shadowSource: "ray-traced-primary",
|
|
60
|
+
temporalReuse: "balanced",
|
|
61
|
+
updateCadenceDivisor: 1,
|
|
62
|
+
}),
|
|
63
|
+
mid: Object.freeze({
|
|
64
|
+
band: "mid",
|
|
65
|
+
rasterMode: "simplified-live",
|
|
66
|
+
rtParticipation: "selective",
|
|
67
|
+
shadowSource: "regional-raster-and-proxy",
|
|
68
|
+
temporalReuse: "aggressive",
|
|
69
|
+
updateCadenceDivisor: 2,
|
|
70
|
+
}),
|
|
71
|
+
far: Object.freeze({
|
|
72
|
+
band: "far",
|
|
73
|
+
rasterMode: "proxy-or-cached",
|
|
74
|
+
rtParticipation: "proxy",
|
|
75
|
+
shadowSource: "merged-proxy-casters",
|
|
76
|
+
temporalReuse: "high",
|
|
77
|
+
updateCadenceDivisor: 8,
|
|
78
|
+
}),
|
|
79
|
+
horizon: Object.freeze({
|
|
80
|
+
band: "horizon",
|
|
81
|
+
rasterMode: "horizon-shell",
|
|
82
|
+
rtParticipation: "disabled",
|
|
83
|
+
shadowSource: "baked-impression",
|
|
84
|
+
temporalReuse: "cached",
|
|
85
|
+
updateCadenceDivisor: 60,
|
|
86
|
+
}),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const rendererAccelerationStructurePolicies = Object.freeze(
|
|
90
|
+
rendererAccelerationStructureUpdateClasses.map((updateClass) =>
|
|
91
|
+
Object.freeze({
|
|
92
|
+
updateClass,
|
|
93
|
+
description:
|
|
94
|
+
{
|
|
95
|
+
static: "Stable static world geometry with infrequent rebuilds.",
|
|
96
|
+
"rigid-dynamic":
|
|
97
|
+
"Rigid transforms that can be refit or relinked without full deformation updates.",
|
|
98
|
+
deforming:
|
|
99
|
+
"Skinned or vertex-deforming content treated as a managed RT cost center.",
|
|
100
|
+
proxy:
|
|
101
|
+
"Low-cost RT proxy or distant representation updates.",
|
|
102
|
+
}[updateClass],
|
|
103
|
+
})
|
|
104
|
+
)
|
|
105
|
+
);
|
|
6
106
|
|
|
7
107
|
function buildRendererWorkerBudgetLevels(jobType, queueClass, levels) {
|
|
8
108
|
return Object.freeze(
|
|
@@ -362,6 +462,75 @@ const rendererWorkerProfileSpecs = {
|
|
|
362
462
|
},
|
|
363
463
|
};
|
|
364
464
|
|
|
465
|
+
function buildRendererInputBoundary(profile) {
|
|
466
|
+
return Object.freeze({
|
|
467
|
+
type: "stable-visual-snapshot",
|
|
468
|
+
owner: rendererDebugOwner,
|
|
469
|
+
profile,
|
|
470
|
+
authority: "visual",
|
|
471
|
+
source: "scene-preparation",
|
|
472
|
+
stable: true,
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function buildRendererRenderStages(profile) {
|
|
477
|
+
return Object.freeze(
|
|
478
|
+
rendererRayTracingStageDefinitions.map((stage) =>
|
|
479
|
+
Object.freeze({
|
|
480
|
+
...stage,
|
|
481
|
+
profile,
|
|
482
|
+
workerJobKeys:
|
|
483
|
+
profile === "xr" && stage.key === "primaryVisibility"
|
|
484
|
+
? Object.freeze(["lateLatch", "visibility"])
|
|
485
|
+
: stage.key === "present"
|
|
486
|
+
? Object.freeze(["submit"])
|
|
487
|
+
: stage.key === "denoiseTemporal" ||
|
|
488
|
+
stage.key === "transparents" ||
|
|
489
|
+
stage.key === "composition"
|
|
490
|
+
? Object.freeze(["postProcess"])
|
|
491
|
+
: stage.key === "primaryVisibility"
|
|
492
|
+
? Object.freeze(["visibility"])
|
|
493
|
+
: stage.key === "shadowAssist" ||
|
|
494
|
+
stage.key === "opaqueFoundation" ||
|
|
495
|
+
stage.key === "rtDirectLighting" ||
|
|
496
|
+
stage.key === "rtReflections" ||
|
|
497
|
+
stage.key === "rtGlobalIllumination"
|
|
498
|
+
? Object.freeze(["mainEncode"])
|
|
499
|
+
: Object.freeze(["mainEncode"]),
|
|
500
|
+
})
|
|
501
|
+
)
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function buildRendererRepresentationBands(profile) {
|
|
506
|
+
return Object.freeze(
|
|
507
|
+
rendererRepresentationBands.map((band) =>
|
|
508
|
+
Object.freeze({
|
|
509
|
+
...rendererRepresentationBandPolicies[band],
|
|
510
|
+
profile,
|
|
511
|
+
})
|
|
512
|
+
)
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function buildRendererAccelerationStructureUpdates(profile) {
|
|
517
|
+
return Object.freeze(
|
|
518
|
+
rendererAccelerationStructurePolicies.map((policy) =>
|
|
519
|
+
Object.freeze({
|
|
520
|
+
...policy,
|
|
521
|
+
profile,
|
|
522
|
+
})
|
|
523
|
+
)
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function assertRendererIdentifier(name, value) {
|
|
528
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
529
|
+
throw new Error(`${name} must be a non-empty string.`);
|
|
530
|
+
}
|
|
531
|
+
return value.trim();
|
|
532
|
+
}
|
|
533
|
+
|
|
365
534
|
function buildRendererWorkerProfile(name, spec) {
|
|
366
535
|
return Object.freeze({
|
|
367
536
|
name,
|
|
@@ -415,6 +584,10 @@ function buildRendererWorkerManifest(name, spec) {
|
|
|
415
584
|
description: spec.description,
|
|
416
585
|
queueClass: rendererWorkerQueueClass,
|
|
417
586
|
schedulerMode: "dag",
|
|
587
|
+
inputBoundary: buildRendererInputBoundary(name),
|
|
588
|
+
renderStages: buildRendererRenderStages(name),
|
|
589
|
+
representationBands: buildRendererRepresentationBands(name),
|
|
590
|
+
accelerationStructureUpdates: buildRendererAccelerationStructureUpdates(name),
|
|
418
591
|
suggestedAllocationIds: Object.freeze([...spec.suggestedAllocationIds]),
|
|
419
592
|
jobs: Object.freeze(
|
|
420
593
|
Object.entries(spec.jobs).map(([jobName, jobSpec]) =>
|
|
@@ -464,6 +637,51 @@ export function getRendererWorkerManifest(name = defaultRendererWorkerProfile) {
|
|
|
464
637
|
return manifest;
|
|
465
638
|
}
|
|
466
639
|
|
|
640
|
+
export function createRayTracingRenderPlan(options = {}) {
|
|
641
|
+
const profile = options.profile ?? defaultRendererWorkerProfile;
|
|
642
|
+
const snapshotId = assertRendererIdentifier(
|
|
643
|
+
"snapshotId",
|
|
644
|
+
options.snapshotId
|
|
645
|
+
);
|
|
646
|
+
const workerManifest = getRendererWorkerManifest(profile);
|
|
647
|
+
const representations = Array.isArray(options.representations)
|
|
648
|
+
? Object.freeze(
|
|
649
|
+
options.representations.map((representation, index) => {
|
|
650
|
+
if (!representation || typeof representation !== "object") {
|
|
651
|
+
throw new Error(`representations[${index}] must be an object.`);
|
|
652
|
+
}
|
|
653
|
+
const band = assertRendererIdentifier(
|
|
654
|
+
`representations[${index}].band`,
|
|
655
|
+
representation.band
|
|
656
|
+
);
|
|
657
|
+
if (!rendererRepresentationBands.includes(band)) {
|
|
658
|
+
throw new Error(
|
|
659
|
+
`representations[${index}].band must be one of: ${rendererRepresentationBands.join(", ")}.`
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
return Object.freeze({
|
|
663
|
+
...representation,
|
|
664
|
+
band,
|
|
665
|
+
});
|
|
666
|
+
})
|
|
667
|
+
)
|
|
668
|
+
: workerManifest.representationBands;
|
|
669
|
+
|
|
670
|
+
return Object.freeze({
|
|
671
|
+
schemaVersion: 1,
|
|
672
|
+
owner: rendererDebugOwner,
|
|
673
|
+
profile,
|
|
674
|
+
inputBoundary: Object.freeze({
|
|
675
|
+
...workerManifest.inputBoundary,
|
|
676
|
+
snapshotId,
|
|
677
|
+
}),
|
|
678
|
+
renderStages: workerManifest.renderStages,
|
|
679
|
+
representationBands: representations,
|
|
680
|
+
accelerationStructureUpdates: workerManifest.accelerationStructureUpdates,
|
|
681
|
+
workerManifest,
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
|
|
467
685
|
function clamp01(value) {
|
|
468
686
|
return Math.min(1, Math.max(0, value));
|
|
469
687
|
}
|