@driftengine/core 4.9.0 → 4.11.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 +133 -11
- package/README.md +43 -38
- package/dist/index.d.ts +7 -4
- package/dist/index.js +5 -4
- 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/backend/api.d.ts +10 -3
- 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 +189 -4
- package/dist/render/backend/webgl2/renderer.js +614 -66
- 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 +24 -4
- package/dist/render/backend/webgpu/buffers.js +116 -59
- package/dist/render/backend/webgpu/clothKernels.d.ts +8 -0
- package/dist/render/backend/webgpu/clothKernels.js +76 -0
- package/dist/render/backend/webgpu/clothOps.d.ts +39 -0
- package/dist/render/backend/webgpu/clothOps.js +78 -0
- package/dist/render/backend/webgpu/clothSetLayout.d.ts +25 -0
- package/dist/render/backend/webgpu/clothSetLayout.js +84 -0
- package/dist/render/backend/webgpu/clothSetPack.d.ts +33 -0
- package/dist/render/backend/webgpu/clothSetPack.js +144 -0
- 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/drawCommand.d.ts +20 -4
- package/dist/render/backend/webgpu/drawCommand.js +11 -3
- package/dist/render/backend/webgpu/flatPass.d.ts +85 -42
- package/dist/render/backend/webgpu/flatPass.js +137 -20
- package/dist/render/backend/webgpu/gpuClothSet.d.ts +80 -0
- package/dist/render/backend/webgpu/gpuClothSet.js +462 -0
- package/dist/render/backend/webgpu/instanced.d.ts +14 -1
- package/dist/render/backend/webgpu/instanced.js +24 -1
- package/dist/render/backend/webgpu/latePass.js +1 -0
- package/dist/render/backend/webgpu/materialStore.d.ts +101 -0
- package/dist/render/backend/webgpu/materialStore.js +265 -0
- package/dist/render/backend/webgpu/motionPass.js +15 -6
- package/dist/render/backend/webgpu/pipelineCache.d.ts +28 -1
- package/dist/render/backend/webgpu/pipelineCache.js +46 -1
- package/dist/render/backend/webgpu/postPass.d.ts +1 -1
- package/dist/render/backend/webgpu/renderer.d.ts +200 -35
- package/dist/render/backend/webgpu/renderer.js +993 -168
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.d.ts +9 -2
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.js +121 -47
- 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 +242 -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/backend/webgpu/vertexFormats.d.ts +22 -0
- package/dist/render/backend/webgpu/vertexFormats.js +97 -0
- 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/glCompressed.js +83 -0
- package/dist/render/globalMediumPass.d.ts +2 -0
- package/dist/render/globalMediumPass.js +1 -0
- package/dist/render/indexWidth.d.ts +17 -0
- package/dist/render/indexWidth.js +23 -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/lightMatrix.d.ts +11 -0
- package/dist/render/lightMatrix.js +14 -0
- package/dist/render/lightmap.d.ts +1 -1
- package/dist/render/mesh.d.ts +14 -0
- package/dist/render/mesh.js +44 -14
- package/dist/render/pointLightSelection.d.ts +4 -4
- package/dist/render/renderQuality.d.ts +30 -0
- package/dist/render/renderQuality.js +5 -0
- package/dist/render/rendererHarness.d.ts +7 -0
- package/dist/render/rendererHarness.js +12 -0
- package/dist/render/screenSpaceReflection.d.ts +2 -1
- package/dist/render/shader.d.ts +15 -0
- package/dist/render/shader.js +41 -0
- package/dist/render/shaders/boneAnimation.d.ts +19 -0
- package/dist/render/shaders/boneAnimation.js +63 -0
- package/dist/render/shaders/depth.d.ts +7 -0
- package/dist/render/shaders/depth.js +35 -10
- package/dist/render/shaders/flat/directionalShadow.js +84 -10
- package/dist/render/shaders/flat/glassTint.d.ts +1 -1
- package/dist/render/shaders/flat/glassTint.js +3 -0
- package/dist/render/shaders/flat/index.d.ts +21 -0
- package/dist/render/shaders/flat/index.js +42 -7
- package/dist/render/shaders/flat/layered.d.ts +19 -0
- package/dist/render/shaders/flat/layered.js +115 -0
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +58 -21
- package/dist/render/shaders/flat/mapReads.d.ts +10 -0
- package/dist/render/shaders/flat/mapReads.js +38 -0
- package/dist/render/shaders/flat/models.js +1 -1
- package/dist/render/shaders/flat/movingSun.d.ts +21 -0
- package/dist/render/shaders/flat/movingSun.js +46 -0
- package/dist/render/shaders/flat/normalMap.d.ts +6 -2
- package/dist/render/shaders/flat/normalMap.js +14 -3
- package/dist/render/shaders/flat/overlay.d.ts +3 -2
- package/dist/render/shaders/flat/overlay.js +42 -20
- package/dist/render/shaders/flat/pointShadow.js +2 -2
- package/dist/render/shaders/flat/preamble.js +59 -27
- package/dist/render/shaders/flat/worldUv.d.ts +22 -0
- package/dist/render/shaders/flat/worldUv.js +113 -0
- 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/flat.wgsl.d.ts +3616 -3042
- package/dist/render/shaders/generated/flat.wgsl.js +3688 -3113
- package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +8 -3
- package/dist/render/shaders/generated/globalMedium.wgsl.js +8 -3
- package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +15 -10
- package/dist/render/shaders/generated/lightVolume.wgsl.js +16 -11
- package/dist/render/shaders/globalMedium.js +9 -1
- package/dist/render/shaders/lightVolume.js +9 -1
- package/dist/render/shaders/outputTransform.d.ts +7 -0
- package/dist/render/shaders/outputTransform.js +28 -14
- package/dist/render/shaders/tangentFrame.d.ts +1 -1
- package/dist/render/shaders/tangentFrame.js +16 -3
- package/dist/render/shaders/vertexChannel.d.ts +7 -0
- package/dist/render/shaders/vertexChannel.js +26 -0
- package/dist/render/skinnedClothRun.d.ts +28 -0
- package/dist/render/skinnedClothRun.js +103 -35
- package/dist/render/staticDraws.d.ts +47 -0
- package/dist/render/staticDraws.js +43 -0
- package/dist/render/surfaceLayers.d.ts +39 -0
- package/dist/render/surfaceLayers.js +26 -0
- package/dist/render/surfaceOverlay.d.ts +16 -3
- package/dist/render/surfaceOverlay.js +11 -6
- package/dist/render/surfaceProjection.d.ts +34 -0
- package/dist/render/surfaceProjection.js +30 -0
- package/dist/render/surfaceTexture.d.ts +68 -7
- package/dist/render/surfaceTexture.js +56 -17
- package/dist/render/vertexPacking.d.ts +48 -0
- package/dist/render/vertexPacking.js +68 -0
- package/package.json +6 -6
- package/src/index.ts +12 -4
- package/src/render/ambientHarmonics.ts +1 -1
- package/src/render/areaLights.ts +4 -4
- package/src/render/backend/api.ts +13 -3
- 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 +723 -79
- 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 +195 -70
- package/src/render/backend/webgpu/clothKernels.ts +86 -0
- package/src/render/backend/webgpu/clothOps.ts +86 -0
- package/src/render/backend/webgpu/clothSetLayout.ts +102 -0
- package/src/render/backend/webgpu/clothSetPack.ts +197 -0
- package/src/render/backend/webgpu/compressedUpload.ts +59 -9
- package/src/render/backend/webgpu/depthPass.ts +51 -10
- package/src/render/backend/webgpu/drawCommand.ts +25 -4
- package/src/render/backend/webgpu/flatPass.ts +212 -41
- package/src/render/backend/webgpu/gpuClothSet.ts +564 -0
- package/src/render/backend/webgpu/instanced.ts +32 -1
- package/src/render/backend/webgpu/latePass.ts +1 -0
- package/src/render/backend/webgpu/materialStore.ts +276 -0
- package/src/render/backend/webgpu/motionPass.ts +18 -7
- package/src/render/backend/webgpu/pipelineCache.ts +53 -7
- package/src/render/backend/webgpu/renderer.ts +1201 -202
- package/src/render/backend/webgpu/shaders/clothSolve.wgsl.ts +121 -47
- 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 +364 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +29 -3
- package/src/render/backend/webgpu/uniformRing.ts +10 -1
- package/src/render/backend/webgpu/vertexFormats.ts +113 -0
- package/src/render/bloomChain.ts +3 -3
- package/src/render/boneAnimation.ts +223 -0
- package/src/render/compressedSource.ts +121 -29
- package/src/render/glCompressed.ts +83 -0
- package/src/render/globalMediumPass.ts +7 -0
- package/src/render/indexWidth.ts +27 -0
- package/src/render/instances.ts +27 -0
- package/src/render/lightBudget.ts +11 -0
- package/src/render/lightMatrix.ts +15 -0
- package/src/render/lightmap.ts +1 -1
- package/src/render/mesh.ts +72 -14
- package/src/render/pointLightSelection.ts +4 -4
- package/src/render/renderQuality.ts +38 -0
- package/src/render/rendererHarness.ts +15 -0
- package/src/render/screenSpaceReflection.ts +2 -1
- package/src/render/shader.ts +59 -0
- package/src/render/shaders/boneAnimation.ts +63 -0
- package/src/render/shaders/depth.ts +45 -10
- package/src/render/shaders/flat/directionalShadow.ts +84 -10
- package/src/render/shaders/flat/glassTint.ts +7 -1
- package/src/render/shaders/flat/index.ts +63 -7
- package/src/render/shaders/flat/layered.ts +115 -0
- package/src/render/shaders/flat/main.ts +58 -21
- package/src/render/shaders/flat/mapReads.ts +38 -0
- package/src/render/shaders/flat/models.ts +1 -1
- package/src/render/shaders/flat/movingSun.ts +46 -0
- package/src/render/shaders/flat/normalMap.ts +14 -3
- package/src/render/shaders/flat/overlay.ts +42 -20
- package/src/render/shaders/flat/pointShadow.ts +2 -2
- package/src/render/shaders/flat/preamble.ts +59 -27
- package/src/render/shaders/flat/worldUv.ts +113 -0
- package/src/render/shaders/generated/depth.wgsl.ts +95 -0
- package/src/render/shaders/generated/flat.wgsl.ts +3688 -3113
- package/src/render/shaders/generated/globalMedium.wgsl.ts +8 -3
- package/src/render/shaders/generated/lightVolume.wgsl.ts +16 -11
- package/src/render/shaders/globalMedium.ts +9 -1
- package/src/render/shaders/lightVolume.ts +9 -1
- package/src/render/shaders/outputTransform.ts +30 -14
- package/src/render/shaders/tangentFrame.ts +16 -3
- package/src/render/shaders/vertexChannel.ts +27 -0
- package/src/render/skinnedClothRun.ts +138 -38
- package/src/render/staticDraws.ts +95 -0
- package/src/render/surfaceLayers.ts +57 -0
- package/src/render/surfaceOverlay.ts +24 -7
- package/src/render/surfaceProjection.ts +57 -0
- package/src/render/surfaceTexture.ts +124 -27
- package/src/render/vertexPacking.ts +109 -0
- package/dist/render/backend/webgpu/gpuSkinnedCloth.d.ts +0 -74
- package/dist/render/backend/webgpu/gpuSkinnedCloth.js +0 -452
- package/src/render/backend/webgpu/gpuSkinnedCloth.ts +0 -502
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { DYNAMIC_ALIGNMENT, MAX_RING_BYTES } from './uniformRing.js';
|
|
2
|
+
/**
|
|
3
|
+
* The lit stage's material blocks, each kept in its slot for as long as later draws ask for the
|
|
4
|
+
* same numbers, so a material that has not changed since the last frame costs no upload at all.
|
|
5
|
+
*
|
|
6
|
+
* **Keyed by content, because nothing else names a material.** A material here is whatever the
|
|
7
|
+
* setters left in the block when a draw took it — a consumer never hands over an object that could
|
|
8
|
+
* carry an identity — so the block's own words are the key: hashed to find a bucket, compared word
|
|
9
|
+
* for word to accept a match, and bound by the slot's offset. What it gives up is a hash and a
|
|
10
|
+
* compare of a few hundred bytes per material change, against copying and uploading the whole
|
|
11
|
+
* block every frame, which a stage of four hundred materials measured at megabytes a frame.
|
|
12
|
+
*
|
|
13
|
+
* **A slot the frame has asked for is never given away within it**, for the reason `UniformRing`
|
|
14
|
+
* exists: a queue write does not interleave with recorded draws, so rewriting a slot a recorded draw
|
|
15
|
+
* reads would hand that draw the new numbers. A frame asking for more distinct blocks than there are
|
|
16
|
+
* slots is refused, counted and skipped, as the ring was. Across frames rewriting is safe, because a
|
|
17
|
+
* write issued after a submit is ordered after the commands in it.
|
|
18
|
+
*
|
|
19
|
+
* **What a new block displaces is a slot nobody asked for this frame or the last**, found by a hand
|
|
20
|
+
* that sweeps the slots in turn; only when every slot was asked for recently does it take one the
|
|
21
|
+
* last frame used. What would make that wrong is a scene whose distinct materials a frame approach
|
|
22
|
+
* the store's size: it then rewrites some of its steady materials every frame, which costs their
|
|
23
|
+
* upload and nothing else.
|
|
24
|
+
*/
|
|
25
|
+
export class MaterialStore {
|
|
26
|
+
device;
|
|
27
|
+
usage;
|
|
28
|
+
label;
|
|
29
|
+
budget;
|
|
30
|
+
/** Replaced by `growTo`, so every bind group over it is rebuilt when that says so. */
|
|
31
|
+
buffer;
|
|
32
|
+
/** Bytes per slot, rounded up to the alignment a dynamic offset requires. */
|
|
33
|
+
slotSize;
|
|
34
|
+
words;
|
|
35
|
+
stride;
|
|
36
|
+
capacity;
|
|
37
|
+
staging;
|
|
38
|
+
ints;
|
|
39
|
+
/** Per slot: its block's hash, the next slot in its bucket or -1, and whether it holds one. */
|
|
40
|
+
hashes;
|
|
41
|
+
next;
|
|
42
|
+
held;
|
|
43
|
+
/** Per slot: the frame that last asked for it, and whether it was written since the last flush. */
|
|
44
|
+
askedIn;
|
|
45
|
+
pending;
|
|
46
|
+
/** Per slot: how many holders pinned it. A pinned slot is never displaced. */
|
|
47
|
+
pins;
|
|
48
|
+
/**
|
|
49
|
+
* How many times the store has forgotten everything it held, which growth does: a holder of an
|
|
50
|
+
* offset compares this, because an offset from before is a slot of a buffer that is gone.
|
|
51
|
+
*/
|
|
52
|
+
epoch = 0;
|
|
53
|
+
/** Per bucket: its first slot, or -1. A power of two at least twice the slots. */
|
|
54
|
+
heads;
|
|
55
|
+
mask;
|
|
56
|
+
lowPending;
|
|
57
|
+
highPending = -1;
|
|
58
|
+
hand = 0;
|
|
59
|
+
/** Starts past zero, so a slot never asked for reads as long unasked. */
|
|
60
|
+
frame = 2;
|
|
61
|
+
constructor(device, bytesPerBlock, slots, usage, label = 'materials.store', budget = null) {
|
|
62
|
+
this.device = device;
|
|
63
|
+
this.usage = usage;
|
|
64
|
+
this.label = label;
|
|
65
|
+
this.budget = budget;
|
|
66
|
+
this.words = Math.ceil(bytesPerBlock / 4);
|
|
67
|
+
this.slotSize = Math.ceil(bytesPerBlock / DYNAMIC_ALIGNMENT) * DYNAMIC_ALIGNMENT;
|
|
68
|
+
this.stride = this.slotSize / 4;
|
|
69
|
+
this.capacity = slots;
|
|
70
|
+
this.lowPending = slots;
|
|
71
|
+
this.staging = new ArrayBuffer(this.slotSize * slots);
|
|
72
|
+
this.ints = new Int32Array(this.staging);
|
|
73
|
+
this.hashes = new Int32Array(slots);
|
|
74
|
+
this.next = new Int32Array(slots);
|
|
75
|
+
this.held = new Uint8Array(slots);
|
|
76
|
+
this.askedIn = new Int32Array(slots);
|
|
77
|
+
this.pending = new Uint8Array(slots);
|
|
78
|
+
this.pins = new Int32Array(slots);
|
|
79
|
+
this.heads = new Int32Array(bucketsFor(slots)).fill(-1);
|
|
80
|
+
this.mask = this.heads.length - 1;
|
|
81
|
+
this.buffer = device.createBuffer({ label, size: this.staging.byteLength, usage });
|
|
82
|
+
}
|
|
83
|
+
/** How many distinct blocks a frame may hold. */
|
|
84
|
+
get slots() {
|
|
85
|
+
return this.capacity;
|
|
86
|
+
}
|
|
87
|
+
/** Start a frame: every slot the last one asked for may be displaced from here on, last. */
|
|
88
|
+
beginFrame() {
|
|
89
|
+
this.frame += 1;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The byte offset of a slot holding exactly `block`, written there if no slot did, or null when
|
|
93
|
+
* every slot is one this frame has asked for. `block` is the whole block, word for word.
|
|
94
|
+
*/
|
|
95
|
+
place(block) {
|
|
96
|
+
this.budget?.ask();
|
|
97
|
+
const hash = hashWords(block, this.words);
|
|
98
|
+
const bucket = hash & this.mask;
|
|
99
|
+
for (let slot = this.heads[bucket]; slot >= 0; slot = this.next[slot]) {
|
|
100
|
+
if (this.hashes[slot] === hash && this.holds(slot, block)) {
|
|
101
|
+
this.askedIn[slot] = this.frame;
|
|
102
|
+
return slot * this.slotSize;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const slot = this.displace();
|
|
106
|
+
if (slot < 0) {
|
|
107
|
+
this.budget?.drop();
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
if (this.held[slot] === 1)
|
|
111
|
+
this.unlink(slot);
|
|
112
|
+
this.ints.set(block, slot * this.stride);
|
|
113
|
+
this.hashes[slot] = hash;
|
|
114
|
+
this.next[slot] = this.heads[bucket];
|
|
115
|
+
this.heads[bucket] = slot;
|
|
116
|
+
this.held[slot] = 1;
|
|
117
|
+
this.askedIn[slot] = this.frame;
|
|
118
|
+
this.pending[slot] = 1;
|
|
119
|
+
if (slot < this.lowPending)
|
|
120
|
+
this.lowPending = slot;
|
|
121
|
+
if (slot > this.highPending)
|
|
122
|
+
this.highPending = slot;
|
|
123
|
+
return slot * this.slotSize;
|
|
124
|
+
}
|
|
125
|
+
/** Upload every slot written since the last flush, one write per run of neighbouring slots. */
|
|
126
|
+
flush() {
|
|
127
|
+
let slot = this.lowPending;
|
|
128
|
+
while (slot <= this.highPending) {
|
|
129
|
+
if (this.pending[slot] !== 1) {
|
|
130
|
+
slot += 1;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
const from = slot;
|
|
134
|
+
while (slot <= this.highPending && this.pending[slot] === 1) {
|
|
135
|
+
this.pending[slot] = 0;
|
|
136
|
+
slot += 1;
|
|
137
|
+
}
|
|
138
|
+
const at = from * this.slotSize;
|
|
139
|
+
this.device.queue.writeBuffer(this.buffer, at, this.staging, at, (slot - from) * this.slotSize);
|
|
140
|
+
}
|
|
141
|
+
this.lowPending = this.capacity;
|
|
142
|
+
this.highPending = -1;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Make room for at least `slots`, forgetting every block held, and say whether the buffer was
|
|
146
|
+
* replaced. Called where `UniformRing.growTo` is, at a frame's start, for the reasons it gives,
|
|
147
|
+
* and refused past the same ceiling.
|
|
148
|
+
*/
|
|
149
|
+
growTo(slots) {
|
|
150
|
+
if (slots <= this.capacity)
|
|
151
|
+
return false;
|
|
152
|
+
if (this.slotSize * slots > MAX_RING_BYTES)
|
|
153
|
+
return false;
|
|
154
|
+
this.buffer.destroy();
|
|
155
|
+
this.capacity = slots;
|
|
156
|
+
this.staging = new ArrayBuffer(this.slotSize * slots);
|
|
157
|
+
this.ints = new Int32Array(this.staging);
|
|
158
|
+
this.hashes = new Int32Array(slots);
|
|
159
|
+
this.next = new Int32Array(slots);
|
|
160
|
+
this.held = new Uint8Array(slots);
|
|
161
|
+
this.askedIn = new Int32Array(slots);
|
|
162
|
+
this.pending = new Uint8Array(slots);
|
|
163
|
+
this.pins = new Int32Array(slots);
|
|
164
|
+
this.heads = new Int32Array(bucketsFor(slots)).fill(-1);
|
|
165
|
+
this.mask = this.heads.length - 1;
|
|
166
|
+
this.lowPending = slots;
|
|
167
|
+
this.epoch += 1;
|
|
168
|
+
this.highPending = -1;
|
|
169
|
+
this.hand = 0;
|
|
170
|
+
this.buffer = this.device.createBuffer({
|
|
171
|
+
label: this.label,
|
|
172
|
+
size: this.staging.byteLength,
|
|
173
|
+
usage: this.usage,
|
|
174
|
+
});
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Keep the block at `offset` where it is until `unpin`: a holder binding it by offset frame after
|
|
179
|
+
* frame — a list recorded once — whether or not anything asks for it. Counted, so two holders of
|
|
180
|
+
* one block each let go of their own pin.
|
|
181
|
+
*/
|
|
182
|
+
pin(offset) {
|
|
183
|
+
const slot = offset / this.slotSize;
|
|
184
|
+
this.pins[slot] = this.pins[slot] + 1;
|
|
185
|
+
}
|
|
186
|
+
unpin(offset) {
|
|
187
|
+
const slot = offset / this.slotSize;
|
|
188
|
+
if (this.pins[slot] > 0)
|
|
189
|
+
this.pins[slot] = this.pins[slot] - 1;
|
|
190
|
+
}
|
|
191
|
+
dispose() {
|
|
192
|
+
this.buffer.destroy();
|
|
193
|
+
}
|
|
194
|
+
holds(slot, block) {
|
|
195
|
+
const base = slot * this.stride;
|
|
196
|
+
for (let word = 0; word < this.words; word++) {
|
|
197
|
+
if (this.ints[base + word] !== block[word])
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* The slot a new block goes in: the first from the hand nobody asked for this frame or the last,
|
|
204
|
+
* else the first this frame has not asked for, else none. One sweep at most.
|
|
205
|
+
*/
|
|
206
|
+
displace() {
|
|
207
|
+
let fallback = -1;
|
|
208
|
+
for (let step = 0; step < this.capacity; step++) {
|
|
209
|
+
const slot = this.hand;
|
|
210
|
+
this.hand = slot + 1 === this.capacity ? 0 : slot + 1;
|
|
211
|
+
if (this.pins[slot] > 0)
|
|
212
|
+
continue;
|
|
213
|
+
const asked = this.askedIn[slot];
|
|
214
|
+
if (asked < this.frame - 1)
|
|
215
|
+
return slot;
|
|
216
|
+
if (fallback < 0 && asked !== this.frame)
|
|
217
|
+
fallback = slot;
|
|
218
|
+
}
|
|
219
|
+
return fallback;
|
|
220
|
+
}
|
|
221
|
+
unlink(slot) {
|
|
222
|
+
const bucket = this.hashes[slot] & this.mask;
|
|
223
|
+
let at = this.heads[bucket];
|
|
224
|
+
if (at === slot) {
|
|
225
|
+
this.heads[bucket] = this.next[slot];
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
while (at >= 0) {
|
|
229
|
+
const after = this.next[at];
|
|
230
|
+
if (after === slot) {
|
|
231
|
+
this.next[at] = this.next[slot];
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
at = after;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function bucketsFor(slots) {
|
|
239
|
+
let buckets = 16;
|
|
240
|
+
while (buckets < slots * 2)
|
|
241
|
+
buckets *= 2;
|
|
242
|
+
return buckets;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* MurmurHash3's 32-bit mix over the block's words. Not FNV, whose multiply carries a difference only
|
|
246
|
+
* upward: the bucket is the hash's low bits, and a material's words are mostly floats, whose low
|
|
247
|
+
* mantissa bits are zero for every value a person types — 0.5, 1, 2 — so blocks differing only in
|
|
248
|
+
* those would all have shared one bucket. The compare that follows a match is what decides.
|
|
249
|
+
*/
|
|
250
|
+
export function hashWords(block, words) {
|
|
251
|
+
let hash = 0x9747b28c;
|
|
252
|
+
for (let word = 0; word < words; word++) {
|
|
253
|
+
let k = Math.imul(block[word], 0xcc9e2d51);
|
|
254
|
+
k = (k << 15) | (k >>> 17);
|
|
255
|
+
hash ^= Math.imul(k, 0x1b873593);
|
|
256
|
+
hash = (hash << 13) | (hash >>> 19);
|
|
257
|
+
hash = (Math.imul(hash, 5) + 0xe6546b64) | 0;
|
|
258
|
+
}
|
|
259
|
+
hash ^= words * 4;
|
|
260
|
+
hash ^= hash >>> 16;
|
|
261
|
+
hash = Math.imul(hash, 0x85ebca6b);
|
|
262
|
+
hash ^= hash >>> 13;
|
|
263
|
+
hash = Math.imul(hash, 0xc2b2ae35);
|
|
264
|
+
return hash ^ (hash >>> 16);
|
|
265
|
+
}
|
|
@@ -197,12 +197,17 @@ export class MotionPass {
|
|
|
197
197
|
const eight = offsets.joints2 >= 0 && offsets.weights2 >= 0;
|
|
198
198
|
/*
|
|
199
199
|
* Five byte counts, each under 256 because a vertex is at most 172 bytes, as five base-256
|
|
200
|
-
* digits: exact below 2^
|
|
200
|
+
* digits, and two bits for the weights' formats: exact below 2^42, so two layouts can never
|
|
201
|
+
* share a key.
|
|
201
202
|
*/
|
|
202
|
-
const key = (((stride * 256 + offsets.joints) * 256 + offsets.weights) * 256 + offsets.joints2 + 1) *
|
|
203
|
+
const key = ((((stride * 256 + offsets.joints) * 256 + offsets.weights) * 256 + offsets.joints2 + 1) *
|
|
203
204
|
256 +
|
|
204
205
|
offsets.weights2 +
|
|
205
|
-
1
|
|
206
|
+
1) *
|
|
207
|
+
4 +
|
|
208
|
+
/* And whether each four weights are floats or fixed point, two bits: `vertexPacking.ts`. */
|
|
209
|
+
(offsets.weightsFormat === 'float32x4' ? 0 : 2) +
|
|
210
|
+
(offsets.weights2Format === 'float32x4' ? 0 : 1);
|
|
206
211
|
const pipelines = cloth ? this.clothPipelines : this.skinnedPipelines;
|
|
207
212
|
const held = pipelines.get(key);
|
|
208
213
|
if (held !== undefined)
|
|
@@ -224,11 +229,15 @@ export class MotionPass {
|
|
|
224
229
|
attributes: [
|
|
225
230
|
{ shaderLocation: 0, offset: 0, format: 'float32x3' },
|
|
226
231
|
{ shaderLocation: 11, offset: offsets.joints, format: 'float32x4' },
|
|
227
|
-
{ shaderLocation: 12, offset: offsets.weights, format:
|
|
232
|
+
{ shaderLocation: 12, offset: offsets.weights, format: offsets.weightsFormat },
|
|
228
233
|
...(eight
|
|
229
234
|
? [
|
|
230
235
|
{ shaderLocation: 14, offset: offsets.joints2, format: 'float32x4' },
|
|
231
|
-
{
|
|
236
|
+
{
|
|
237
|
+
shaderLocation: 15,
|
|
238
|
+
offset: offsets.weights2,
|
|
239
|
+
format: offsets.weights2Format,
|
|
240
|
+
},
|
|
232
241
|
]
|
|
233
242
|
: []),
|
|
234
243
|
],
|
|
@@ -570,7 +579,7 @@ export class MotionPass {
|
|
|
570
579
|
}
|
|
571
580
|
pass.setBindGroup(0, this.group, [i * MOTION_DRAW_STRIDE]);
|
|
572
581
|
pass.setVertexBuffer(0, mesh.vertexBuffers[0]);
|
|
573
|
-
pass.setIndexBuffer(mesh.indexBuffer,
|
|
582
|
+
pass.setIndexBuffer(mesh.indexBuffer, mesh.indexFormat);
|
|
574
583
|
pass.drawIndexed(mesh.indexCount, instances);
|
|
575
584
|
}
|
|
576
585
|
}
|
|
@@ -52,6 +52,12 @@ export declare class PipelineCache {
|
|
|
52
52
|
readonly glassShadows: boolean;
|
|
53
53
|
/** How to describe each lit pipeline again, so `enable` can rebuild every one. */
|
|
54
54
|
private readonly litDescribers;
|
|
55
|
+
/**
|
|
56
|
+
* Bumped when a switch's rebuilt set lands, which replaces pipelines already handed out: a holder
|
|
57
|
+
* of one — a recorded render bundle — compares this and records again rather than drawing on
|
|
58
|
+
* with the pipelines from before the switch.
|
|
59
|
+
*/
|
|
60
|
+
version: number;
|
|
55
61
|
/** Bumped by every switch, so a lit build started before one is not kept after it. */
|
|
56
62
|
private litGeneration;
|
|
57
63
|
/** The last rebuild, which the next one waits for so they land in the order they were asked. */
|
|
@@ -73,6 +79,15 @@ export declare class PipelineCache {
|
|
|
73
79
|
* wait for readiness rather than discovering it mid-frame.
|
|
74
80
|
*/
|
|
75
81
|
private readonly compiling;
|
|
82
|
+
/** Keys whose compile off the main thread failed, which `lit` then builds where asked for. */
|
|
83
|
+
private readonly failed;
|
|
84
|
+
/**
|
|
85
|
+
* Whether a lit pipeline first asked for inside a frame is compiled off the main thread while
|
|
86
|
+
* its draw is left out — `RenderQuality.pipelineCompile` `'skip'` — rather than built where it is
|
|
87
|
+
* asked for, which holds the GPU process for the compile. The renderer turns it off while it
|
|
88
|
+
* records static draws, which must hold every draw they were given.
|
|
89
|
+
*/
|
|
90
|
+
skipsCompiling: boolean;
|
|
76
91
|
constructor(device: GPUDevice, format: GPUTextureFormat, sampleCount?: number, glassShadows?: boolean,
|
|
77
92
|
/** The profile's `clusteredLights`, which compiles the clustered arm in or leaves it out. */
|
|
78
93
|
clusteredLights?: boolean);
|
|
@@ -90,7 +105,8 @@ export declare class PipelineCache {
|
|
|
90
105
|
/** Whether this is a lit pipeline, which `enable` rebuilds. See `litSwitches`. */
|
|
91
106
|
lit?: boolean): Promise<GPURenderPipeline>;
|
|
92
107
|
/**
|
|
93
|
-
* Resolve once every pipeline asked for so far has finished compiling
|
|
108
|
+
* Resolve once every pipeline asked for so far has finished compiling, a switch's rebuild
|
|
109
|
+
* among them.
|
|
94
110
|
*
|
|
95
111
|
* A renderer awaits this before its first frame, which is what moves the compile out of the
|
|
96
112
|
* queue and in front of it. A failure is not rethrown: a pipeline that cannot be built will
|
|
@@ -112,6 +128,17 @@ export declare class PipelineCache {
|
|
|
112
128
|
get(key: string, describe: () => GPURenderPipelineDescriptor,
|
|
113
129
|
/** Whether this is a lit pipeline, which `enable` rebuilds. See `litSwitches`. */
|
|
114
130
|
lit?: boolean): GPURenderPipeline;
|
|
131
|
+
/**
|
|
132
|
+
* A lit pipeline for a draw inside a frame: the one built, or one built here — or, where draws
|
|
133
|
+
* skip (`skipsCompiling`), none, with its compile started off the main thread for a later frame to
|
|
134
|
+
* find. A draw handed none is left out.
|
|
135
|
+
*
|
|
136
|
+
* **What skipping gives up** is the draw for the frames its compile takes, a few to a few dozen on
|
|
137
|
+
* a phone, against a frame held for the compile where it is asked for: 130 to 400 ms, reported on
|
|
138
|
+
* a phone for a material first drawn mid-scene. A compile that fails is built here the next time,
|
|
139
|
+
* so the device reports it rather than the draw vanishing for good.
|
|
140
|
+
*/
|
|
141
|
+
lit(key: string, describe: () => GPURenderPipelineDescriptor): GPURenderPipeline | null;
|
|
115
142
|
/**
|
|
116
143
|
* Turn one of the lit stage's switches on, for every lit pipeline built from now on and every one
|
|
117
144
|
* already built. See `litSwitches`.
|
|
@@ -51,6 +51,12 @@ export class PipelineCache {
|
|
|
51
51
|
glassShadows;
|
|
52
52
|
/** How to describe each lit pipeline again, so `enable` can rebuild every one. */
|
|
53
53
|
litDescribers = new Map();
|
|
54
|
+
/**
|
|
55
|
+
* Bumped when a switch's rebuilt set lands, which replaces pipelines already handed out: a holder
|
|
56
|
+
* of one — a recorded render bundle — compares this and records again rather than drawing on
|
|
57
|
+
* with the pipelines from before the switch.
|
|
58
|
+
*/
|
|
59
|
+
version = 0;
|
|
54
60
|
/** Bumped by every switch, so a lit build started before one is not kept after it. */
|
|
55
61
|
litGeneration = 0;
|
|
56
62
|
/** The last rebuild, which the next one waits for so they land in the order they were asked. */
|
|
@@ -72,6 +78,15 @@ export class PipelineCache {
|
|
|
72
78
|
* wait for readiness rather than discovering it mid-frame.
|
|
73
79
|
*/
|
|
74
80
|
compiling = new Map();
|
|
81
|
+
/** Keys whose compile off the main thread failed, which `lit` then builds where asked for. */
|
|
82
|
+
failed = new Set();
|
|
83
|
+
/**
|
|
84
|
+
* Whether a lit pipeline first asked for inside a frame is compiled off the main thread while
|
|
85
|
+
* its draw is left out — `RenderQuality.pipelineCompile` `'skip'` — rather than built where it is
|
|
86
|
+
* asked for, which holds the GPU process for the compile. The renderer turns it off while it
|
|
87
|
+
* records static draws, which must hold every draw they were given.
|
|
88
|
+
*/
|
|
89
|
+
skipsCompiling = false;
|
|
75
90
|
constructor(device, format, sampleCount = 1, glassShadows = true,
|
|
76
91
|
/** The profile's `clusteredLights`, which compiles the clustered arm in or leaves it out. */
|
|
77
92
|
clusteredLights = false) {
|
|
@@ -121,12 +136,19 @@ export class PipelineCache {
|
|
|
121
136
|
this.pipelines.set(key, built);
|
|
122
137
|
this.compiling.delete(key);
|
|
123
138
|
return built;
|
|
139
|
+
},
|
|
140
|
+
/* Out of the list, or `ready` waits on it for ever; `lit` builds it where asked next. */
|
|
141
|
+
(error) => {
|
|
142
|
+
this.compiling.delete(key);
|
|
143
|
+
this.failed.add(key);
|
|
144
|
+
throw error;
|
|
124
145
|
});
|
|
125
146
|
this.compiling.set(key, promise);
|
|
126
147
|
return promise;
|
|
127
148
|
}
|
|
128
149
|
/**
|
|
129
|
-
* Resolve once every pipeline asked for so far has finished compiling
|
|
150
|
+
* Resolve once every pipeline asked for so far has finished compiling, a switch's rebuild
|
|
151
|
+
* among them.
|
|
130
152
|
*
|
|
131
153
|
* A renderer awaits this before its first frame, which is what moves the compile out of the
|
|
132
154
|
* queue and in front of it. A failure is not rethrown: a pipeline that cannot be built will
|
|
@@ -137,6 +159,8 @@ export class PipelineCache {
|
|
|
137
159
|
while (this.compiling.size > 0) {
|
|
138
160
|
await Promise.allSettled([...this.compiling.values()]);
|
|
139
161
|
}
|
|
162
|
+
/* And a switch's rebuild, whose set replaces the pipelines in one swap: see `enable`. */
|
|
163
|
+
await this.litRebuild;
|
|
140
164
|
}
|
|
141
165
|
/** The pipeline for this key if it is already built, without building one. */
|
|
142
166
|
peek(key) {
|
|
@@ -165,6 +189,26 @@ export class PipelineCache {
|
|
|
165
189
|
this.pipelines.set(key, built);
|
|
166
190
|
return built;
|
|
167
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* A lit pipeline for a draw inside a frame: the one built, or one built here — or, where draws
|
|
194
|
+
* skip (`skipsCompiling`), none, with its compile started off the main thread for a later frame to
|
|
195
|
+
* find. A draw handed none is left out.
|
|
196
|
+
*
|
|
197
|
+
* **What skipping gives up** is the draw for the frames its compile takes, a few to a few dozen on
|
|
198
|
+
* a phone, against a frame held for the compile where it is asked for: 130 to 400 ms, reported on
|
|
199
|
+
* a phone for a material first drawn mid-scene. A compile that fails is built here the next time,
|
|
200
|
+
* so the device reports it rather than the draw vanishing for good.
|
|
201
|
+
*/
|
|
202
|
+
lit(key, describe) {
|
|
203
|
+
const existing = this.pipelines.get(key);
|
|
204
|
+
if (existing !== undefined)
|
|
205
|
+
return existing;
|
|
206
|
+
if (!this.skipsCompiling || this.failed.has(key))
|
|
207
|
+
return this.get(key, describe, true);
|
|
208
|
+
if (!this.compiling.has(key))
|
|
209
|
+
void this.getAsync(key, describe, true).catch(() => { });
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
168
212
|
/**
|
|
169
213
|
* Turn one of the lit stage's switches on, for every lit pipeline built from now on and every one
|
|
170
214
|
* already built. See `litSwitches`.
|
|
@@ -201,6 +245,7 @@ export class PipelineCache {
|
|
|
201
245
|
if (result.status === 'fulfilled')
|
|
202
246
|
this.pipelines.set(result.value[0], result.value[1]);
|
|
203
247
|
}
|
|
248
|
+
this.version += 1;
|
|
204
249
|
});
|
|
205
250
|
return this.litRebuild;
|
|
206
251
|
}
|
|
@@ -75,7 +75,7 @@ export declare const AO_FRAG_SIZE: 96;
|
|
|
75
75
|
export declare const AO_BLUR_FIELDS: UniformFields;
|
|
76
76
|
export declare const AO_BLUR_SIZE: 48;
|
|
77
77
|
export declare const MEDIUM_FRAG_FIELDS: UniformFields;
|
|
78
|
-
export declare const MEDIUM_FRAG_SIZE:
|
|
78
|
+
export declare const MEDIUM_FRAG_SIZE: 304;
|
|
79
79
|
export declare const MEDIUM_UPSAMPLE_FIELDS: UniformFields;
|
|
80
80
|
export declare const MEDIUM_UPSAMPLE_SIZE: 32;
|
|
81
81
|
export declare const BLOOM_PREFILTER_FIELDS: UniformFields;
|