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