@driftengine/core 4.4.1 → 4.5.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/CHANGELOG.json +72 -0
- package/README.md +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +7 -0
- package/dist/render/backend/api.d.ts +17 -11
- package/dist/render/backend/webgl2/glassTintPrograms.d.ts +13 -0
- package/dist/render/backend/webgl2/glassTintPrograms.js +38 -0
- package/dist/render/backend/webgl2/renderer.d.ts +82 -8
- package/dist/render/backend/webgl2/renderer.js +373 -105
- package/dist/render/backend/webgpu/buffers.d.ts +19 -1
- package/dist/render/backend/webgpu/buffers.js +27 -1
- package/dist/render/backend/webgpu/depthPass.d.ts +1 -1
- package/dist/render/backend/webgpu/depthPass.js +2 -1
- package/dist/render/backend/webgpu/flatPass.d.ts +2 -0
- package/dist/render/backend/webgpu/flatPass.js +20 -1
- package/dist/render/backend/webgpu/glassTintPass.d.ts +28 -0
- package/dist/render/backend/webgpu/glassTintPass.js +205 -0
- package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +60 -1
- package/dist/render/backend/webgpu/gpuDrivenPass.js +305 -5
- package/dist/render/backend/webgpu/instanced.d.ts +12 -1
- package/dist/render/backend/webgpu/instanced.js +20 -1
- package/dist/render/backend/webgpu/latePass.d.ts +26 -0
- package/dist/render/backend/webgpu/latePass.js +133 -0
- package/dist/render/backend/webgpu/lightVolumePass.js +5 -1
- package/dist/render/backend/webgpu/motionPass.d.ts +95 -0
- package/dist/render/backend/webgpu/motionPass.js +491 -0
- package/dist/render/backend/webgpu/pipelineCache.d.ts +10 -1
- package/dist/render/backend/webgpu/pipelineCache.js +11 -1
- package/dist/render/backend/webgpu/pointGlassTintPass.d.ts +32 -0
- package/dist/render/backend/webgpu/pointGlassTintPass.js +193 -0
- package/dist/render/backend/webgpu/pointShadowPass.d.ts +35 -6
- package/dist/render/backend/webgpu/pointShadowPass.js +105 -20
- package/dist/render/backend/webgpu/postPass.d.ts +21 -15
- package/dist/render/backend/webgpu/postPass.js +17 -4
- package/dist/render/backend/webgpu/renderer.d.ts +181 -54
- package/dist/render/backend/webgpu/renderer.js +1011 -394
- package/dist/render/backend/webgpu/scatterPass.js +2 -1
- package/dist/render/backend/webgpu/skinPaletteRing.d.ts +4 -0
- package/dist/render/backend/webgpu/skinPaletteRing.js +4 -0
- package/dist/render/backend/webgpu/snapshotMips.d.ts +29 -0
- package/dist/render/backend/webgpu/snapshotMips.js +52 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +5 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.js +1 -1
- package/dist/render/flameLight.js +15 -2
- package/dist/render/glass.d.ts +45 -0
- package/dist/render/glass.js +39 -0
- package/dist/render/glassCasters.d.ts +40 -0
- package/dist/render/glassCasters.js +84 -0
- package/dist/render/glassShadow.d.ts +78 -0
- package/dist/render/glassShadow.js +125 -0
- package/dist/render/globalMediumPass.d.ts +2 -3
- package/dist/render/globalMediumPass.js +2 -8
- package/dist/render/gpudriven/lit.d.ts +12 -0
- package/dist/render/gpudriven/lit.js +23 -0
- package/dist/render/gpudriven/materialTable.d.ts +15 -5
- package/dist/render/gpudriven/materialTable.js +26 -6
- package/dist/render/gpudriven/pipeline.d.ts +1 -1
- package/dist/render/gpudriven/pipeline.js +4 -3
- package/dist/render/gpudriven/sunGlass.d.ts +35 -0
- package/dist/render/gpudriven/sunGlass.js +89 -0
- package/dist/render/lightBudget.d.ts +17 -5
- package/dist/render/lightBudget.js +17 -5
- package/dist/render/pointGlassTint.d.ts +59 -0
- package/dist/render/pointGlassTint.js +198 -0
- package/dist/render/pointShadowArray.d.ts +56 -3
- package/dist/render/pointShadowArray.js +127 -16
- package/dist/render/pointShadowBudget.js +51 -41
- package/dist/render/pointShadowMap.d.ts +23 -1
- package/dist/render/pointShadowMap.js +27 -1
- package/dist/render/recon/changeFrames.d.ts +15 -0
- package/dist/render/recon/changeFrames.js +21 -0
- package/dist/render/recon/disocclusion.d.ts +36 -3
- package/dist/render/recon/disocclusion.js +56 -4
- package/dist/render/recon/lateRouting.d.ts +14 -0
- package/dist/render/recon/lateRouting.js +18 -0
- package/dist/render/recon/mover.d.ts +57 -0
- package/dist/render/recon/mover.js +86 -0
- package/dist/render/recon/resolve.js +95 -12
- package/dist/render/renderQuality.d.ts +35 -0
- package/dist/render/renderQuality.js +7 -0
- package/dist/render/rendererHarness.js +12 -0
- package/dist/render/sceneTarget.d.ts +9 -1
- package/dist/render/sceneTarget.js +59 -13
- package/dist/render/shaders/depth.d.ts +16 -1
- package/dist/render/shaders/depth.js +85 -9
- package/dist/render/shaders/flat/directionalShadow.js +91 -6
- package/dist/render/shaders/flat/glassTint.d.ts +6 -0
- package/dist/render/shaders/flat/glassTint.js +25 -0
- package/dist/render/shaders/flat/index.d.ts +13 -0
- package/dist/render/shaders/flat/index.js +4 -0
- package/dist/render/shaders/flat/lampGlass.d.ts +1 -0
- package/dist/render/shaders/flat/lampGlass.js +183 -0
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +115 -15
- package/dist/render/shaders/flat/pointShadow.d.ts +1 -1
- package/dist/render/shaders/flat/pointShadow.js +2 -0
- package/dist/render/shaders/flat/preamble.js +21 -6
- package/dist/render/shaders/generated/depth.wgsl.d.ts +227 -4
- package/dist/render/shaders/generated/depth.wgsl.js +227 -4
- package/dist/render/shaders/generated/flat.wgsl.d.ts +360 -312
- package/dist/render/shaders/generated/flat.wgsl.js +376 -328
- package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +3 -13
- package/dist/render/shaders/generated/globalMedium.wgsl.js +3 -13
- package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +3 -13
- package/dist/render/shaders/generated/lightVolume.wgsl.js +4 -14
- package/dist/render/shaders/generated/octahedralResolve.wgsl.d.ts +29 -0
- package/dist/render/shaders/generated/octahedralResolve.wgsl.js +29 -0
- package/dist/render/shaders/generated/temporalResolve.wgsl.d.ts +31 -6
- package/dist/render/shaders/generated/temporalResolve.wgsl.js +31 -6
- package/dist/render/shaders/globalMedium.js +6 -6
- package/dist/render/shaders/gpudriven/blendRaster.wgsl.d.ts +7 -0
- package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +86 -1
- package/dist/render/shaders/gpudriven/cull.wgsl.d.ts +1 -1
- package/dist/render/shaders/gpudriven/cull.wgsl.js +11 -2
- package/dist/render/shaders/gpudriven/materialTable.wgsl.d.ts +1 -1
- package/dist/render/shaders/gpudriven/materialTable.wgsl.js +3 -1
- package/dist/render/shaders/gpudriven/shade.wgsl.js +50 -1
- package/dist/render/shaders/gpudriven/sunGlass.wgsl.d.ts +9 -0
- package/dist/render/shaders/gpudriven/sunGlass.wgsl.js +133 -0
- package/dist/render/shaders/lightVolume.js +6 -6
- package/dist/render/shaders/octahedralResolve.d.ts +8 -0
- package/dist/render/shaders/octahedralResolve.js +48 -17
- package/dist/render/shaders/recon/lateDepth.wgsl.d.ts +1 -0
- package/dist/render/shaders/recon/lateDepth.wgsl.js +44 -0
- package/dist/render/shaders/recon/motion.wgsl.d.ts +1 -1
- package/dist/render/shaders/recon/motion.wgsl.js +84 -0
- package/dist/render/shaders/recon/resolve.wgsl.d.ts +1 -1
- package/dist/render/shaders/recon/resolve.wgsl.js +112 -8
- package/dist/render/shaders/temporalResolve.js +133 -5
- package/dist/render/shadowCasters.d.ts +20 -1
- package/dist/render/shadowCasters.js +15 -1
- package/dist/render/shadowMap.d.ts +54 -5
- package/dist/render/shadowMap.js +190 -36
- package/dist/render/sunGlassTint.d.ts +25 -0
- package/dist/render/sunGlassTint.js +79 -0
- package/dist/render/temporalAa.d.ts +145 -0
- package/dist/render/temporalAa.js +190 -0
- package/dist/render/temporalPass.d.ts +16 -14
- package/dist/render/temporalPass.js +59 -14
- package/dist/render/translucentQueue.d.ts +6 -0
- package/dist/render/translucentQueue.js +15 -1
- package/package.json +6 -6
- package/src/index.ts +10 -0
- package/src/render/backend/api.ts +15 -9
- package/src/render/backend/webgl2/glassTintPrograms.ts +58 -0
- package/src/render/backend/webgl2/renderer.ts +422 -109
- package/src/render/backend/webgpu/buffers.ts +50 -2
- package/src/render/backend/webgpu/depthPass.ts +2 -1
- package/src/render/backend/webgpu/flatPass.ts +22 -1
- package/src/render/backend/webgpu/glassTintPass.ts +244 -0
- package/src/render/backend/webgpu/gpuDrivenPass.ts +349 -5
- package/src/render/backend/webgpu/instanced.ts +21 -1
- package/src/render/backend/webgpu/latePass.ts +152 -0
- package/src/render/backend/webgpu/lightVolumePass.ts +5 -1
- package/src/render/backend/webgpu/motionPass.ts +535 -0
- package/src/render/backend/webgpu/pipelineCache.ts +12 -1
- package/src/render/backend/webgpu/pointGlassTintPass.ts +233 -0
- package/src/render/backend/webgpu/pointShadowPass.ts +158 -26
- package/src/render/backend/webgpu/postPass.ts +34 -4
- package/src/render/backend/webgpu/renderer.ts +1178 -425
- package/src/render/backend/webgpu/scatterPass.ts +2 -1
- package/src/render/backend/webgpu/skinPaletteRing.ts +4 -0
- package/src/render/backend/webgpu/snapshotMips.ts +80 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +1 -1
- package/src/render/flameLight.ts +15 -2
- package/src/render/glass.ts +74 -0
- package/src/render/glassCasters.ts +164 -0
- package/src/render/glassShadow.ts +153 -0
- package/src/render/globalMediumPass.ts +4 -11
- package/src/render/gpudriven/lit.ts +31 -0
- package/src/render/gpudriven/materialTable.ts +28 -6
- package/src/render/gpudriven/pipeline.ts +4 -3
- package/src/render/gpudriven/sunGlass.ts +117 -0
- package/src/render/lightBudget.ts +19 -5
- package/src/render/pointGlassTint.ts +250 -0
- package/src/render/pointShadowArray.ts +188 -22
- package/src/render/pointShadowBudget.ts +49 -39
- package/src/render/pointShadowMap.ts +41 -0
- package/src/render/recon/changeFrames.ts +24 -0
- package/src/render/recon/disocclusion.ts +54 -3
- package/src/render/recon/lateRouting.ts +29 -0
- package/src/render/recon/mover.ts +118 -0
- package/src/render/recon/resolve.ts +137 -10
- package/src/render/renderQuality.ts +47 -0
- package/src/render/rendererHarness.ts +12 -0
- package/src/render/sceneTarget.ts +66 -12
- package/src/render/shaders/depth.ts +109 -9
- package/src/render/shaders/flat/directionalShadow.ts +97 -6
- package/src/render/shaders/flat/glassTint.ts +25 -0
- package/src/render/shaders/flat/index.ts +17 -0
- package/src/render/shaders/flat/lampGlass.ts +185 -0
- package/src/render/shaders/flat/main.ts +115 -15
- package/src/render/shaders/flat/pointShadow.ts +2 -0
- package/src/render/shaders/flat/preamble.ts +21 -6
- package/src/render/shaders/generated/depth.wgsl.ts +234 -4
- package/src/render/shaders/generated/flat.wgsl.ts +376 -328
- package/src/render/shaders/generated/globalMedium.wgsl.ts +3 -13
- package/src/render/shaders/generated/lightVolume.wgsl.ts +4 -14
- package/src/render/shaders/generated/octahedralResolve.wgsl.ts +30 -0
- package/src/render/shaders/generated/temporalResolve.wgsl.ts +31 -6
- package/src/render/shaders/globalMedium.ts +6 -6
- package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +88 -1
- package/src/render/shaders/gpudriven/cull.wgsl.ts +11 -2
- package/src/render/shaders/gpudriven/materialTable.wgsl.ts +3 -1
- package/src/render/shaders/gpudriven/shade.wgsl.ts +50 -1
- package/src/render/shaders/gpudriven/sunGlass.wgsl.ts +139 -0
- package/src/render/shaders/lightVolume.ts +6 -6
- package/src/render/shaders/octahedralResolve.ts +50 -17
- package/src/render/shaders/recon/lateDepth.wgsl.ts +47 -0
- package/src/render/shaders/recon/motion.wgsl.ts +84 -0
- package/src/render/shaders/recon/resolve.wgsl.ts +112 -8
- package/src/render/shaders/temporalResolve.ts +141 -5
- package/src/render/shadowCasters.ts +28 -1
- package/src/render/shadowMap.ts +231 -37
- package/src/render/sunGlassTint.ts +86 -0
- package/src/render/temporalAa.ts +227 -0
- package/src/render/temporalPass.ts +88 -12
- package/src/render/translucentQueue.ts +20 -1
|
@@ -34,6 +34,16 @@ export class PipelineCache {
|
|
|
34
34
|
*/
|
|
35
35
|
readonly sampleCount: number;
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Whether the lit pipelines here read glass in their shadows: `glassShadows` other than `'off'`.
|
|
39
|
+
*
|
|
40
|
+
* **Carried by the cache for the reason the format is**: one value every lit pipeline is built
|
|
41
|
+
* with, chosen once by the renderer. It is the `GLASS_SHADOWS` override the generated lit shader
|
|
42
|
+
* branches on (see `FlatShaderOptions.glassShadows`), so off specialises the lookups away rather
|
|
43
|
+
* than leaving code that returns at its first line and still costs the pass its registers.
|
|
44
|
+
*/
|
|
45
|
+
readonly glassShadows: boolean;
|
|
46
|
+
|
|
37
47
|
private readonly device: GPUDevice;
|
|
38
48
|
private readonly pipelines = new Map<string, GPURenderPipeline>();
|
|
39
49
|
/**
|
|
@@ -52,10 +62,11 @@ export class PipelineCache {
|
|
|
52
62
|
*/
|
|
53
63
|
private readonly compiling = new Map<string, Promise<GPURenderPipeline>>();
|
|
54
64
|
|
|
55
|
-
constructor(device: GPUDevice, format: GPUTextureFormat, sampleCount = 1) {
|
|
65
|
+
constructor(device: GPUDevice, format: GPUTextureFormat, sampleCount = 1, glassShadows = true) {
|
|
56
66
|
this.device = device;
|
|
57
67
|
this.format = format;
|
|
58
68
|
this.sampleCount = sampleCount;
|
|
69
|
+
this.glassShadows = glassShadows;
|
|
59
70
|
}
|
|
60
71
|
|
|
61
72
|
/** How many distinct pipelines have been built. Read by tests and diagnostics. */
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a lamp's light keeps through glass, on this device: an `rgba8unorm` array of one layer a
|
|
3
|
+
* light, the face-sized colour scratch a face's glass is multiplied into, and the resolve that
|
|
4
|
+
* copies a face into its octahedral region. The twin of `pointGlassTint.ts`, which says what the
|
|
5
|
+
* channels hold and why the chain exists.
|
|
6
|
+
*
|
|
7
|
+
* **The chain is a `SnapshotMips` per layer**, made the first time that layer is touched and
|
|
8
|
+
* recorded once a round for the layers the round touched — a layer's levels are its own
|
|
9
|
+
* subresources here, so nothing is staged the way the other backend must.
|
|
10
|
+
*/
|
|
11
|
+
import { mat3 } from 'gl-matrix';
|
|
12
|
+
|
|
13
|
+
import { pointShadowFaceRotation } from '../../pointShadowImage.ts';
|
|
14
|
+
import {
|
|
15
|
+
OCTAHEDRAL_RESOLVE_TINT_FRAG_WGSL,
|
|
16
|
+
OCTAHEDRAL_RESOLVE_VERT_WGSL,
|
|
17
|
+
OCTAHEDRALRESOLVE_BINDINGS,
|
|
18
|
+
} from '../../shaders/generated/octahedralResolve.wgsl.ts';
|
|
19
|
+
import { SnapshotMips } from './snapshotMips.ts';
|
|
20
|
+
import type { MipPipelines } from './surfaceTexturePass.ts';
|
|
21
|
+
import { UniformRing } from './uniformRing.ts';
|
|
22
|
+
|
|
23
|
+
const USAGE_RENDER_ATTACHMENT = 0x10;
|
|
24
|
+
const USAGE_TEXTURE_BINDING = 0x4;
|
|
25
|
+
const USAGE_UNIFORM_DST = 0x40 | 0x8; // UNIFORM | COPY_DST
|
|
26
|
+
const VISIBILITY_FRAGMENT = 0x2;
|
|
27
|
+
const TINT = OCTAHEDRALRESOLVE_BINDINGS.OCTAHEDRAL_RESOLVE_TINT_FRAG;
|
|
28
|
+
|
|
29
|
+
const faceRotation = mat3.create();
|
|
30
|
+
const rotationColumn = new Float32Array(3);
|
|
31
|
+
|
|
32
|
+
export class GpuPointGlassTint {
|
|
33
|
+
readonly texture: GPUTexture;
|
|
34
|
+
/** The whole array, for the lit pass. */
|
|
35
|
+
readonly view: GPUTextureView;
|
|
36
|
+
/** The colour scratch a face's glass is multiplied into. */
|
|
37
|
+
readonly scratchView: GPUTextureView;
|
|
38
|
+
readonly edge: number;
|
|
39
|
+
|
|
40
|
+
private readonly device: GPUDevice;
|
|
41
|
+
private readonly pipelines: MipPipelines;
|
|
42
|
+
private readonly scratch: GPUTexture;
|
|
43
|
+
private readonly layerViews: GPUTextureView[] = [];
|
|
44
|
+
private readonly mips: (SnapshotMips | undefined)[] = [];
|
|
45
|
+
private readonly pipeline: GPURenderPipeline;
|
|
46
|
+
private readonly bindGroup: GPUBindGroup;
|
|
47
|
+
private readonly ring: UniformRing;
|
|
48
|
+
private readonly heldFaces: Uint8Array;
|
|
49
|
+
private readonly dirty: Uint8Array;
|
|
50
|
+
|
|
51
|
+
constructor(
|
|
52
|
+
device: GPUDevice,
|
|
53
|
+
pipelines: MipPipelines,
|
|
54
|
+
faceSize: number,
|
|
55
|
+
edge: number,
|
|
56
|
+
layers: number,
|
|
57
|
+
/** Resolves a frame can make: the array's own figure, since every face resolves once. */
|
|
58
|
+
slots: number,
|
|
59
|
+
encoder: GPUCommandEncoder,
|
|
60
|
+
) {
|
|
61
|
+
this.device = device;
|
|
62
|
+
this.pipelines = pipelines;
|
|
63
|
+
this.edge = edge;
|
|
64
|
+
this.heldFaces = new Uint8Array(layers);
|
|
65
|
+
/* Every layer's chain is filled once at birth, so a level nothing has touched is white too. */
|
|
66
|
+
this.dirty = new Uint8Array(layers).fill(1);
|
|
67
|
+
this.texture = device.createTexture({
|
|
68
|
+
label: 'pointShadow.tint',
|
|
69
|
+
size: [edge, edge, layers],
|
|
70
|
+
format: 'rgba8unorm',
|
|
71
|
+
mipLevelCount: Math.floor(Math.log2(Math.max(1, edge))) + 1,
|
|
72
|
+
usage: USAGE_RENDER_ATTACHMENT | USAGE_TEXTURE_BINDING,
|
|
73
|
+
});
|
|
74
|
+
this.view = this.texture.createView({ dimension: '2d-array' });
|
|
75
|
+
for (let layer = 0; layer < layers; layer++) {
|
|
76
|
+
this.layerViews.push(
|
|
77
|
+
this.texture.createView({
|
|
78
|
+
label: `pointShadow.tint.layer${layer}`,
|
|
79
|
+
dimension: '2d',
|
|
80
|
+
baseArrayLayer: layer,
|
|
81
|
+
arrayLayerCount: 1,
|
|
82
|
+
baseMipLevel: 0,
|
|
83
|
+
mipLevelCount: 1,
|
|
84
|
+
}),
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
this.scratch = device.createTexture({
|
|
88
|
+
label: 'pointShadow.tint.scratch',
|
|
89
|
+
size: [faceSize, faceSize],
|
|
90
|
+
format: 'rgba8unorm',
|
|
91
|
+
usage: USAGE_RENDER_ATTACHMENT | USAGE_TEXTURE_BINDING,
|
|
92
|
+
});
|
|
93
|
+
this.scratchView = this.scratch.createView();
|
|
94
|
+
|
|
95
|
+
this.ring = new UniformRing(
|
|
96
|
+
device,
|
|
97
|
+
TINT.uniformSize,
|
|
98
|
+
slots,
|
|
99
|
+
USAGE_UNIFORM_DST,
|
|
100
|
+
'pointShadow.tint.uniforms',
|
|
101
|
+
);
|
|
102
|
+
const layout = device.createBindGroupLayout({
|
|
103
|
+
label: 'pointShadow.tint.resolve.layout',
|
|
104
|
+
entries: [
|
|
105
|
+
{
|
|
106
|
+
binding: TINT.uniforms,
|
|
107
|
+
visibility: VISIBILITY_FRAGMENT,
|
|
108
|
+
buffer: { type: 'uniform', hasDynamicOffset: true, minBindingSize: TINT.uniformSize },
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
binding: TINT.textures.uFace.texture,
|
|
112
|
+
visibility: VISIBILITY_FRAGMENT,
|
|
113
|
+
texture: { sampleType: 'float' },
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
binding: TINT.textures.uFace.sampler,
|
|
117
|
+
visibility: VISIBILITY_FRAGMENT,
|
|
118
|
+
sampler: { type: 'non-filtering' },
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
});
|
|
122
|
+
this.bindGroup = device.createBindGroup({
|
|
123
|
+
label: 'pointShadow.tint.resolve',
|
|
124
|
+
layout,
|
|
125
|
+
entries: [
|
|
126
|
+
{ binding: TINT.uniforms, resource: { buffer: this.ring.buffer, size: TINT.uniformSize } },
|
|
127
|
+
{ binding: TINT.textures.uFace.texture, resource: this.scratchView },
|
|
128
|
+
{
|
|
129
|
+
binding: TINT.textures.uFace.sampler,
|
|
130
|
+
resource: device.createSampler({ label: 'pointShadow.tint.resolve.sampler' }),
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
});
|
|
134
|
+
this.pipeline = device.createRenderPipeline({
|
|
135
|
+
label: 'pointShadow.tint.resolve',
|
|
136
|
+
layout: device.createPipelineLayout({ bindGroupLayouts: [layout] }),
|
|
137
|
+
vertex: {
|
|
138
|
+
module: device.createShaderModule({ code: OCTAHEDRAL_RESOLVE_VERT_WGSL }),
|
|
139
|
+
entryPoint: 'main',
|
|
140
|
+
},
|
|
141
|
+
fragment: {
|
|
142
|
+
module: device.createShaderModule({ code: OCTAHEDRAL_RESOLVE_TINT_FRAG_WGSL }),
|
|
143
|
+
entryPoint: 'main',
|
|
144
|
+
targets: [{ format: 'rgba8unorm' }],
|
|
145
|
+
},
|
|
146
|
+
primitive: { topology: 'triangle-list' },
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
/* Every layer born clear: a light no pane has reached lets everything through. */
|
|
150
|
+
for (const view of this.layerViews) {
|
|
151
|
+
encoder
|
|
152
|
+
.beginRenderPass({
|
|
153
|
+
label: 'pointShadow.tint.clear',
|
|
154
|
+
colorAttachments: [{ view, loadOp: 'clear', clearValue: [1, 1, 1, 1], storeOp: 'store' }],
|
|
155
|
+
})
|
|
156
|
+
.end();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Whether `face` of `layer` holds glass: a face that does must be cleared before it is reused. */
|
|
161
|
+
held(layer: number, face: number): boolean {
|
|
162
|
+
return (((this.heldFaces[layer] ?? 0) >> face) & 1) === 1;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Copy the colour scratch into `face`'s region of `layer`, and remember whether it held glass. */
|
|
166
|
+
resolve(encoder: GPUCommandEncoder, layer: number, face: number, drawn: boolean): void {
|
|
167
|
+
const view = this.layerViews[layer];
|
|
168
|
+
if (view === undefined) return;
|
|
169
|
+
const slot = this.ring.allocate();
|
|
170
|
+
if (slot === null) return;
|
|
171
|
+
pointShadowFaceRotation(face, faceRotation);
|
|
172
|
+
/* WGSL pads a mat3x3 to three four-float columns; the generated struct expects that. */
|
|
173
|
+
const rotation = TINT.fields.uFaceRotation.offset;
|
|
174
|
+
for (let column = 0; column < 3; column++) {
|
|
175
|
+
rotationColumn[0] = faceRotation[column * 3] as number;
|
|
176
|
+
rotationColumn[1] = faceRotation[column * 3 + 1] as number;
|
|
177
|
+
rotationColumn[2] = faceRotation[column * 3 + 2] as number;
|
|
178
|
+
this.ring.writeFloats(slot, rotation + column * 16, rotationColumn);
|
|
179
|
+
}
|
|
180
|
+
this.ring.writeInt(slot, TINT.fields.uFaceIndex.offset, face);
|
|
181
|
+
this.ring.writeFloat(slot, TINT.fields.uEdge.offset, this.edge);
|
|
182
|
+
|
|
183
|
+
const pass = encoder.beginRenderPass({
|
|
184
|
+
label: 'pointShadow.tint.resolve',
|
|
185
|
+
colorAttachments: [{ view, loadOp: 'load', storeOp: 'store' }],
|
|
186
|
+
});
|
|
187
|
+
pass.setPipeline(this.pipeline);
|
|
188
|
+
pass.setBindGroup(0, this.bindGroup, [slot]);
|
|
189
|
+
pass.draw(3);
|
|
190
|
+
pass.end();
|
|
191
|
+
|
|
192
|
+
const bit = 1 << face;
|
|
193
|
+
const was = this.heldFaces[layer] ?? 0;
|
|
194
|
+
this.heldFaces[layer] = drawn ? was | bit : was & ~bit;
|
|
195
|
+
this.dirty[layer] = 1;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** The chain frost reads, for every layer touched since the last call. */
|
|
199
|
+
recordMips(encoder: GPUCommandEncoder): void {
|
|
200
|
+
for (let layer = 0; layer < this.dirty.length; layer++) {
|
|
201
|
+
if (this.dirty[layer] !== 1) continue;
|
|
202
|
+
this.dirty[layer] = 0;
|
|
203
|
+
let chain = this.mips[layer];
|
|
204
|
+
if (chain === undefined) {
|
|
205
|
+
chain = new SnapshotMips(
|
|
206
|
+
this.device,
|
|
207
|
+
this.pipelines,
|
|
208
|
+
this.texture,
|
|
209
|
+
'rgba8unorm',
|
|
210
|
+
layer,
|
|
211
|
+
'pointShadow.tint.mips',
|
|
212
|
+
);
|
|
213
|
+
this.mips[layer] = chain;
|
|
214
|
+
}
|
|
215
|
+
chain.record(encoder);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
beginFrame(): void {
|
|
220
|
+
this.ring.reset();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** Before the round's submit: the slots are written and must be up before any pass reads. */
|
|
224
|
+
flush(): void {
|
|
225
|
+
this.ring.flush();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
dispose(): void {
|
|
229
|
+
this.texture.destroy();
|
|
230
|
+
this.scratch.destroy();
|
|
231
|
+
this.ring.dispose();
|
|
232
|
+
}
|
|
233
|
+
}
|
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
OCTAHEDRAL_RESOLVE_VERT_WGSL,
|
|
19
19
|
} from '../../shaders/generated/octahedralResolve.wgsl.ts';
|
|
20
20
|
import { SHADOW_FORMAT } from './depthPass.ts';
|
|
21
|
+
import { GpuPointGlassTint } from './pointGlassTintPass.ts';
|
|
22
|
+
import type { MipPipelines } from './surfaceTexturePass.ts';
|
|
21
23
|
import { UniformRing } from './uniformRing.ts';
|
|
22
24
|
|
|
23
25
|
/**
|
|
@@ -47,6 +49,9 @@ const VISIBILITY_FRAGMENT = 0x2;
|
|
|
47
49
|
* silently not landing in its layer.
|
|
48
50
|
*/
|
|
49
51
|
const RESOLVE_SLOTS = (POINT_SHADOW_POOL + LIVE_POINT_SHADOW_MAPS) * FACE_COUNT;
|
|
52
|
+
/** Twice once glass arrives: a face resolves its opaque depth and then its glass. */
|
|
53
|
+
const DEPTH_RESOLVE_SLOTS = 2 * RESOLVE_SLOTS;
|
|
54
|
+
const USAGE_COPY = 0x01 | 0x02; // COPY_SRC | COPY_DST
|
|
50
55
|
|
|
51
56
|
/** `mat3x3<f32>` then four scalars, at std140's sixteen-byte columns: see the generated struct. */
|
|
52
57
|
const RESOLVE_UNIFORM_SIZE = 64;
|
|
@@ -66,15 +71,19 @@ const rotationColumn = new Float32Array(3);
|
|
|
66
71
|
* both say the resolve reads the scratch.
|
|
67
72
|
*/
|
|
68
73
|
export class GpuPointShadowArray {
|
|
69
|
-
/**
|
|
70
|
-
readonly view: GPUTextureView;
|
|
74
|
+
/** Lights this array holds; the storage doubles under it once glass arrives. */
|
|
71
75
|
readonly layers: number;
|
|
72
76
|
/** Where a face is rendered before it is resolved. One, shared by every light. */
|
|
73
77
|
readonly scratchView: GPUTextureView;
|
|
74
78
|
|
|
75
|
-
|
|
79
|
+
/** Replaced once, when glass first casts: see `growForGlass`. */
|
|
80
|
+
private texture: GPUTexture;
|
|
81
|
+
private arrayView: GPUTextureView;
|
|
82
|
+
private tint: GpuPointGlassTint | null = null;
|
|
83
|
+
private readonly device: GPUDevice;
|
|
84
|
+
private readonly faceSize: number;
|
|
76
85
|
private readonly scratch: GPUTexture;
|
|
77
|
-
private
|
|
86
|
+
private layerViews: GPUTextureView[] = [];
|
|
78
87
|
private readonly pipeline: GPURenderPipeline;
|
|
79
88
|
private readonly bindGroup: GPUBindGroup;
|
|
80
89
|
/**
|
|
@@ -102,24 +111,12 @@ export class GpuPointShadowArray {
|
|
|
102
111
|
) {
|
|
103
112
|
this.layers =
|
|
104
113
|
Math.min(lightCount, POINT_SHADOW_POOL) + LIVE_POINT_SHADOW_MAPS + Math.max(0, extraLayers);
|
|
114
|
+
this.device = device;
|
|
115
|
+
this.faceSize = faceSize;
|
|
105
116
|
|
|
106
|
-
this.texture = device.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
format: SHADOW_FORMAT,
|
|
110
|
-
usage: USAGE_RENDER_ATTACHMENT | USAGE_TEXTURE_BINDING,
|
|
111
|
-
});
|
|
112
|
-
this.view = this.texture.createView({ dimension: '2d-array' });
|
|
113
|
-
for (let layer = 0; layer < this.layers; layer++) {
|
|
114
|
-
this.layerViews.push(
|
|
115
|
-
this.texture.createView({
|
|
116
|
-
label: `pointShadow.layer${layer}`,
|
|
117
|
-
dimension: '2d',
|
|
118
|
-
baseArrayLayer: layer,
|
|
119
|
-
arrayLayerCount: 1,
|
|
120
|
-
}),
|
|
121
|
-
);
|
|
122
|
-
}
|
|
117
|
+
this.texture = depthArray(device, this.layers);
|
|
118
|
+
this.arrayView = this.texture.createView({ dimension: '2d-array' });
|
|
119
|
+
this.layerViews = layerViewsOf(this.texture, this.layers);
|
|
123
120
|
|
|
124
121
|
this.scratch = device.createTexture({
|
|
125
122
|
label: 'pointShadow.scratch',
|
|
@@ -132,7 +129,7 @@ export class GpuPointShadowArray {
|
|
|
132
129
|
this.ring = new UniformRing(
|
|
133
130
|
device,
|
|
134
131
|
RESOLVE_UNIFORM_SIZE,
|
|
135
|
-
|
|
132
|
+
DEPTH_RESOLVE_SLOTS,
|
|
136
133
|
USAGE_UNIFORM_DST,
|
|
137
134
|
'pointShadow.resolve.uniforms',
|
|
138
135
|
);
|
|
@@ -206,7 +203,102 @@ export class GpuPointShadowArray {
|
|
|
206
203
|
far: number,
|
|
207
204
|
near: number,
|
|
208
205
|
): void {
|
|
209
|
-
|
|
206
|
+
this.resolveInto(encoder, layer * this.stride, face, far, near);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** The face's glass depth into light `layer`'s glass layer, 2L + 1. */
|
|
210
|
+
resolveGlass(
|
|
211
|
+
encoder: GPUCommandEncoder,
|
|
212
|
+
layer: number,
|
|
213
|
+
face: number,
|
|
214
|
+
far: number,
|
|
215
|
+
near: number,
|
|
216
|
+
): void {
|
|
217
|
+
if (this.tint === null) return;
|
|
218
|
+
this.resolveInto(encoder, 2 * layer + 1, face, far, near);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** The array, as the shader samples it. Replaced once, when glass first casts. */
|
|
222
|
+
get view(): GPUTextureView {
|
|
223
|
+
return this.arrayView;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
get hasGlass(): boolean {
|
|
227
|
+
return this.tint !== null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Physical layers a light: 1, or 2 once glass has interleaved the array. */
|
|
231
|
+
get stride(): number {
|
|
232
|
+
return this.tint === null ? 1 : 2;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** The lights' glass tint, once glass has cast. */
|
|
236
|
+
get glassTint(): GpuPointGlassTint | null {
|
|
237
|
+
return this.tint;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Interleave the array for glass, on `encoder`: light L's depth moves to 2L and its glass takes
|
|
242
|
+
* 2L + 1, every glass layer born at the far plane, and the tint made. The reasoning, and why no
|
|
243
|
+
* map re-bakes, is `PointShadowArray.growForGlass`'s.
|
|
244
|
+
*
|
|
245
|
+
* Returns the texture it replaced, which the caller destroys once `encoder` is submitted — the
|
|
246
|
+
* copies out of it are recorded there — or null when the array had already grown.
|
|
247
|
+
*/
|
|
248
|
+
growForGlass(
|
|
249
|
+
encoder: GPUCommandEncoder,
|
|
250
|
+
tintEdge: number,
|
|
251
|
+
pipelines: MipPipelines,
|
|
252
|
+
): GPUTexture | null {
|
|
253
|
+
if (this.tint !== null) return null;
|
|
254
|
+
const old = this.texture;
|
|
255
|
+
const texture = depthArray(this.device, this.layers * 2);
|
|
256
|
+
for (let layer = 0; layer < this.layers; layer++) {
|
|
257
|
+
encoder.copyTextureToTexture(
|
|
258
|
+
{ texture: old, origin: { x: 0, y: 0, z: layer } },
|
|
259
|
+
{ texture, origin: { x: 0, y: 0, z: 2 * layer } },
|
|
260
|
+
[OCTAHEDRAL_EDGE, OCTAHEDRAL_EDGE, 1],
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
this.texture = texture;
|
|
264
|
+
this.arrayView = texture.createView({ dimension: '2d-array' });
|
|
265
|
+
this.layerViews = layerViewsOf(texture, this.layers * 2);
|
|
266
|
+
/* Nothing is nearer a lamp than a pane that has not been drawn. */
|
|
267
|
+
for (let layer = 0; layer < this.layers; layer++) {
|
|
268
|
+
encoder
|
|
269
|
+
.beginRenderPass({
|
|
270
|
+
label: 'pointShadow.glass.clear',
|
|
271
|
+
colorAttachments: [],
|
|
272
|
+
depthStencilAttachment: {
|
|
273
|
+
view: this.layerViews[2 * layer + 1] as GPUTextureView,
|
|
274
|
+
depthClearValue: 1,
|
|
275
|
+
depthLoadOp: 'clear',
|
|
276
|
+
depthStoreOp: 'store',
|
|
277
|
+
},
|
|
278
|
+
})
|
|
279
|
+
.end();
|
|
280
|
+
}
|
|
281
|
+
this.tint = new GpuPointGlassTint(
|
|
282
|
+
this.device,
|
|
283
|
+
pipelines,
|
|
284
|
+
this.faceSize,
|
|
285
|
+
tintEdge,
|
|
286
|
+
this.layers,
|
|
287
|
+
RESOLVE_SLOTS,
|
|
288
|
+
encoder,
|
|
289
|
+
);
|
|
290
|
+
return old;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** The scratch into physical layer `physical`'s region for `face`. */
|
|
294
|
+
private resolveInto(
|
|
295
|
+
encoder: GPUCommandEncoder,
|
|
296
|
+
physical: number,
|
|
297
|
+
face: number,
|
|
298
|
+
far: number,
|
|
299
|
+
near: number,
|
|
300
|
+
): void {
|
|
301
|
+
const view = this.layerViews[physical];
|
|
210
302
|
if (view === undefined) return;
|
|
211
303
|
const slot = this.ring.allocate();
|
|
212
304
|
if (slot === null) return;
|
|
@@ -242,25 +334,39 @@ export class GpuPointShadowArray {
|
|
|
242
334
|
/** Start a frame's bakes. See the ring: slots are reused and nothing is freed. */
|
|
243
335
|
beginFrame(): void {
|
|
244
336
|
this.ring.reset();
|
|
337
|
+
this.tint?.beginFrame();
|
|
245
338
|
}
|
|
246
339
|
|
|
247
340
|
/** Upload every slot this frame's resolves took, in one write, before the encoder is submitted. */
|
|
248
341
|
flush(): void {
|
|
249
342
|
this.ring.flush();
|
|
343
|
+
this.tint?.flush();
|
|
250
344
|
}
|
|
251
345
|
|
|
252
346
|
dispose(): void {
|
|
253
347
|
this.texture.destroy();
|
|
254
348
|
this.scratch.destroy();
|
|
255
349
|
this.ring.dispose();
|
|
350
|
+
this.tint?.dispose();
|
|
256
351
|
}
|
|
257
352
|
}
|
|
258
353
|
|
|
259
354
|
/** What a face's pass needs from the caller: somewhere to record, and the casters to record. */
|
|
260
355
|
export type PointShadowFacePass = (view: GPUTextureView, viewProj: mat4) => void;
|
|
261
356
|
|
|
262
|
-
/**
|
|
263
|
-
|
|
357
|
+
/**
|
|
358
|
+
* And what turns that face into its region of the light's octahedral layer — and, where the face
|
|
359
|
+
* offered glass, draws and resolves that too, lit from the bake origin `x`, `y`, `z`.
|
|
360
|
+
*/
|
|
361
|
+
export type PointShadowResolve = (
|
|
362
|
+
layer: number,
|
|
363
|
+
face: number,
|
|
364
|
+
far: number,
|
|
365
|
+
near: number,
|
|
366
|
+
x: number,
|
|
367
|
+
y: number,
|
|
368
|
+
z: number,
|
|
369
|
+
) => void;
|
|
264
370
|
|
|
265
371
|
export class GpuPointShadowMap implements PointShadowSource {
|
|
266
372
|
readonly size: number;
|
|
@@ -369,7 +475,7 @@ export class GpuPointShadowMap implements PointShadowSource {
|
|
|
369
475
|
array.scratchView,
|
|
370
476
|
pointShadowFaceViewProj(face, plan.x, plan.y, plan.z, plan.near, plan.range, scratch),
|
|
371
477
|
);
|
|
372
|
-
resolveFace(this.layer, face, plan.range, plan.near);
|
|
478
|
+
resolveFace(this.layer, face, plan.range, plan.near, plan.x, plan.y, plan.z);
|
|
373
479
|
}
|
|
374
480
|
|
|
375
481
|
this.image.completeBake(last, plan.x, plan.y, plan.z, plan.range, plan.near, plan.sourceRadius);
|
|
@@ -379,3 +485,29 @@ export class GpuPointShadowMap implements PointShadowSource {
|
|
|
379
485
|
/** Nothing to free: every GPU object belongs to `GpuPointShadowArray` now. */
|
|
380
486
|
dispose(): void {}
|
|
381
487
|
}
|
|
488
|
+
|
|
489
|
+
/** The depth array, copyable so glass can interleave it without a re-bake. */
|
|
490
|
+
function depthArray(device: GPUDevice, layers: number): GPUTexture {
|
|
491
|
+
return device.createTexture({
|
|
492
|
+
label: 'pointShadow.array',
|
|
493
|
+
size: [OCTAHEDRAL_EDGE, OCTAHEDRAL_EDGE, layers],
|
|
494
|
+
format: SHADOW_FORMAT,
|
|
495
|
+
usage: USAGE_RENDER_ATTACHMENT | USAGE_TEXTURE_BINDING | USAGE_COPY,
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/** One single-layer view per physical layer: a depth attachment takes exactly one. */
|
|
500
|
+
function layerViewsOf(texture: GPUTexture, layers: number): GPUTextureView[] {
|
|
501
|
+
const views: GPUTextureView[] = [];
|
|
502
|
+
for (let layer = 0; layer < layers; layer++) {
|
|
503
|
+
views.push(
|
|
504
|
+
texture.createView({
|
|
505
|
+
label: `pointShadow.layer${layer}`,
|
|
506
|
+
dimension: '2d',
|
|
507
|
+
baseArrayLayer: layer,
|
|
508
|
+
arrayLayerCount: 1,
|
|
509
|
+
}),
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
return views;
|
|
513
|
+
}
|
|
@@ -331,7 +331,37 @@ export function postPipeline(
|
|
|
331
331
|
format: GPUTextureFormat,
|
|
332
332
|
blend?: GPUBlendState,
|
|
333
333
|
): GPURenderPipeline {
|
|
334
|
-
return cache.get(key, () =>
|
|
334
|
+
return cache.get(key, () =>
|
|
335
|
+
describePost(device, layout, key, fragment, [
|
|
336
|
+
blend === undefined ? { format } : { format, blend },
|
|
337
|
+
]),
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* The same over several colour targets, in the fragment's output order: the temporal resolve
|
|
343
|
+
* writes its picture and each pixel's flicker record in one pass. `targets` is the caller's, held
|
|
344
|
+
* rather than built per call, since this is asked for every frame.
|
|
345
|
+
*/
|
|
346
|
+
export function postPipelineTargets(
|
|
347
|
+
cache: PipelineCache,
|
|
348
|
+
device: GPUDevice,
|
|
349
|
+
layout: GPUBindGroupLayout,
|
|
350
|
+
key: string,
|
|
351
|
+
fragment: string,
|
|
352
|
+
targets: GPUColorTargetState[],
|
|
353
|
+
): GPURenderPipeline {
|
|
354
|
+
return cache.get(key, () => describePost(device, layout, key, fragment, targets));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function describePost(
|
|
358
|
+
device: GPUDevice,
|
|
359
|
+
layout: GPUBindGroupLayout,
|
|
360
|
+
key: string,
|
|
361
|
+
fragment: string,
|
|
362
|
+
targets: GPUColorTargetState[],
|
|
363
|
+
): GPURenderPipelineDescriptor {
|
|
364
|
+
return {
|
|
335
365
|
label: key,
|
|
336
366
|
layout: device.createPipelineLayout({ bindGroupLayouts: [layout] }),
|
|
337
367
|
vertex: {
|
|
@@ -341,7 +371,7 @@ export function postPipeline(
|
|
|
341
371
|
fragment: {
|
|
342
372
|
module: shaderModule(device, { label: key, code: fragment }),
|
|
343
373
|
entryPoint: 'main',
|
|
344
|
-
targets
|
|
374
|
+
targets,
|
|
345
375
|
},
|
|
346
376
|
/*
|
|
347
377
|
* No depth at all, which is what makes this different from every other pipeline here: the
|
|
@@ -349,11 +379,11 @@ export function postPipeline(
|
|
|
349
379
|
* disables the test, the write and culling by hand for the same draw.
|
|
350
380
|
*/
|
|
351
381
|
primitive: { topology: 'triangle-list' as const, cullMode: 'none' as const },
|
|
352
|
-
}
|
|
382
|
+
};
|
|
353
383
|
}
|
|
354
384
|
|
|
355
385
|
/**
|
|
356
|
-
* The temporal resolve's block and its
|
|
386
|
+
* The temporal resolve's block and its four inputs.
|
|
357
387
|
*
|
|
358
388
|
* Named like the occlusion's beside it, and for the same reason `BLOOM_DOWNSAMPLE_FIELDS` is: a
|
|
359
389
|
* block read by assumption rather than by name is a uniform written to the wrong offset, which
|