@driftengine/core 4.8.7 → 4.10.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 +123 -11
- package/README.md +43 -38
- package/dist/index.d.ts +8 -1
- package/dist/index.js +11 -3
- package/dist/render/ambientHarmonics.d.ts +1 -1
- package/dist/render/ambientHarmonics.js +1 -1
- package/dist/render/areaLights.d.ts +4 -4
- package/dist/render/areaLights.js +1 -1
- package/dist/render/atmosphere.d.ts +13 -0
- package/dist/render/atmosphere.js +9 -1
- package/dist/render/backend/api.d.ts +19 -4
- package/dist/render/backend/createRenderer.d.ts +6 -0
- package/dist/render/backend/createRenderer.js +4 -1
- package/dist/render/backend/select.js +11 -5
- package/dist/render/backend/webgl2/boneAnimations.d.ts +29 -0
- package/dist/render/backend/webgl2/boneAnimations.js +80 -0
- package/dist/render/backend/webgl2/instanced.d.ts +5 -1
- package/dist/render/backend/webgl2/instanced.js +13 -1
- package/dist/render/backend/webgl2/renderer.d.ts +180 -4
- package/dist/render/backend/webgl2/renderer.js +550 -40
- package/dist/render/backend/webgl2/skinPaletteTexture.d.ts +5 -3
- package/dist/render/backend/webgl2/skinPaletteTexture.js +6 -4
- package/dist/render/backend/webgpu/boneAnimations.d.ts +34 -0
- package/dist/render/backend/webgpu/boneAnimations.js +76 -0
- package/dist/render/backend/webgpu/buffers.d.ts +19 -4
- package/dist/render/backend/webgpu/buffers.js +18 -11
- package/dist/render/backend/webgpu/compressedUpload.d.ts +0 -1
- package/dist/render/backend/webgpu/compressedUpload.js +53 -8
- package/dist/render/backend/webgpu/depthPass.d.ts +59 -3
- package/dist/render/backend/webgpu/depthPass.js +49 -14
- package/dist/render/backend/webgpu/device.d.ts +7 -1
- package/dist/render/backend/webgpu/device.js +31 -3
- package/dist/render/backend/webgpu/drawCommand.d.ts +18 -4
- package/dist/render/backend/webgpu/drawCommand.js +10 -3
- package/dist/render/backend/webgpu/flatPass.d.ts +100 -42
- package/dist/render/backend/webgpu/flatPass.js +191 -43
- package/dist/render/backend/webgpu/instanced.d.ts +14 -1
- package/dist/render/backend/webgpu/instanced.js +24 -1
- package/dist/render/backend/webgpu/materialStore.d.ts +101 -0
- package/dist/render/backend/webgpu/materialStore.js +265 -0
- package/dist/render/backend/webgpu/pipelineCache.d.ts +6 -0
- package/dist/render/backend/webgpu/pipelineCache.js +7 -0
- package/dist/render/backend/webgpu/postPass.d.ts +51 -9
- package/dist/render/backend/webgpu/postPass.js +16 -2
- package/dist/render/backend/webgpu/reflectionSurfacePass.d.ts +30 -0
- package/dist/render/backend/webgpu/reflectionSurfacePass.js +100 -0
- package/dist/render/backend/webgpu/renderer.d.ts +283 -37
- package/dist/render/backend/webgpu/renderer.js +1363 -202
- package/dist/render/backend/webgpu/skinPaletteRing.d.ts +7 -3
- package/dist/render/backend/webgpu/skinPaletteRing.js +13 -7
- package/dist/render/backend/webgpu/skinPaletteTexture.d.ts +6 -0
- package/dist/render/backend/webgpu/skinPaletteTexture.js +7 -1
- package/dist/render/backend/webgpu/staticBundles.d.ts +125 -0
- package/dist/render/backend/webgpu/staticBundles.js +241 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +10 -3
- package/dist/render/backend/webgpu/surfaceTexturePass.js +20 -2
- package/dist/render/backend/webgpu/uniformRing.d.ts +21 -0
- package/dist/render/backend/webgpu/uniformRing.js +9 -1
- package/dist/render/bloomChain.d.ts +3 -3
- package/dist/render/boneAnimation.d.ts +92 -0
- package/dist/render/boneAnimation.js +133 -0
- package/dist/render/compressedSource.d.ts +33 -13
- package/dist/render/compressedSource.js +70 -25
- package/dist/render/displayRange.d.ts +31 -0
- package/dist/render/displayRange.js +47 -0
- package/dist/render/filmicCurve.d.ts +74 -0
- package/dist/render/filmicCurve.js +203 -0
- package/dist/render/fog.d.ts +4 -1
- package/dist/render/fog.js +12 -2
- package/dist/render/frame/resources.d.ts +2 -2
- package/dist/render/frame/resources.js +6 -0
- package/dist/render/frameReflections.d.ts +40 -0
- package/dist/render/frameReflections.js +48 -0
- package/dist/render/glCompressed.js +83 -0
- package/dist/render/instances.d.ts +26 -0
- package/dist/render/lightBudget.d.ts +10 -0
- package/dist/render/lightBudget.js +10 -0
- package/dist/render/lightmap.d.ts +1 -1
- package/dist/render/mesh.d.ts +5 -0
- package/dist/render/mesh.js +6 -0
- package/dist/render/pass.d.ts +49 -0
- package/dist/render/pointLightSelection.d.ts +4 -4
- package/dist/render/reflectionSurfaceTargets.d.ts +28 -0
- package/dist/render/reflectionSurfaceTargets.js +99 -0
- package/dist/render/renderQuality.d.ts +39 -8
- package/dist/render/renderQuality.js +4 -0
- package/dist/render/sceneTarget.d.ts +5 -0
- package/dist/render/sceneTarget.js +24 -1
- package/dist/render/screenSpaceReflection.d.ts +7 -1
- package/dist/render/screenSpaceReflection.js +5 -0
- package/dist/render/shaders/arcane.d.ts +1 -1
- package/dist/render/shaders/bolt.d.ts +1 -1
- package/dist/render/shaders/boneAnimation.d.ts +19 -0
- package/dist/render/shaders/boneAnimation.js +63 -0
- package/dist/render/shaders/caustics.d.ts +1 -1
- package/dist/render/shaders/depth.d.ts +7 -0
- package/dist/render/shaders/depth.js +35 -10
- package/dist/render/shaders/film.d.ts +1 -1
- package/dist/render/shaders/filmLook.js +2 -1
- package/dist/render/shaders/fire.d.ts +1 -1
- package/dist/render/shaders/flat/glassTint.d.ts +1 -1
- package/dist/render/shaders/flat/glassTint.js +2 -0
- package/dist/render/shaders/flat/index.d.ts +16 -0
- package/dist/render/shaders/flat/index.js +33 -5
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +43 -5
- package/dist/render/shaders/flat/models.js +1 -1
- package/dist/render/shaders/flat/overlay.d.ts +16 -0
- package/dist/render/shaders/flat/overlay.js +117 -0
- package/dist/render/shaders/flat/pointShadow.js +3 -3
- package/dist/render/shaders/flat/preamble.js +38 -24
- package/dist/render/shaders/flat/reflectionSurface.d.ts +21 -0
- package/dist/render/shaders/flat/reflectionSurface.js +61 -0
- package/dist/render/shaders/fog.d.ts +1 -1
- package/dist/render/shaders/fog.js +9 -2
- package/dist/render/shaders/fringe.d.ts +29 -0
- package/dist/render/shaders/fringe.js +55 -0
- package/dist/render/shaders/generated/arcane.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/arcane.wgsl.js +1 -1
- package/dist/render/shaders/generated/bolt.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/bolt.wgsl.js +1 -1
- package/dist/render/shaders/generated/caustics.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/caustics.wgsl.js +1 -1
- package/dist/render/shaders/generated/depth.wgsl.d.ts +93 -0
- package/dist/render/shaders/generated/depth.wgsl.js +93 -0
- package/dist/render/shaders/generated/film.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/film.wgsl.js +1 -1
- package/dist/render/shaders/generated/fire.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/fire.wgsl.js +1 -1
- package/dist/render/shaders/generated/flat.wgsl.d.ts +3254 -2920
- package/dist/render/shaders/generated/flat.wgsl.js +3292 -2957
- 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 +4 -4
- package/dist/render/shaders/generated/particle.wgsl.js +4 -4
- package/dist/render/shaders/generated/rush.wgsl.d.ts +56 -26
- package/dist/render/shaders/generated/rush.wgsl.js +56 -26
- package/dist/render/shaders/generated/scatter.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/scatter.wgsl.js +1 -1
- package/dist/render/shaders/generated/smoke.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/smoke.wgsl.js +1 -1
- package/dist/render/shaders/generated/ssrResolve.wgsl.d.ts +29 -0
- package/dist/render/shaders/generated/ssrResolve.wgsl.js +29 -0
- package/dist/render/shaders/generated/ssrTrace.wgsl.d.ts +22 -2
- package/dist/render/shaders/generated/ssrTrace.wgsl.js +22 -2
- package/dist/render/shaders/generated/water.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/water.wgsl.js +1 -1
- package/dist/render/shaders/gpudriven/fog.wgsl.d.ts +2 -2
- package/dist/render/shaders/gpudriven/fog.wgsl.js +11 -3
- package/dist/render/shaders/line.d.ts +1 -1
- package/dist/render/shaders/outputTransform.d.ts +7 -0
- package/dist/render/shaders/outputTransform.js +28 -14
- package/dist/render/shaders/particle.d.ts +3 -3
- package/dist/render/shaders/rush.js +54 -11
- package/dist/render/shaders/scatter.d.ts +1 -1
- package/dist/render/shaders/smoke.d.ts +1 -1
- package/dist/render/shaders/ssrResolve.d.ts +17 -0
- package/dist/render/shaders/ssrResolve.js +51 -0
- package/dist/render/shaders/ssrTrace.js +26 -0
- package/dist/render/shaders/vertexChannel.d.ts +7 -0
- package/dist/render/shaders/vertexChannel.js +26 -0
- package/dist/render/shaders/water.d.ts +1 -1
- package/dist/render/ssrPass.d.ts +16 -0
- package/dist/render/ssrPass.js +106 -1
- package/dist/render/staticDraws.d.ts +47 -0
- package/dist/render/staticDraws.js +43 -0
- package/dist/render/surfaceOverlay.d.ts +132 -0
- package/dist/render/surfaceOverlay.js +85 -0
- package/dist/render/surfaceTexture.d.ts +57 -7
- package/dist/render/surfaceTexture.js +125 -17
- package/dist/render/temporalAa.d.ts +14 -0
- package/dist/render/temporalAa.js +20 -0
- package/dist/render/vertexDefaults.d.ts +17 -0
- package/dist/render/vertexDefaults.js +24 -0
- package/package.json +6 -6
- package/src/index.ts +24 -2
- package/src/render/ambientHarmonics.ts +1 -1
- package/src/render/areaLights.ts +4 -4
- package/src/render/atmosphere.ts +22 -1
- package/src/render/backend/api.ts +22 -3
- package/src/render/backend/createRenderer.ts +18 -1
- package/src/render/backend/select.ts +10 -5
- package/src/render/backend/webgl2/boneAnimations.ts +105 -0
- package/src/render/backend/webgl2/instanced.ts +20 -1
- package/src/render/backend/webgl2/renderer.ts +672 -37
- package/src/render/backend/webgl2/skinPaletteTexture.ts +6 -4
- package/src/render/backend/webgpu/boneAnimations.ts +97 -0
- package/src/render/backend/webgpu/buffers.ts +42 -16
- package/src/render/backend/webgpu/compressedUpload.ts +59 -9
- package/src/render/backend/webgpu/depthPass.ts +51 -10
- package/src/render/backend/webgpu/device.ts +42 -3
- package/src/render/backend/webgpu/drawCommand.ts +22 -4
- package/src/render/backend/webgpu/flatPass.ts +250 -40
- package/src/render/backend/webgpu/instanced.ts +32 -1
- package/src/render/backend/webgpu/materialStore.ts +276 -0
- package/src/render/backend/webgpu/pipelineCache.ts +7 -0
- package/src/render/backend/webgpu/postPass.ts +18 -0
- package/src/render/backend/webgpu/reflectionSurfacePass.ts +114 -0
- package/src/render/backend/webgpu/renderer.ts +1655 -230
- package/src/render/backend/webgpu/skinPaletteRing.ts +13 -7
- package/src/render/backend/webgpu/skinPaletteTexture.ts +7 -1
- package/src/render/backend/webgpu/staticBundles.ts +363 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +29 -3
- package/src/render/backend/webgpu/uniformRing.ts +10 -1
- package/src/render/bloomChain.ts +3 -3
- package/src/render/boneAnimation.ts +223 -0
- package/src/render/compressedSource.ts +121 -29
- package/src/render/displayRange.ts +61 -0
- package/src/render/filmicCurve.ts +229 -0
- package/src/render/fog.ts +19 -3
- package/src/render/frame/resources.ts +6 -0
- package/src/render/frameReflections.ts +72 -0
- package/src/render/glCompressed.ts +83 -0
- package/src/render/instances.ts +27 -0
- package/src/render/lightBudget.ts +11 -0
- package/src/render/lightmap.ts +1 -1
- package/src/render/mesh.ts +6 -0
- package/src/render/pass.ts +53 -1
- package/src/render/pointLightSelection.ts +4 -4
- package/src/render/reflectionSurfaceTargets.ts +105 -0
- package/src/render/renderQuality.ts +44 -8
- package/src/render/sceneTarget.ts +30 -2
- package/src/render/screenSpaceReflection.ts +7 -1
- package/src/render/shaders/boneAnimation.ts +63 -0
- package/src/render/shaders/depth.ts +45 -10
- package/src/render/shaders/filmLook.ts +2 -1
- package/src/render/shaders/flat/glassTint.ts +5 -1
- package/src/render/shaders/flat/index.ts +49 -5
- package/src/render/shaders/flat/main.ts +43 -5
- package/src/render/shaders/flat/models.ts +1 -1
- package/src/render/shaders/flat/overlay.ts +117 -0
- package/src/render/shaders/flat/pointShadow.ts +3 -3
- package/src/render/shaders/flat/preamble.ts +38 -24
- package/src/render/shaders/flat/reflectionSurface.ts +61 -0
- package/src/render/shaders/fog.ts +9 -2
- package/src/render/shaders/fringe.ts +59 -0
- package/src/render/shaders/generated/arcane.wgsl.ts +1 -1
- package/src/render/shaders/generated/bolt.wgsl.ts +1 -1
- package/src/render/shaders/generated/caustics.wgsl.ts +1 -1
- package/src/render/shaders/generated/depth.wgsl.ts +95 -0
- package/src/render/shaders/generated/film.wgsl.ts +1 -1
- package/src/render/shaders/generated/fire.wgsl.ts +1 -1
- package/src/render/shaders/generated/flat.wgsl.ts +3292 -2957
- package/src/render/shaders/generated/line.wgsl.ts +1 -1
- package/src/render/shaders/generated/particle.wgsl.ts +4 -4
- package/src/render/shaders/generated/rush.wgsl.ts +56 -26
- package/src/render/shaders/generated/scatter.wgsl.ts +1 -1
- package/src/render/shaders/generated/smoke.wgsl.ts +1 -1
- package/src/render/shaders/generated/ssrResolve.wgsl.ts +30 -0
- package/src/render/shaders/generated/ssrTrace.wgsl.ts +22 -2
- package/src/render/shaders/generated/water.wgsl.ts +1 -1
- package/src/render/shaders/gpudriven/fog.wgsl.ts +11 -3
- package/src/render/shaders/outputTransform.ts +30 -14
- package/src/render/shaders/rush.ts +54 -11
- package/src/render/shaders/ssrResolve.ts +52 -0
- package/src/render/shaders/ssrTrace.ts +26 -0
- package/src/render/shaders/vertexChannel.ts +27 -0
- package/src/render/ssrPass.ts +138 -1
- package/src/render/staticDraws.ts +95 -0
- package/src/render/surfaceOverlay.ts +195 -0
- package/src/render/surfaceTexture.ts +161 -27
- package/src/render/temporalAa.ts +20 -0
- package/src/render/vertexDefaults.ts +28 -0
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
* different rig at every set — the comparison is what tells the two apart.
|
|
28
28
|
*
|
|
29
29
|
* **It was capped at 128 until 4.8.6, and the cap declined draws.** A stage of three dozen idling
|
|
30
|
-
* extras beside two
|
|
31
|
-
* the consumer stood its crowd in the bind pose. The ring now holds as many palettes as a frame
|
|
30
|
+
* extras beside two characters filled it in one frame and every skinned draw past it was refused,
|
|
31
|
+
* so the consumer stood its crowd in the bind pose. The ring now holds as many palettes as a frame
|
|
32
32
|
* holds draws (`RenderQuality.drawsPerFrame`), since no frame can bind more palettes than it draws;
|
|
33
33
|
* a slot still allocates its texture only the first time a frame reaches it, so a scene with two
|
|
34
34
|
* characters holds two. **What it costs** is a texture and a cached bind group a slot, 1.5 KB for a
|
|
@@ -45,8 +45,12 @@
|
|
|
45
45
|
export declare class SkinPaletteRing {
|
|
46
46
|
private readonly capacity;
|
|
47
47
|
private readonly slots;
|
|
48
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* What each slot was last written with, for the comparison that lets a pass reuse it: a copy
|
|
50
|
+
* that grows with the largest rig the slot has held, and how much of it the last rig filled.
|
|
51
|
+
*/
|
|
49
52
|
private readonly held;
|
|
53
|
+
private readonly heldLength;
|
|
50
54
|
/** The slot each palette array last went into, by identity. Weak, so a caller's array can go. */
|
|
51
55
|
private readonly lastSlot;
|
|
52
56
|
private used;
|
|
@@ -28,8 +28,8 @@ import { SkinPaletteTexture } from './skinPaletteTexture.js';
|
|
|
28
28
|
* different rig at every set — the comparison is what tells the two apart.
|
|
29
29
|
*
|
|
30
30
|
* **It was capped at 128 until 4.8.6, and the cap declined draws.** A stage of three dozen idling
|
|
31
|
-
* extras beside two
|
|
32
|
-
* the consumer stood its crowd in the bind pose. The ring now holds as many palettes as a frame
|
|
31
|
+
* extras beside two characters filled it in one frame and every skinned draw past it was refused,
|
|
32
|
+
* so the consumer stood its crowd in the bind pose. The ring now holds as many palettes as a frame
|
|
33
33
|
* holds draws (`RenderQuality.drawsPerFrame`), since no frame can bind more palettes than it draws;
|
|
34
34
|
* a slot still allocates its texture only the first time a frame reaches it, so a scene with two
|
|
35
35
|
* characters holds two. **What it costs** is a texture and a cached bind group a slot, 1.5 KB for a
|
|
@@ -46,8 +46,12 @@ import { SkinPaletteTexture } from './skinPaletteTexture.js';
|
|
|
46
46
|
export class SkinPaletteRing {
|
|
47
47
|
capacity;
|
|
48
48
|
slots = [];
|
|
49
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* What each slot was last written with, for the comparison that lets a pass reuse it: a copy
|
|
51
|
+
* that grows with the largest rig the slot has held, and how much of it the last rig filled.
|
|
52
|
+
*/
|
|
50
53
|
held = [];
|
|
54
|
+
heldLength = [];
|
|
51
55
|
/** The slot each palette array last went into, by identity. Weak, so a caller's array can go. */
|
|
52
56
|
lastSlot = new WeakMap();
|
|
53
57
|
used = 0;
|
|
@@ -114,12 +118,13 @@ export class SkinPaletteRing {
|
|
|
114
118
|
slot.update(device, palette);
|
|
115
119
|
this.uploads += 1;
|
|
116
120
|
let copy = this.held[at];
|
|
117
|
-
if (copy === undefined || copy.length
|
|
118
|
-
/* Once per slot and
|
|
121
|
+
if (copy === undefined || copy.length < palette.length) {
|
|
122
|
+
/* Once per slot and larger rig, the way the slot's own texture is: never per frame. */
|
|
119
123
|
copy = new Float32Array(palette.length);
|
|
120
124
|
this.held[at] = copy;
|
|
121
125
|
}
|
|
122
126
|
copy.set(palette);
|
|
127
|
+
this.heldLength[at] = palette.length;
|
|
123
128
|
this.lastSlot.set(palette, at);
|
|
124
129
|
return at;
|
|
125
130
|
}
|
|
@@ -132,14 +137,15 @@ export class SkinPaletteRing {
|
|
|
132
137
|
slot.dispose();
|
|
133
138
|
this.slots.length = 0;
|
|
134
139
|
this.held.length = 0;
|
|
140
|
+
this.heldLength.length = 0;
|
|
135
141
|
this.used = 0;
|
|
136
142
|
}
|
|
137
143
|
/** Whether a slot was last written with exactly these numbers. */
|
|
138
144
|
holds(slot, palette) {
|
|
139
145
|
const copy = this.held[slot];
|
|
140
|
-
if (copy === undefined ||
|
|
146
|
+
if (copy === undefined || this.heldLength[slot] !== palette.length)
|
|
141
147
|
return false;
|
|
142
|
-
for (let i = 0; i <
|
|
148
|
+
for (let i = 0; i < palette.length; i++)
|
|
143
149
|
if (copy[i] !== palette[i])
|
|
144
150
|
return false;
|
|
145
151
|
return true;
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
export declare class SkinPaletteTexture {
|
|
2
2
|
private texture;
|
|
3
|
+
/**
|
|
4
|
+
* How many joints the texture has room for, which is the most any rig in this slot has had.
|
|
5
|
+
* **It grows and never shrinks**: the shader reads a joint by index, so a texture wider than the
|
|
6
|
+
* rig is read exactly as one that fits, and a slot that a 10-joint prop and a 296-joint character
|
|
7
|
+
* take in turn keeps one texture rather than making one at every change of rig.
|
|
8
|
+
*/
|
|
3
9
|
private joints;
|
|
4
10
|
/**
|
|
5
11
|
* The view, cached rather than created per call.
|
|
@@ -20,6 +20,12 @@ const TEXTURE_USAGE = {
|
|
|
20
20
|
};
|
|
21
21
|
export class SkinPaletteTexture {
|
|
22
22
|
texture = null;
|
|
23
|
+
/**
|
|
24
|
+
* How many joints the texture has room for, which is the most any rig in this slot has had.
|
|
25
|
+
* **It grows and never shrinks**: the shader reads a joint by index, so a texture wider than the
|
|
26
|
+
* rig is read exactly as one that fits, and a slot that a 10-joint prop and a 296-joint character
|
|
27
|
+
* take in turn keeps one texture rather than making one at every change of rig.
|
|
28
|
+
*/
|
|
23
29
|
joints = 0;
|
|
24
30
|
/**
|
|
25
31
|
* The view, cached rather than created per call.
|
|
@@ -34,7 +40,7 @@ export class SkinPaletteTexture {
|
|
|
34
40
|
validateSkinPalette(palette);
|
|
35
41
|
const joints = palette.length / 16;
|
|
36
42
|
const width = paletteTextureWidth(joints);
|
|
37
|
-
if (this.texture === null ||
|
|
43
|
+
if (this.texture === null || joints > this.joints) {
|
|
38
44
|
this.texture?.destroy();
|
|
39
45
|
this.cachedView = null;
|
|
40
46
|
this.texture = device.createTexture({
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A static list on this backend: its draws recorded once, through the renderer's own draw path,
|
|
3
|
+
* into slots the list owns, and replayed as one render bundle per view.
|
|
4
|
+
*
|
|
5
|
+
* **What a recording holds is the frame's draws as the renderer would have recorded them**, taken
|
|
6
|
+
* from a pool of the list's instead of the frame graph's: a pipeline, a bind group, vertex buffers
|
|
7
|
+
* and four dynamic offsets each. Three of the four never change once recorded — the draw's own
|
|
8
|
+
* slot in `entries`, written once, and its material's slot in the store, pinned. The other two are
|
|
9
|
+
* the view's: the pass block and the view block, which differ between the frame, a mirror and a
|
|
10
|
+
* capture of the same frame. So each view a frame draws the list into takes a *copy* — a slot of
|
|
11
|
+
* `passes` and one of `views`, written for that view — and a bundle is encoded per copy from the
|
|
12
|
+
* same commands with that copy's two offsets. A frame that draws a list twice uploads two blocks
|
|
13
|
+
* and executes two bundles, and records nothing.
|
|
14
|
+
*
|
|
15
|
+
* **Its own buffers, and bind groups over them.** A bundle bakes the buffers its groups bind, and
|
|
16
|
+
* the frame's rings are reset every frame, so a list binds rings of its own: the renderer swaps
|
|
17
|
+
* them in while it records the list's draws, and the groups that recording builds go into the
|
|
18
|
+
* list's cache rather than the frame's.
|
|
19
|
+
*
|
|
20
|
+
* **Recorded again when what it baked has moved**: the renderer's groups (a texture forgotten, a
|
|
21
|
+
* frame texture rebound, a ring grown), the pipeline cache's lit set, the store's slots, or the
|
|
22
|
+
* list's own rings. Each is a number compared at replay, so a stale recording is never replayed.
|
|
23
|
+
*
|
|
24
|
+
* What it gives up: culling, since a bundle draws everything recorded into it; and memory, a pass
|
|
25
|
+
* block a view a list, which is a few kilobytes.
|
|
26
|
+
*/
|
|
27
|
+
import type { StaticDrawsList, StaticEntry } from '../../staticDraws.ts';
|
|
28
|
+
import type { CommandPool, DrawCommand } from './drawCommand.ts';
|
|
29
|
+
import type { PipelineCache } from './pipelineCache.ts';
|
|
30
|
+
import { UniformRing } from './uniformRing.ts';
|
|
31
|
+
/** What one recording was made against, and what it recorded. */
|
|
32
|
+
export interface StaticRecording {
|
|
33
|
+
readonly cache: PipelineCache;
|
|
34
|
+
/** The material block's words the list started from: the pass's state, which entries patch. */
|
|
35
|
+
readonly start: Int32Array;
|
|
36
|
+
readonly startHash: number;
|
|
37
|
+
readonly cutout: string;
|
|
38
|
+
readonly surfaceKept: boolean;
|
|
39
|
+
/** Stamps compared at replay; any that moved makes the recording stale. */
|
|
40
|
+
cacheVersion: number;
|
|
41
|
+
groupEpoch: number;
|
|
42
|
+
storeEpoch: number;
|
|
43
|
+
ringEpoch: number;
|
|
44
|
+
/** The frame pass's commands, and the reflection pass's surface halves, with the record's offsets. */
|
|
45
|
+
readonly frame: CommandPool;
|
|
46
|
+
readonly surface: CommandPool;
|
|
47
|
+
/** Material slots pinned for these commands, and the material changes recording them asked for. */
|
|
48
|
+
readonly pins: number[];
|
|
49
|
+
materialAsks: number;
|
|
50
|
+
/** Under the screen-space skin blur a skin entry splits into halves no bundle replays. */
|
|
51
|
+
immediate: boolean;
|
|
52
|
+
/** Per copy, the bundles `executeBundles` takes; null until a copy is first replayed. */
|
|
53
|
+
readonly frameBundles: (readonly GPURenderBundle[] | null)[];
|
|
54
|
+
readonly surfaceBundles: (readonly GPURenderBundle[] | null)[];
|
|
55
|
+
/** The frame it was last replayed in, which picks the one to let go. */
|
|
56
|
+
usedIn: number;
|
|
57
|
+
}
|
|
58
|
+
export declare class GpuStaticDraws implements StaticDrawsList {
|
|
59
|
+
private readonly device;
|
|
60
|
+
readonly list: StaticDrawsList;
|
|
61
|
+
private readonly passSize;
|
|
62
|
+
private readonly viewSize;
|
|
63
|
+
disposed: boolean;
|
|
64
|
+
/** The draws' own slots, one a draw, written by the recording. */
|
|
65
|
+
readonly drawRing: UniformRing;
|
|
66
|
+
/** A pass block and a view block a copy. */
|
|
67
|
+
passes: UniformRing;
|
|
68
|
+
views: UniformRing;
|
|
69
|
+
/** Bumped whenever the three rings are made again, which invalidates every recording. */
|
|
70
|
+
ringEpoch: number;
|
|
71
|
+
/** The list's own lit groups and blank group, built over its rings by the renderer. */
|
|
72
|
+
groups: Map<unknown, unknown>;
|
|
73
|
+
blank: GPUBindGroup | null;
|
|
74
|
+
/** The group epoch the cache above was built in. */
|
|
75
|
+
groupEpoch: number;
|
|
76
|
+
/** The store epoch the list's pins were taken in: pins from before a forgetting are let go of. */
|
|
77
|
+
pinEpoch: number;
|
|
78
|
+
readonly recordings: StaticRecording[];
|
|
79
|
+
private copies;
|
|
80
|
+
private copyFrame;
|
|
81
|
+
constructor(device: GPUDevice, list: StaticDrawsList, passSize: number, viewSize: number, drawSize: number);
|
|
82
|
+
/** The captured entries, which the renderer replays to record them. */
|
|
83
|
+
get entries(): readonly StaticEntry[];
|
|
84
|
+
get draws(): number;
|
|
85
|
+
/**
|
|
86
|
+
* The next view's copy in frame `frame`, growing the copies when a frame asks for more than there
|
|
87
|
+
* are — which makes every recording stale, since the groups bound the old rings.
|
|
88
|
+
*/
|
|
89
|
+
takeCopy(frame: number): number;
|
|
90
|
+
/** The two offsets copy `copy` binds. */
|
|
91
|
+
passOffset(copy: number): number;
|
|
92
|
+
viewOffset(copy: number): number;
|
|
93
|
+
/** Copy `copy`'s pass block, written whole. Uploaded by `uploadCopy`. */
|
|
94
|
+
writePass(copy: number, block: Int32Array): void;
|
|
95
|
+
/** Copy `copy`'s two blocks to the device, and no other copy's: each view writes only its own. */
|
|
96
|
+
uploadCopy(copy: number): void;
|
|
97
|
+
/** The draws' own slots, after a recording wrote them. */
|
|
98
|
+
uploadDraws(): void;
|
|
99
|
+
/** A recording that matches, if one does: the same target, starting state and side passes. */
|
|
100
|
+
find(cache: PipelineCache, start: Int32Array, startHash: number, cutout: string, surfaceKept: boolean): StaticRecording | null;
|
|
101
|
+
/** A new, empty recording, letting the least recently used go past the most kept. */
|
|
102
|
+
open(cache: PipelineCache, start: Int32Array, startHash: number, cutout: string, surfaceKept: boolean, unpin: (offset: number) => void): StaticRecording;
|
|
103
|
+
/** Empty a recording to record it again: its pins let go, its commands and bundles dropped. */
|
|
104
|
+
clear(recording: StaticRecording, unpin: (offset: number) => void): void;
|
|
105
|
+
/** Every recording, let go; what disposal and a forgetting store both do. */
|
|
106
|
+
forget(unpin: ((offset: number) => void) | null): void;
|
|
107
|
+
dispose(unpin: ((offset: number) => void) | null): void;
|
|
108
|
+
private growCopies;
|
|
109
|
+
/** Every copy's slot, taken once: a copy is a fixed offset, never handed out again. */
|
|
110
|
+
private claimCopies;
|
|
111
|
+
}
|
|
112
|
+
/** A command the recording takes in place of the frame graph's. */
|
|
113
|
+
export declare function takeRecorded(pool: CommandPool): DrawCommand;
|
|
114
|
+
/** What a render bundle must agree with: the pass it is executed in. */
|
|
115
|
+
export interface BundleTarget {
|
|
116
|
+
readonly colorFormats: (GPUTextureFormat | null)[];
|
|
117
|
+
readonly depthStencilFormat: GPUTextureFormat;
|
|
118
|
+
readonly sampleCount: number;
|
|
119
|
+
readonly depthReadOnly: boolean;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* One copy's bundle of a pool's commands: each lit command as recorded but for the pass and view
|
|
123
|
+
* offsets, which are the copy's. Allocates, and runs once a copy a recording, never per frame.
|
|
124
|
+
*/
|
|
125
|
+
export declare function encodeBundle(device: GPUDevice, target: BundleTarget, pool: CommandPool, passOffset: number, viewOffset: number, label: string): readonly GPURenderBundle[];
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { createCommandPool, resetPool, takeCommand } from './drawCommand.js';
|
|
2
|
+
import { UniformRing } from './uniformRing.js';
|
|
3
|
+
const USAGE_UNIFORM_DST = 0x0040 | 0x0008;
|
|
4
|
+
/** Views a list starts with room for: the frame's and one capture's. Doubles when a frame asks more. */
|
|
5
|
+
const FIRST_COPIES = 2;
|
|
6
|
+
/** Recordings a list keeps before letting the oldest go: a target and a starting state each. */
|
|
7
|
+
const MOST_RECORDINGS = 4;
|
|
8
|
+
export class GpuStaticDraws {
|
|
9
|
+
device;
|
|
10
|
+
list;
|
|
11
|
+
passSize;
|
|
12
|
+
viewSize;
|
|
13
|
+
disposed = false;
|
|
14
|
+
/** The draws' own slots, one a draw, written by the recording. */
|
|
15
|
+
drawRing;
|
|
16
|
+
/** A pass block and a view block a copy. */
|
|
17
|
+
passes;
|
|
18
|
+
views;
|
|
19
|
+
/** Bumped whenever the three rings are made again, which invalidates every recording. */
|
|
20
|
+
ringEpoch = 0;
|
|
21
|
+
/** The list's own lit groups and blank group, built over its rings by the renderer. */
|
|
22
|
+
groups = new Map();
|
|
23
|
+
blank = null;
|
|
24
|
+
/** The group epoch the cache above was built in. */
|
|
25
|
+
groupEpoch = -1;
|
|
26
|
+
/** The store epoch the list's pins were taken in: pins from before a forgetting are let go of. */
|
|
27
|
+
pinEpoch = -1;
|
|
28
|
+
recordings = [];
|
|
29
|
+
copies = 0;
|
|
30
|
+
copyFrame = -1;
|
|
31
|
+
constructor(device, list, passSize, viewSize, drawSize) {
|
|
32
|
+
this.device = device;
|
|
33
|
+
this.list = list;
|
|
34
|
+
this.passSize = passSize;
|
|
35
|
+
this.viewSize = viewSize;
|
|
36
|
+
this.drawRing = new UniformRing(device, drawSize, Math.max(1, list.draws), USAGE_UNIFORM_DST, 'static.draws');
|
|
37
|
+
this.passes = new UniformRing(device, passSize, FIRST_COPIES, USAGE_UNIFORM_DST, 'static.passes');
|
|
38
|
+
this.views = new UniformRing(device, viewSize, FIRST_COPIES, USAGE_UNIFORM_DST, 'static.views');
|
|
39
|
+
this.claimCopies();
|
|
40
|
+
}
|
|
41
|
+
/** The captured entries, which the renderer replays to record them. */
|
|
42
|
+
get entries() {
|
|
43
|
+
return this.list.entries;
|
|
44
|
+
}
|
|
45
|
+
get draws() {
|
|
46
|
+
return this.list.draws;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The next view's copy in frame `frame`, growing the copies when a frame asks for more than there
|
|
50
|
+
* are — which makes every recording stale, since the groups bound the old rings.
|
|
51
|
+
*/
|
|
52
|
+
takeCopy(frame) {
|
|
53
|
+
if (frame !== this.copyFrame) {
|
|
54
|
+
this.copyFrame = frame;
|
|
55
|
+
this.copies = 0;
|
|
56
|
+
}
|
|
57
|
+
const copy = this.copies;
|
|
58
|
+
this.copies += 1;
|
|
59
|
+
if (copy >= this.passes.slots)
|
|
60
|
+
this.growCopies(this.passes.slots * 2);
|
|
61
|
+
return copy;
|
|
62
|
+
}
|
|
63
|
+
/** The two offsets copy `copy` binds. */
|
|
64
|
+
passOffset(copy) {
|
|
65
|
+
return copy * this.passes.slotSize;
|
|
66
|
+
}
|
|
67
|
+
viewOffset(copy) {
|
|
68
|
+
return copy * this.views.slotSize;
|
|
69
|
+
}
|
|
70
|
+
/** Copy `copy`'s pass block, written whole. Uploaded by `uploadCopy`. */
|
|
71
|
+
writePass(copy, block) {
|
|
72
|
+
this.passes.writeBlock(this.passOffset(copy), block);
|
|
73
|
+
}
|
|
74
|
+
/** Copy `copy`'s two blocks to the device, and no other copy's: each view writes only its own. */
|
|
75
|
+
uploadCopy(copy) {
|
|
76
|
+
this.passes.uploadSlot(this.passOffset(copy));
|
|
77
|
+
this.views.uploadSlot(this.viewOffset(copy));
|
|
78
|
+
}
|
|
79
|
+
/** The draws' own slots, after a recording wrote them. */
|
|
80
|
+
uploadDraws() {
|
|
81
|
+
this.drawRing.flush();
|
|
82
|
+
}
|
|
83
|
+
/** A recording that matches, if one does: the same target, starting state and side passes. */
|
|
84
|
+
find(cache, start, startHash, cutout, surfaceKept) {
|
|
85
|
+
for (const recording of this.recordings) {
|
|
86
|
+
if (recording.cache === cache &&
|
|
87
|
+
recording.startHash === startHash &&
|
|
88
|
+
recording.cutout === cutout &&
|
|
89
|
+
recording.surfaceKept === surfaceKept &&
|
|
90
|
+
sameWords(recording.start, start)) {
|
|
91
|
+
return recording;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
/** A new, empty recording, letting the least recently used go past the most kept. */
|
|
97
|
+
open(cache, start, startHash, cutout, surfaceKept, unpin) {
|
|
98
|
+
if (this.recordings.length >= MOST_RECORDINGS) {
|
|
99
|
+
let oldest = 0;
|
|
100
|
+
for (let i = 1; i < this.recordings.length; i++) {
|
|
101
|
+
if (this.recordings[i].usedIn <
|
|
102
|
+
this.recordings[oldest].usedIn)
|
|
103
|
+
oldest = i;
|
|
104
|
+
}
|
|
105
|
+
const [gone] = this.recordings.splice(oldest, 1);
|
|
106
|
+
if (gone !== undefined)
|
|
107
|
+
release(gone, unpin);
|
|
108
|
+
}
|
|
109
|
+
const recording = {
|
|
110
|
+
cache,
|
|
111
|
+
start: new Int32Array(start),
|
|
112
|
+
startHash,
|
|
113
|
+
cutout,
|
|
114
|
+
surfaceKept,
|
|
115
|
+
cacheVersion: -1,
|
|
116
|
+
groupEpoch: -1,
|
|
117
|
+
storeEpoch: -1,
|
|
118
|
+
ringEpoch: -1,
|
|
119
|
+
frame: createCommandPool(this.list.draws),
|
|
120
|
+
surface: createCommandPool(0),
|
|
121
|
+
pins: [],
|
|
122
|
+
materialAsks: 0,
|
|
123
|
+
immediate: false,
|
|
124
|
+
frameBundles: [],
|
|
125
|
+
surfaceBundles: [],
|
|
126
|
+
usedIn: -1,
|
|
127
|
+
};
|
|
128
|
+
this.recordings.push(recording);
|
|
129
|
+
return recording;
|
|
130
|
+
}
|
|
131
|
+
/** Empty a recording to record it again: its pins let go, its commands and bundles dropped. */
|
|
132
|
+
clear(recording, unpin) {
|
|
133
|
+
release(recording, unpin);
|
|
134
|
+
resetPool(recording.frame);
|
|
135
|
+
resetPool(recording.surface);
|
|
136
|
+
recording.frameBundles.length = 0;
|
|
137
|
+
recording.surfaceBundles.length = 0;
|
|
138
|
+
recording.immediate = false;
|
|
139
|
+
recording.materialAsks = 0;
|
|
140
|
+
}
|
|
141
|
+
/** Every recording, let go; what disposal and a forgetting store both do. */
|
|
142
|
+
forget(unpin) {
|
|
143
|
+
for (const recording of this.recordings)
|
|
144
|
+
release(recording, unpin);
|
|
145
|
+
this.recordings.length = 0;
|
|
146
|
+
}
|
|
147
|
+
dispose(unpin) {
|
|
148
|
+
this.disposed = true;
|
|
149
|
+
this.forget(unpin);
|
|
150
|
+
this.drawRing.dispose();
|
|
151
|
+
this.passes.dispose();
|
|
152
|
+
this.views.dispose();
|
|
153
|
+
}
|
|
154
|
+
growCopies(copies) {
|
|
155
|
+
this.passes.dispose();
|
|
156
|
+
this.views.dispose();
|
|
157
|
+
this.passes = new UniformRing(this.device, this.passSize, copies, USAGE_UNIFORM_DST, 'static.passes');
|
|
158
|
+
this.views = new UniformRing(this.device, this.viewSize, copies, USAGE_UNIFORM_DST, 'static.views');
|
|
159
|
+
this.claimCopies();
|
|
160
|
+
this.ringEpoch += 1;
|
|
161
|
+
this.groups = new Map();
|
|
162
|
+
this.blank = null;
|
|
163
|
+
}
|
|
164
|
+
/** Every copy's slot, taken once: a copy is a fixed offset, never handed out again. */
|
|
165
|
+
claimCopies() {
|
|
166
|
+
for (let copy = 0; copy < this.passes.slots; copy++) {
|
|
167
|
+
this.passes.allocate();
|
|
168
|
+
this.views.allocate();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/** A command the recording takes in place of the frame graph's. */
|
|
173
|
+
export function takeRecorded(pool) {
|
|
174
|
+
const command = pool.commands[takeCommand(pool)];
|
|
175
|
+
command.offsetCount = 0;
|
|
176
|
+
command.vertexCount = 0;
|
|
177
|
+
command.indexBuffer = null;
|
|
178
|
+
command.indexed = false;
|
|
179
|
+
command.instances = 1;
|
|
180
|
+
command.indirect = null;
|
|
181
|
+
return command;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* One copy's bundle of a pool's commands: each lit command as recorded but for the pass and view
|
|
185
|
+
* offsets, which are the copy's. Allocates, and runs once a copy a recording, never per frame.
|
|
186
|
+
*/
|
|
187
|
+
export function encodeBundle(device, target, pool, passOffset, viewOffset, label) {
|
|
188
|
+
const encoder = device.createRenderBundleEncoder({
|
|
189
|
+
label,
|
|
190
|
+
colorFormats: target.colorFormats,
|
|
191
|
+
depthStencilFormat: target.depthStencilFormat,
|
|
192
|
+
sampleCount: target.sampleCount,
|
|
193
|
+
depthReadOnly: target.depthReadOnly,
|
|
194
|
+
stencilReadOnly: target.depthReadOnly,
|
|
195
|
+
});
|
|
196
|
+
for (let i = 0; i < pool.taken; i++) {
|
|
197
|
+
const command = pool.commands[i];
|
|
198
|
+
if (command.pipeline === null || command.bindGroup === null)
|
|
199
|
+
continue;
|
|
200
|
+
if (command.offsetCount !== 4) {
|
|
201
|
+
throw new Error(`static draws: a recorded command bound ${command.offsetCount} offsets, not a lit draw's four`);
|
|
202
|
+
}
|
|
203
|
+
encoder.setPipeline(command.pipeline);
|
|
204
|
+
encoder.setBindGroup(0, command.bindGroup, [
|
|
205
|
+
command.offsetA,
|
|
206
|
+
passOffset,
|
|
207
|
+
command.offsetC,
|
|
208
|
+
viewOffset,
|
|
209
|
+
]);
|
|
210
|
+
for (let slot = 0; slot < command.vertexCount; slot++) {
|
|
211
|
+
const buffer = command.vertexBuffers[slot];
|
|
212
|
+
if (buffer != null)
|
|
213
|
+
encoder.setVertexBuffer(slot, buffer);
|
|
214
|
+
}
|
|
215
|
+
if (command.indexed && command.indexBuffer !== null) {
|
|
216
|
+
encoder.setIndexBuffer(command.indexBuffer, 'uint32');
|
|
217
|
+
if (command.indirect !== null)
|
|
218
|
+
encoder.drawIndexedIndirect(command.indirect, 0);
|
|
219
|
+
else
|
|
220
|
+
encoder.drawIndexed(command.count, command.instances);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
encoder.draw(command.count, command.instances);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return [encoder.finish({ label })];
|
|
227
|
+
}
|
|
228
|
+
function release(recording, unpin) {
|
|
229
|
+
if (unpin !== null)
|
|
230
|
+
for (const offset of recording.pins)
|
|
231
|
+
unpin(offset);
|
|
232
|
+
recording.pins.length = 0;
|
|
233
|
+
}
|
|
234
|
+
function sameWords(a, b) {
|
|
235
|
+
if (a.length !== b.length)
|
|
236
|
+
return false;
|
|
237
|
+
for (let i = 0; i < a.length; i++)
|
|
238
|
+
if (a[i] !== b[i])
|
|
239
|
+
return false;
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { SurfaceTextureOptions } from '../../surfaceTexture.ts';
|
|
2
2
|
import type { LightmapTexels } from '../../lightmap.ts';
|
|
3
3
|
import type { SceneCaptureTexels } from '../../sceneCapture.ts';
|
|
4
|
-
import type { CompressedTextureFormat, SurfaceSource } from '../../compressedSource.ts';
|
|
4
|
+
import type { CompressedTextureFormat, CompressedTextureSource, SurfaceSource } from '../../compressedSource.ts';
|
|
5
5
|
import type { PipelineCache } from './pipelineCache.ts';
|
|
6
6
|
/** How many levels an image of this size has, counting the one it arrives with. */
|
|
7
7
|
export declare function mipLevelCount(width: number, height: number): number;
|
|
@@ -63,7 +63,9 @@ export declare class GpuSurfaceTexture {
|
|
|
63
63
|
/** Its view, which the flat group binds beside this image when it is a material's albedo. */
|
|
64
64
|
effectsView: GPUTextureView | null;
|
|
65
65
|
/** The block format a compressed texture holds, or null for one uploaded from images. */
|
|
66
|
-
private
|
|
66
|
+
private blockFormat;
|
|
67
|
+
/** Whether the image is colour, decoded from sRGB as it is sampled: what blocks swapped in keep. */
|
|
68
|
+
private readonly srgb;
|
|
67
69
|
constructor(device: GPUDevice, pipelines: PipelineCache, source: SurfaceSource | readonly SurfaceSource[] | LightmapTexels | SceneCaptureTexels, options?: SurfaceTextureOptions, compressed?: readonly CompressedTextureFormat[]);
|
|
68
70
|
/** A view of every layer, as the shaders' `texture_2d_array` bindings read it. */
|
|
69
71
|
private arrayView;
|
|
@@ -84,8 +86,13 @@ export declare class GpuSurfaceTexture {
|
|
|
84
86
|
* hit that whenever the preview's decode won the race. The view is new then, and the texture it
|
|
85
87
|
* replaced is handed back rather than destroyed: a draw recorded earlier in this frame may still
|
|
86
88
|
* read it, so the renderer destroys it once nothing can, and drops every binding of the old view.
|
|
89
|
+
*
|
|
90
|
+
* **Blocks replace an image the same way**, at any size: a texture of their format holds them,
|
|
91
|
+
* read in the colour space this one was made for, and the image's is handed back. Refused, as a
|
|
92
|
+
* texture made from them would be, where `compressed` — the formats the device samples — does not
|
|
93
|
+
* take them. The way back is refused: an image cannot replace blocks.
|
|
87
94
|
*/
|
|
88
|
-
update(source: TexImageSource): GPUTexture | null;
|
|
95
|
+
update(source: TexImageSource | CompressedTextureSource, compressed?: readonly CompressedTextureFormat[]): GPUTexture | null;
|
|
89
96
|
private allocate;
|
|
90
97
|
dispose(): void;
|
|
91
98
|
private upload;
|
|
@@ -3,7 +3,7 @@ import { toHalfFloats } from '../../halfFloat.js';
|
|
|
3
3
|
import { isLightmapTexels } from '../../lightmap.js';
|
|
4
4
|
import { isSceneCaptureTexels } from '../../sceneCapture.js';
|
|
5
5
|
import { layerSize, refuseArrayUpdate, sourceSize } from '../../textureSource.js';
|
|
6
|
-
import { compressedLayers, planBlocks, refuseBlockUpdate } from '../../compressedSource.js';
|
|
6
|
+
import { compressedLayers, isCompressedSource, planBlocks, refuseBlockUpdate, } from '../../compressedSource.js';
|
|
7
7
|
import { createCompressedTexture } from './compressedUpload.js';
|
|
8
8
|
import { shaderModule } from './shaderModules.js';
|
|
9
9
|
/**
|
|
@@ -154,6 +154,8 @@ export class GpuSurfaceTexture {
|
|
|
154
154
|
effectsView = null;
|
|
155
155
|
/** The block format a compressed texture holds, or null for one uploaded from images. */
|
|
156
156
|
blockFormat;
|
|
157
|
+
/** Whether the image is colour, decoded from sRGB as it is sampled: what blocks swapped in keep. */
|
|
158
|
+
srgb;
|
|
157
159
|
constructor(device, pipelines, source, options = {}, compressed = []) {
|
|
158
160
|
this.device = device;
|
|
159
161
|
this.pipelines = pipelines;
|
|
@@ -164,6 +166,7 @@ export class GpuSurfaceTexture {
|
|
|
164
166
|
const listed = (baked !== null || capture !== null ? [] : Array.isArray(source) ? source : [source]);
|
|
165
167
|
const blocks = compressedLayers(listed);
|
|
166
168
|
const srgb = (options.colorSpace ?? 'linear') === 'srgb';
|
|
169
|
+
this.srgb = srgb;
|
|
167
170
|
/* `SRGB8_ALPHA8`'s equivalent. Decoded in the sampler, before filtering, which is the only
|
|
168
171
|
place it is correct — `surfaceTexture.ts` makes the argument in full. A capture takes the
|
|
169
172
|
world's own format, which its pass resolves into. */
|
|
@@ -294,12 +297,27 @@ export class GpuSurfaceTexture {
|
|
|
294
297
|
* hit that whenever the preview's decode won the race. The view is new then, and the texture it
|
|
295
298
|
* replaced is handed back rather than destroyed: a draw recorded earlier in this frame may still
|
|
296
299
|
* read it, so the renderer destroys it once nothing can, and drops every binding of the old view.
|
|
300
|
+
*
|
|
301
|
+
* **Blocks replace an image the same way**, at any size: a texture of their format holds them,
|
|
302
|
+
* read in the colour space this one was made for, and the image's is handed back. Refused, as a
|
|
303
|
+
* texture made from them would be, where `compressed` — the formats the device samples — does not
|
|
304
|
+
* take them. The way back is refused: an image cannot replace blocks.
|
|
297
305
|
*/
|
|
298
|
-
update(source) {
|
|
306
|
+
update(source, compressed = []) {
|
|
299
307
|
const replaced = this.texture;
|
|
300
308
|
if (replaced === null)
|
|
301
309
|
return null;
|
|
302
310
|
refuseArrayUpdate(this.layers);
|
|
311
|
+
if (isCompressedSource(source)) {
|
|
312
|
+
const plan = planBlocks([source], this.srgb, compressed);
|
|
313
|
+
this.blockFormat = source.format;
|
|
314
|
+
this.width = source.width;
|
|
315
|
+
this.height = source.height;
|
|
316
|
+
this.levels = this.mipmapped ? plan.levels : 1;
|
|
317
|
+
this.texture = createCompressedTexture(this.device, plan.name, [source], this.levels);
|
|
318
|
+
this.current = this.arrayView(this.texture);
|
|
319
|
+
return replaced;
|
|
320
|
+
}
|
|
303
321
|
refuseBlockUpdate(this.blockFormat);
|
|
304
322
|
const { width, height } = sourceSize(source);
|
|
305
323
|
if (width === this.width && height === this.height) {
|
|
@@ -7,6 +7,21 @@ import type { BudgetCounter } from '../budget.ts';
|
|
|
7
7
|
* an alignment restated somewhere else is an alignment that can disagree.
|
|
8
8
|
*/
|
|
9
9
|
export declare const DYNAMIC_ALIGNMENT = 256;
|
|
10
|
+
/**
|
|
11
|
+
* The most staging one ring may hold, and therefore the most device memory it may take.
|
|
12
|
+
*
|
|
13
|
+
* **A ceiling on the growth, because a runaway consumer should degrade rather than crash.** Past
|
|
14
|
+
* this the ring refuses to grow and goes on skipping work and reporting it, which is the right end
|
|
15
|
+
* state for a frame asking for something no device should be asked for. What growth changes is
|
|
16
|
+
* where that line sits: at a number chosen against a phone's memory rather than at whatever the
|
|
17
|
+
* ring happened to be constructed with.
|
|
18
|
+
*
|
|
19
|
+
* Sixty-four mebibytes is far past any legitimate scene — the flat vertex ring's slot is 1,540
|
|
20
|
+
* bytes, so this is forty-three thousand draws, and the fragment ring's is at most 13,312, which
|
|
21
|
+
* is five thousand material changes in one frame. The device buffer matches the staging, so a ring
|
|
22
|
+
* at this ceiling holds 128 MiB in total and only ever reaches it by being asked to.
|
|
23
|
+
*/
|
|
24
|
+
export declare const MAX_RING_BYTES: number;
|
|
10
25
|
/**
|
|
11
26
|
* Per-draw uniforms, in slots, written once and bound by offset.
|
|
12
27
|
*
|
|
@@ -159,5 +174,11 @@ export declare class UniformRing {
|
|
|
159
174
|
* staging without passing a method that lowers `clean` — which is why the staging stays private.
|
|
160
175
|
*/
|
|
161
176
|
flush(): void;
|
|
177
|
+
/**
|
|
178
|
+
* Upload one slot now, and nothing else: for a ring whose slots are all claimed once and written
|
|
179
|
+
* one at a time — a static list's copies — where `flush` would send every slot above the lowest
|
|
180
|
+
* one written. Leaves the watermark as it was, so a later `flush` still owes what it owed.
|
|
181
|
+
*/
|
|
182
|
+
uploadSlot(slot: number): void;
|
|
162
183
|
dispose(): void;
|
|
163
184
|
}
|
|
@@ -20,7 +20,7 @@ export const DYNAMIC_ALIGNMENT = 256;
|
|
|
20
20
|
* is five thousand material changes in one frame. The device buffer matches the staging, so a ring
|
|
21
21
|
* at this ceiling holds 128 MiB in total and only ever reaches it by being asked to.
|
|
22
22
|
*/
|
|
23
|
-
const MAX_RING_BYTES = 64 * 1024 * 1024;
|
|
23
|
+
export const MAX_RING_BYTES = 64 * 1024 * 1024;
|
|
24
24
|
/**
|
|
25
25
|
* Per-draw uniforms, in slots, written once and bound by offset.
|
|
26
26
|
*
|
|
@@ -252,6 +252,14 @@ export class UniformRing {
|
|
|
252
252
|
this.device.queue.writeBuffer(this.buffer, from, this.staging, from, end - from);
|
|
253
253
|
this.clean = end;
|
|
254
254
|
}
|
|
255
|
+
/**
|
|
256
|
+
* Upload one slot now, and nothing else: for a ring whose slots are all claimed once and written
|
|
257
|
+
* one at a time — a static list's copies — where `flush` would send every slot above the lowest
|
|
258
|
+
* one written. Leaves the watermark as it was, so a later `flush` still owes what it owed.
|
|
259
|
+
*/
|
|
260
|
+
uploadSlot(slot) {
|
|
261
|
+
this.device.queue.writeBuffer(this.buffer, slot, this.staging, slot, this.slotSize);
|
|
262
|
+
}
|
|
255
263
|
dispose() {
|
|
256
264
|
this.buffer.destroy();
|
|
257
265
|
}
|
|
@@ -62,7 +62,7 @@ export declare function bloomProfileWarning(quality: {
|
|
|
62
62
|
* already takes. Absent, or a field absent, is the response every frame had before 4.8.6.
|
|
63
63
|
*
|
|
64
64
|
* **Why it exists: one band over the colour less the threshold washes a stage built for another
|
|
65
|
-
* engine.**
|
|
65
|
+
* engine.** A ramped bloom keeps `saturate((L − threshold) / 2)` of a colour rather than
|
|
66
66
|
* the colour less the threshold, and spreads it over six bands, each in a tint of its own. A
|
|
67
67
|
* stage authored against that — a threshold of 0.1 and tints summing below one — came out white
|
|
68
68
|
* here, because subtracting so low a threshold keeps nearly every light in the frame at full
|
|
@@ -72,8 +72,8 @@ export interface BloomResponse {
|
|
|
72
72
|
/**
|
|
73
73
|
* Scene units over which a colour comes in, from none of it at the threshold to all of it this
|
|
74
74
|
* far past. **0, the default, subtracts instead**: a colour keeps what it has past the threshold,
|
|
75
|
-
* the response since bloom shipped.
|
|
76
|
-
* wants it passes `2 / e`, as it passes the threshold over the exposure.
|
|
75
|
+
* the response since bloom shipped. A ramped bloom's is 2 after exposure, so a frame exposed by
|
|
76
|
+
* `e` that wants it passes `2 / e`, as it passes the threshold over the exposure.
|
|
77
77
|
*
|
|
78
78
|
* Brightness is still the largest channel rather than luminance, for the reason
|
|
79
79
|
* `shaders/bloom.ts` gives; on a neutral colour the two are equal.
|