@aethervtt/shard-render 0.0.1 → 0.1.0
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/assets.d.ts +3 -0
- package/dist/assets.js +43 -2
- package/dist/atmosphere-nodes.d.ts +9 -0
- package/dist/atmosphere-nodes.js +57 -18
- package/dist/atmosphere-plugin.js +8 -2
- package/dist/baseline/atmosphere.d.ts +16 -0
- package/dist/baseline/atmosphere.js +301 -0
- package/dist/baseline/data-texture.d.ts +36 -0
- package/dist/baseline/data-texture.js +181 -0
- package/dist/baseline/environment.d.ts +14 -0
- package/dist/baseline/environment.js +368 -0
- package/dist/baseline/exposure.d.ts +25 -0
- package/dist/baseline/exposure.js +120 -0
- package/dist/baseline/lights.d.ts +33 -0
- package/dist/baseline/lights.js +133 -0
- package/dist/baseline/textures.d.ts +97 -0
- package/dist/baseline/textures.js +203 -0
- package/dist/camera.d.ts +2 -0
- package/dist/camera.js +4 -0
- package/dist/culling-shaders.js +21 -2
- package/dist/culling.d.ts +11 -2
- package/dist/culling.js +44 -18
- package/dist/cutaway-plugin.d.ts +48 -0
- package/dist/cutaway-plugin.js +364 -0
- package/dist/cutaway.d.ts +27 -0
- package/dist/cutaway.js +49 -0
- package/dist/data-store.d.ts +69 -0
- package/dist/data-store.js +105 -0
- package/dist/debug-shaders.js +17 -3
- package/dist/debug-views.js +4 -2
- package/dist/deferred-shaders.js +2 -0
- package/dist/deferred.js +24 -3
- package/dist/deform.d.ts +5 -4
- package/dist/deform.js +4 -9
- package/dist/describe-plugin.d.ts +4 -2
- package/dist/describe-plugin.js +75 -3
- package/dist/display-nodes.js +15 -4
- package/dist/dynamic-resolution.d.ts +2 -0
- package/dist/dynamic-resolution.js +6 -1
- package/dist/environment-plugin.js +17 -2
- package/dist/environment-state.d.ts +7 -3
- package/dist/environment-state.js +20 -9
- package/dist/environment.js +7 -1
- package/dist/features.d.ts +49 -0
- package/dist/features.js +106 -0
- package/dist/forward.d.ts +38 -2
- package/dist/forward.js +582 -184
- package/dist/fxaa.js +12 -2
- package/dist/gizmos-plugin.js +7 -0
- package/dist/gizmos.d.ts +3 -3
- package/dist/gizmos.js +11 -13
- package/dist/gpu-assets.d.ts +58 -4
- package/dist/gpu-assets.js +191 -8
- package/dist/graph.d.ts +11 -0
- package/dist/graph.js +30 -5
- package/dist/health.d.ts +58 -0
- package/dist/health.js +112 -0
- package/dist/index.d.ts +30 -11
- package/dist/index.js +286 -9
- package/dist/instances.d.ts +119 -8
- package/dist/instances.js +387 -78
- package/dist/interior-plugin.d.ts +121 -0
- package/dist/interior-plugin.js +465 -0
- package/dist/interior.d.ts +24 -0
- package/dist/interior.js +13 -0
- package/dist/layers.d.ts +35 -0
- package/dist/layers.js +49 -0
- package/dist/lens-plugin.d.ts +43 -0
- package/dist/lens-plugin.js +304 -0
- package/dist/lens.d.ts +41 -0
- package/dist/lens.js +99 -0
- package/dist/lights.d.ts +50 -5
- package/dist/lights.js +101 -22
- package/dist/material-pipelines.d.ts +37 -4
- package/dist/material-pipelines.js +102 -5
- package/dist/materials.d.ts +23 -0
- package/dist/materials.js +44 -1
- package/dist/outline-plugin.d.ts +48 -0
- package/dist/outline-plugin.js +529 -0
- package/dist/outline.d.ts +15 -0
- package/dist/outline.js +42 -0
- package/dist/overlays.js +1 -0
- package/dist/picking.d.ts +17 -0
- package/dist/picking.js +40 -11
- package/dist/pixel-perfect-plugin.js +10 -2
- package/dist/plugin.d.ts +58 -6
- package/dist/plugin.js +191 -24
- package/dist/post-common.d.ts +2 -0
- package/dist/post-common.js +3 -0
- package/dist/post-nodes.d.ts +2 -1
- package/dist/post-nodes.js +111 -19
- package/dist/post-plugin.js +5 -3
- package/dist/post-shaders.js +51 -6
- package/dist/projection.d.ts +24 -0
- package/dist/projection.js +87 -0
- package/dist/readback.d.ts +1 -0
- package/dist/readback.js +1 -0
- package/dist/render-scale.js +1 -0
- package/dist/screen-effects.d.ts +97 -0
- package/dist/screen-effects.js +192 -0
- package/dist/shaders.d.ts +13 -0
- package/dist/shaders.js +131 -29
- package/dist/shadow-catcher.d.ts +9 -0
- package/dist/shadow-catcher.js +133 -0
- package/dist/shadows.d.ts +25 -1
- package/dist/shadows.js +97 -19
- package/dist/standard-fields.d.ts +1 -0
- package/dist/standard-fields.js +5 -0
- package/dist/standard.js +12 -0
- package/dist/stats.d.ts +45 -1
- package/dist/stats.js +91 -3
- package/dist/surface-model.d.ts +57 -0
- package/dist/surface-model.js +289 -0
- package/dist/surface-shaders.d.ts +3 -0
- package/dist/surface-shaders.js +160 -0
- package/dist/surface.d.ts +40 -0
- package/dist/surface.js +86 -0
- package/dist/target.d.ts +1 -13
- package/dist/target.js +1 -26
- package/dist/testing.d.ts +10 -0
- package/dist/testing.js +33 -1
- package/dist/tier.d.ts +13 -0
- package/dist/tier.js +14 -0
- package/dist/timer.d.ts +2 -0
- package/dist/timer.js +7 -1
- package/dist/variants.d.ts +37 -0
- package/dist/variants.js +104 -0
- package/dist/view-visibility-plugin.d.ts +61 -0
- package/dist/view-visibility-plugin.js +267 -0
- package/dist/view-visibility.d.ts +28 -0
- package/dist/view-visibility.js +46 -0
- package/dist/view.d.ts +29 -1
- package/dist/view.js +57 -34
- package/package.json +15 -11
package/dist/deferred-shaders.js
CHANGED
|
@@ -66,6 +66,7 @@ import shard::mesh::mesh_vertex_at;
|
|
|
66
66
|
import shard::pbr::material::pbr_input;
|
|
67
67
|
import shard::pbr::standard::material;
|
|
68
68
|
import shard::pbr::gbuffer::{ GBufferOutput, pack_gbuffer };
|
|
69
|
+
@if(CUTAWAY) import shard::cutaway::cutaway_clip;
|
|
69
70
|
|
|
70
71
|
const FLAG_RECEIVER: u32 = 4u;
|
|
71
72
|
|
|
@@ -85,6 +86,7 @@ const FLAG_RECEIVER: u32 = 4u;
|
|
|
85
86
|
|
|
86
87
|
/** The surface stage only: what the material makes of the surface, packed for lighting. */
|
|
87
88
|
@fragment fn fs(in: VertexOutput) -> GBufferOutput {
|
|
89
|
+
@if(CUTAWAY) cutaway_clip(in.world_position, in.clip.xy, in.flags);
|
|
88
90
|
let p = pbr_input(in);
|
|
89
91
|
@if(MASK) if (p.alpha < material.alphaCutoff) { discard; }
|
|
90
92
|
return pack_gbuffer(p, (in.flags & FLAG_RECEIVER) != 0u, view.exposure);
|
package/dist/deferred.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { ShardError } from "@aethervtt/shard-core";
|
|
2
2
|
import { GpuBuffer } from "@aethervtt/shard-gpu";
|
|
3
3
|
import { Culler } from "./culling.js";
|
|
4
|
+
import { addRenderFeatures } from "./features.js";
|
|
4
5
|
import {
|
|
5
6
|
drawMaterials,
|
|
6
7
|
ForwardStateResource,
|
|
7
8
|
gbufferEmissiveFormat,
|
|
9
|
+
INTERIOR_DEFINES,
|
|
8
10
|
PASS_GBUFFER,
|
|
9
11
|
PASS_OPAQUE,
|
|
10
12
|
sceneColor,
|
|
@@ -12,6 +14,7 @@ import {
|
|
|
12
14
|
} from "./forward.js";
|
|
13
15
|
import { RenderPhase } from "./graph.js";
|
|
14
16
|
import { Gpu, Graph, RenderDescribers, Shaders, Views } from "./plugin.js";
|
|
17
|
+
import { depthReadEntry } from "./tier.js";
|
|
15
18
|
import { Cameras, cameraOf } from "./view.js";
|
|
16
19
|
const GBUFFER_CHANNELS = ["albedo", "normal", "roughness", "metallic", "emissive"];
|
|
17
20
|
const deferredView = (view) => cameraOf(view)?.deferred === true;
|
|
@@ -80,7 +83,11 @@ function lightingNode() {
|
|
|
80
83
|
const cam = cameraOf(ctx.view);
|
|
81
84
|
const pv = state.views.get(ctx.view.name);
|
|
82
85
|
if (!pv) return;
|
|
83
|
-
const
|
|
86
|
+
const mode = state.interiorMode;
|
|
87
|
+
const module = ctx.world.resource(Shaders).module(
|
|
88
|
+
gpu,
|
|
89
|
+
mode !== 0 ? { root: "shard::pbr::deferred_lighting", defines: INTERIOR_DEFINES[mode] } : { root: "shard::pbr::deferred_lighting" }
|
|
90
|
+
);
|
|
84
91
|
if (!module) {
|
|
85
92
|
gpu.pipelines.skipped++;
|
|
86
93
|
return;
|
|
@@ -93,7 +100,7 @@ function lightingNode() {
|
|
|
93
100
|
{ binding: 0, visibility: F, texture: { sampleType: "float" } },
|
|
94
101
|
{ binding: 1, visibility: F, texture: { sampleType: "unfilterable-float" } },
|
|
95
102
|
{ binding: 2, visibility: F, texture: { sampleType: "float" } },
|
|
96
|
-
|
|
103
|
+
depthReadEntry(gpu, 3, F)
|
|
97
104
|
]
|
|
98
105
|
});
|
|
99
106
|
layoutGeneration = gpu.generation;
|
|
@@ -271,7 +278,7 @@ function describeDeferred(world) {
|
|
|
271
278
|
deferredMeshes: cam.draws.visible,
|
|
272
279
|
forwardMeshes: cam.forwardOnly.visible + cam.transparent.visible,
|
|
273
280
|
forwardReasons: forwardReason(cam, world)
|
|
274
|
-
} : { path: "forward", msaa: cam.msaa };
|
|
281
|
+
} : cam.msaaCapped ? { path: "forward", msaa: cam.msaa, msaaLimit: "the device can't multisample rgba16float" } : { path: "forward", msaa: cam.msaa };
|
|
275
282
|
}
|
|
276
283
|
return { views };
|
|
277
284
|
}
|
|
@@ -281,6 +288,20 @@ function addDeferredNodes(app) {
|
|
|
281
288
|
graph.declare({ name: "gbuffer0", format: "rgba8unorm-srgb" });
|
|
282
289
|
graph.declare({ name: "gbuffer1", format: "rgba16float" });
|
|
283
290
|
graph.declare({ name: "gbuffer2", format: gbufferEmissiveFormat(gpu) });
|
|
291
|
+
addRenderFeatures(app.world, {
|
|
292
|
+
name: "render/deferred",
|
|
293
|
+
description: "The deferred path: G-buffer, lighting, and forward-only materials.",
|
|
294
|
+
nodes: [
|
|
295
|
+
"deferred-gbuffer",
|
|
296
|
+
"gbuffer-fill",
|
|
297
|
+
"deferred-lighting",
|
|
298
|
+
"deferred-forward",
|
|
299
|
+
"gbuffer-debug"
|
|
300
|
+
],
|
|
301
|
+
baseline: {
|
|
302
|
+
strategy: "The same passes, reading lights and clusters through the baseline accessors"
|
|
303
|
+
}
|
|
304
|
+
});
|
|
284
305
|
graph.addNode("deferred-gbuffer", gbufferNode);
|
|
285
306
|
graph.addNode("gbuffer-fill", gbufferFillNode);
|
|
286
307
|
graph.addNode("deferred-lighting", lightingNode());
|
package/dist/deform.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { GpuContext } from '@aethervtt/shard-gpu';
|
|
2
2
|
import type { Mesh } from '@aethervtt/shard-mesh';
|
|
3
|
+
import { DataStore } from './data-store.js';
|
|
3
4
|
/**
|
|
4
5
|
* Per-slot deform record, 12 words (mirrors `Deform` in `shard::mesh` and `shard::cull`):
|
|
5
6
|
*
|
|
@@ -47,9 +48,9 @@ export declare class DeformStore {
|
|
|
47
48
|
private vertexWords;
|
|
48
49
|
private wasted;
|
|
49
50
|
readonly meshes: Map<Mesh, MeshDeform>;
|
|
50
|
-
readonly recordBuffer:
|
|
51
|
-
readonly poseBuffer:
|
|
52
|
-
readonly vertexBuffer:
|
|
51
|
+
readonly recordBuffer: DataStore;
|
|
52
|
+
readonly poseBuffer: DataStore;
|
|
53
|
+
readonly vertexBuffer: DataStore;
|
|
53
54
|
private recordLo;
|
|
54
55
|
private recordHi;
|
|
55
56
|
private vertexDirty;
|
package/dist/deform.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DataStore } from "./data-store.js";
|
|
2
2
|
const DEFORM_WORDS = 12;
|
|
3
3
|
const DEFORM_BYTES = DEFORM_WORDS * 4;
|
|
4
4
|
const SKIN_WORDS = 6;
|
|
@@ -26,14 +26,9 @@ class DeformStore {
|
|
|
26
26
|
constructor(gpu) {
|
|
27
27
|
this.gpu = gpu;
|
|
28
28
|
this.generation = gpu.generation;
|
|
29
|
-
|
|
30
|
-
this.
|
|
31
|
-
|
|
32
|
-
usage,
|
|
33
|
-
size: DEFORM_BYTES * 256
|
|
34
|
-
});
|
|
35
|
-
this.poseBuffer = new GpuBuffer(gpu, { label: "deform/poses", usage, size: 16 * 1024 });
|
|
36
|
-
this.vertexBuffer = new GpuBuffer(gpu, { label: "deform/vertices", usage, size: 4096 });
|
|
29
|
+
this.recordBuffer = new DataStore(gpu, { label: "deform/records", size: DEFORM_BYTES * 256 });
|
|
30
|
+
this.poseBuffer = new DataStore(gpu, { label: "deform/poses", size: 16 * 1024 });
|
|
31
|
+
this.vertexBuffer = new DataStore(gpu, { label: "deform/vertices", size: 4096 });
|
|
37
32
|
}
|
|
38
33
|
/** Slots the record array holds. */
|
|
39
34
|
get capacity() {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The lighting, culling, and
|
|
3
|
-
*
|
|
2
|
+
* The lighting, culling, renderScale, surfaces, uploads, and frames sections of `render.describe`,
|
|
3
|
+
* and the
|
|
4
|
+
* `gpu.stats` method, for agents and the editor. Only introspection: an app that ships without it
|
|
5
|
+
* renders the same.
|
|
4
6
|
*/
|
|
5
7
|
export declare const renderDescribePlugin: import("@aethervtt/shard-runtime").Plugin;
|
package/dist/describe-plugin.js
CHANGED
|
@@ -1,17 +1,89 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineSchema, t } from "@aethervtt/shard-core";
|
|
2
|
+
import { definePlugin, FrameDemand } from "@aethervtt/shard-runtime";
|
|
2
3
|
import { describeCulling, describeLighting } from "./debug-views.js";
|
|
3
|
-
import {
|
|
4
|
+
import { GroundLayer } from "./layers.js";
|
|
5
|
+
import { Gpu, RenderDescribers, RenderOptions, Window } from "./plugin.js";
|
|
4
6
|
import { describeRenderScale } from "./render-scale.js";
|
|
7
|
+
import { RenderStats } from "./stats.js";
|
|
8
|
+
import { Cameras } from "./view.js";
|
|
9
|
+
function describeSurfaces(world) {
|
|
10
|
+
const window = world.tryResource(Window);
|
|
11
|
+
return world.resource(Gpu).surfaces.map((s) => ({
|
|
12
|
+
label: s.label,
|
|
13
|
+
size: [s.width, s.height],
|
|
14
|
+
alpha: s.alpha,
|
|
15
|
+
pixelRatio: s.pixelRatio,
|
|
16
|
+
thisApp: s === window
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
19
|
+
function describeUploads(world) {
|
|
20
|
+
const stats = world.resource(RenderStats);
|
|
21
|
+
const bytes = (r) => ({
|
|
22
|
+
bytes: { ...r.bytes },
|
|
23
|
+
sceneBytes: r.sceneBytes,
|
|
24
|
+
created: r.created
|
|
25
|
+
});
|
|
26
|
+
const rebuilds = (r) => ({
|
|
27
|
+
chunksRebuilt: r.chunksRebuilt,
|
|
28
|
+
meshesRebuilt: r.meshesRebuilt,
|
|
29
|
+
shadowMapsRendered: r.shadowMapsRendered
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
frames: stats.frames,
|
|
33
|
+
lastFrame: bytes(stats.lastFrame),
|
|
34
|
+
recent: bytes(stats.recent),
|
|
35
|
+
rebuilds: { lastFrame: rebuilds(stats.lastFrame), recent: rebuilds(stats.recent) }
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function describeGround(world) {
|
|
39
|
+
const bands = {};
|
|
40
|
+
world.query({ with: [GroundLayer] }).each((_e, row, table) => {
|
|
41
|
+
const band = String(table.column(GroundLayer, "band")[row]);
|
|
42
|
+
bands[band] = (bands[band] ?? 0) + 1;
|
|
43
|
+
});
|
|
44
|
+
const views = {};
|
|
45
|
+
for (const cam of world.resource(Cameras).values())
|
|
46
|
+
views[`camera:${cam.entity}`] = {
|
|
47
|
+
instances: cam.ground.visible + cam.overlay.visible,
|
|
48
|
+
draws: cam.ground.length + cam.overlay.length
|
|
49
|
+
};
|
|
50
|
+
return { bands, views };
|
|
51
|
+
}
|
|
52
|
+
const gpuStats = {
|
|
53
|
+
name: "gpu.stats",
|
|
54
|
+
description: "Live GPU buffers and textures, by owner: each app's render plugin is one, and 'gpu' holds what apps on the device share.",
|
|
55
|
+
params: defineSchema("render/GpuStatsParams", {
|
|
56
|
+
owner: t.string({ description: "One owner. Empty: this app, every owner, and the total." })
|
|
57
|
+
}),
|
|
58
|
+
handler: ({ world }, p) => {
|
|
59
|
+
const gpu = world.resource(Gpu);
|
|
60
|
+
if (p.owner) return { owner: p.owner, ...gpu.stats(p.owner) };
|
|
61
|
+
return {
|
|
62
|
+
owner: world.resource(RenderOptions).owner,
|
|
63
|
+
total: gpu.stats(),
|
|
64
|
+
owners: Object.fromEntries(gpu.owners().map((o) => [o, gpu.stats(o)]))
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
};
|
|
5
68
|
const renderDescribePlugin = definePlugin({
|
|
6
69
|
name: "render/describe",
|
|
7
70
|
dependencies: ["render/forward"],
|
|
8
|
-
build() {
|
|
71
|
+
build(app) {
|
|
72
|
+
app.addMethod(gpuStats);
|
|
9
73
|
},
|
|
10
74
|
ready(app) {
|
|
11
75
|
const describers = app.world.initResource(RenderDescribers);
|
|
12
76
|
describers.set("lighting", (world) => describeLighting(world));
|
|
13
77
|
describers.set("culling", (world) => describeCulling(world));
|
|
14
78
|
describers.set("renderScale", (world) => describeRenderScale(world));
|
|
79
|
+
describers.set("surfaces", describeSurfaces);
|
|
80
|
+
describers.set("uploads", describeUploads);
|
|
81
|
+
describers.set("ground", describeGround);
|
|
82
|
+
describers.set("frames", (world) => world.resource(FrameDemand).describe());
|
|
83
|
+
describers.set(
|
|
84
|
+
"gpuObjects",
|
|
85
|
+
(world) => world.resource(Gpu).stats(world.resource(RenderOptions).owner)
|
|
86
|
+
);
|
|
15
87
|
}
|
|
16
88
|
});
|
|
17
89
|
export {
|
package/dist/display-nodes.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { addRenderFeatures } from "./features.js";
|
|
1
2
|
import { Textures } from "@aethervtt/shard-texture";
|
|
2
3
|
import { GpuAssetsResource } from "./gpu-assets.js";
|
|
3
4
|
import { RenderPhase } from "./graph.js";
|
|
@@ -53,15 +54,17 @@ function tonemapNode() {
|
|
|
53
54
|
}
|
|
54
55
|
const format = ctx.texture("ldr").format;
|
|
55
56
|
const srgb = format.endsWith("-srgb");
|
|
57
|
+
const alpha = cam.alphaOutput;
|
|
56
58
|
const pipeline = cache.render(
|
|
57
59
|
ctx,
|
|
58
|
-
`tonemap/${format}`,
|
|
60
|
+
`tonemap/${format}${alpha ? "/alpha" : ""}`,
|
|
59
61
|
"shard::post::tonemap",
|
|
60
62
|
"fs",
|
|
61
63
|
[layout],
|
|
62
64
|
[{ format }],
|
|
63
65
|
{
|
|
64
|
-
SRGB_TARGET: srgb
|
|
66
|
+
SRGB_TARGET: srgb,
|
|
67
|
+
TRANSPARENT: alpha
|
|
65
68
|
}
|
|
66
69
|
);
|
|
67
70
|
if (!pipeline) return;
|
|
@@ -144,13 +147,15 @@ function upscaleNode() {
|
|
|
144
147
|
const input = ctx.texture("display");
|
|
145
148
|
const target = ctx.texture("view-target");
|
|
146
149
|
const format = target.format;
|
|
150
|
+
const alpha = cameraOf(ctx.view).alphaOutput;
|
|
147
151
|
const pipeline = cache.render(
|
|
148
152
|
ctx,
|
|
149
|
-
`upscale/${format}`,
|
|
153
|
+
`upscale/${format}${alpha ? "/alpha" : ""}`,
|
|
150
154
|
"shard::post::upscale",
|
|
151
155
|
"fs",
|
|
152
156
|
[layout],
|
|
153
|
-
[{ format }]
|
|
157
|
+
[{ format }],
|
|
158
|
+
{ TRANSPARENT: alpha }
|
|
154
159
|
);
|
|
155
160
|
if (!pipeline) return;
|
|
156
161
|
const settings = ctx.world.tryResource(RenderScale);
|
|
@@ -180,6 +185,12 @@ function upscaleNode() {
|
|
|
180
185
|
function addDisplayNodes(world) {
|
|
181
186
|
const graph = world.resource(Graph);
|
|
182
187
|
graph.declare({ name: "display", format: "view" });
|
|
188
|
+
addRenderFeatures(world, {
|
|
189
|
+
name: "render/display",
|
|
190
|
+
description: "Tonemapping and the upscale to the display.",
|
|
191
|
+
nodes: ["tonemap", "post/upscale"],
|
|
192
|
+
baseline: { strategy: "The same fragment passes" }
|
|
193
|
+
});
|
|
183
194
|
graph.addNode("tonemap", tonemapNode());
|
|
184
195
|
graph.addNode("post/upscale", upscaleNode());
|
|
185
196
|
}
|
|
@@ -27,6 +27,8 @@ export declare class RenderScaleController {
|
|
|
27
27
|
private probeAge;
|
|
28
28
|
private probeWait;
|
|
29
29
|
private hold;
|
|
30
|
+
/** Verifying an upward probe: it needs frames to measure, so on-demand apps keep rendering. */
|
|
31
|
+
get probing(): boolean;
|
|
30
32
|
/**
|
|
31
33
|
* One frame. `skip`: pipelines are compiling, so the frame says nothing about steady cost.
|
|
32
34
|
* `displayMs`: one refresh of the display, the budget when `targetMs` is 0.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineSystem, Last } from "@aethervtt/shard-core";
|
|
2
|
-
import { DisplayRate, definePlugin, Time } from "@aethervtt/shard-runtime";
|
|
2
|
+
import { DisplayRate, definePlugin, FrameDemand, Time } from "@aethervtt/shard-runtime";
|
|
3
3
|
import { Gpu, Graph, RenderSet } from "./plugin.js";
|
|
4
4
|
import {
|
|
5
5
|
HIGHEST,
|
|
@@ -30,6 +30,10 @@ class RenderScaleController {
|
|
|
30
30
|
probeAge = 0;
|
|
31
31
|
probeWait = 3;
|
|
32
32
|
hold = 0;
|
|
33
|
+
/** Verifying an upward probe: it needs frames to measure, so on-demand apps keep rendering. */
|
|
34
|
+
get probing() {
|
|
35
|
+
return this.probeFrom > 0;
|
|
36
|
+
}
|
|
33
37
|
/**
|
|
34
38
|
* One frame. `skip`: pipelines are compiling, so the frame says nothing about steady cost.
|
|
35
39
|
* `displayMs`: one refresh of the display, the budget when `targetMs` is 0.
|
|
@@ -175,6 +179,7 @@ const updateRenderScale = defineSystem({
|
|
|
175
179
|
gpu.pipelines.pending > 0,
|
|
176
180
|
display
|
|
177
181
|
);
|
|
182
|
+
world.tryResource(FrameDemand)?.set("render/render-scale", controller.probing);
|
|
178
183
|
}
|
|
179
184
|
});
|
|
180
185
|
const dynamicResolutionPlugin = definePlugin({
|
|
@@ -4,15 +4,17 @@ import {
|
|
|
4
4
|
DefaultEnvironment,
|
|
5
5
|
describeEnvironment,
|
|
6
6
|
EnvironmentMap,
|
|
7
|
+
Environments,
|
|
7
8
|
ProceduralSky,
|
|
8
9
|
prepareEnvironments,
|
|
9
10
|
runEnvironmentWork,
|
|
10
11
|
Skybox
|
|
11
12
|
} from "./environment.js";
|
|
12
13
|
import { ENVIRONMENT_SHADERS } from "./environment-shaders.js";
|
|
14
|
+
import { addRenderFeatures } from "./features.js";
|
|
13
15
|
import { ForwardStateResource, isCamera, skyNode } from "./forward.js";
|
|
14
16
|
import { RenderPhase } from "./graph.js";
|
|
15
|
-
import { Graph, RenderDescribers, RenderSet, Shaders } from "./plugin.js";
|
|
17
|
+
import { Gpu, Graph, RenderDescribers, RenderSet, Shaders } from "./plugin.js";
|
|
16
18
|
import { registerShaders } from "./shaders.js";
|
|
17
19
|
const environmentPlugin = definePlugin({
|
|
18
20
|
name: "render/environment",
|
|
@@ -22,11 +24,24 @@ const environmentPlugin = definePlugin({
|
|
|
22
24
|
app.world.initResource(DefaultEnvironment);
|
|
23
25
|
app.addSystems(Last, prepareEnvironments.inSet(RenderSet.Prepare));
|
|
24
26
|
},
|
|
25
|
-
ready(app) {
|
|
27
|
+
async ready(app) {
|
|
26
28
|
const world = app.world;
|
|
29
|
+
const baseline = world.resource(Gpu).tier === "baseline" ? await import("./baseline/environment.js") : void 0;
|
|
27
30
|
registerShaders(world.resource(Shaders), ENVIRONMENT_SHADERS);
|
|
31
|
+
if (baseline) {
|
|
32
|
+
registerShaders(world.resource(Shaders), baseline.BASELINE_ENVIRONMENT_SHADERS);
|
|
33
|
+
world.resource(Environments).baseline = baseline;
|
|
34
|
+
}
|
|
28
35
|
world.initResource(RenderDescribers).set("environment", (w) => describeEnvironment(w));
|
|
29
36
|
const graph = world.resource(Graph);
|
|
37
|
+
addRenderFeatures(world, {
|
|
38
|
+
name: "render/environment",
|
|
39
|
+
description: "Image-based lighting: cube conversion, specular prefilter, SH irradiance, BRDF LUT; and the sky.",
|
|
40
|
+
nodes: ["environment", "sky"],
|
|
41
|
+
baseline: {
|
|
42
|
+
strategy: "Prefilter, SH and BRDF LUT as fragment passes into the cube faces and mips"
|
|
43
|
+
}
|
|
44
|
+
});
|
|
30
45
|
graph.addNode("environment", {
|
|
31
46
|
kind: "raw",
|
|
32
47
|
phase: RenderPhase.Setup,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { GpuContext } from '@aethervtt/shard-gpu';
|
|
2
2
|
import type { Texture } from '@aethervtt/shard-texture';
|
|
3
3
|
import type { CameraAtmosphere } from './atmosphere-state.js';
|
|
4
|
+
import { DataStore } from './data-store.js';
|
|
4
5
|
import type { CameraData } from './view.js';
|
|
5
6
|
/** Frames between rebakes of a moving camera's atmosphere environment. */
|
|
6
7
|
export declare const REBAKE_FRAMES = 8;
|
|
@@ -19,7 +20,8 @@ export declare class Environment {
|
|
|
19
20
|
specular: GPUTexture;
|
|
20
21
|
specularView: GPUTextureView;
|
|
21
22
|
sourceView: GPUTextureView;
|
|
22
|
-
|
|
23
|
+
/** SH9 of the diffuse irradiance: `@data(uniform)` in the forward shader. */
|
|
24
|
+
readonly sh: DataStore;
|
|
23
25
|
/** 'pending' until the GPU has prefiltered the current key. */
|
|
24
26
|
state: 'pending' | 'ready';
|
|
25
27
|
/** How many times it's been (re)baked or prefiltered. */
|
|
@@ -59,12 +61,14 @@ export declare class EnvironmentStore {
|
|
|
59
61
|
lutReady: boolean;
|
|
60
62
|
emptyCube: GPUTexture | undefined;
|
|
61
63
|
emptyView: GPUTextureView | undefined;
|
|
62
|
-
emptySh:
|
|
64
|
+
emptySh: DataStore | undefined;
|
|
63
65
|
sampler: GPUSampler | undefined;
|
|
64
66
|
generation: number;
|
|
65
67
|
frame: number;
|
|
66
68
|
/** The frame the prefilter work last ran (it runs once per frame, for all views). */
|
|
67
69
|
ranFrame: number;
|
|
70
|
+
/** The baseline tier's prefilter (0064): fragment passes, loaded only on a baseline device. */
|
|
71
|
+
baseline: typeof import('./baseline/environment.js') | undefined;
|
|
68
72
|
ensure(gpu: GpuContext): void;
|
|
69
73
|
}
|
|
70
74
|
export declare const Environments: import("@aethervtt/shard-core").ResourceDef<EnvironmentStore>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defineResource } from "@aethervtt/shard-core";
|
|
2
|
-
import {
|
|
2
|
+
import { DataStore } from "./data-store.js";
|
|
3
|
+
import { bindingDimension } from "./tier.js";
|
|
3
4
|
const REBAKE_FRAMES = 8;
|
|
4
5
|
const SOURCE_SIZE = 512;
|
|
5
6
|
const SKY_SIZE = 256;
|
|
@@ -15,6 +16,7 @@ class Environment {
|
|
|
15
16
|
specular;
|
|
16
17
|
specularView;
|
|
17
18
|
sourceView;
|
|
19
|
+
/** SH9 of the diffuse irradiance: `@data(uniform)` in the forward shader. */
|
|
18
20
|
sh;
|
|
19
21
|
/** 'pending' until the GPU has prefiltered the current key. */
|
|
20
22
|
state = "pending";
|
|
@@ -37,20 +39,25 @@ class Environment {
|
|
|
37
39
|
size: [this.size, this.size, 6],
|
|
38
40
|
format: "rgba16float",
|
|
39
41
|
mipLevelCount: mips,
|
|
40
|
-
|
|
42
|
+
// Written by compute on the full tier; rendered face by face on baseline (0064).
|
|
43
|
+
usage: GPUTextureUsage.TEXTURE_BINDING | (gpu.tier === "full" ? GPUTextureUsage.STORAGE_BINDING : GPUTextureUsage.RENDER_ATTACHMENT) | GPUTextureUsage.COPY_SRC,
|
|
44
|
+
...bindingDimension(gpu, "cube")
|
|
41
45
|
});
|
|
42
46
|
this.specular = gpu.device.createTexture({
|
|
43
47
|
label: `environment/${kind}/specular`,
|
|
44
48
|
size: [SPECULAR_SIZE, SPECULAR_SIZE, 6],
|
|
45
49
|
format: "rgba16float",
|
|
46
50
|
mipLevelCount: SPECULAR_MIPS,
|
|
47
|
-
|
|
51
|
+
// Written by compute on the full tier; rendered face by face on baseline (0064).
|
|
52
|
+
usage: GPUTextureUsage.TEXTURE_BINDING | (gpu.tier === "full" ? GPUTextureUsage.STORAGE_BINDING : GPUTextureUsage.RENDER_ATTACHMENT) | GPUTextureUsage.COPY_SRC,
|
|
53
|
+
...bindingDimension(gpu, "cube")
|
|
48
54
|
});
|
|
49
55
|
this.sourceView = this.source.createView({ dimension: "cube" });
|
|
50
56
|
this.specularView = this.specular.createView({ dimension: "cube" });
|
|
51
|
-
this.sh = new
|
|
57
|
+
this.sh = new DataStore(gpu, {
|
|
52
58
|
label: `environment/${kind}/sh`,
|
|
53
|
-
|
|
59
|
+
kind: "uniform",
|
|
60
|
+
usage: GPUBufferUsage.COPY_SRC,
|
|
54
61
|
size: 9 * 16
|
|
55
62
|
});
|
|
56
63
|
}
|
|
@@ -74,6 +81,8 @@ class EnvironmentStore {
|
|
|
74
81
|
frame = 0;
|
|
75
82
|
/** The frame the prefilter work last ran (it runs once per frame, for all views). */
|
|
76
83
|
ranFrame = -1;
|
|
84
|
+
/** The baseline tier's prefilter (0064): fragment passes, loaded only on a baseline device. */
|
|
85
|
+
baseline;
|
|
77
86
|
ensure(gpu) {
|
|
78
87
|
if (this.generation === gpu.generation && this.lut) return;
|
|
79
88
|
this.generation = gpu.generation;
|
|
@@ -84,19 +93,21 @@ class EnvironmentStore {
|
|
|
84
93
|
label: "environment/brdf-lut",
|
|
85
94
|
size: [LUT_SIZE, LUT_SIZE],
|
|
86
95
|
format: "rgba16float",
|
|
87
|
-
|
|
96
|
+
// Computed on the full tier; drawn by a fragment pass on baseline (0064).
|
|
97
|
+
usage: GPUTextureUsage.TEXTURE_BINDING | (gpu.tier === "full" ? GPUTextureUsage.STORAGE_BINDING : GPUTextureUsage.RENDER_ATTACHMENT)
|
|
88
98
|
});
|
|
89
99
|
this.lutReady = false;
|
|
90
100
|
this.emptyCube = gpu.device.createTexture({
|
|
91
101
|
label: "environment/empty",
|
|
92
102
|
size: [1, 1, 6],
|
|
93
103
|
format: "rgba16float",
|
|
94
|
-
usage: GPUTextureUsage.TEXTURE_BINDING
|
|
104
|
+
usage: GPUTextureUsage.TEXTURE_BINDING,
|
|
105
|
+
...bindingDimension(gpu, "cube")
|
|
95
106
|
});
|
|
96
107
|
this.emptyView = this.emptyCube.createView({ dimension: "cube" });
|
|
97
|
-
this.emptySh = new
|
|
108
|
+
this.emptySh = new DataStore(gpu, {
|
|
98
109
|
label: "environment/empty-sh",
|
|
99
|
-
|
|
110
|
+
kind: "uniform",
|
|
100
111
|
size: 9 * 16
|
|
101
112
|
});
|
|
102
113
|
this.sampler = gpu.device.createSampler({
|
package/dist/environment.js
CHANGED
|
@@ -90,6 +90,7 @@ const ProceduralSky = defineComponent(
|
|
|
90
90
|
const DefaultEnvironment = defineResource(
|
|
91
91
|
"render/DefaultEnvironment",
|
|
92
92
|
{
|
|
93
|
+
hostWritable: true,
|
|
93
94
|
description: "The environment (map or procedural sky) for cameras that have none. AmbientLight is the fallback when neither is set.",
|
|
94
95
|
init: () => ({ map: null, sky: null, background: true })
|
|
95
96
|
}
|
|
@@ -194,6 +195,7 @@ const prepareEnvironments = defineSystem({
|
|
|
194
195
|
} else if (map?.texture) useMap(map, skybox);
|
|
195
196
|
else if (defaults.map?.texture)
|
|
196
197
|
useMap(defaults.map, defaults.background ? Math.max(skybox, 1) : skybox);
|
|
198
|
+
if (cam.alphaOutput) entry.background = -1;
|
|
197
199
|
store.cameras.set(e, entry);
|
|
198
200
|
view.data.environment = entry;
|
|
199
201
|
}
|
|
@@ -312,6 +314,10 @@ function runEnvironmentWork(ctx) {
|
|
|
312
314
|
const gpu = ctx.gpu;
|
|
313
315
|
const store = world.resource(Environments);
|
|
314
316
|
if (store.ranFrame === store.frame) return;
|
|
317
|
+
if (gpu.tier === "baseline") {
|
|
318
|
+
store.baseline?.runBaselineEnvironment(ctx, store);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
315
321
|
if (store.pending.length === 0 && store.lutReady) {
|
|
316
322
|
store.ranFrame = store.frame;
|
|
317
323
|
return;
|
|
@@ -422,7 +428,7 @@ function runEnvironmentWork(ctx) {
|
|
|
422
428
|
mipLevelCount: 1
|
|
423
429
|
})
|
|
424
430
|
},
|
|
425
|
-
{ binding: 1, resource: { buffer: env.sh.
|
|
431
|
+
{ binding: 1, resource: { buffer: env.sh.gpuBuffer } }
|
|
426
432
|
]
|
|
427
433
|
})
|
|
428
434
|
);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type ComponentDef, type World } from '@aethervtt/shard-core';
|
|
2
|
+
/**
|
|
3
|
+
* Logs `render/feature-missing` once per world and plugin: `what` asked for a feature whose
|
|
4
|
+
* `plugin` (one forwardPlugin includes) isn't installed, and `outcome` is what happens instead.
|
|
5
|
+
*/
|
|
6
|
+
export declare function warnFeatureMissing(world: World, what: string, plugin: string, outcome: string): void;
|
|
7
|
+
/** How a feature runs on the baseline tier: a short account of its strategy there. */
|
|
8
|
+
export interface BaselineStrategy {
|
|
9
|
+
strategy: string;
|
|
10
|
+
}
|
|
11
|
+
export interface RenderFeature {
|
|
12
|
+
/** `package/feature`, e.g. `render/bloom`. */
|
|
13
|
+
readonly name: string;
|
|
14
|
+
readonly description: string;
|
|
15
|
+
/** The graph nodes it adds. */
|
|
16
|
+
readonly nodes: readonly string[];
|
|
17
|
+
/** What it does on the baseline tier, or `'unsupported'`. */
|
|
18
|
+
readonly baseline: BaselineStrategy | 'unsupported';
|
|
19
|
+
/** Components that mean a scene uses it (for `render/feature-unsupported` on baseline). */
|
|
20
|
+
readonly components?: readonly ComponentDef[];
|
|
21
|
+
}
|
|
22
|
+
export declare const RenderFeatures: import("@aethervtt/shard-core").ResourceDef<Map<string, RenderFeature>>;
|
|
23
|
+
/** Registers features a plugin installs. Plugins call it where they add their graph nodes. */
|
|
24
|
+
export declare function addRenderFeatures(world: World, ...features: RenderFeature[]): void;
|
|
25
|
+
/** The feature a graph node belongs to, if one registered it. */
|
|
26
|
+
export declare function featureOfNode(world: World, node: string): RenderFeature | undefined;
|
|
27
|
+
/** `render.describe → features`: each installed feature, its nodes, and its baseline strategy. */
|
|
28
|
+
export declare function describeFeatures(world: World): {
|
|
29
|
+
name: string;
|
|
30
|
+
nodes: string[];
|
|
31
|
+
baseline: string;
|
|
32
|
+
}[];
|
|
33
|
+
/**
|
|
34
|
+
* The graph nodes of features the baseline tier doesn't run (`baseline: 'unsupported'`): the
|
|
35
|
+
* graph skips them on a baseline device, so they never ask for what it lacks.
|
|
36
|
+
*/
|
|
37
|
+
export declare function unsupportedNodes(world: World): ReadonlySet<string>;
|
|
38
|
+
/**
|
|
39
|
+
* Raises `render/feature-unsupported` for `feature` (a feature, or a part of one) that a scene uses
|
|
40
|
+
* on the baseline tier: RenderHealth turns degraded until `clearUnsupported`.
|
|
41
|
+
*/
|
|
42
|
+
export declare function reportUnsupported(world: World, feature: string, message: string): void;
|
|
43
|
+
export declare function clearUnsupported(world: World, feature: string): void;
|
|
44
|
+
/**
|
|
45
|
+
* On the baseline tier: reports each unsupported feature whose components are in the world, and
|
|
46
|
+
* clears the report once they're gone. A query per component, made once; issues change only when
|
|
47
|
+
* a feature's use does.
|
|
48
|
+
*/
|
|
49
|
+
export declare function checkUnsupportedFeatures(world: World): void;
|