@plasius/gpu-world-generator 0.0.11 → 0.0.13
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/README.md +24 -0
- package/dist/index.cjs +166 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +46 -1
- package/dist/index.d.ts +46 -1
- package/dist/index.js +163 -2
- package/dist/index.js.map +1 -1
- package/docs/adrs/adr-0005-render-representation-tiers-and-proxy-outputs.md +51 -0
- package/docs/adrs/index.md +2 -1
- package/docs/tdrs/index.md +1 -0
- package/docs/tdrs/tdr-0002-render-representation-tier-contract.md +60 -0
- package/package.json +8 -8
- package/src/tiles.ts +0 -2
- package/src/worker.ts +268 -0
package/README.md
CHANGED
|
@@ -81,6 +81,30 @@ console.log(bake.jobs.find((job) => job.key === "assetSerialize"));
|
|
|
81
81
|
debug allocation tags for integration with `@plasius/gpu-performance` and
|
|
82
82
|
`@plasius/gpu-debug`.
|
|
83
83
|
|
|
84
|
+
## Render Representation Plans
|
|
85
|
+
|
|
86
|
+
`@plasius/gpu-world-generator` now also publishes explicit chunk
|
|
87
|
+
representation-tier plans so renderer and worker packages can coordinate near,
|
|
88
|
+
mid, far, and horizon outputs without guessing from distance alone.
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
import { createWorldGeneratorRepresentationPlan } from "@plasius/gpu-world-generator";
|
|
92
|
+
|
|
93
|
+
const plan = createWorldGeneratorRepresentationPlan({
|
|
94
|
+
chunkId: "hex-12-9",
|
|
95
|
+
profile: "streaming",
|
|
96
|
+
gameplayImportance: "critical",
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
console.log(plan.bands);
|
|
100
|
+
console.log(plan.representations.find((entry) => entry.output === "rtProxy"));
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Each plan exposes raster-facing and RT-facing outputs separately, plus refresh
|
|
104
|
+
cadence, shadow relevance, chunk-identity preservation, and scheduling metadata
|
|
105
|
+
that downstream renderer and worker packages can prioritize by band and
|
|
106
|
+
importance.
|
|
107
|
+
|
|
84
108
|
## Demo
|
|
85
109
|
The WebGPU mixed-forest demo lives in `demo/`. Run it with:
|
|
86
110
|
|
package/dist/index.cjs
CHANGED
|
@@ -57,6 +57,7 @@ __export(index_exports, {
|
|
|
57
57
|
createFractalPrepassRunner: () => createFractalPrepassRunner,
|
|
58
58
|
createMeshBuilder: () => createMeshBuilder,
|
|
59
59
|
createPerfMonitor: () => createPerfMonitor,
|
|
60
|
+
createWorldGeneratorRepresentationPlan: () => createWorldGeneratorRepresentationPlan,
|
|
60
61
|
defaultFieldParams: () => defaultFieldParams,
|
|
61
62
|
defaultFractalMandelSettings: () => defaultFractalMandelSettings,
|
|
62
63
|
defaultWorldGeneratorWorkerProfile: () => defaultWorldGeneratorWorkerProfile,
|
|
@@ -94,6 +95,8 @@ __export(index_exports, {
|
|
|
94
95
|
unpackTerrain: () => unpackTerrain,
|
|
95
96
|
validateTileAssetPayload: () => validateTileAssetPayload,
|
|
96
97
|
worldGeneratorDebugOwner: () => worldGeneratorDebugOwner,
|
|
98
|
+
worldGeneratorRepresentationBands: () => worldGeneratorRepresentationBands,
|
|
99
|
+
worldGeneratorRepresentationOutputs: () => worldGeneratorRepresentationOutputs,
|
|
97
100
|
worldGeneratorWorkerManifests: () => worldGeneratorWorkerManifests,
|
|
98
101
|
worldGeneratorWorkerProfileNames: () => worldGeneratorWorkerProfileNames,
|
|
99
102
|
worldGeneratorWorkerProfiles: () => worldGeneratorWorkerProfiles,
|
|
@@ -1404,7 +1407,6 @@ function serializeTileAssetBinary(asset) {
|
|
|
1404
1407
|
view.setUint32(cursor, materialStride >>> 0, true);
|
|
1405
1408
|
cursor += 4;
|
|
1406
1409
|
view.setUint32(cursor, featureStride >>> 0, true);
|
|
1407
|
-
cursor += 4;
|
|
1408
1410
|
let writeOffset = HEADER_BYTES;
|
|
1409
1411
|
new Float32Array(buffer, writeOffset, heightCount).set(asset.height);
|
|
1410
1412
|
writeOffset += heightBytes;
|
|
@@ -1469,7 +1471,6 @@ function parseTileAssetBinary(input) {
|
|
|
1469
1471
|
const materialStride = view.getUint32(cursor, true);
|
|
1470
1472
|
cursor += 4;
|
|
1471
1473
|
const featureStride = view.getUint32(cursor, true);
|
|
1472
|
-
cursor += 4;
|
|
1473
1474
|
const heightBytes = heightCount * 4;
|
|
1474
1475
|
const fieldBytes = fieldCount * 4;
|
|
1475
1476
|
const materialBytes = materialCount;
|
|
@@ -1890,6 +1891,64 @@ function createMeshBuilder(sizeOrOptions = 1) {
|
|
|
1890
1891
|
var worldGeneratorDebugOwner = "world-generator";
|
|
1891
1892
|
var worldGeneratorWorkerQueueClass = "voxel";
|
|
1892
1893
|
var defaultWorldGeneratorWorkerProfile = "streaming";
|
|
1894
|
+
var worldGeneratorRepresentationBands = Object.freeze([
|
|
1895
|
+
"near",
|
|
1896
|
+
"mid",
|
|
1897
|
+
"far",
|
|
1898
|
+
"horizon"
|
|
1899
|
+
]);
|
|
1900
|
+
var worldGeneratorRepresentationOutputs = Object.freeze([
|
|
1901
|
+
"liveGeometry",
|
|
1902
|
+
"simplifiedGeometry",
|
|
1903
|
+
"rtProxy",
|
|
1904
|
+
"mergedProxy",
|
|
1905
|
+
"horizonShell"
|
|
1906
|
+
]);
|
|
1907
|
+
var worldGeneratorRepresentationBandPriorityHints = Object.freeze({
|
|
1908
|
+
near: 400,
|
|
1909
|
+
mid: 300,
|
|
1910
|
+
far: 200,
|
|
1911
|
+
horizon: 100
|
|
1912
|
+
});
|
|
1913
|
+
function assertWorldGeneratorIdentifier(name, value) {
|
|
1914
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
1915
|
+
throw new Error(`${name} must be a non-empty string.`);
|
|
1916
|
+
}
|
|
1917
|
+
return value.trim();
|
|
1918
|
+
}
|
|
1919
|
+
function normalizeWorldGeneratorImportance(name, value) {
|
|
1920
|
+
if (value === "medium" || value === "high" || value === "critical") {
|
|
1921
|
+
return value;
|
|
1922
|
+
}
|
|
1923
|
+
throw new Error(`${name} must be one of: medium, high, critical.`);
|
|
1924
|
+
}
|
|
1925
|
+
function buildWorldGeneratorRepresentationDescriptor(options) {
|
|
1926
|
+
return Object.freeze({
|
|
1927
|
+
id: `${options.chunkId}.${options.band}.${options.output}`,
|
|
1928
|
+
chunkId: options.chunkId,
|
|
1929
|
+
profile: options.profile,
|
|
1930
|
+
band: options.band,
|
|
1931
|
+
output: options.output,
|
|
1932
|
+
rasterMode: options.rasterMode,
|
|
1933
|
+
rtParticipation: options.rtParticipation,
|
|
1934
|
+
shadowRelevance: options.shadowRelevance,
|
|
1935
|
+
refreshCadence: Object.freeze({
|
|
1936
|
+
kind: options.refreshCadence.kind,
|
|
1937
|
+
divisor: options.refreshCadence.divisor
|
|
1938
|
+
}),
|
|
1939
|
+
preservesChunkIdentity: options.preservesChunkIdentity,
|
|
1940
|
+
sourceChunkIds: Object.freeze([options.chunkId]),
|
|
1941
|
+
sourceJobKeys: Object.freeze([...options.sourceJobKeys]),
|
|
1942
|
+
suggestedAllocationIds: Object.freeze([...options.suggestedAllocationIds]),
|
|
1943
|
+
scheduling: Object.freeze({
|
|
1944
|
+
owner: "renderer",
|
|
1945
|
+
queueClass: worldGeneratorWorkerQueueClass,
|
|
1946
|
+
priorityHint: worldGeneratorRepresentationBandPriorityHints[options.band],
|
|
1947
|
+
gameplayImportance: options.gameplayImportance,
|
|
1948
|
+
representationBand: options.band
|
|
1949
|
+
})
|
|
1950
|
+
});
|
|
1951
|
+
}
|
|
1893
1952
|
function buildBudgetLevels(jobType, queueClass, levels) {
|
|
1894
1953
|
return Object.freeze(
|
|
1895
1954
|
levels.map(
|
|
@@ -2470,6 +2529,108 @@ function getWorldGeneratorWorkerManifest(name = defaultWorldGeneratorWorkerProfi
|
|
|
2470
2529
|
}
|
|
2471
2530
|
return manifest;
|
|
2472
2531
|
}
|
|
2532
|
+
function createWorldGeneratorRepresentationPlan(options) {
|
|
2533
|
+
const profile = options.profile ?? defaultWorldGeneratorWorkerProfile;
|
|
2534
|
+
const spec = worldGeneratorWorkerProfileSpecs[profile];
|
|
2535
|
+
if (!spec) {
|
|
2536
|
+
const available = worldGeneratorWorkerProfileNames.join(", ");
|
|
2537
|
+
throw new Error(
|
|
2538
|
+
`Unknown world-generator worker profile "${profile}". Available: ${available}.`
|
|
2539
|
+
);
|
|
2540
|
+
}
|
|
2541
|
+
const chunkId = assertWorldGeneratorIdentifier("chunkId", options.chunkId);
|
|
2542
|
+
const gameplayImportance = normalizeWorldGeneratorImportance(
|
|
2543
|
+
"gameplayImportance",
|
|
2544
|
+
options.gameplayImportance ?? "high"
|
|
2545
|
+
);
|
|
2546
|
+
const highValueAllocations = spec.suggestedAllocationIds;
|
|
2547
|
+
const farFieldAllocations = spec.suggestedAllocationIds.filter(
|
|
2548
|
+
(allocationId) => allocationId.includes("mesh") || allocationId.includes("asset") || allocationId.includes("tile")
|
|
2549
|
+
);
|
|
2550
|
+
const bakeSourceJobKeys = profile === "bake" ? ["meshBuild", "tileBake", "assetSerialize"] : ["meshBuild", "tileBake"];
|
|
2551
|
+
const representations = Object.freeze([
|
|
2552
|
+
buildWorldGeneratorRepresentationDescriptor({
|
|
2553
|
+
profile,
|
|
2554
|
+
chunkId,
|
|
2555
|
+
band: "near",
|
|
2556
|
+
output: "liveGeometry",
|
|
2557
|
+
rasterMode: "full-live",
|
|
2558
|
+
rtParticipation: "full",
|
|
2559
|
+
shadowRelevance: "ray-traced-primary",
|
|
2560
|
+
refreshCadence: { kind: "per-frame", divisor: 1 },
|
|
2561
|
+
preservesChunkIdentity: true,
|
|
2562
|
+
sourceJobKeys: ["meshBuild"],
|
|
2563
|
+
gameplayImportance: gameplayImportance === "medium" ? "high" : gameplayImportance,
|
|
2564
|
+
suggestedAllocationIds: highValueAllocations
|
|
2565
|
+
}),
|
|
2566
|
+
buildWorldGeneratorRepresentationDescriptor({
|
|
2567
|
+
profile,
|
|
2568
|
+
chunkId,
|
|
2569
|
+
band: "mid",
|
|
2570
|
+
output: "simplifiedGeometry",
|
|
2571
|
+
rasterMode: "simplified-live",
|
|
2572
|
+
rtParticipation: "selective",
|
|
2573
|
+
shadowRelevance: "selective-raster",
|
|
2574
|
+
refreshCadence: { kind: "interval", divisor: 2 },
|
|
2575
|
+
preservesChunkIdentity: true,
|
|
2576
|
+
sourceJobKeys: ["meshBuild"],
|
|
2577
|
+
gameplayImportance,
|
|
2578
|
+
suggestedAllocationIds: highValueAllocations
|
|
2579
|
+
}),
|
|
2580
|
+
buildWorldGeneratorRepresentationDescriptor({
|
|
2581
|
+
profile,
|
|
2582
|
+
chunkId,
|
|
2583
|
+
band: "mid",
|
|
2584
|
+
output: "rtProxy",
|
|
2585
|
+
rasterMode: "not-rendered",
|
|
2586
|
+
rtParticipation: "proxy",
|
|
2587
|
+
shadowRelevance: "selective-raster",
|
|
2588
|
+
refreshCadence: { kind: "interval", divisor: 2 },
|
|
2589
|
+
preservesChunkIdentity: true,
|
|
2590
|
+
sourceJobKeys: ["meshBuild", "tileBake"],
|
|
2591
|
+
gameplayImportance,
|
|
2592
|
+
suggestedAllocationIds: highValueAllocations
|
|
2593
|
+
}),
|
|
2594
|
+
buildWorldGeneratorRepresentationDescriptor({
|
|
2595
|
+
profile,
|
|
2596
|
+
chunkId,
|
|
2597
|
+
band: "far",
|
|
2598
|
+
output: "mergedProxy",
|
|
2599
|
+
rasterMode: "proxy",
|
|
2600
|
+
rtParticipation: "proxy",
|
|
2601
|
+
shadowRelevance: "proxy-caster",
|
|
2602
|
+
refreshCadence: { kind: "interval", divisor: 8 },
|
|
2603
|
+
preservesChunkIdentity: true,
|
|
2604
|
+
sourceJobKeys: bakeSourceJobKeys,
|
|
2605
|
+
gameplayImportance: "medium",
|
|
2606
|
+
suggestedAllocationIds: farFieldAllocations.length > 0 ? farFieldAllocations : highValueAllocations
|
|
2607
|
+
}),
|
|
2608
|
+
buildWorldGeneratorRepresentationDescriptor({
|
|
2609
|
+
profile,
|
|
2610
|
+
chunkId,
|
|
2611
|
+
band: "horizon",
|
|
2612
|
+
output: "horizonShell",
|
|
2613
|
+
rasterMode: "horizon-shell",
|
|
2614
|
+
rtParticipation: "disabled",
|
|
2615
|
+
shadowRelevance: "baked-impression",
|
|
2616
|
+
refreshCadence: { kind: "interval", divisor: 60 },
|
|
2617
|
+
preservesChunkIdentity: true,
|
|
2618
|
+
sourceJobKeys: profile === "bake" ? ["tileBake", "assetSerialize"] : ["tileBake"],
|
|
2619
|
+
gameplayImportance: "medium",
|
|
2620
|
+
suggestedAllocationIds: farFieldAllocations.length > 0 ? farFieldAllocations : highValueAllocations
|
|
2621
|
+
})
|
|
2622
|
+
]);
|
|
2623
|
+
return Object.freeze({
|
|
2624
|
+
schemaVersion: 1,
|
|
2625
|
+
owner: worldGeneratorDebugOwner,
|
|
2626
|
+
profile,
|
|
2627
|
+
chunkId,
|
|
2628
|
+
representations,
|
|
2629
|
+
bands: Object.freeze(
|
|
2630
|
+
[...new Set(representations.map((representation) => representation.band))]
|
|
2631
|
+
)
|
|
2632
|
+
});
|
|
2633
|
+
}
|
|
2473
2634
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2474
2635
|
0 && (module.exports = {
|
|
2475
2636
|
DEFAULT_TILE_SIZE_WORLD,
|
|
@@ -2498,6 +2659,7 @@ function getWorldGeneratorWorkerManifest(name = defaultWorldGeneratorWorkerProfi
|
|
|
2498
2659
|
createFractalPrepassRunner,
|
|
2499
2660
|
createMeshBuilder,
|
|
2500
2661
|
createPerfMonitor,
|
|
2662
|
+
createWorldGeneratorRepresentationPlan,
|
|
2501
2663
|
defaultFieldParams,
|
|
2502
2664
|
defaultFractalMandelSettings,
|
|
2503
2665
|
defaultWorldGeneratorWorkerProfile,
|
|
@@ -2535,6 +2697,8 @@ function getWorldGeneratorWorkerManifest(name = defaultWorldGeneratorWorkerProfi
|
|
|
2535
2697
|
unpackTerrain,
|
|
2536
2698
|
validateTileAssetPayload,
|
|
2537
2699
|
worldGeneratorDebugOwner,
|
|
2700
|
+
worldGeneratorRepresentationBands,
|
|
2701
|
+
worldGeneratorRepresentationOutputs,
|
|
2538
2702
|
worldGeneratorWorkerManifests,
|
|
2539
2703
|
worldGeneratorWorkerProfileNames,
|
|
2540
2704
|
worldGeneratorWorkerProfiles,
|