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