@aethervtt/shard-render 0.0.1 → 0.2.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/ablation.d.ts +74 -0
- package/dist/ablation.js +117 -0
- 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 +157 -30
- package/dist/atmosphere-plugin.js +8 -2
- package/dist/atmosphere-shaders.js +17 -5
- 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 +133 -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/crowd.d.ts +21 -0
- package/dist/crowd.js +121 -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/decals-plugin.d.ts +5 -0
- package/dist/decals-plugin.js +54 -0
- package/dist/decals.d.ts +69 -0
- package/dist/decals.js +326 -0
- package/dist/deferred-shaders.js +2 -0
- package/dist/deferred.js +28 -4
- 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 +37 -13
- 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/foliage-budget.d.ts +64 -0
- package/dist/foliage-budget.js +108 -0
- package/dist/foliage-path.d.ts +19 -0
- package/dist/foliage-path.js +7 -0
- package/dist/foliage-shaders.d.ts +11 -0
- package/dist/foliage-shaders.js +504 -0
- package/dist/foliage.d.ts +194 -0
- package/dist/foliage.js +1027 -0
- package/dist/forward.d.ts +38 -2
- package/dist/forward.js +608 -186
- 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 +45 -0
- package/dist/graph.js +169 -19
- package/dist/health.d.ts +58 -0
- package/dist/health.js +112 -0
- package/dist/index.d.ts +40 -11
- package/dist/index.js +343 -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/overlay-registry.d.ts +3 -1
- package/dist/overlay-registry.js +3 -1
- package/dist/overlays.js +6 -2
- package/dist/perf-overlay.d.ts +9 -0
- package/dist/perf-overlay.js +125 -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 +69 -7
- package/dist/plugin.js +223 -25
- package/dist/post-common.d.ts +22 -2
- package/dist/post-common.js +82 -2
- package/dist/post-nodes.d.ts +7 -1
- package/dist/post-nodes.js +235 -111
- package/dist/post-plugin.js +5 -3
- package/dist/post-scene.d.ts +130 -0
- package/dist/post-scene.js +164 -0
- package/dist/post-shaders.js +85 -62
- package/dist/post.js +11 -1
- 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/ribbons-plugin.d.ts +5 -0
- package/dist/ribbons-plugin.js +59 -0
- package/dist/ribbons.d.ts +44 -0
- package/dist/ribbons.js +288 -0
- package/dist/scenario.d.ts +70 -0
- package/dist/scenario.js +125 -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 +189 -34
- package/dist/shadow-catcher.d.ts +9 -0
- package/dist/shadow-catcher.js +133 -0
- package/dist/shadows.d.ts +27 -2
- package/dist/shadows.js +100 -20
- package/dist/standard-fields.d.ts +1 -0
- package/dist/standard-fields.js +5 -0
- package/dist/standard.js +18 -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 +11 -0
- package/dist/testing.js +39 -1
- package/dist/tier.d.ts +13 -0
- package/dist/tier.js +14 -0
- package/dist/timer.d.ts +18 -1
- package/dist/timer.js +73 -20
- package/dist/variants.d.ts +37 -0
- package/dist/variants.js +104 -0
- package/dist/vfx-draw.d.ts +37 -0
- package/dist/vfx-draw.js +291 -0
- package/dist/vfx-state.d.ts +42 -0
- package/dist/vfx-state.js +81 -0
- package/dist/vfx.d.ts +69 -0
- package/dist/vfx.js +103 -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 +23 -11
package/dist/forward.js
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { assetServer } from "@aethervtt/shard-assets";
|
|
2
|
+
import {
|
|
3
|
+
defineResource,
|
|
4
|
+
defineSystem,
|
|
5
|
+
Last,
|
|
6
|
+
PostUpdate,
|
|
7
|
+
PreUpdate
|
|
8
|
+
} from "@aethervtt/shard-core";
|
|
2
9
|
import { GpuBuffer } from "@aethervtt/shard-gpu";
|
|
3
10
|
import { definePlugin, LogResource, Time } from "@aethervtt/shard-runtime";
|
|
4
11
|
import { setTextureCapabilities, Textures } from "@aethervtt/shard-texture";
|
|
@@ -23,11 +30,16 @@ import {
|
|
|
23
30
|
MAX_LIGHTS_PER_CLUSTER,
|
|
24
31
|
ViewLightList
|
|
25
32
|
} from "./clusters.js";
|
|
26
|
-
import { Culler, cullTransparent, GpuCuller } from "./culling.js";
|
|
33
|
+
import { Culler, cullGround, cullTransparent, GpuCuller } from "./culling.js";
|
|
34
|
+
import { Cutaway, CutawayPath, CutawayView } from "./cutaway.js";
|
|
35
|
+
import { DataStore, dataEntry } from "./data-store.js";
|
|
27
36
|
import { addDisplayNodes } from "./display-nodes.js";
|
|
28
37
|
import { Environments, environmentParams } from "./environment-state.js";
|
|
38
|
+
import { addRenderFeatures } from "./features.js";
|
|
39
|
+
import { FoliagePath } from "./foliage-path.js";
|
|
29
40
|
import { GpuAssets, GpuAssetsResource } from "./gpu-assets.js";
|
|
30
41
|
import { RenderPhase } from "./graph.js";
|
|
42
|
+
import { clearHealthIssue, MaterialFallbacks, raiseHealthIssue } from "./health.js";
|
|
31
43
|
import {
|
|
32
44
|
DeformPath,
|
|
33
45
|
InstanceData,
|
|
@@ -44,10 +56,14 @@ import {
|
|
|
44
56
|
NotShadowReceiver,
|
|
45
57
|
observeInstanceRemovals,
|
|
46
58
|
prepareInstances,
|
|
59
|
+
ShadowWhenHidden,
|
|
47
60
|
SkinnedMesh,
|
|
48
61
|
VisibilityRange
|
|
49
62
|
} from "./instances.js";
|
|
63
|
+
import { INTERIOR_BINDINGS, InteriorPath } from "./interior.js";
|
|
50
64
|
import { observeOriginShifts } from "./large-world.js";
|
|
65
|
+
import { GroundLayer, RenderLayers } from "./layers.js";
|
|
66
|
+
import { expireLensFields, Lens, LensFields, LensPath } from "./lens.js";
|
|
51
67
|
import {
|
|
52
68
|
AmbientLight,
|
|
53
69
|
DirectionalLight,
|
|
@@ -69,8 +85,9 @@ import {
|
|
|
69
85
|
variantCull
|
|
70
86
|
} from "./material-pipelines.js";
|
|
71
87
|
import { isTransparent } from "./materials.js";
|
|
88
|
+
import { Outline, OutlinePath, observeOutlinesWithoutPass } from "./outline.js";
|
|
72
89
|
import { PixelPerfect, PixelPerfectPath, PixelTargets } from "./pixel-perfect.js";
|
|
73
|
-
import { Gpu, Graph, RenderSet, Shaders, Views } from "./plugin.js";
|
|
90
|
+
import { Gpu, Graph, RenderDescribers, RenderSet, Shaders, Views } from "./plugin.js";
|
|
74
91
|
import {
|
|
75
92
|
Antialiasing,
|
|
76
93
|
AutoExposure,
|
|
@@ -84,6 +101,13 @@ import {
|
|
|
84
101
|
Vignette
|
|
85
102
|
} from "./post.js";
|
|
86
103
|
import { RenderScale } from "./render-scale.js";
|
|
104
|
+
import {
|
|
105
|
+
describeScreenEffects,
|
|
106
|
+
expireScreenEffects,
|
|
107
|
+
runScreenEffects,
|
|
108
|
+
ScreenEffectHandlers,
|
|
109
|
+
ScreenEffects
|
|
110
|
+
} from "./screen-effects.js";
|
|
87
111
|
import { ViewUniform, viewLayout } from "./shaders.js";
|
|
88
112
|
import {
|
|
89
113
|
assignLocalShadows,
|
|
@@ -92,15 +116,29 @@ import {
|
|
|
92
116
|
fitCascades,
|
|
93
117
|
fitLocalShadows,
|
|
94
118
|
LocalShadows,
|
|
119
|
+
markShadowDrawn,
|
|
95
120
|
packShadowData,
|
|
96
121
|
SHADOW_DATA_FLOATS,
|
|
97
122
|
ShadowPassUniforms,
|
|
98
123
|
ShadowsResource,
|
|
124
|
+
shadowRedraw,
|
|
99
125
|
shadowViewLayout
|
|
100
126
|
} from "./shadows.js";
|
|
101
127
|
import { SkinAssetType, Skins } from "./skin-asset.js";
|
|
102
128
|
import { GpuMemory, RenderCounters, RenderStats } from "./stats.js";
|
|
129
|
+
import { bindingDimension } from "./tier.js";
|
|
130
|
+
import { addShaderVariantSource, materialVariants, ShaderVariantSources } from "./variants.js";
|
|
103
131
|
import {
|
|
132
|
+
Beam,
|
|
133
|
+
Decal,
|
|
134
|
+
DecalReceiver,
|
|
135
|
+
DecalsPath,
|
|
136
|
+
observeVfxWithoutPlugin,
|
|
137
|
+
RibbonsPath,
|
|
138
|
+
Trail
|
|
139
|
+
} from "./vfx.js";
|
|
140
|
+
import {
|
|
141
|
+
CameraMoved,
|
|
104
142
|
Cameras,
|
|
105
143
|
cameraOf,
|
|
106
144
|
DeferredPath,
|
|
@@ -109,6 +147,12 @@ import {
|
|
|
109
147
|
Tonemapping,
|
|
110
148
|
ViewSettings
|
|
111
149
|
} from "./view.js";
|
|
150
|
+
import {
|
|
151
|
+
HiddenSetsResource,
|
|
152
|
+
NO_HIDDEN,
|
|
153
|
+
observeViewVisibilityWithoutPlugin,
|
|
154
|
+
ViewVisibility
|
|
155
|
+
} from "./view-visibility.js";
|
|
112
156
|
import { ComputedVisibility, computeVisibility, Visibility } from "./visibility.js";
|
|
113
157
|
const ForwardStateResource = defineResource("render/ForwardState");
|
|
114
158
|
const State = ForwardStateResource;
|
|
@@ -161,7 +205,8 @@ const cameraCull = {
|
|
|
161
205
|
lodScale: 1,
|
|
162
206
|
orthographic: false,
|
|
163
207
|
lodState: void 0,
|
|
164
|
-
updateLod: true
|
|
208
|
+
updateLod: true,
|
|
209
|
+
layers: 4294967295
|
|
165
210
|
};
|
|
166
211
|
const shadowCull = {
|
|
167
212
|
planes: null,
|
|
@@ -170,19 +215,23 @@ const shadowCull = {
|
|
|
170
215
|
lodScale: 1,
|
|
171
216
|
orthographic: false,
|
|
172
217
|
lodState: void 0,
|
|
173
|
-
updateLod: false
|
|
218
|
+
updateLod: false,
|
|
219
|
+
layers: 4294967295
|
|
174
220
|
};
|
|
175
221
|
function cameraParams(cam, out) {
|
|
176
222
|
out.planes = cam.frozenFrustum ?? cam.frustum;
|
|
177
223
|
out.eye = cam.frozenPosition ?? cam.position;
|
|
178
224
|
out.orthographic = cam.orthographic;
|
|
225
|
+
out.layers = cam.layers;
|
|
226
|
+
out.hidden = cam.hidden?.bits;
|
|
227
|
+
out.hiddenBase = cam.hidden ? cam.hidden.base : NO_HIDDEN;
|
|
179
228
|
out.lodScale = cam.orthographic ? 2 / cam.orthoHeight : 1 / Math.tan(cam.fovY / 2);
|
|
180
229
|
if (cam.lodState.length < cam.lodCapacity)
|
|
181
230
|
cam.lodState = new Uint8Array(cam.lodCapacity).fill(LOD_UNSET);
|
|
182
231
|
out.lodState = cam.lodState;
|
|
183
232
|
return out;
|
|
184
233
|
}
|
|
185
|
-
const
|
|
234
|
+
const forwardQueue = defineSystem({
|
|
186
235
|
name: "render/forward-queue",
|
|
187
236
|
description: "Culls instances per camera and shadow view (on the GPU when it can), clusters lights, and writes view uniforms.",
|
|
188
237
|
run: (_, world) => {
|
|
@@ -197,7 +246,8 @@ const queue = defineSystem({
|
|
|
197
246
|
stats.clear();
|
|
198
247
|
state.frame++;
|
|
199
248
|
ensureLayouts(gpu, state, world.resource(GpuAssetsResource), store);
|
|
200
|
-
|
|
249
|
+
chooseInterior(gpu, world, state);
|
|
250
|
+
state.pipelines.beginFrame(state.frame, world.resource(Shaders).revision);
|
|
201
251
|
shadows.uniforms.reset();
|
|
202
252
|
culler.beginFrame();
|
|
203
253
|
const gpuCull = culler.active;
|
|
@@ -219,8 +269,19 @@ const queue = defineSystem({
|
|
|
219
269
|
if (gpuCull) {
|
|
220
270
|
culler.add(store, cam.draws, params, culler.lodCamera(cam.entity), forwardOnly);
|
|
221
271
|
cullTransparent(store, cam.transparent, params, cam.forward);
|
|
272
|
+
cullGround(store, cam.ground, params, cam.overlay);
|
|
222
273
|
} else {
|
|
223
|
-
store.cullCpu(
|
|
274
|
+
store.cullCpu(
|
|
275
|
+
cam.draws,
|
|
276
|
+
params,
|
|
277
|
+
void 0,
|
|
278
|
+
cam.transparent,
|
|
279
|
+
cam.forward,
|
|
280
|
+
forwardOnly,
|
|
281
|
+
false,
|
|
282
|
+
cam.ground,
|
|
283
|
+
cam.overlay
|
|
284
|
+
);
|
|
224
285
|
}
|
|
225
286
|
store.orderNearFirst(cam.draws, cam.position);
|
|
226
287
|
if (view.order < mainOrder) {
|
|
@@ -228,14 +289,18 @@ const queue = defineSystem({
|
|
|
228
289
|
mainOrder = view.order;
|
|
229
290
|
}
|
|
230
291
|
}
|
|
231
|
-
const shadowParams = (cam, planes) => {
|
|
292
|
+
const shadowParams = (cam, planes, own) => {
|
|
232
293
|
cameraParams(cam, shadowCull);
|
|
233
294
|
shadowCull.planes = planes;
|
|
234
295
|
shadowCull.updateLod = false;
|
|
296
|
+
if (!own || !cam.hidden?.shadows) {
|
|
297
|
+
shadowCull.hidden = void 0;
|
|
298
|
+
shadowCull.hiddenBase = NO_HIDDEN;
|
|
299
|
+
}
|
|
235
300
|
return shadowCull;
|
|
236
301
|
};
|
|
237
|
-
const cullShadow = (cam, draws, planes, box) => {
|
|
238
|
-
const params = shadowParams(cam, planes);
|
|
302
|
+
const cullShadow = (cam, draws, planes, box, own = false) => {
|
|
303
|
+
const params = shadowParams(cam, planes, own);
|
|
239
304
|
if (gpuCull) {
|
|
240
305
|
culler.add(store, draws, params, culler.lodCamera(cam.entity));
|
|
241
306
|
if (box) store.casterBounds(box);
|
|
@@ -246,16 +311,19 @@ const queue = defineSystem({
|
|
|
246
311
|
};
|
|
247
312
|
const local = shadows.local;
|
|
248
313
|
fitLocalShadows(local, settings.shadowMapSize);
|
|
314
|
+
store.checkMeshVersions();
|
|
249
315
|
if (main) {
|
|
250
316
|
for (let i = 0; i < local.spots.length; i++) {
|
|
251
317
|
const view = local.spotViews[i];
|
|
252
318
|
cullShadow(main, view.draws, view.frustum);
|
|
253
319
|
view.offset = shadows.uniforms.push(view.viewProj);
|
|
320
|
+
view.redraw = shadowRedraw(view, local.spots[i]?.cachedShadow ?? false, store);
|
|
254
321
|
}
|
|
255
322
|
for (let i = 0; i < local.points.length * 6; i++) {
|
|
256
323
|
const view = local.pointViews[i];
|
|
257
324
|
cullShadow(main, view.draws, view.frustum);
|
|
258
325
|
view.offset = shadows.uniforms.push(view.viewProj);
|
|
326
|
+
view.redraw = shadowRedraw(view, local.points[i / 6 | 0]?.cachedShadow ?? false, store);
|
|
259
327
|
}
|
|
260
328
|
}
|
|
261
329
|
for (const view of world.resource(Views).list) {
|
|
@@ -264,7 +332,20 @@ const queue = defineSystem({
|
|
|
264
332
|
const pv = viewGpu(gpu, state, view.name);
|
|
265
333
|
pv.ao = void 0;
|
|
266
334
|
pv.lightList.build(cam, lights.records, lights.high);
|
|
267
|
-
pv.clusters.upload(pv.lightList, cam, settings.clusterFar);
|
|
335
|
+
if (pv.clusters) pv.clusters.upload(pv.lightList, cam, settings.clusterFar);
|
|
336
|
+
else {
|
|
337
|
+
const b = pv.baseline;
|
|
338
|
+
state.baseline.binBaselineLights(
|
|
339
|
+
b,
|
|
340
|
+
pv.lightList,
|
|
341
|
+
lights.data,
|
|
342
|
+
cam,
|
|
343
|
+
settings.clusterFar,
|
|
344
|
+
settings.baselineMaxLights
|
|
345
|
+
);
|
|
346
|
+
if (b.dropped !== b.reported)
|
|
347
|
+
reportLightBudget(world, view.name, b, settings.baselineMaxLights);
|
|
348
|
+
}
|
|
268
349
|
const sun = lights.shadowSun;
|
|
269
350
|
if (sun) {
|
|
270
351
|
fitCascades(
|
|
@@ -272,11 +353,12 @@ const queue = defineSystem({
|
|
|
272
353
|
cam,
|
|
273
354
|
sun,
|
|
274
355
|
settings.cascadeMapSize,
|
|
275
|
-
(v, planes, box) => cullShadow(cam, v.draws, planes, box)
|
|
356
|
+
(v, planes, box) => cullShadow(cam, v.draws, planes, box, true)
|
|
276
357
|
);
|
|
277
358
|
for (let i = 0; i < pv.cascades.count; i++) {
|
|
278
359
|
const v = pv.cascades.views[i];
|
|
279
360
|
v.offset = shadows.uniforms.push(v.viewProj);
|
|
361
|
+
v.redraw = shadowRedraw(v, sun.cached, store);
|
|
280
362
|
}
|
|
281
363
|
} else {
|
|
282
364
|
pv.cascades.count = 0;
|
|
@@ -302,8 +384,8 @@ const queue = defineSystem({
|
|
|
302
384
|
st.visible += cam.forwardOnly.visible;
|
|
303
385
|
st.drawCalls += cam.forwardOnly.cullView >= 0 ? culler.drawCounts.get(cam.forwardOnly) ?? 0 : cam.forwardOnly.length;
|
|
304
386
|
}
|
|
305
|
-
st.visible += cam.transparent.visible;
|
|
306
|
-
st.drawCalls += cam.transparent.length;
|
|
387
|
+
st.visible += cam.transparent.visible + cam.ground.visible;
|
|
388
|
+
st.drawCalls += cam.transparent.length + cam.ground.length;
|
|
307
389
|
stats.set(view.name, st);
|
|
308
390
|
}
|
|
309
391
|
}
|
|
@@ -318,10 +400,11 @@ function viewGpu(gpu, state, name) {
|
|
|
318
400
|
size: viewLayout.size
|
|
319
401
|
}),
|
|
320
402
|
lightList: new ViewLightList(),
|
|
321
|
-
clusters: new ClusterBuffers(gpu, name),
|
|
322
|
-
|
|
403
|
+
clusters: state.baseline ? void 0 : new ClusterBuffers(gpu, name),
|
|
404
|
+
baseline: state.baseline?.baselineViewLights(gpu, name),
|
|
405
|
+
shadowData: new DataStore(gpu, {
|
|
323
406
|
label: `${name}/shadows`,
|
|
324
|
-
|
|
407
|
+
kind: "uniform",
|
|
325
408
|
size: SHADOW_DATA_FLOATS * 4
|
|
326
409
|
}),
|
|
327
410
|
cascades: new Cascades(),
|
|
@@ -351,6 +434,14 @@ const ambientScratch = new Float32Array(3);
|
|
|
351
434
|
const envScratch = new Float32Array(4);
|
|
352
435
|
const jitterScratch = new Float32Array(4);
|
|
353
436
|
const noTransmittance = new Float32Array(16).fill(1);
|
|
437
|
+
const pixelScaleScratch = new Float32Array(4);
|
|
438
|
+
function viewPixelScale(cam, out) {
|
|
439
|
+
out[0] = cam.pixelRatio * cam.width / Math.max(1, cam.displayWidth);
|
|
440
|
+
out[1] = cam.orthographic ? cam.orthoHeight / Math.max(1, cam.height) : 2 * Math.tan(cam.fovY / 2) / Math.max(1, cam.height);
|
|
441
|
+
out[2] = cam.orthographic ? 1 : 0;
|
|
442
|
+
out[3] = 0;
|
|
443
|
+
return out;
|
|
444
|
+
}
|
|
354
445
|
function writeViewUniform(world, state, pv, cam, ambient, clusterFar) {
|
|
355
446
|
environmentParams(world.resource(Environments), cam, envScratch);
|
|
356
447
|
const [near, far] = clusterRange(cam, clusterFar);
|
|
@@ -381,7 +472,8 @@ function writeViewUniform(world, state, pv, cam, ambient, clusterFar) {
|
|
|
381
472
|
viewProjNoJitter: cam.viewProjNoJitter,
|
|
382
473
|
prevViewProj: cam.prevViewProj,
|
|
383
474
|
jitter: jitterScratch,
|
|
384
|
-
sunTransmittance: world.tryResource(Atmospheres)?.cameras.get(cam.entity)?.sunTransmittance ?? noTransmittance
|
|
475
|
+
sunTransmittance: world.tryResource(Atmospheres)?.cameras.get(cam.entity)?.sunTransmittance ?? noTransmittance,
|
|
476
|
+
pixelScale: viewPixelScale(cam, pixelScaleScratch)
|
|
385
477
|
});
|
|
386
478
|
pv.uniform.write(new Float32Array(state.viewBytes.buffer, 0, viewLayout.size / 4));
|
|
387
479
|
}
|
|
@@ -392,7 +484,7 @@ const upload = defineSystem({
|
|
|
392
484
|
const store = world.resource(Instances);
|
|
393
485
|
const culler = world.resource(Culler);
|
|
394
486
|
if (culler.active) {
|
|
395
|
-
culler.prepare(store);
|
|
487
|
+
culler.prepare(store, world.tryResource(HiddenSetsResource));
|
|
396
488
|
store.gpuVisible = culler.visible;
|
|
397
489
|
}
|
|
398
490
|
store.finishFrame();
|
|
@@ -408,15 +500,33 @@ const VERTEX_BUFFERS = [
|
|
|
408
500
|
{ arrayStride: 16, attributes: [{ shaderLocation: 4, offset: 0, format: "float32x4" }] }
|
|
409
501
|
];
|
|
410
502
|
const FORWARD_DEFINES = [
|
|
411
|
-
{ PREMULTIPLY: false, MASK: false },
|
|
412
|
-
{ PREMULTIPLY: true, MASK: false },
|
|
413
|
-
{ PREMULTIPLY: false, MASK: true },
|
|
414
|
-
{ PREMULTIPLY: true, MASK: true }
|
|
503
|
+
{ PREMULTIPLY: false, MASK: false, OPAQUE: false },
|
|
504
|
+
{ PREMULTIPLY: true, MASK: false, OPAQUE: false },
|
|
505
|
+
{ PREMULTIPLY: false, MASK: true, OPAQUE: false },
|
|
506
|
+
{ PREMULTIPLY: true, MASK: true, OPAQUE: false },
|
|
507
|
+
{ PREMULTIPLY: false, MASK: false, OPAQUE: true },
|
|
508
|
+
{ PREMULTIPLY: true, MASK: false, OPAQUE: true },
|
|
509
|
+
{ PREMULTIPLY: false, MASK: true, OPAQUE: true },
|
|
510
|
+
{ PREMULTIPLY: true, MASK: true, OPAQUE: true }
|
|
511
|
+
];
|
|
512
|
+
const CUT_KEY = 2 ** 40;
|
|
513
|
+
const INTERIOR_KEY = 2 ** 41;
|
|
514
|
+
const CUTAWAY_DEFINES = FORWARD_DEFINES.map((d) => ({ ...d, CUTAWAY: true }));
|
|
515
|
+
const INTERIOR_DEFINES = [
|
|
516
|
+
void 0,
|
|
517
|
+
{ SKY_VISIBILITY: true },
|
|
518
|
+
{ BLOCKED_LIGHTS: true },
|
|
519
|
+
{ SKY_VISIBILITY: true, BLOCKED_LIGHTS: true }
|
|
415
520
|
];
|
|
416
|
-
|
|
521
|
+
const LIT_DEFINES = INTERIOR_DEFINES.map(
|
|
522
|
+
(extra) => [FORWARD_DEFINES, CUTAWAY_DEFINES].map(
|
|
523
|
+
(set) => extra ? set.map((d) => ({ ...d, ...extra })) : set
|
|
524
|
+
)
|
|
525
|
+
);
|
|
526
|
+
function materialLayout(gpu, state, assets, type, store, cut) {
|
|
417
527
|
return gpu.layouts.pipelineLayout({
|
|
418
|
-
label: `forward/${type.name}`,
|
|
419
|
-
bindGroupLayouts: [state.layouts.view, assets.layoutOf(type), store.layout]
|
|
528
|
+
label: cut ? `forward/${type.name}/cutaway` : `forward/${type.name}`,
|
|
529
|
+
bindGroupLayouts: cut ? [state.layouts.view, assets.layoutOf(type), store.layout, cut] : [state.layouts.view, assets.layoutOf(type), store.layout]
|
|
420
530
|
});
|
|
421
531
|
}
|
|
422
532
|
const PASS_OPAQUE = 0;
|
|
@@ -424,11 +534,16 @@ const PASS_TRANSPARENT = 1;
|
|
|
424
534
|
const PASS_GBUFFER = 3;
|
|
425
535
|
const PASS_PREPASS = 5;
|
|
426
536
|
const PASS_PICK = 7;
|
|
537
|
+
const PASS_GROUND = 9;
|
|
538
|
+
const PASS_PICK_GROUND = 11;
|
|
539
|
+
const PASS_DEPTH = 13;
|
|
540
|
+
const GROUND_DEPTH_BIAS = 8;
|
|
541
|
+
const GROUND_SLOPE_BIAS = 2;
|
|
427
542
|
const PICK_TARGETS = [
|
|
428
543
|
{ format: "r32uint" },
|
|
429
544
|
{ format: "rgba32float" }
|
|
430
545
|
];
|
|
431
|
-
const PREPASS_TARGETS = [{ format: "
|
|
546
|
+
const PREPASS_TARGETS = [{ format: "rg16float" }, { format: "rg16float" }];
|
|
432
547
|
function drawMaterials(ctx, state, pv, cam, draws, pass) {
|
|
433
548
|
const store = ctx.world.resource(Instances);
|
|
434
549
|
const assets = ctx.world.resource(GpuAssetsResource);
|
|
@@ -439,6 +554,9 @@ function drawMaterials(ctx, state, pv, cam, draws, pass) {
|
|
|
439
554
|
const args = ctx.world.resource(Culler).args.buffer;
|
|
440
555
|
renderPass.setBindGroup(0, viewBindGroup(gpu, ctx.world, pv, cam));
|
|
441
556
|
renderPass.setBindGroup(2, instances);
|
|
557
|
+
const cutaway = ctx.world.tryResource(CutawayPath);
|
|
558
|
+
const cutGroup = cutaway?.bindGroup(gpu, cam.entity);
|
|
559
|
+
if (cutGroup) renderPass.setBindGroup(3, cutGroup);
|
|
442
560
|
let current;
|
|
443
561
|
let boundMaterial;
|
|
444
562
|
let boundPositions;
|
|
@@ -447,52 +565,15 @@ function drawMaterials(ctx, state, pv, cam, draws, pass) {
|
|
|
447
565
|
let switches = 0;
|
|
448
566
|
for (let d = 0; d < draws.length; d++) {
|
|
449
567
|
const item = draws.items[d];
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
const
|
|
453
|
-
const
|
|
454
|
-
const
|
|
455
|
-
const
|
|
456
|
-
const pick = pass === PASS_PICK;
|
|
457
|
-
if (gbuffer && !type.standard) continue;
|
|
458
|
-
const msaa = gbuffer || prepass || pick ? 1 : cam.msaa;
|
|
459
|
-
const key = ((pass * 1024 + typeOrdinal(type)) * 16 + variant) * 8 + msaa;
|
|
460
|
-
let pipeline = state.pipelines.cached(key);
|
|
568
|
+
const own = item.batch.material;
|
|
569
|
+
if (!inPass(own.type, pass)) continue;
|
|
570
|
+
const cut = cutGroup !== void 0 && item.batch.cutaway > 0 && cutaway.cuts(cam.entity, item.batch.index) ? cutaway : void 0;
|
|
571
|
+
const fallback = state.pipelines.failing(own.type);
|
|
572
|
+
const material = fallback ? state.pipelines.proxy(own) : own;
|
|
573
|
+
const pipeline = materialPipeline(ctx, state, cam, store, assets, material, pass, true, cut);
|
|
461
574
|
if (!pipeline) {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
const slot = pick ? 40 : prepass ? 32 + (type.standard ? 0 : 4) + (mask ? 2 : 0) : (gbuffer ? 16 : type.standard ? 0 : 4) + (premultiply ? 1 : 0) + (mask ? 2 : 0);
|
|
465
|
-
const module = state.pipelines.module(
|
|
466
|
-
ctx.world,
|
|
467
|
-
gpu,
|
|
468
|
-
type,
|
|
469
|
-
slot,
|
|
470
|
-
pick ? "shard::pick" : prepass ? type.standard ? "shard::prepass" : "shard::prepass::plain" : gbuffer ? "shard::pbr::gbuffer_pass" : type.standard ? "shard::pbr::forward" : "shard::unlit::forward",
|
|
471
|
-
FORWARD_DEFINES[slot & 3]
|
|
472
|
-
);
|
|
473
|
-
if (!module) {
|
|
474
|
-
gpu.pipelines.skipped++;
|
|
475
|
-
continue;
|
|
476
|
-
}
|
|
477
|
-
const transparent = isTransparent(blend) && !pick;
|
|
478
|
-
pipeline = state.pipelines.create(gpu, key, {
|
|
479
|
-
label: `${pick ? "pick" : prepass ? "prepass" : gbuffer ? "gbuffer" : "forward"}/${type.name}/${blend}/x${msaa}/${variantCull(variant)}`,
|
|
480
|
-
layout: materialLayout(gpu, state, assets, type, store),
|
|
481
|
-
vertex: { module, entryPoint: "vs", buffers: VERTEX_BUFFERS },
|
|
482
|
-
fragment: {
|
|
483
|
-
module,
|
|
484
|
-
entryPoint: "fs",
|
|
485
|
-
targets: pick ? PICK_TARGETS : prepass ? PREPASS_TARGETS : gbuffer ? state.gbufferTargets : [{ format: "rgba16float", blend: blendState(blend) }]
|
|
486
|
-
},
|
|
487
|
-
primitive: { topology: "triangle-list", cullMode: variantCull(variant), frontFace: "ccw" },
|
|
488
|
-
depthStencil: {
|
|
489
|
-
format: "depth32float",
|
|
490
|
-
depthWriteEnabled: !transparent,
|
|
491
|
-
depthCompare: transparent ? "greater-equal" : "greater"
|
|
492
|
-
},
|
|
493
|
-
multisample: { count: msaa }
|
|
494
|
-
});
|
|
495
|
-
if (!pipeline) continue;
|
|
575
|
+
if (!fallback && state.pipelines.failing(own.type)) d--;
|
|
576
|
+
continue;
|
|
496
577
|
}
|
|
497
578
|
const mat = assets.material(ctx.world, material);
|
|
498
579
|
if (!mat?.bindGroup) continue;
|
|
@@ -529,14 +610,127 @@ function drawMaterials(ctx, state, pv, cam, draws, pass) {
|
|
|
529
610
|
renderPass.draw(gm.count, item.count, 0, item.first);
|
|
530
611
|
}
|
|
531
612
|
}
|
|
613
|
+
if (draws.cullView < 0) warmPipelines(ctx, state, cam, store, assets, pass, cutGroup && cutaway);
|
|
532
614
|
return switches;
|
|
533
615
|
}
|
|
616
|
+
function inPass(type, pass) {
|
|
617
|
+
if (pass === PASS_GBUFFER) return type.standard;
|
|
618
|
+
if (pass === PASS_PICK || pass === PASS_PICK_GROUND) return type.pickable;
|
|
619
|
+
return true;
|
|
620
|
+
}
|
|
621
|
+
function materialPipeline(ctx, state, cam, store, assets, material, pass, draw, cut) {
|
|
622
|
+
const gpu = ctx.gpu;
|
|
623
|
+
const type = material.type;
|
|
624
|
+
const variant = materialVariant(material);
|
|
625
|
+
const blend = variantBlend(variant);
|
|
626
|
+
const gbuffer = pass === PASS_GBUFFER;
|
|
627
|
+
const depthOnly = pass === PASS_DEPTH;
|
|
628
|
+
const prepass = pass === PASS_PREPASS || depthOnly;
|
|
629
|
+
const pick = pass === PASS_PICK || pass === PASS_PICK_GROUND;
|
|
630
|
+
const ground = pass === PASS_GROUND || pass === PASS_PICK_GROUND;
|
|
631
|
+
const msaa = gbuffer || prepass || pick ? 1 : cam.msaa;
|
|
632
|
+
const mode = gbuffer || prepass || pick ? 0 : state.interiorMode;
|
|
633
|
+
const key = ((pass * 1024 + typeOrdinal(type)) * 16 + variant) * 8 + msaa + (cut ? CUT_KEY : 0) + mode * INTERIOR_KEY;
|
|
634
|
+
let pipeline = state.pipelines.cached(key);
|
|
635
|
+
if (!pipeline) {
|
|
636
|
+
const premultiply = blend === "premultiplied";
|
|
637
|
+
const mask = blend === "mask";
|
|
638
|
+
const opaque = !gbuffer && !prepass && !pick && !isTransparent(blend);
|
|
639
|
+
const slot = (cut ? 64 : 0) + (pick ? 40 : prepass ? 32 + (type.standard ? 0 : 4) + (mask ? 2 : 0) : (gbuffer ? 16 : opaque ? 48 : 0) + (type.standard ? 0 : 4) + (premultiply ? 1 : 0) + (mask ? 2 : 0));
|
|
640
|
+
const module = state.pipelines.module(
|
|
641
|
+
ctx.world,
|
|
642
|
+
gpu,
|
|
643
|
+
type,
|
|
644
|
+
slot,
|
|
645
|
+
pick ? "shard::pick" : prepass ? type.standard ? "shard::prepass" : "shard::prepass::plain" : gbuffer ? "shard::pbr::gbuffer_pass" : type.standard ? "shard::pbr::forward" : "shard::unlit::forward",
|
|
646
|
+
// Only what the slot tells apart: the prepass and picking don't premultiply.
|
|
647
|
+
LIT_DEFINES[mode][cut ? 1 : 0][pick ? 0 : prepass ? mask ? 2 : 0 : (premultiply ? 1 : 0) | (mask ? 2 : 0) | (opaque ? 4 : 0)]
|
|
648
|
+
);
|
|
649
|
+
if (!module) {
|
|
650
|
+
if (draw && !state.pipelines.failing(type)) gpu.pipelines.skipped++;
|
|
651
|
+
return void 0;
|
|
652
|
+
}
|
|
653
|
+
const transparent = isTransparent(blend) && !pick;
|
|
654
|
+
pipeline = state.pipelines.create(
|
|
655
|
+
gpu,
|
|
656
|
+
key,
|
|
657
|
+
{
|
|
658
|
+
label: `${pick ? "pick" : prepass ? "prepass" : gbuffer ? "gbuffer" : ground ? "ground" : "forward"}/${type.name}/${blend}/x${msaa}/${variantCull(variant)}${cut ? "/cutaway" : ""}`,
|
|
659
|
+
layout: materialLayout(gpu, state, assets, type, store, cut?.layout(gpu)),
|
|
660
|
+
vertex: { module, entryPoint: "vs", buffers: VERTEX_BUFFERS },
|
|
661
|
+
fragment: {
|
|
662
|
+
module,
|
|
663
|
+
entryPoint: depthOnly ? "fs_depth" : "fs",
|
|
664
|
+
targets: pick ? PICK_TARGETS : depthOnly ? [] : prepass ? PREPASS_TARGETS : gbuffer ? state.gbufferTargets : [{ format: "rgba16float", blend: blendState(blend) }]
|
|
665
|
+
},
|
|
666
|
+
primitive: {
|
|
667
|
+
topology: "triangle-list",
|
|
668
|
+
cullMode: variantCull(variant),
|
|
669
|
+
frontFace: "ccw"
|
|
670
|
+
},
|
|
671
|
+
depthStencil: {
|
|
672
|
+
format: "depth32float",
|
|
673
|
+
// Ground bands never write depth (so they never fight); picking them writes it, so the
|
|
674
|
+
// nearest band wins at equal depth.
|
|
675
|
+
depthWriteEnabled: pick || !transparent && !ground,
|
|
676
|
+
depthCompare: transparent || ground ? "greater-equal" : "greater",
|
|
677
|
+
// Ground bands lie on their floor: a few ULPs and a slope term keep them in front of it
|
|
678
|
+
// at any angle (reversed Z: toward the camera). Bands don't write depth, so they never
|
|
679
|
+
// fight each other; the order does that.
|
|
680
|
+
depthBias: ground ? GROUND_DEPTH_BIAS : 0,
|
|
681
|
+
depthBiasSlopeScale: ground ? GROUND_SLOPE_BIAS : 0
|
|
682
|
+
},
|
|
683
|
+
multisample: { count: msaa }
|
|
684
|
+
},
|
|
685
|
+
type
|
|
686
|
+
);
|
|
687
|
+
if (!pipeline) return void 0;
|
|
688
|
+
}
|
|
689
|
+
return pipeline;
|
|
690
|
+
}
|
|
691
|
+
function warmPipelines(ctx, state, cam, store, assets, pass, cut) {
|
|
692
|
+
const slot = pass * 8 + cam.msaa + (cut ? CUT_KEY : 0) + state.interiorMode * INTERIOR_KEY;
|
|
693
|
+
if (state.pipelines.warmed(slot, store.structureVersion, ctx.gpu.generation)) return;
|
|
694
|
+
const ground = pass === PASS_GROUND || pass === PASS_PICK_GROUND;
|
|
695
|
+
const batches = store.batches;
|
|
696
|
+
for (let i = 0; i < batches.length; i++) {
|
|
697
|
+
const batch = batches[i];
|
|
698
|
+
if (batch.count === 0 || batch.ground !== ground) continue;
|
|
699
|
+
if (pass === PASS_TRANSPARENT ? !batch.transparent : batch.transparent && pass !== PASS_PICK) {
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
if (pass === PASS_OPAQUE && cam.deferred && batch.deferrable) continue;
|
|
703
|
+
const own = batch.material;
|
|
704
|
+
if (!inPass(own.type, pass)) continue;
|
|
705
|
+
const material = state.pipelines.failing(own.type) ? state.pipelines.proxy(own) : own;
|
|
706
|
+
materialPipeline(ctx, state, cam, store, assets, material, pass, false);
|
|
707
|
+
if (cut && batch.cutaway > 0)
|
|
708
|
+
materialPipeline(ctx, state, cam, store, assets, material, pass, false, cut);
|
|
709
|
+
assets.material(ctx.world, material);
|
|
710
|
+
}
|
|
711
|
+
if (ctx.gpu.pipelines.pending === 0) {
|
|
712
|
+
state.pipelines.markWarmed(slot, store.structureVersion, ctx.gpu.generation);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
534
715
|
function recordSwitches(ctx, switches) {
|
|
535
716
|
const stats = ctx.world.resource(RenderStats).get(ctx.view.name);
|
|
536
717
|
if (stats) stats.pipelineSwitches = Math.max(stats.pipelineSwitches ?? 0, switches);
|
|
537
718
|
}
|
|
538
719
|
const isCamera = (view) => cameraOf(view) !== void 0;
|
|
539
720
|
const msaaOf = (view) => cameraOf(view)?.msaa ?? 1;
|
|
721
|
+
function reportLightBudget(world, view, b, max) {
|
|
722
|
+
b.reported = b.dropped;
|
|
723
|
+
if (b.dropped === 0) {
|
|
724
|
+
clearHealthIssue(world, "render/light-budget", view);
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
raiseHealthIssue(world, {
|
|
728
|
+
code: "render/light-budget",
|
|
729
|
+
severity: "degraded",
|
|
730
|
+
ref: view,
|
|
731
|
+
message: `${b.inView} point and spot lights are in view, over the baseline tier's ${Math.min(max, 128)}: the ${b.dropped} farthest aren't shaded`
|
|
732
|
+
});
|
|
733
|
+
}
|
|
540
734
|
function whiteTexture(gpu, state) {
|
|
541
735
|
if (!state.white || state.white.generation !== gpu.generation) {
|
|
542
736
|
const texture = gpu.device.createTexture({
|
|
@@ -557,7 +751,8 @@ function emptyShadow(gpu, state) {
|
|
|
557
751
|
label: "shadows/empty",
|
|
558
752
|
size: [1, 1, 1],
|
|
559
753
|
format: "depth32float",
|
|
560
|
-
usage: GPUTextureUsage.TEXTURE_BINDING
|
|
754
|
+
usage: GPUTextureUsage.TEXTURE_BINDING,
|
|
755
|
+
...bindingDimension(gpu, "2d-array")
|
|
561
756
|
}),
|
|
562
757
|
generation: gpu.generation
|
|
563
758
|
};
|
|
@@ -604,29 +799,35 @@ function viewBindGroup(gpu, world, pv, cam) {
|
|
|
604
799
|
const source = baked?.sourceView ?? envs.emptyView;
|
|
605
800
|
const sh = baked?.sh ?? envs.emptySh;
|
|
606
801
|
const ao = pv.ao ?? whiteTexture(gpu, state);
|
|
607
|
-
const
|
|
802
|
+
const b = pv.baseline;
|
|
803
|
+
const lightsVersion = b ? b.lights.version : lights.buffer.version;
|
|
804
|
+
const clustersVersion = b ? b.bits.version : pv.clusters.clusters.version;
|
|
805
|
+
const interior = state.interiorMode !== 0 ? world.tryResource(InteriorPath) : void 0;
|
|
806
|
+
const key = `${idOf(ao)}/${gpu.generation}/${pv.uniform.version}/${lightsVersion}/${clustersVersion}/${lights.directional.version}/${pv.shadowData.version}/${idOf(cascades)}/${idOf(spots)}/${idOf(points)}/${baked ? idOf(baked.source) : 0}/${sh.version}/${state.globals.version}${interior ? `/${state.interiorMode}/${interior.version}` : ""}`;
|
|
608
807
|
if (!pv.bindGroup || pv.bound !== key) {
|
|
808
|
+
const entries = [
|
|
809
|
+
{ binding: 0, resource: { buffer: pv.uniform.buffer } },
|
|
810
|
+
{ binding: 1, resource: b ? b.lights.resource() : { buffer: lights.buffer.buffer } },
|
|
811
|
+
{ binding: 2, resource: b ? b.bits.resource() : { buffer: pv.clusters.clusters.buffer } },
|
|
812
|
+
{ binding: 3, resource: lights.directional.resource() },
|
|
813
|
+
{ binding: 4, resource: pv.shadowData.resource() },
|
|
814
|
+
{ binding: 5, resource: cascades.createView({ dimension: "2d-array" }) },
|
|
815
|
+
{ binding: 6, resource: spots.createView({ dimension: "2d-array" }) },
|
|
816
|
+
{ binding: 7, resource: points.createView({ dimension: "2d-array" }) },
|
|
817
|
+
{ binding: 8, resource: state.shadowSampler },
|
|
818
|
+
{ binding: 9, resource: specular },
|
|
819
|
+
{ binding: 10, resource: envs.lut.createView() },
|
|
820
|
+
{ binding: 11, resource: envs.sampler },
|
|
821
|
+
{ binding: 12, resource: sh.resource() },
|
|
822
|
+
{ binding: 13, resource: source },
|
|
823
|
+
{ binding: 14, resource: { buffer: state.globals.buffer } },
|
|
824
|
+
{ binding: 15, resource: ao.createView() }
|
|
825
|
+
];
|
|
826
|
+
if (interior) entries.push(...interior.entries(gpu));
|
|
609
827
|
pv.bindGroup = gpu.device.createBindGroup({
|
|
610
828
|
label: "forward/view",
|
|
611
829
|
layout: state.layouts.view,
|
|
612
|
-
entries
|
|
613
|
-
{ binding: 0, resource: { buffer: pv.uniform.buffer } },
|
|
614
|
-
{ binding: 1, resource: { buffer: lights.buffer.buffer } },
|
|
615
|
-
{ binding: 2, resource: { buffer: pv.clusters.clusters.buffer } },
|
|
616
|
-
{ binding: 3, resource: { buffer: lights.directional.buffer } },
|
|
617
|
-
{ binding: 4, resource: { buffer: pv.shadowData.buffer } },
|
|
618
|
-
{ binding: 5, resource: cascades.createView({ dimension: "2d-array" }) },
|
|
619
|
-
{ binding: 6, resource: spots.createView({ dimension: "2d-array" }) },
|
|
620
|
-
{ binding: 7, resource: points.createView({ dimension: "2d-array" }) },
|
|
621
|
-
{ binding: 8, resource: state.shadowSampler },
|
|
622
|
-
{ binding: 9, resource: specular },
|
|
623
|
-
{ binding: 10, resource: envs.lut.createView() },
|
|
624
|
-
{ binding: 11, resource: envs.sampler },
|
|
625
|
-
{ binding: 12, resource: { buffer: sh.buffer } },
|
|
626
|
-
{ binding: 13, resource: source },
|
|
627
|
-
{ binding: 14, resource: { buffer: state.globals.buffer } },
|
|
628
|
-
{ binding: 15, resource: ao.createView() }
|
|
629
|
-
]
|
|
830
|
+
entries
|
|
630
831
|
});
|
|
631
832
|
pv.bound = key;
|
|
632
833
|
}
|
|
@@ -649,6 +850,40 @@ function forwardNode(state) {
|
|
|
649
850
|
}
|
|
650
851
|
};
|
|
651
852
|
}
|
|
853
|
+
function groundNode(state) {
|
|
854
|
+
return {
|
|
855
|
+
kind: "render",
|
|
856
|
+
phase: RenderPhase.Ground,
|
|
857
|
+
enabled: (view) => (cameraOf(view)?.ground.length ?? 0) > 0,
|
|
858
|
+
reads: ["clusters", "shadow-cascades", "shadow-local", "environment", "culled", "ssao"],
|
|
859
|
+
writes: ["scene-color", "hdr"],
|
|
860
|
+
color: (view) => sceneColor(view),
|
|
861
|
+
depth: { resource: "scene-depth", readOnly: true },
|
|
862
|
+
run: (ctx) => {
|
|
863
|
+
const cam = cameraOf(ctx.view);
|
|
864
|
+
const pv = state.views.get(ctx.view.name);
|
|
865
|
+
if (!pv) return;
|
|
866
|
+
drawMaterials(ctx, state, pv, cam, cam.ground, PASS_GROUND);
|
|
867
|
+
}
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
function overlayNode(state) {
|
|
871
|
+
return {
|
|
872
|
+
kind: "render",
|
|
873
|
+
phase: RenderPhase.Overlay3d,
|
|
874
|
+
enabled: (view) => (cameraOf(view)?.overlay.length ?? 0) > 0,
|
|
875
|
+
reads: ["clusters", "shadow-cascades", "shadow-local", "environment", "culled", "ssao"],
|
|
876
|
+
writes: ["scene-color", "hdr"],
|
|
877
|
+
color: (view) => sceneColor(view),
|
|
878
|
+
depth: { resource: "scene-depth", readOnly: true },
|
|
879
|
+
run: (ctx) => {
|
|
880
|
+
const cam = cameraOf(ctx.view);
|
|
881
|
+
const pv = state.views.get(ctx.view.name);
|
|
882
|
+
if (!pv) return;
|
|
883
|
+
drawMaterials(ctx, state, pv, cam, cam.overlay, PASS_GROUND);
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
}
|
|
652
887
|
function transparentNode(state) {
|
|
653
888
|
return {
|
|
654
889
|
kind: "render",
|
|
@@ -670,11 +905,12 @@ function clusterNode(state) {
|
|
|
670
905
|
return {
|
|
671
906
|
kind: "raw",
|
|
672
907
|
phase: RenderPhase.Setup,
|
|
673
|
-
|
|
908
|
+
// Baseline bins on the CPU (baseline/lights.ts): no compute.
|
|
909
|
+
enabled: (view) => isCamera(view) && !state.baseline,
|
|
674
910
|
writes: ["clusters"],
|
|
675
911
|
run: (ctx) => {
|
|
676
912
|
const pv = state.views.get(ctx.view.name);
|
|
677
|
-
if (!pv) return;
|
|
913
|
+
if (!pv?.clusters) return;
|
|
678
914
|
const gpu = ctx.gpu;
|
|
679
915
|
const module = ctx.world.resource(Shaders).module(gpu, { root: "shard::lighting::cluster" });
|
|
680
916
|
if (!module) {
|
|
@@ -732,6 +968,18 @@ function clusterNode(state) {
|
|
|
732
968
|
}
|
|
733
969
|
};
|
|
734
970
|
}
|
|
971
|
+
let skippedBefore = 0;
|
|
972
|
+
function beginShadowView(ctx, view) {
|
|
973
|
+
if (!view.redraw && view.drawn) return false;
|
|
974
|
+
skippedBefore = ctx.gpu.pipelines.skipped;
|
|
975
|
+
ctx.world.resource(RenderStats).current.shadowMapsRendered++;
|
|
976
|
+
return true;
|
|
977
|
+
}
|
|
978
|
+
function endShadowView(ctx, view) {
|
|
979
|
+
if (ctx.gpu.pipelines.skipped === skippedBefore)
|
|
980
|
+
markShadowDrawn(view, ctx.world.resource(Instances));
|
|
981
|
+
else view.drawn = false;
|
|
982
|
+
}
|
|
735
983
|
function cascadeNode(state) {
|
|
736
984
|
return {
|
|
737
985
|
kind: "raw",
|
|
@@ -744,17 +992,27 @@ function cascadeNode(state) {
|
|
|
744
992
|
const shadows = ctx.world.resource(ShadowsResource);
|
|
745
993
|
const size = ctx.world.resource(LightingSettings).cascadeMapSize;
|
|
746
994
|
const texture = pv.cascades.ensureTexture(ctx.gpu, size, ctx.view.name);
|
|
995
|
+
const foliage = ctx.world.tryResource(FoliagePath);
|
|
996
|
+
const cam = cameraOf(ctx.view);
|
|
997
|
+
const animated = foliage?.animatedShadows(cam) ?? false;
|
|
747
998
|
for (let i = 0; i < pv.cascades.count; i++) {
|
|
999
|
+
const view = pv.cascades.views[i];
|
|
1000
|
+
if (animated && i === 0) view.redraw = true;
|
|
1001
|
+
if (!beginShadowView(ctx, view)) continue;
|
|
748
1002
|
drawShadowCasters(
|
|
749
1003
|
ctx,
|
|
750
1004
|
texture.createView({ dimension: "2d", baseArrayLayer: i, arrayLayerCount: 1 }),
|
|
751
|
-
|
|
752
|
-
|
|
1005
|
+
view,
|
|
1006
|
+
view.offset,
|
|
753
1007
|
shadows.uniforms,
|
|
754
1008
|
state.pipelines,
|
|
755
1009
|
state.layouts.shadowView,
|
|
756
|
-
`shadows/cascade${i}
|
|
1010
|
+
`shadows/cascade${i}`,
|
|
1011
|
+
foliage,
|
|
1012
|
+
cam,
|
|
1013
|
+
i
|
|
757
1014
|
);
|
|
1015
|
+
endShadowView(ctx, view);
|
|
758
1016
|
}
|
|
759
1017
|
}
|
|
760
1018
|
};
|
|
@@ -776,6 +1034,7 @@ function localShadowNode(state) {
|
|
|
776
1034
|
const layer = (texture, i) => texture.createView({ dimension: "2d", baseArrayLayer: i, arrayLayerCount: 1 });
|
|
777
1035
|
for (let i = 0; i < local.spots.length; i++) {
|
|
778
1036
|
const view = local.spotViews[i];
|
|
1037
|
+
if (!beginShadowView(ctx, view)) continue;
|
|
779
1038
|
drawShadowCasters(
|
|
780
1039
|
ctx,
|
|
781
1040
|
layer(local.spotTexture, i),
|
|
@@ -786,9 +1045,11 @@ function localShadowNode(state) {
|
|
|
786
1045
|
state.layouts.shadowView,
|
|
787
1046
|
`shadows/spot${i}`
|
|
788
1047
|
);
|
|
1048
|
+
endShadowView(ctx, view);
|
|
789
1049
|
}
|
|
790
1050
|
for (let i = 0; i < local.points.length * 6; i++) {
|
|
791
1051
|
const view = local.pointViews[i];
|
|
1052
|
+
if (!beginShadowView(ctx, view)) continue;
|
|
792
1053
|
drawShadowCasters(
|
|
793
1054
|
ctx,
|
|
794
1055
|
layer(local.pointTexture, i),
|
|
@@ -799,6 +1060,7 @@ function localShadowNode(state) {
|
|
|
799
1060
|
state.layouts.shadowView,
|
|
800
1061
|
`shadows/point${i}`
|
|
801
1062
|
);
|
|
1063
|
+
endShadowView(ctx, view);
|
|
802
1064
|
}
|
|
803
1065
|
}
|
|
804
1066
|
};
|
|
@@ -907,13 +1169,31 @@ function sceneColor(view, clear) {
|
|
|
907
1169
|
if (clear === void 0) return msaa ? MSAA_LOAD : PLAIN_LOAD;
|
|
908
1170
|
return [{ resource: "scene-color", clear, resolve: msaa ? "hdr" : void 0 }];
|
|
909
1171
|
}
|
|
910
|
-
function
|
|
1172
|
+
function depthPrepassNode(state) {
|
|
1173
|
+
return {
|
|
1174
|
+
kind: "render",
|
|
1175
|
+
phase: RenderPhase.Prepass,
|
|
1176
|
+
enabled: (view) => state.baseline !== void 0 && msaaOf(view) > 1,
|
|
1177
|
+
reads: ["culled"],
|
|
1178
|
+
writes: ["depth"],
|
|
1179
|
+
depth: { resource: "depth", clear: 0 },
|
|
1180
|
+
run: (ctx) => {
|
|
1181
|
+
const pv = state.views.get(ctx.view.name);
|
|
1182
|
+
const cam = cameraOf(ctx.view);
|
|
1183
|
+
if (!pv || !cam) return;
|
|
1184
|
+
drawMaterials(ctx, state, pv, cam, cam.draws, PASS_DEPTH);
|
|
1185
|
+
if (cam.deferred) drawMaterials(ctx, state, pv, cam, cam.forwardOnly, PASS_DEPTH);
|
|
1186
|
+
}
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
function depthResolveNode(state) {
|
|
911
1190
|
let bindGroups = /* @__PURE__ */ new WeakMap();
|
|
912
1191
|
let generation = -1;
|
|
913
1192
|
return {
|
|
914
1193
|
kind: "render",
|
|
915
1194
|
phase: RenderPhase.Resolve,
|
|
916
|
-
|
|
1195
|
+
// Baseline draws `depth` in its own prepass instead (depthPrepassNode).
|
|
1196
|
+
enabled: (view) => state.baseline === void 0 && msaaOf(view) > 1,
|
|
917
1197
|
reads: ["scene-depth"],
|
|
918
1198
|
writes: ["depth"],
|
|
919
1199
|
depth: { resource: "depth", clear: 0 },
|
|
@@ -961,39 +1241,36 @@ function depthResolveNode() {
|
|
|
961
1241
|
}
|
|
962
1242
|
};
|
|
963
1243
|
}
|
|
964
|
-
function
|
|
1244
|
+
function viewEntries(gpu) {
|
|
965
1245
|
const F = GPUShaderStage.FRAGMENT;
|
|
966
|
-
const storage = (binding) => ({
|
|
967
|
-
binding,
|
|
968
|
-
visibility: F,
|
|
969
|
-
buffer: { type: "read-only-storage" }
|
|
970
|
-
});
|
|
971
1246
|
const depthArray = (binding) => ({
|
|
972
1247
|
binding,
|
|
973
1248
|
visibility: F,
|
|
974
1249
|
texture: { sampleType: "depth", viewDimension: "2d-array" }
|
|
975
1250
|
});
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1251
|
+
return [
|
|
1252
|
+
{ binding: 0, visibility: GPUShaderStage.VERTEX | F, buffer: { type: "uniform" } },
|
|
1253
|
+
// Storage on the full tier; on baseline (0064) lights, directional lights, shadow data and SH
|
|
1254
|
+
// are uniform blocks and the cluster bits a data texture.
|
|
1255
|
+
dataEntry(gpu, 1, F, "uniform"),
|
|
1256
|
+
dataEntry(gpu, 2, F, "texture"),
|
|
1257
|
+
dataEntry(gpu, 3, F, "uniform"),
|
|
1258
|
+
dataEntry(gpu, 4, F, "uniform"),
|
|
1259
|
+
depthArray(5),
|
|
1260
|
+
depthArray(6),
|
|
1261
|
+
depthArray(7),
|
|
1262
|
+
{ binding: 8, visibility: F, sampler: { type: "comparison" } },
|
|
1263
|
+
{ binding: 9, visibility: F, texture: { sampleType: "float", viewDimension: "cube" } },
|
|
1264
|
+
{ binding: 10, visibility: F, texture: { sampleType: "float" } },
|
|
1265
|
+
{ binding: 11, visibility: F, sampler: { type: "filtering" } },
|
|
1266
|
+
dataEntry(gpu, 12, F, "uniform"),
|
|
1267
|
+
{ binding: 13, visibility: F, texture: { sampleType: "float", viewDimension: "cube" } },
|
|
1268
|
+
{ binding: 14, visibility: GPUShaderStage.VERTEX | F, buffer: { type: "uniform" } },
|
|
1269
|
+
{ binding: 15, visibility: F, texture: { sampleType: "float" } }
|
|
1270
|
+
];
|
|
1271
|
+
}
|
|
1272
|
+
function createLayouts(gpu, assets, store) {
|
|
1273
|
+
const view = gpu.layouts.bindGroupLayout({ label: "forward/view", entries: viewEntries(gpu) });
|
|
997
1274
|
const C = GPUShaderStage.COMPUTE;
|
|
998
1275
|
const cluster = gpu.layouts.bindGroupLayout({
|
|
999
1276
|
label: "light-clusters",
|
|
@@ -1008,6 +1285,8 @@ function createLayouts(gpu, assets, store) {
|
|
|
1008
1285
|
const shadowView = shadowViewLayout(gpu);
|
|
1009
1286
|
return {
|
|
1010
1287
|
view,
|
|
1288
|
+
viewBase: view,
|
|
1289
|
+
viewInterior: void 0,
|
|
1011
1290
|
pipeline: gpu.layouts.pipelineLayout({
|
|
1012
1291
|
label: "forward",
|
|
1013
1292
|
bindGroupLayouts: [view, assets.materialLayout, store.layout]
|
|
@@ -1024,6 +1303,29 @@ function createLayouts(gpu, assets, store) {
|
|
|
1024
1303
|
})
|
|
1025
1304
|
};
|
|
1026
1305
|
}
|
|
1306
|
+
function interiorViewLayout(gpu, state) {
|
|
1307
|
+
const layouts = state.layouts;
|
|
1308
|
+
if (layouts.viewInterior) return layouts.viewInterior;
|
|
1309
|
+
const F = GPUShaderStage.FRAGMENT;
|
|
1310
|
+
layouts.viewInterior = gpu.layouts.bindGroupLayout({
|
|
1311
|
+
label: "forward/view/interior",
|
|
1312
|
+
entries: [
|
|
1313
|
+
...viewEntries(gpu),
|
|
1314
|
+
{
|
|
1315
|
+
binding: INTERIOR_BINDINGS.data,
|
|
1316
|
+
visibility: F,
|
|
1317
|
+
texture: { sampleType: "uint", viewDimension: "2d-array" }
|
|
1318
|
+
},
|
|
1319
|
+
{ binding: INTERIOR_BINDINGS.table, visibility: F, buffer: { type: "uniform" } }
|
|
1320
|
+
]
|
|
1321
|
+
});
|
|
1322
|
+
return layouts.viewInterior;
|
|
1323
|
+
}
|
|
1324
|
+
function chooseInterior(gpu, world, state) {
|
|
1325
|
+
const mode = world.tryResource(InteriorPath)?.mode ?? 0;
|
|
1326
|
+
state.interiorMode = mode;
|
|
1327
|
+
state.layouts.view = mode !== 0 ? interiorViewLayout(gpu, state) : state.layouts.viewBase;
|
|
1328
|
+
}
|
|
1027
1329
|
function ensureLayouts(gpu, state, assets, store) {
|
|
1028
1330
|
if (state.layoutGeneration === gpu.generation) return;
|
|
1029
1331
|
state.layoutGeneration = gpu.generation;
|
|
@@ -1039,6 +1341,7 @@ function forwardCorePlugin(options = {}) {
|
|
|
1039
1341
|
provides: [
|
|
1040
1342
|
// core
|
|
1041
1343
|
Environments,
|
|
1344
|
+
MaterialFallbacks,
|
|
1042
1345
|
DeferredPath,
|
|
1043
1346
|
DeformPath,
|
|
1044
1347
|
PostFeatures,
|
|
@@ -1065,6 +1368,7 @@ function forwardCorePlugin(options = {}) {
|
|
|
1065
1368
|
MorphWeights,
|
|
1066
1369
|
NotShadowCaster,
|
|
1067
1370
|
NotShadowReceiver,
|
|
1371
|
+
ShadowWhenHidden,
|
|
1068
1372
|
SkinnedMesh,
|
|
1069
1373
|
VisibilityRange,
|
|
1070
1374
|
AmbientLight,
|
|
@@ -1075,6 +1379,14 @@ function forwardCorePlugin(options = {}) {
|
|
|
1075
1379
|
SpotLight,
|
|
1076
1380
|
ComputedVisibility,
|
|
1077
1381
|
Visibility,
|
|
1382
|
+
// per-view visibility and cutaways (0070)
|
|
1383
|
+
ViewVisibility,
|
|
1384
|
+
HiddenSetsResource,
|
|
1385
|
+
Cutaway,
|
|
1386
|
+
CutawayView,
|
|
1387
|
+
CutawayPath,
|
|
1388
|
+
// interior lighting (0069)
|
|
1389
|
+
InteriorPath,
|
|
1078
1390
|
Cameras,
|
|
1079
1391
|
RenderPath,
|
|
1080
1392
|
Tonemapping,
|
|
@@ -1087,6 +1399,18 @@ function forwardCorePlugin(options = {}) {
|
|
|
1087
1399
|
SkinAssetType,
|
|
1088
1400
|
Skins,
|
|
1089
1401
|
RenderScale,
|
|
1402
|
+
// tabletop (0057)
|
|
1403
|
+
GroundLayer,
|
|
1404
|
+
RenderLayers,
|
|
1405
|
+
Outline,
|
|
1406
|
+
OutlinePath,
|
|
1407
|
+
Trail,
|
|
1408
|
+
Beam,
|
|
1409
|
+
Decal,
|
|
1410
|
+
DecalReceiver,
|
|
1411
|
+
RibbonsPath,
|
|
1412
|
+
DecalsPath,
|
|
1413
|
+
CameraMoved,
|
|
1090
1414
|
// shadows
|
|
1091
1415
|
ShadowsResource,
|
|
1092
1416
|
// post
|
|
@@ -1102,11 +1426,21 @@ function forwardCorePlugin(options = {}) {
|
|
|
1102
1426
|
// pixel-perfect
|
|
1103
1427
|
PixelPerfect,
|
|
1104
1428
|
PixelPerfectPath,
|
|
1105
|
-
PixelTargets
|
|
1429
|
+
PixelTargets,
|
|
1430
|
+
// lens fields
|
|
1431
|
+
Lens,
|
|
1432
|
+
LensFields,
|
|
1433
|
+
LensPath,
|
|
1434
|
+
// screen effects
|
|
1435
|
+
ScreenEffects,
|
|
1436
|
+
ScreenEffectHandlers,
|
|
1437
|
+
// shard shaders bake (0064)
|
|
1438
|
+
ShaderVariantSources
|
|
1106
1439
|
],
|
|
1107
1440
|
dependencies: ["render", "core/transform"],
|
|
1108
1441
|
build(app) {
|
|
1109
1442
|
const w = app.world;
|
|
1443
|
+
addShaderVariantSource(w, materialVariants);
|
|
1110
1444
|
w.initResource(Meshes);
|
|
1111
1445
|
w.initResource(Skins);
|
|
1112
1446
|
w.initResource(Materials);
|
|
@@ -1119,98 +1453,186 @@ function forwardCorePlugin(options = {}) {
|
|
|
1119
1453
|
w.initResource(Environments);
|
|
1120
1454
|
w.initResource(ViewSettings).msaa = options.msaa ?? 4;
|
|
1121
1455
|
Object.assign(w.initResource(RenderScale), options.renderScale);
|
|
1456
|
+
w.initResource(LensFields);
|
|
1457
|
+
w.initResource(ScreenEffects);
|
|
1458
|
+
w.initResource(ScreenEffectHandlers);
|
|
1459
|
+
w.initResource(RenderDescribers).set("screenEffects", describeScreenEffects);
|
|
1122
1460
|
observeInstanceRemovals(w);
|
|
1461
|
+
observeOutlinesWithoutPass(w);
|
|
1462
|
+
observeVfxWithoutPlugin(w);
|
|
1463
|
+
observeViewVisibilityWithoutPlugin(w);
|
|
1123
1464
|
observeLightRemovals(w);
|
|
1124
1465
|
observeOriginShifts(w);
|
|
1125
|
-
app.addSystems(PostUpdate, computeVisibility.after(TransformSystems), applyPhysicalCameras).addSystems(
|
|
1466
|
+
app.addSystems(PreUpdate, runScreenEffects).addSystems(PostUpdate, computeVisibility.after(TransformSystems), applyPhysicalCameras).addSystems(
|
|
1126
1467
|
Last,
|
|
1468
|
+
expireLensFields.inSet(RenderSet.Begin),
|
|
1469
|
+
expireScreenEffects.inSet(RenderSet.Begin),
|
|
1127
1470
|
extractCameras.inSet(RenderSet.Extract),
|
|
1128
1471
|
extractLights.inSet(RenderSet.Extract),
|
|
1129
1472
|
prepareInstances.inSet(RenderSet.Prepare),
|
|
1130
1473
|
prepareLights.inSet(RenderSet.Prepare),
|
|
1131
|
-
|
|
1474
|
+
forwardQueue.inSet(RenderSet.Queue),
|
|
1132
1475
|
upload.inSet(RenderSet.Upload)
|
|
1133
1476
|
);
|
|
1134
1477
|
},
|
|
1135
|
-
ready(app) {
|
|
1478
|
+
async ready(app) {
|
|
1136
1479
|
const gpu = app.world.resource(Gpu);
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1480
|
+
const [baseline, baselineTextures] = gpu.tier === "baseline" ? await Promise.all([import("./baseline/lights.js"), import("./baseline/textures.js")]) : [void 0, void 0];
|
|
1481
|
+
app.scoped(() => {
|
|
1482
|
+
setTextureCapabilities({
|
|
1483
|
+
bc: gpu.features.has("texture-compression-bc"),
|
|
1484
|
+
astc: gpu.features.has("texture-compression-astc"),
|
|
1485
|
+
etc2: gpu.features.has("texture-compression-etc2")
|
|
1486
|
+
});
|
|
1487
|
+
const assets = new GpuAssets(gpu, app.world.initResource(GpuMemory));
|
|
1488
|
+
if (baselineTextures) {
|
|
1489
|
+
assets.baseline = baselineTextures;
|
|
1490
|
+
app.world.initResource(RenderDescribers).set("twins", () => assets.describeTwins());
|
|
1491
|
+
}
|
|
1492
|
+
assets.counts = app.world.initResource(RenderStats).current;
|
|
1493
|
+
const store = new InstanceStore(gpu);
|
|
1494
|
+
app.insertResource(GpuAssetsResource, assets);
|
|
1495
|
+
unwatch.set(app, assets.watch(assetServer(app.world)));
|
|
1496
|
+
app.world.owners.addDescriber("gpu", (owner) => {
|
|
1497
|
+
const server = assetServer(app.world);
|
|
1498
|
+
const total = { buffers: 0, textures: 0, bytes: 0 };
|
|
1499
|
+
for (const path of server.leasesOf(owner)) {
|
|
1500
|
+
const item = server.item(path);
|
|
1501
|
+
const o = assets.objectsOf(item);
|
|
1502
|
+
total.buffers += o.buffers;
|
|
1503
|
+
total.textures += o.textures;
|
|
1504
|
+
total.bytes += o.bytes;
|
|
1505
|
+
}
|
|
1506
|
+
return total;
|
|
1507
|
+
});
|
|
1508
|
+
app.insertResource(Instances, store);
|
|
1509
|
+
app.insertResource(Culler, new GpuCuller(gpu));
|
|
1510
|
+
app.insertResource(
|
|
1511
|
+
Lights,
|
|
1512
|
+
new LightStore(gpu, app.world.resource(LightingSettings).maxLights)
|
|
1513
|
+
);
|
|
1514
|
+
app.insertResource(ShadowsResource, {
|
|
1515
|
+
local: new LocalShadows(),
|
|
1516
|
+
uniforms: new ShadowPassUniforms(gpu)
|
|
1517
|
+
});
|
|
1518
|
+
const state = {
|
|
1519
|
+
views: /* @__PURE__ */ new Map(),
|
|
1520
|
+
viewBytes: new DataView(new ArrayBuffer(viewLayout.size)),
|
|
1521
|
+
shadowFloats: new Float32Array(SHADOW_DATA_FLOATS),
|
|
1522
|
+
layouts: createLayouts(gpu, assets, store),
|
|
1523
|
+
layoutGeneration: gpu.generation,
|
|
1524
|
+
emptyShadow: void 0,
|
|
1525
|
+
white: void 0,
|
|
1526
|
+
shadowSampler: void 0,
|
|
1527
|
+
frame: 0,
|
|
1528
|
+
warned: { budget: "", overflow: "" },
|
|
1529
|
+
globals: new GpuBuffer(gpu, {
|
|
1530
|
+
label: "globals",
|
|
1531
|
+
usage: GPUBufferUsage.UNIFORM,
|
|
1532
|
+
size: 16
|
|
1533
|
+
}),
|
|
1534
|
+
globalsData: new Float32Array(4),
|
|
1535
|
+
pipelines: new MaterialPipelines(),
|
|
1536
|
+
baseline,
|
|
1537
|
+
cullerActive: false,
|
|
1538
|
+
interiorMode: 0,
|
|
1539
|
+
gbufferTargets: [
|
|
1540
|
+
{ format: "rgba8unorm-srgb" },
|
|
1541
|
+
{ format: "rgba16float" },
|
|
1542
|
+
{ format: gbufferEmissiveFormat(gpu) }
|
|
1543
|
+
]
|
|
1544
|
+
};
|
|
1545
|
+
app.insertResource(MaterialFallbacks, {
|
|
1546
|
+
count: () => state.pipelines.failureCount,
|
|
1547
|
+
list: () => state.pipelines.failures()
|
|
1548
|
+
});
|
|
1549
|
+
store.batchKey = (b) => typeOrdinal(b.material.type) * 16 + materialVariant(b.material);
|
|
1550
|
+
app.insertResource(State, state);
|
|
1551
|
+
const graph = app.world.resource(Graph);
|
|
1552
|
+
graph.declare({ name: "scene-color", format: "rgba16float", sampleCount: msaaOf });
|
|
1553
|
+
graph.declare({ name: "scene-depth", format: "depth32float", sampleCount: msaaOf });
|
|
1554
|
+
graph.declare({ name: "hdr", format: "rgba16float" });
|
|
1555
|
+
graph.declare({ name: "depth", format: "depth32float" });
|
|
1556
|
+
graph.declare({ name: "ldr", format: "view" });
|
|
1557
|
+
addRenderFeatures(app.world, {
|
|
1558
|
+
name: "render/forward",
|
|
1559
|
+
description: "Opaque, ground and transparent mesh passes, and the MSAA depth resolve.",
|
|
1560
|
+
nodes: [
|
|
1561
|
+
"forward-opaque",
|
|
1562
|
+
"forward-ground",
|
|
1563
|
+
"forward-transparent",
|
|
1564
|
+
"forward-overlay",
|
|
1565
|
+
"depth-resolve",
|
|
1566
|
+
"depth-prepass"
|
|
1567
|
+
],
|
|
1568
|
+
baseline: {
|
|
1569
|
+
strategy: "Instances, visibility and deform data in data textures; MSAA depth from a single-sample depth prepass"
|
|
1570
|
+
}
|
|
1571
|
+
});
|
|
1572
|
+
addRenderFeatures(app.world, {
|
|
1573
|
+
name: "render/culling",
|
|
1574
|
+
description: "GPU frustum, range and LOD culling with indirect draws.",
|
|
1575
|
+
nodes: ["instance-cull"],
|
|
1576
|
+
baseline: { strategy: "CPU culling and direct draws" }
|
|
1577
|
+
});
|
|
1578
|
+
addRenderFeatures(app.world, {
|
|
1579
|
+
name: "render/light-clusters",
|
|
1580
|
+
description: "Clustered light binning (Forward+).",
|
|
1581
|
+
nodes: ["light-clusters"],
|
|
1582
|
+
baseline: { strategy: "CPU binning into a 128-light bitmask texture" }
|
|
1583
|
+
});
|
|
1584
|
+
addRenderFeatures(app.world, {
|
|
1585
|
+
name: "render/shadows",
|
|
1586
|
+
description: "Cascaded directional shadows, and spot and point shadow layers.",
|
|
1587
|
+
nodes: ["shadows/cascades", "shadows/local"],
|
|
1588
|
+
baseline: { strategy: "The same render passes" }
|
|
1589
|
+
});
|
|
1590
|
+
graph.addNode("instance-cull", cullNode(state));
|
|
1591
|
+
graph.addNode("light-clusters", clusterNode(state));
|
|
1592
|
+
graph.addNode("shadows/cascades", cascadeNode(state));
|
|
1593
|
+
graph.addNode("shadows/local", localShadowNode(state));
|
|
1594
|
+
graph.addNode("forward-opaque", forwardNode(state));
|
|
1595
|
+
graph.addNode("forward-ground", groundNode(state));
|
|
1596
|
+
graph.addNode("forward-overlay", overlayNode(state));
|
|
1597
|
+
graph.addNode("forward-transparent", transparentNode(state));
|
|
1598
|
+
graph.addNode("depth-resolve", depthResolveNode(state));
|
|
1599
|
+
graph.addNode("depth-prepass", depthPrepassNode(state));
|
|
1600
|
+
addDisplayNodes(app.world);
|
|
1154
1601
|
});
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
layouts: createLayouts(gpu, assets, store),
|
|
1160
|
-
layoutGeneration: gpu.generation,
|
|
1161
|
-
emptyShadow: void 0,
|
|
1162
|
-
white: void 0,
|
|
1163
|
-
shadowSampler: void 0,
|
|
1164
|
-
frame: 0,
|
|
1165
|
-
warned: { budget: "", overflow: "" },
|
|
1166
|
-
globals: new GpuBuffer(gpu, { label: "globals", usage: GPUBufferUsage.UNIFORM, size: 16 }),
|
|
1167
|
-
globalsData: new Float32Array(4),
|
|
1168
|
-
pipelines: new MaterialPipelines(),
|
|
1169
|
-
cullerActive: false,
|
|
1170
|
-
gbufferTargets: [
|
|
1171
|
-
{ format: "rgba8unorm-srgb" },
|
|
1172
|
-
{ format: "rgba16float" },
|
|
1173
|
-
{ format: gbufferEmissiveFormat(gpu) }
|
|
1174
|
-
]
|
|
1175
|
-
};
|
|
1176
|
-
store.batchKey = (b) => typeOrdinal(b.material.type) * 16 + materialVariant(b.material);
|
|
1177
|
-
app.insertResource(State, state);
|
|
1178
|
-
const graph = app.world.resource(Graph);
|
|
1179
|
-
graph.declare({ name: "scene-color", format: "rgba16float", sampleCount: msaaOf });
|
|
1180
|
-
graph.declare({ name: "scene-depth", format: "depth32float", sampleCount: msaaOf });
|
|
1181
|
-
graph.declare({ name: "hdr", format: "rgba16float" });
|
|
1182
|
-
graph.declare({ name: "depth", format: "depth32float" });
|
|
1183
|
-
graph.declare({ name: "ldr", format: "view" });
|
|
1184
|
-
graph.addNode("instance-cull", cullNode(state));
|
|
1185
|
-
graph.addNode("light-clusters", clusterNode(state));
|
|
1186
|
-
graph.addNode("shadows/cascades", cascadeNode(state));
|
|
1187
|
-
graph.addNode("shadows/local", localShadowNode(state));
|
|
1188
|
-
graph.addNode("forward-opaque", forwardNode(state));
|
|
1189
|
-
graph.addNode("forward-transparent", transparentNode(state));
|
|
1190
|
-
graph.addNode("depth-resolve", depthResolveNode());
|
|
1191
|
-
addDisplayNodes(app.world);
|
|
1602
|
+
},
|
|
1603
|
+
dispose(app) {
|
|
1604
|
+
unwatch.get(app)?.();
|
|
1605
|
+
unwatch.delete(app);
|
|
1192
1606
|
}
|
|
1193
1607
|
});
|
|
1194
1608
|
}
|
|
1609
|
+
const unwatch = /* @__PURE__ */ new WeakMap();
|
|
1195
1610
|
function gbufferEmissiveFormat(gpu) {
|
|
1196
1611
|
return gpu.features.has("rg11b10ufloat-renderable") ? "rg11b10ufloat" : "rgba16float";
|
|
1197
1612
|
}
|
|
1198
1613
|
export {
|
|
1199
1614
|
ForwardStateResource,
|
|
1615
|
+
INTERIOR_DEFINES,
|
|
1200
1616
|
Mesh3d,
|
|
1201
1617
|
MeshMaterial,
|
|
1618
|
+
PASS_DEPTH,
|
|
1202
1619
|
PASS_GBUFFER,
|
|
1620
|
+
PASS_GROUND,
|
|
1203
1621
|
PASS_OPAQUE,
|
|
1204
1622
|
PASS_PICK,
|
|
1623
|
+
PASS_PICK_GROUND,
|
|
1205
1624
|
PASS_PREPASS,
|
|
1206
1625
|
PASS_TRANSPARENT,
|
|
1207
1626
|
PICK_TARGETS,
|
|
1627
|
+
VERTEX_BUFFERS,
|
|
1208
1628
|
drawMaterials,
|
|
1209
1629
|
forwardCorePlugin,
|
|
1630
|
+
forwardQueue,
|
|
1210
1631
|
gbufferEmissiveFormat,
|
|
1211
1632
|
isCamera,
|
|
1212
1633
|
sceneColor,
|
|
1213
1634
|
skyNode,
|
|
1214
1635
|
upload,
|
|
1215
|
-
viewBindGroup
|
|
1636
|
+
viewBindGroup,
|
|
1637
|
+
viewPixelScale
|
|
1216
1638
|
};
|