@driftengine/core 4.8.1 → 4.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +142 -0
- package/README.md +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +8 -0
- package/dist/input/actionMap.d.ts +6 -1
- package/dist/input/actionMap.js +20 -7
- package/dist/input/input.d.ts +28 -0
- package/dist/input/input.js +71 -9
- package/dist/input/inputHarness.d.ts +9 -0
- package/dist/input/inputHarness.js +34 -2
- package/dist/render/backend/materialChanges.d.ts +7 -2
- package/dist/render/backend/materialChanges.js +8 -3
- package/dist/render/backend/webgl2/renderer.d.ts +75 -3
- package/dist/render/backend/webgl2/renderer.js +209 -22
- package/dist/render/backend/webgpu/buffers.d.ts +2 -0
- package/dist/render/backend/webgpu/buffers.js +2 -0
- package/dist/render/backend/webgpu/causticsPass.js +2 -1
- package/dist/render/backend/webgpu/depthPass.d.ts +13 -3
- package/dist/render/backend/webgpu/depthPass.js +4 -3
- package/dist/render/backend/webgpu/filmPass.js +2 -1
- package/dist/render/backend/webgpu/flatPass.d.ts +5 -0
- package/dist/render/backend/webgpu/flatPass.js +18 -8
- package/dist/render/backend/webgpu/linePass.js +2 -1
- package/dist/render/backend/webgpu/panelPass.js +2 -1
- package/dist/render/backend/webgpu/particlePass.js +2 -1
- package/dist/render/backend/webgpu/pipelineCache.d.ts +40 -8
- package/dist/render/backend/webgpu/pipelineCache.js +76 -9
- package/dist/render/backend/webgpu/plumePass.js +2 -1
- package/dist/render/backend/webgpu/postPass.d.ts +12 -2
- package/dist/render/backend/webgpu/postPass.js +19 -5
- package/dist/render/backend/webgpu/renderer.d.ts +66 -2
- package/dist/render/backend/webgpu/renderer.js +189 -38
- package/dist/render/backend/webgpu/sdfTextPass.js +2 -1
- package/dist/render/backend/webgpu/textPass.js +2 -1
- package/dist/render/backend/webgpu/waterPass.js +2 -1
- package/dist/render/backend/webgpu/windStreakPass.js +2 -1
- package/dist/render/boltBatch.js +2 -1
- package/dist/render/causticsRenderer.js +2 -1
- package/dist/render/cutoutCaster.d.ts +3 -1
- package/dist/render/cutoutCaster.js +4 -0
- package/dist/render/decalPass.js +2 -1
- package/dist/render/globalMediumPass.js +3 -2
- package/dist/render/lineBatch.js +5 -1
- package/dist/render/mapsWithoutUvs.d.ts +24 -0
- package/dist/render/mapsWithoutUvs.js +24 -0
- package/dist/render/mesh.d.ts +2 -0
- package/dist/render/mesh.js +3 -0
- package/dist/render/oitPass.js +2 -1
- package/dist/render/particleBatch.js +3 -2
- package/dist/render/particlePool.d.ts +21 -2
- package/dist/render/particlePool.js +72 -37
- package/dist/render/pass.d.ts +12 -0
- package/dist/render/plumeRenderer.js +3 -2
- package/dist/render/renderQuality.d.ts +16 -8
- package/dist/render/sceneCoverage.d.ts +54 -0
- package/dist/render/sceneCoverage.js +72 -0
- package/dist/render/sdfTextRenderer.js +2 -1
- package/dist/render/shaders/depth.js +3 -2
- package/dist/render/shaders/flat/glassTint.d.ts +28 -0
- package/dist/render/shaders/flat/glassTint.js +31 -0
- package/dist/render/shaders/flat/index.d.ts +12 -0
- package/dist/render/shaders/flat/index.js +13 -2
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +60 -25
- package/dist/render/shaders/flat/surfaceEffects.d.ts +1 -1
- package/dist/render/shaders/flat/surfaceEffects.js +6 -2
- package/dist/render/shaders/generated/depth.wgsl.d.ts +32 -12
- package/dist/render/shaders/generated/depth.wgsl.js +32 -12
- package/dist/render/shaders/generated/flat.wgsl.d.ts +89 -0
- package/dist/render/shaders/generated/flat.wgsl.js +127 -38
- package/dist/render/shaders/generated/line.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/line.wgsl.js +1 -1
- package/dist/render/shaders/generated/particle.wgsl.d.ts +3 -3
- package/dist/render/shaders/generated/particle.wgsl.js +3 -3
- package/dist/render/shaders/generated/rush.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/rush.wgsl.js +1 -1
- package/dist/render/shaders/generated/sdfText.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/sdfText.wgsl.js +1 -1
- package/dist/render/shaders/generated/temporalResolve.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/temporalResolve.wgsl.js +1 -1
- package/dist/render/shaders/line.d.ts +1 -1
- package/dist/render/shaders/outputTransform.d.ts +14 -1
- package/dist/render/shaders/outputTransform.js +23 -1
- package/dist/render/shaders/particle.d.ts +3 -3
- package/dist/render/shaders/recon/resolve.wgsl.js +15 -1
- package/dist/render/shaders/rush.js +13 -2
- package/dist/render/shaders/sdfText.d.ts +1 -1
- package/dist/render/shaders/temporalResolve.js +12 -5
- package/dist/render/ssrPass.js +2 -1
- package/dist/render/surfaceTexture.d.ts +13 -0
- package/dist/render/surfaceTexture.js +4 -0
- package/dist/render/textRenderer.js +2 -1
- package/dist/render/vertexDefaults.js +1 -0
- package/dist/render/viewModel.d.ts +29 -0
- package/dist/render/viewModel.js +35 -0
- package/dist/render/waterRenderer.js +2 -1
- package/dist/render/windStreakRenderer.js +2 -1
- package/package.json +6 -6
- package/src/index.ts +22 -1
- package/src/input/actionMap.ts +23 -8
- package/src/input/input.ts +79 -9
- package/src/input/inputHarness.ts +27 -2
- package/src/render/backend/materialChanges.ts +8 -3
- package/src/render/backend/webgl2/renderer.ts +223 -29
- package/src/render/backend/webgpu/buffers.ts +5 -0
- package/src/render/backend/webgpu/causticsPass.ts +2 -1
- package/src/render/backend/webgpu/depthPass.ts +4 -3
- package/src/render/backend/webgpu/filmPass.ts +2 -1
- package/src/render/backend/webgpu/flatPass.ts +43 -26
- package/src/render/backend/webgpu/linePass.ts +2 -1
- package/src/render/backend/webgpu/panelPass.ts +2 -1
- package/src/render/backend/webgpu/particlePass.ts +2 -1
- package/src/render/backend/webgpu/pipelineCache.ts +82 -8
- package/src/render/backend/webgpu/plumePass.ts +2 -1
- package/src/render/backend/webgpu/postPass.ts +20 -5
- package/src/render/backend/webgpu/renderer.ts +191 -37
- package/src/render/backend/webgpu/sdfTextPass.ts +2 -1
- package/src/render/backend/webgpu/textPass.ts +2 -1
- package/src/render/backend/webgpu/waterPass.ts +2 -1
- package/src/render/backend/webgpu/windStreakPass.ts +2 -1
- package/src/render/boltBatch.ts +2 -1
- package/src/render/causticsRenderer.ts +2 -1
- package/src/render/cutoutCaster.ts +15 -2
- package/src/render/decalPass.ts +2 -1
- package/src/render/globalMediumPass.ts +3 -2
- package/src/render/lineBatch.ts +3 -1
- package/src/render/mapsWithoutUvs.ts +43 -0
- package/src/render/mesh.ts +3 -0
- package/src/render/oitPass.ts +2 -1
- package/src/render/particleBatch.ts +3 -2
- package/src/render/particlePool.ts +107 -56
- package/src/render/pass.ts +12 -0
- package/src/render/plumeRenderer.ts +3 -2
- package/src/render/renderQuality.ts +16 -8
- package/src/render/sceneCoverage.ts +78 -0
- package/src/render/sdfTextRenderer.ts +2 -1
- package/src/render/shaders/depth.ts +3 -2
- package/src/render/shaders/flat/glassTint.ts +48 -0
- package/src/render/shaders/flat/index.ts +25 -2
- package/src/render/shaders/flat/main.ts +60 -25
- package/src/render/shaders/flat/surfaceEffects.ts +6 -2
- package/src/render/shaders/generated/depth.wgsl.ts +32 -12
- package/src/render/shaders/generated/flat.wgsl.ts +127 -38
- package/src/render/shaders/generated/line.wgsl.ts +1 -1
- package/src/render/shaders/generated/particle.wgsl.ts +3 -3
- package/src/render/shaders/generated/rush.wgsl.ts +1 -1
- package/src/render/shaders/generated/sdfText.wgsl.ts +1 -1
- package/src/render/shaders/generated/temporalResolve.wgsl.ts +1 -1
- package/src/render/shaders/outputTransform.ts +24 -1
- package/src/render/shaders/recon/resolve.wgsl.ts +15 -1
- package/src/render/shaders/rush.ts +13 -2
- package/src/render/shaders/temporalResolve.ts +12 -5
- package/src/render/ssrPass.ts +2 -1
- package/src/render/surfaceTexture.ts +16 -0
- package/src/render/textRenderer.ts +2 -1
- package/src/render/vertexDefaults.ts +1 -0
- package/src/render/viewModel.ts +41 -0
- package/src/render/waterRenderer.ts +2 -1
- package/src/render/windStreakRenderer.ts +2 -1
|
@@ -5,8 +5,9 @@ import {
|
|
|
5
5
|
FLAT_VERT_WGSL,
|
|
6
6
|
} from '../../shaders/generated/flat.wgsl.ts';
|
|
7
7
|
import { vertexBufferLayouts } from './buffers.ts';
|
|
8
|
-
import type { PipelineCache } from './pipelineCache.ts';
|
|
8
|
+
import type { LitSwitch, PipelineCache } from './pipelineCache.ts';
|
|
9
9
|
import { shaderModule } from './shaderModules.ts';
|
|
10
|
+
import { SCENE_ALPHA_COVERS } from '../../sceneCoverage.ts';
|
|
10
11
|
|
|
11
12
|
/** The material maps the flat pass reads: `2d-array` colour, filtered, not shadow maps. */
|
|
12
13
|
const SURFACE_MAPS: ReadonlySet<string> = new Set([
|
|
@@ -199,17 +200,27 @@ export function flatFragmentBindings(variant: FlatVariant): StageBindings {
|
|
|
199
200
|
}
|
|
200
201
|
|
|
201
202
|
/**
|
|
202
|
-
* The lit fragment stage's constants, set from the cache
|
|
203
|
+
* The lit fragment stage's constants, set from the cache's switches, keyed by each one's **id**.
|
|
203
204
|
*
|
|
204
205
|
* Not by its name, because an override declared with `@id` answers to the id alone — WebGPU's
|
|
205
206
|
* "pipeline-overridable constant identifier string" — and naga writes one on every specialisation
|
|
206
207
|
* constant. Keyed by name, Chrome refused every lit pipeline: *Pipeline overridable constant
|
|
207
|
-
* "GLASS_SHADOWS" not found*, and the frame drew nothing.
|
|
208
|
+
* "GLASS_SHADOWS" not found*, and the frame drew nothing. Every override the variant declares is
|
|
209
|
+
* set, so a switch added to the shader and not to the cache is refused here rather than defaulted.
|
|
208
210
|
*/
|
|
209
211
|
function flatFragmentConstants(variant: FlatVariant, cache: PipelineCache): Record<string, number> {
|
|
210
|
-
const
|
|
211
|
-
if (
|
|
212
|
-
|
|
212
|
+
const overrides = flatFragmentBindings(variant).overrides;
|
|
213
|
+
if (overrides?.['GLASS_SHADOWS'] === undefined) {
|
|
214
|
+
throw new Error(`flatPass: variant "${variant}" has no glass switch`);
|
|
215
|
+
}
|
|
216
|
+
const constants: Record<string, number> = {};
|
|
217
|
+
for (const [name, id] of Object.entries(overrides)) {
|
|
218
|
+
if (!(name in cache.litSwitches)) {
|
|
219
|
+
throw new Error(`flatPass: variant "${variant}" declares a switch "${name}" no cache sets`);
|
|
220
|
+
}
|
|
221
|
+
constants[String(id)] = cache.litSwitches[name as LitSwitch] ? 1 : 0;
|
|
222
|
+
}
|
|
223
|
+
return constants;
|
|
213
224
|
}
|
|
214
225
|
|
|
215
226
|
/** The depth format both backends compare with. */
|
|
@@ -507,23 +518,26 @@ export function flatPipeline(
|
|
|
507
518
|
/** Culls nothing, for a surface seen from both faces. The key must carry it: `|2s`. */
|
|
508
519
|
doubleSided = false,
|
|
509
520
|
): GPURenderPipeline {
|
|
510
|
-
return cache.get(
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
521
|
+
return cache.get(
|
|
522
|
+
key,
|
|
523
|
+
() =>
|
|
524
|
+
flatDescriptor(
|
|
525
|
+
cache,
|
|
526
|
+
device,
|
|
527
|
+
layout,
|
|
528
|
+
variant,
|
|
529
|
+
key,
|
|
530
|
+
present,
|
|
531
|
+
translucent,
|
|
532
|
+
skinned,
|
|
533
|
+
morphed,
|
|
534
|
+
depthWrite,
|
|
535
|
+
depthLayer,
|
|
536
|
+
instanced,
|
|
537
|
+
oit,
|
|
538
|
+
doubleSided,
|
|
539
|
+
),
|
|
540
|
+
true,
|
|
527
541
|
);
|
|
528
542
|
}
|
|
529
543
|
|
|
@@ -549,8 +563,11 @@ export function flatPipelineAsync(
|
|
|
549
563
|
/** Whether this pipeline reads morph deltas. See `FlatVertexOptions`. */
|
|
550
564
|
morphed = false,
|
|
551
565
|
): Promise<GPURenderPipeline> {
|
|
552
|
-
return cache.getAsync(
|
|
553
|
-
|
|
566
|
+
return cache.getAsync(
|
|
567
|
+
key,
|
|
568
|
+
() =>
|
|
569
|
+
flatDescriptor(cache, device, layout, variant, key, present, translucent, skinned, morphed),
|
|
570
|
+
true,
|
|
554
571
|
);
|
|
555
572
|
}
|
|
556
573
|
|
|
@@ -665,7 +682,7 @@ function flatDescriptor(
|
|
|
665
682
|
dstFactor: 'one-minus-src-alpha',
|
|
666
683
|
operation: 'add',
|
|
667
684
|
},
|
|
668
|
-
alpha:
|
|
685
|
+
alpha: SCENE_ALPHA_COVERS,
|
|
669
686
|
},
|
|
670
687
|
}
|
|
671
688
|
: { format: cache.format }),
|
|
@@ -9,6 +9,7 @@ import type { UniformFields } from './scatterPass.ts';
|
|
|
9
9
|
import { DEPTH_FORMAT } from './flatPass.ts';
|
|
10
10
|
import type { PipelineCache } from './pipelineCache.ts';
|
|
11
11
|
import { shaderModule } from './shaderModules.ts';
|
|
12
|
+
import { SCENE_ALPHA_COVERS } from '../../sceneCoverage.ts';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* The line pass: a polyline with a real width, four vertices a segment.
|
|
@@ -222,7 +223,7 @@ export function linePipeline(
|
|
|
222
223
|
dstFactor: 'one-minus-src-alpha',
|
|
223
224
|
operation: 'add',
|
|
224
225
|
},
|
|
225
|
-
alpha:
|
|
226
|
+
alpha: SCENE_ALPHA_COVERS,
|
|
226
227
|
},
|
|
227
228
|
},
|
|
228
229
|
],
|
|
@@ -7,6 +7,7 @@ import type { UniformFields } from './scatterPass.ts';
|
|
|
7
7
|
import { DEPTH_FORMAT } from './flatPass.ts';
|
|
8
8
|
import type { PipelineCache } from './pipelineCache.ts';
|
|
9
9
|
import { shaderModule } from './shaderModules.ts';
|
|
10
|
+
import { SCENE_ALPHA_COVERS } from '../../sceneCoverage.ts';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* The panel pass: two triangles in screen space, one colour, one alpha.
|
|
@@ -121,7 +122,7 @@ export function panelPipeline(
|
|
|
121
122
|
format: cache.format,
|
|
122
123
|
blend: {
|
|
123
124
|
color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },
|
|
124
|
-
alpha:
|
|
125
|
+
alpha: SCENE_ALPHA_COVERS,
|
|
125
126
|
},
|
|
126
127
|
},
|
|
127
128
|
],
|
|
@@ -8,6 +8,7 @@ import { DEPTH_FORMAT } from './flatPass.ts';
|
|
|
8
8
|
import type { PipelineCache } from './pipelineCache.ts';
|
|
9
9
|
import type { UniformFields } from './scatterPass.ts';
|
|
10
10
|
import { shaderModule } from './shaderModules.ts';
|
|
11
|
+
import { SCENE_ALPHA_COVERS } from '../../sceneCoverage.ts';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* The particle pass: a crossed pair of quads per live particle, rebuilt every frame.
|
|
@@ -367,7 +368,7 @@ export function particlePipeline(
|
|
|
367
368
|
dstFactor: 'one-minus-src-alpha',
|
|
368
369
|
operation: 'add',
|
|
369
370
|
},
|
|
370
|
-
alpha:
|
|
371
|
+
alpha: SCENE_ALPHA_COVERS,
|
|
371
372
|
},
|
|
372
373
|
},
|
|
373
374
|
],
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { noLitSwitches, type LitSwitch } from '../../shaders/flat/glassTint.ts';
|
|
2
|
+
|
|
3
|
+
export type { LitSwitch };
|
|
4
|
+
|
|
1
5
|
/**
|
|
2
6
|
* Render pipelines, built once and looked up by name.
|
|
3
7
|
*
|
|
@@ -35,15 +39,30 @@ export class PipelineCache {
|
|
|
35
39
|
readonly sampleCount: number;
|
|
36
40
|
|
|
37
41
|
/**
|
|
38
|
-
*
|
|
42
|
+
* Which of the lit stage's switches the pipelines built now have on: the pipeline-overridable
|
|
43
|
+
* constants the generated lit shader branches on (`GLASS_SHADOWS` and `litSwitchesGlsl`), which
|
|
44
|
+
* the device compiles away where they are off. Read by `flatPass` when it describes a pipeline.
|
|
39
45
|
*
|
|
40
|
-
* **
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
46
|
+
* **Each is code a scene pays for in every lit pixel's registers whether it uses it or not**, so
|
|
47
|
+
* each is off until it is used: glass until the renderer is offered a pane, a fixture until one is
|
|
48
|
+
* loaded, the effects until a material carries a table, DriftLight until a volume is set — and
|
|
49
|
+
* then on for good, through `enable`. Clustering is the profile's, fixed at construction. Every
|
|
50
|
+
* scene paid for all of it from 4.5.0 to 4.8.1, and on a phone that was the lit pass: about 30%
|
|
51
|
+
* of the lit shader's instructions for glass alone, measured on RADV. **What it costs** is the
|
|
52
|
+
* frames between a feature's first use and its recompiled pipelines, which draw without it.
|
|
44
53
|
*/
|
|
54
|
+
readonly litSwitches: Record<LitSwitch, boolean> = noLitSwitches();
|
|
55
|
+
|
|
56
|
+
/** Whether glass may ever be switched on here: `glassShadows` other than `'off'`. */
|
|
45
57
|
readonly glassShadows: boolean;
|
|
46
58
|
|
|
59
|
+
/** How to describe each lit pipeline again, so `enable` can rebuild every one. */
|
|
60
|
+
private readonly litDescribers = new Map<string, () => GPURenderPipelineDescriptor>();
|
|
61
|
+
/** Bumped by every switch, so a lit build started before one is not kept after it. */
|
|
62
|
+
private litGeneration = 0;
|
|
63
|
+
/** The last rebuild, which the next one waits for so they land in the order they were asked. */
|
|
64
|
+
private litRebuild: Promise<void> = Promise.resolve();
|
|
65
|
+
|
|
47
66
|
private readonly device: GPUDevice;
|
|
48
67
|
private readonly pipelines = new Map<string, GPURenderPipeline>();
|
|
49
68
|
/**
|
|
@@ -62,11 +81,19 @@ export class PipelineCache {
|
|
|
62
81
|
*/
|
|
63
82
|
private readonly compiling = new Map<string, Promise<GPURenderPipeline>>();
|
|
64
83
|
|
|
65
|
-
constructor(
|
|
84
|
+
constructor(
|
|
85
|
+
device: GPUDevice,
|
|
86
|
+
format: GPUTextureFormat,
|
|
87
|
+
sampleCount = 1,
|
|
88
|
+
glassShadows = true,
|
|
89
|
+
/** The profile's `clusteredLights`, which compiles the clustered arm in or leaves it out. */
|
|
90
|
+
clusteredLights = false,
|
|
91
|
+
) {
|
|
66
92
|
this.device = device;
|
|
67
93
|
this.format = format;
|
|
68
94
|
this.sampleCount = sampleCount;
|
|
69
95
|
this.glassShadows = glassShadows;
|
|
96
|
+
this.litSwitches.CLUSTERED_LIGHTS = clusteredLights;
|
|
70
97
|
}
|
|
71
98
|
|
|
72
99
|
/** How many distinct pipelines have been built. Read by tests and diagnostics. */
|
|
@@ -85,11 +112,15 @@ export class PipelineCache {
|
|
|
85
112
|
async getAsync(
|
|
86
113
|
key: string,
|
|
87
114
|
describe: () => GPURenderPipelineDescriptor,
|
|
115
|
+
/** Whether this is a lit pipeline, which `enable` rebuilds. See `litSwitches`. */
|
|
116
|
+
lit = false,
|
|
88
117
|
): Promise<GPURenderPipeline> {
|
|
118
|
+
if (lit) this.litDescribers.set(key, describe);
|
|
89
119
|
const existing = this.pipelines.get(key);
|
|
90
120
|
if (existing !== undefined) return existing;
|
|
91
121
|
const inFlight = this.compiling.get(key);
|
|
92
122
|
if (inFlight !== undefined) return inFlight;
|
|
123
|
+
const generation = this.litGeneration;
|
|
93
124
|
|
|
94
125
|
/*
|
|
95
126
|
* **Falls back where the async form is missing.** It is part of the standard, but a device
|
|
@@ -103,7 +134,8 @@ export class PipelineCache {
|
|
|
103
134
|
? this.device.createRenderPipelineAsync(describe())
|
|
104
135
|
: Promise.resolve(this.device.createRenderPipeline(describe()));
|
|
105
136
|
const promise = build.then((built) => {
|
|
106
|
-
|
|
137
|
+
/* A lit pipeline started before a switch is not kept: the switch rebuilds it. */
|
|
138
|
+
if (!lit || generation === this.litGeneration) this.pipelines.set(key, built);
|
|
107
139
|
this.compiling.delete(key);
|
|
108
140
|
return built;
|
|
109
141
|
});
|
|
@@ -142,7 +174,13 @@ export class PipelineCache {
|
|
|
142
174
|
* too**: a nested object with vertex buffer layouts and blend state, constructed to be
|
|
143
175
|
* thrown away on every hit. Passing a function means a hit costs one `Map` lookup.
|
|
144
176
|
*/
|
|
145
|
-
get(
|
|
177
|
+
get(
|
|
178
|
+
key: string,
|
|
179
|
+
describe: () => GPURenderPipelineDescriptor,
|
|
180
|
+
/** Whether this is a lit pipeline, which `enable` rebuilds. See `litSwitches`. */
|
|
181
|
+
lit = false,
|
|
182
|
+
): GPURenderPipeline {
|
|
183
|
+
if (lit) this.litDescribers.set(key, describe);
|
|
146
184
|
const existing = this.pipelines.get(key);
|
|
147
185
|
if (existing !== undefined) return existing;
|
|
148
186
|
const built = this.device.createRenderPipeline(describe());
|
|
@@ -150,6 +188,42 @@ export class PipelineCache {
|
|
|
150
188
|
return built;
|
|
151
189
|
}
|
|
152
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Turn one of the lit stage's switches on, for every lit pipeline built from now on and every one
|
|
193
|
+
* already built. See `litSwitches`.
|
|
194
|
+
*
|
|
195
|
+
* **The rebuilt set lands at once**, when every one of them has compiled, so no frame draws some
|
|
196
|
+
* surfaces with a feature and others without. Until then the pipelines already built go on drawing
|
|
197
|
+
* as they were. Once only: a switch is not turned off again when the last user of it leaves,
|
|
198
|
+
* because a second switch would cost a second compile for a saving the first use showed was not
|
|
199
|
+
* needed. Glass is never turned on where the profile asked for no glass shadows.
|
|
200
|
+
*/
|
|
201
|
+
enable(feature: LitSwitch): Promise<void> {
|
|
202
|
+
if (this.litSwitches[feature]) return this.litRebuild;
|
|
203
|
+
if (feature === 'GLASS_SHADOWS' && !this.glassShadows) return this.litRebuild;
|
|
204
|
+
this.litSwitches[feature] = true;
|
|
205
|
+
this.litGeneration += 1;
|
|
206
|
+
const generation = this.litGeneration;
|
|
207
|
+
this.litRebuild = this.litRebuild.then(async () => {
|
|
208
|
+
/* A later switch has its own rebuild, which describes every pipeline with this one on too. */
|
|
209
|
+
if (generation !== this.litGeneration) return;
|
|
210
|
+
const rebuilt = [...this.litDescribers].map(([key, describe]) => {
|
|
211
|
+
const descriptor = describe();
|
|
212
|
+
const build =
|
|
213
|
+
typeof this.device.createRenderPipelineAsync === 'function'
|
|
214
|
+
? this.device.createRenderPipelineAsync(descriptor)
|
|
215
|
+
: Promise.resolve(this.device.createRenderPipeline(descriptor));
|
|
216
|
+
return build.then((pipeline) => [key, pipeline] as const);
|
|
217
|
+
});
|
|
218
|
+
const results = await Promise.allSettled(rebuilt);
|
|
219
|
+
if (generation !== this.litGeneration) return;
|
|
220
|
+
for (const result of results) {
|
|
221
|
+
if (result.status === 'fulfilled') this.pipelines.set(result.value[0], result.value[1]);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
return this.litRebuild;
|
|
225
|
+
}
|
|
226
|
+
|
|
153
227
|
/**
|
|
154
228
|
* Drop every pipeline.
|
|
155
229
|
*
|
|
@@ -9,6 +9,7 @@ import { DEPTH_FORMAT } from './flatPass.ts';
|
|
|
9
9
|
import type { PipelineCache } from './pipelineCache.ts';
|
|
10
10
|
import type { UniformFields } from './scatterPass.ts';
|
|
11
11
|
import { shaderModule } from './shaderModules.ts';
|
|
12
|
+
import { SCENE_ALPHA_COVERS } from '../../sceneCoverage.ts';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* The plume pass: crossed quads per plume, displaced and faded in the shaders.
|
|
@@ -251,7 +252,7 @@ function blendState(blend: 'additive' | 'alpha'): GPUBlendState {
|
|
|
251
252
|
}
|
|
252
253
|
: {
|
|
253
254
|
color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },
|
|
254
|
-
alpha:
|
|
255
|
+
alpha: SCENE_ALPHA_COVERS,
|
|
255
256
|
};
|
|
256
257
|
}
|
|
257
258
|
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
import type { UniformFields } from './scatterPass.ts';
|
|
40
40
|
import type { PipelineCache } from './pipelineCache.ts';
|
|
41
41
|
import { shaderModule } from './shaderModules.ts';
|
|
42
|
+
import { SCENE_ALPHA_KEEPS, SCENE_ALPHA_TRANSMITS } from '../../sceneCoverage.ts';
|
|
42
43
|
|
|
43
44
|
/**
|
|
44
45
|
* The composite: everything that happens to the frame after the world is drawn.
|
|
@@ -271,7 +272,7 @@ export function createPostStageLayout(
|
|
|
271
272
|
*/
|
|
272
273
|
export const OIT_COMPOSITE_BLEND: GPUBlendState = {
|
|
273
274
|
color: { srcFactor: 'one-minus-src-alpha', dstFactor: 'src-alpha', operation: 'add' },
|
|
274
|
-
alpha:
|
|
275
|
+
alpha: SCENE_ALPHA_TRANSMITS,
|
|
275
276
|
};
|
|
276
277
|
|
|
277
278
|
/**
|
|
@@ -290,17 +291,19 @@ export const OIT_COMPOSITE_BLEND: GPUBlendState = {
|
|
|
290
291
|
* exactly the integral — and the composite therefore never samples the colour it is modifying,
|
|
291
292
|
* which is the read both backends are entitled to call undefined.
|
|
292
293
|
*
|
|
293
|
-
* Alpha is
|
|
294
|
-
*
|
|
294
|
+
* Alpha is the transmittance again, `share * a`: the scene's alpha is how much of the pixel is
|
|
295
|
+
* still the opaque surface, which is what the composite lets ambient occlusion darken, and a
|
|
296
|
+
* medium thick enough to hide a corner hides its occlusion too (`sceneCoverage.ts`). It reaches
|
|
297
|
+
* no canvas: both are opaque, and the composite writes 1.
|
|
295
298
|
*/
|
|
296
299
|
export const MEDIUM_TRANSMIT_BLEND: GPUBlendState = {
|
|
297
300
|
color: { srcFactor: 'one', dstFactor: 'src-alpha', operation: 'add' },
|
|
298
|
-
alpha:
|
|
301
|
+
alpha: SCENE_ALPHA_TRANSMITS,
|
|
299
302
|
};
|
|
300
303
|
|
|
301
304
|
export const DECAL_MULTIPLY_BLEND: GPUBlendState = {
|
|
302
305
|
color: { srcFactor: 'zero', dstFactor: 'src', operation: 'add' },
|
|
303
|
-
alpha:
|
|
306
|
+
alpha: SCENE_ALPHA_KEEPS,
|
|
304
307
|
};
|
|
305
308
|
|
|
306
309
|
/**
|
|
@@ -316,6 +319,18 @@ export const PREMULTIPLIED_OVER_BLEND: GPUBlendState = {
|
|
|
316
319
|
alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
|
|
317
320
|
};
|
|
318
321
|
|
|
322
|
+
/**
|
|
323
|
+
* The same `over` for the resolve that lands a reflection on the scene, keeping the scene's alpha.
|
|
324
|
+
*
|
|
325
|
+
* A reflection is a layer of the surface it is seen in, so the surface's share of the pixel is
|
|
326
|
+
* unchanged (`sceneCoverage.ts`). The trace keeps `PREMULTIPLIED_OVER_BLEND`, because its target is
|
|
327
|
+
* the reflection's own and its alpha is the coverage this resolve reads.
|
|
328
|
+
*/
|
|
329
|
+
export const REFLECTION_OVER_BLEND: GPUBlendState = {
|
|
330
|
+
color: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },
|
|
331
|
+
alpha: SCENE_ALPHA_KEEPS,
|
|
332
|
+
};
|
|
333
|
+
|
|
319
334
|
export const ADDITIVE_BLEND: GPUBlendState = {
|
|
320
335
|
color: { srcFactor: 'one', dstFactor: 'one', operation: 'add' },
|
|
321
336
|
alpha: { srcFactor: 'one', dstFactor: 'one', operation: 'add' },
|