@woosh/meep-engine 3.32.0 → 3.33.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/build/bundle-worker-terrain.js +1 -1
- package/package.json +1 -1
- package/src/core/collection/table/RowFirstTable.d.ts +8 -0
- package/src/core/collection/table/RowFirstTable.d.ts.map +1 -1
- package/src/core/collection/table/RowFirstTable.js +40 -11
- package/src/engine/graphics/ecs/sprite/Sprite.d.ts +3 -0
- package/src/engine/graphics/ecs/sprite/Sprite.d.ts.map +1 -1
- package/src/engine/graphics/ecs/sprite/Sprite.js +3 -0
- package/src/engine/graphics/ecs/sprite/SpriteSystemPE.d.ts +3 -0
- package/src/engine/graphics/ecs/sprite/SpriteSystemPE.d.ts.map +1 -1
- package/src/engine/graphics/ecs/sprite/SpriteSystemPE.js +3 -0
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/shade/device/mock/SoftwareGPURenderPassEncoder.d.ts +20 -3
- package/src/shade/device/mock/SoftwareGPURenderPassEncoder.d.ts.map +1 -1
- package/src/shade/device/mock/SoftwareGPURenderPassEncoder.js +21 -3
- package/src/shade/renderer/GraphicsContext.d.ts +11 -0
- package/src/shade/renderer/GraphicsContext.d.ts.map +1 -1
- package/src/shade/renderer/GraphicsContext.js +52 -38
- package/src/shade/renderer/buffer/GPUBufferReadbackAllocator.d.ts +6 -5
- package/src/shade/renderer/buffer/GPUBufferReadbackAllocator.d.ts.map +1 -1
- package/src/shade/renderer/buffer/GPUBufferReadbackAllocator.js +241 -240
- package/src/shade/renderer/buffer/GPUBufferStagingAllocator.d.ts +51 -0
- package/src/shade/renderer/buffer/GPUBufferStagingAllocator.d.ts.map +1 -1
- package/src/shade/renderer/buffer/GPUBufferStagingAllocator.js +214 -28
- package/src/shade/renderer/dynamic/DynamicMeshAVBOITChannel.d.ts +2 -1
- package/src/shade/renderer/dynamic/DynamicMeshAVBOITChannel.d.ts.map +1 -1
- package/src/shade/renderer/dynamic/DynamicMeshAVBOITChannel.js +19 -17
- package/src/shade/renderer/dynamic/GPUDynamicMeshDrawList.d.ts +70 -0
- package/src/shade/renderer/dynamic/GPUDynamicMeshDrawList.d.ts.map +1 -0
- package/src/shade/renderer/dynamic/GPUDynamicMeshDrawList.js +257 -0
- package/src/shade/renderer/dynamic/GPUDynamicMeshRenderer.d.ts.map +1 -1
- package/src/shade/renderer/dynamic/GPUDynamicMeshRenderer.js +19 -34
- package/src/shade/renderer/dynamic/GPUDynamicMeshResidency.d.ts +6 -20
- package/src/shade/renderer/dynamic/GPUDynamicMeshResidency.d.ts.map +1 -1
- package/src/shade/renderer/dynamic/GPUDynamicMeshResidency.js +42 -36
- package/src/shade/renderer/dynamic/chunk_dynamic_mesh_avboit_vertex.d.ts +2 -1
- package/src/shade/renderer/dynamic/chunk_dynamic_mesh_avboit_vertex.d.ts.map +1 -1
- package/src/shade/renderer/dynamic/chunk_dynamic_mesh_avboit_vertex.js +4 -3
- package/src/shade/renderer/dynamic/chunk_dynamic_mesh_vertex.d.ts +4 -3
- package/src/shade/renderer/dynamic/chunk_dynamic_mesh_vertex.d.ts.map +1 -1
- package/src/shade/renderer/dynamic/chunk_dynamic_mesh_vertex.js +7 -4
- package/src/shade/renderer/dynamic/graph_dynamic_mesh_avboit.d.ts +14 -15
- package/src/shade/renderer/dynamic/graph_dynamic_mesh_avboit.d.ts.map +1 -1
- package/src/shade/renderer/dynamic/graph_dynamic_mesh_avboit.js +49 -83
- package/src/shade/renderer/dynamic/shader_dynamic_mesh.d.ts +19 -3
- package/src/shade/renderer/dynamic/shader_dynamic_mesh.d.ts.map +1 -1
- package/src/shade/renderer/dynamic/shader_dynamic_mesh.js +26 -6
- package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_draw.d.ts +2 -1
- package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_draw.d.ts.map +1 -1
- package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_draw.js +12 -6
- package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_splat.d.ts +3 -2
- package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_splat.d.ts.map +1 -1
- package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_splat.js +21 -13
- package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
- package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_LEDGER.md +20 -0
- package/src/shade/renderer/shader/type/serde/write_gpu_typed_buffer.d.ts.map +1 -1
- package/src/shade/renderer/shader/type/serde/write_gpu_typed_buffer.js +8 -1
|
@@ -1,240 +1,241 @@
|
|
|
1
|
-
import { assert } from "../../../core/assert.js";
|
|
2
|
-
import { binarySearchHighIndex } from "../../../core/collection/array/binarySearchHighIndex.js";
|
|
3
|
-
import { round_up } from "../../../core/math/round_up.js";
|
|
4
|
-
import { MEGABYTE } from "../../../core/science/units/memory/MEGABYTE.js";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Sizes are rounded up to this when a buffer is created, so that batches whose byte count breathes
|
|
8
|
-
* by a few rows keep landing in the same buffer instead of each size making a new one.
|
|
9
|
-
*
|
|
10
|
-
* @type {number}
|
|
11
|
-
*/
|
|
12
|
-
const SIZE_GRANULARITY = 256;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* How many bytes of *free* readback buffers are kept. Buffers handed out are the holder's to bound;
|
|
16
|
-
* this bounds what an idle pool pins after a spike — one 50k-row batch is ~3 MB, and without a cap
|
|
17
|
-
* it would stay for the life of the process, which is what the ring this replaced did.
|
|
18
|
-
*
|
|
19
|
-
* @type {number}
|
|
20
|
-
*/
|
|
21
|
-
const DEFAULT_BUDGET_BYTES = 16 * MEGABYTE;
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* @type {number}
|
|
25
|
-
*/
|
|
26
|
-
let id_count = 0;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* @param {{size: number}} a
|
|
30
|
-
* @param {{size: number}} b
|
|
31
|
-
* @returns {number}
|
|
32
|
-
*/
|
|
33
|
-
function by_size(a, b) {
|
|
34
|
-
return a.size - b.size;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Persistent `MAP_READ | COPY_DST` buffers for reading GPU data back — {@link GPUBufferStagingAllocator}
|
|
39
|
-
* run the other way.
|
|
40
|
-
*
|
|
41
|
-
* The same shape
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
*
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
*
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
* @param {
|
|
87
|
-
* @param {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
assert.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
this.#
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
*
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
*
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
*
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
*
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
* @
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
assert.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
usage
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
assert.
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
assert.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
*
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
}
|
|
1
|
+
import { assert } from "../../../core/assert.js";
|
|
2
|
+
import { binarySearchHighIndex } from "../../../core/collection/array/binarySearchHighIndex.js";
|
|
3
|
+
import { round_up } from "../../../core/math/round_up.js";
|
|
4
|
+
import { MEGABYTE } from "../../../core/science/units/memory/MEGABYTE.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Sizes are rounded up to this when a buffer is created, so that batches whose byte count breathes
|
|
8
|
+
* by a few rows keep landing in the same buffer instead of each size making a new one.
|
|
9
|
+
*
|
|
10
|
+
* @type {number}
|
|
11
|
+
*/
|
|
12
|
+
const SIZE_GRANULARITY = 256;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* How many bytes of *free* readback buffers are kept. Buffers handed out are the holder's to bound;
|
|
16
|
+
* this bounds what an idle pool pins after a spike — one 50k-row batch is ~3 MB, and without a cap
|
|
17
|
+
* it would stay for the life of the process, which is what the ring this replaced did.
|
|
18
|
+
*
|
|
19
|
+
* @type {number}
|
|
20
|
+
*/
|
|
21
|
+
const DEFAULT_BUDGET_BYTES = 16 * MEGABYTE;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @type {number}
|
|
25
|
+
*/
|
|
26
|
+
let id_count = 0;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {{size: number}} a
|
|
30
|
+
* @param {{size: number}} b
|
|
31
|
+
* @returns {number}
|
|
32
|
+
*/
|
|
33
|
+
function by_size(a, b) {
|
|
34
|
+
return a.size - b.size;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Persistent `MAP_READ | COPY_DST` buffers for reading GPU data back — {@link GPUBufferStagingAllocator}
|
|
39
|
+
* run the other way.
|
|
40
|
+
*
|
|
41
|
+
* The same shape: a size-sorted free list, `get(min_size)` serving the smallest buffer that fits
|
|
42
|
+
* and creating one when none does, `release(buffer)` putting it back. Where the staging pool re-maps
|
|
43
|
+
* a buffer on release because the next writer needs it mapped, this one *unmaps*, because the next
|
|
44
|
+
* copy into it needs it unmapped; the mapping is the holder's, taken after the copy has landed and
|
|
45
|
+
* read at the holder's own apply point. Unlike the staging pool, this one has no bound on how much
|
|
46
|
+
* larger than the request a buffer it serves may be, no size classes and no reclaim.
|
|
47
|
+
*
|
|
48
|
+
* Why not `buffer_allocator_main`: `TransientResourcePool`'s reuse safety rests on transients being
|
|
49
|
+
* GPU-only, and a readback buffer is held by the CPU across frames.
|
|
50
|
+
*
|
|
51
|
+
* Why not a ring: a ring hands out `null` when its slots are busy and leaves the caller to defer a
|
|
52
|
+
* whole frame's work, and growing a slot destroys a perfectly good smaller buffer. A free list makes
|
|
53
|
+
* another buffer and keeps both sizes.
|
|
54
|
+
*
|
|
55
|
+
* @see GPUDatabaseReadback the holder
|
|
56
|
+
*/
|
|
57
|
+
export class GPUBufferReadbackAllocator {
|
|
58
|
+
/**
|
|
59
|
+
* @type {GPUDevice}
|
|
60
|
+
*/
|
|
61
|
+
#device;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Free buffers, ascending by size.
|
|
65
|
+
* @type {GPUBuffer[]}
|
|
66
|
+
*/
|
|
67
|
+
#ready = [];
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @type {number}
|
|
71
|
+
*/
|
|
72
|
+
#ready_byte_size = 0;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Bytes in buffers handed out and not yet given back.
|
|
76
|
+
* @type {number}
|
|
77
|
+
*/
|
|
78
|
+
#held_byte_size = 0;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @type {number}
|
|
82
|
+
*/
|
|
83
|
+
#budget_bytes;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {GPUDevice} device
|
|
87
|
+
* @param {Object} [options]
|
|
88
|
+
* @param {number} [options.budget_bytes] free bytes kept before the largest free buffers are destroyed
|
|
89
|
+
*/
|
|
90
|
+
constructor(device, { budget_bytes = DEFAULT_BUDGET_BYTES } = {}) {
|
|
91
|
+
assert.defined(device, 'device');
|
|
92
|
+
assert.isNonNegativeInteger(budget_bytes, 'budget_bytes');
|
|
93
|
+
|
|
94
|
+
this.#device = device;
|
|
95
|
+
this.#budget_bytes = budget_bytes;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Bytes in every buffer this pool has made and not destroyed, held or free.
|
|
100
|
+
* @returns {number}
|
|
101
|
+
*/
|
|
102
|
+
get gpu_memory_usage() {
|
|
103
|
+
return this.#ready_byte_size + this.#held_byte_size;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Bytes sitting in the free list.
|
|
108
|
+
* @returns {number}
|
|
109
|
+
*/
|
|
110
|
+
get ready_byte_size() {
|
|
111
|
+
return this.#ready_byte_size;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Bytes in buffers currently handed out.
|
|
116
|
+
* @returns {number}
|
|
117
|
+
*/
|
|
118
|
+
get held_byte_size() {
|
|
119
|
+
return this.#held_byte_size;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Free buffers.
|
|
124
|
+
* @returns {number}
|
|
125
|
+
*/
|
|
126
|
+
get ready_count() {
|
|
127
|
+
return this.#ready.length;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The smallest free buffer of at least `min_size_bytes`, or a new one. **Unmapped**, ready to be
|
|
132
|
+
* the destination of a copy. Contents are whatever the last use left.
|
|
133
|
+
*
|
|
134
|
+
* @param {number} min_size_bytes
|
|
135
|
+
* @returns {GPUBuffer}
|
|
136
|
+
*/
|
|
137
|
+
get(min_size_bytes) {
|
|
138
|
+
assert.isNonNegativeInteger(min_size_bytes, 'min_size_bytes');
|
|
139
|
+
assert.greaterThan(min_size_bytes, 0, 'min_size_bytes');
|
|
140
|
+
|
|
141
|
+
const ready = this.#ready;
|
|
142
|
+
const index = binarySearchHighIndex(ready, { size: min_size_bytes }, by_size);
|
|
143
|
+
|
|
144
|
+
let result;
|
|
145
|
+
|
|
146
|
+
if (index < ready.length) {
|
|
147
|
+
result = ready[index];
|
|
148
|
+
|
|
149
|
+
ready.splice(index, 1);
|
|
150
|
+
|
|
151
|
+
this.#ready_byte_size -= result.size;
|
|
152
|
+
} else {
|
|
153
|
+
result = this.#device.createBuffer({
|
|
154
|
+
label: `readback ${id_count++}`,
|
|
155
|
+
size: round_up(min_size_bytes, SIZE_GRANULARITY),
|
|
156
|
+
// the only usage pair WebGPU permits alongside MAP_READ
|
|
157
|
+
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
|
|
158
|
+
mappedAtCreation: false,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
assert.greaterThanOrEqual(result.size, min_size_bytes, 'result.size < min_size_bytes');
|
|
163
|
+
assert.equal(result.mapState, "unmapped", 'result.mapState !== "unmapped"');
|
|
164
|
+
|
|
165
|
+
this.#held_byte_size += result.size;
|
|
166
|
+
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Give a buffer back. A mapped buffer is unmapped here; a buffer whose mapping is still pending
|
|
172
|
+
* cannot be given back — let the mapping settle, or {@link discard} it.
|
|
173
|
+
*
|
|
174
|
+
* @param {GPUBuffer} buffer
|
|
175
|
+
*/
|
|
176
|
+
release(buffer) {
|
|
177
|
+
assert.defined(buffer, 'buffer');
|
|
178
|
+
assert.equal(buffer.usage, GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST, 'buffer.usage !== GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST');
|
|
179
|
+
|
|
180
|
+
const state = buffer.mapState;
|
|
181
|
+
|
|
182
|
+
if (state === "mapped") {
|
|
183
|
+
buffer.unmap();
|
|
184
|
+
} else if (state !== "unmapped") {
|
|
185
|
+
throw new Error(`Invalid map state: ${state}`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
this.#held_byte_size -= buffer.size;
|
|
189
|
+
|
|
190
|
+
const ready = this.#ready;
|
|
191
|
+
const index = binarySearchHighIndex(ready, buffer, by_size);
|
|
192
|
+
|
|
193
|
+
ready.splice(index, 0, buffer);
|
|
194
|
+
|
|
195
|
+
this.#ready_byte_size += buffer.size;
|
|
196
|
+
|
|
197
|
+
this.#trim();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Destroy a buffer that was handed out, instead of giving it back — for a holder being torn down
|
|
202
|
+
* while a mapping is still pending, which WebGPU lets `destroy()` abort but not `unmap()`.
|
|
203
|
+
*
|
|
204
|
+
* @param {GPUBuffer} buffer
|
|
205
|
+
*/
|
|
206
|
+
discard(buffer) {
|
|
207
|
+
assert.defined(buffer, 'buffer');
|
|
208
|
+
|
|
209
|
+
this.#held_byte_size -= buffer.size;
|
|
210
|
+
|
|
211
|
+
buffer.destroy();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Keep the free list under budget, dropping the largest buffers first: they cost the most, and a
|
|
216
|
+
* spike is the least likely size to be asked for again.
|
|
217
|
+
*/
|
|
218
|
+
#trim() {
|
|
219
|
+
const ready = this.#ready;
|
|
220
|
+
|
|
221
|
+
while (this.#ready_byte_size > this.#budget_bytes && ready.length > 0) {
|
|
222
|
+
const largest = ready.pop();
|
|
223
|
+
|
|
224
|
+
this.#ready_byte_size -= largest.size;
|
|
225
|
+
|
|
226
|
+
largest.destroy();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
destroy() {
|
|
231
|
+
const ready = this.#ready;
|
|
232
|
+
|
|
233
|
+
for (let i = 0; i < ready.length; i++) {
|
|
234
|
+
ready[i].destroy();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
ready.length = 0;
|
|
238
|
+
|
|
239
|
+
this.#ready_byte_size = 0;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
@@ -6,6 +6,13 @@
|
|
|
6
6
|
* that is the cheaper road; for thousands of small writes a frame the round trips are the cost, which
|
|
7
7
|
* is why a {@link ShadeGPUCommandContext} brings only its large writes here (see
|
|
8
8
|
* `MAPPED_UPLOAD_THRESHOLD`).
|
|
9
|
+
*
|
|
10
|
+
* A buffer is made up to a quarter larger than asked, so a size that drifts keeps its buffer, and is
|
|
11
|
+
* handed out again only to requests at least half its size ({@link REUSE_LIMIT_SCALE}), so a burst's
|
|
12
|
+
* large buffers do not end up mapped whole for every smaller write after it. Of buffers of one size
|
|
13
|
+
* the one given back last goes out first, so a steady writer keeps one and the rest go idle. What
|
|
14
|
+
* sits idle is destroyed by {@link update}, as {@link TransientResourcePool#update} does for the
|
|
15
|
+
* frame's transients: without that, a burst's buffers stayed until the device went.
|
|
9
16
|
*/
|
|
10
17
|
export class GPUBufferStagingAllocator {
|
|
11
18
|
/**
|
|
@@ -13,6 +20,38 @@ export class GPUBufferStagingAllocator {
|
|
|
13
20
|
* @param {GPUDevice} device
|
|
14
21
|
*/
|
|
15
22
|
constructor(device: GPUDevice);
|
|
23
|
+
/**
|
|
24
|
+
* Idle bytes above which buffers idle for {@link param_over_budget_idle_frames} are destroyed.
|
|
25
|
+
* The same as the upload blocks' budget.
|
|
26
|
+
*
|
|
27
|
+
* @type {number}
|
|
28
|
+
*/
|
|
29
|
+
param_soft_byte_budget: number;
|
|
30
|
+
/**
|
|
31
|
+
* Frames a buffer may sit idle while the pool is over its budget: long enough that a write made
|
|
32
|
+
* every few frames keeps its buffer, short enough that a burst's go within a second or so.
|
|
33
|
+
*
|
|
34
|
+
* @type {number}
|
|
35
|
+
*/
|
|
36
|
+
param_over_budget_idle_frames: number;
|
|
37
|
+
/**
|
|
38
|
+
* An idle buffer is destroyed after this many frames even with the pool under its budget.
|
|
39
|
+
*
|
|
40
|
+
* @type {number}
|
|
41
|
+
*/
|
|
42
|
+
param_backstop_frames: number;
|
|
43
|
+
/**
|
|
44
|
+
* Most buffers destroyed by one {@link update}.
|
|
45
|
+
*
|
|
46
|
+
* @type {number}
|
|
47
|
+
*/
|
|
48
|
+
param_max_reclaim_per_frame: number;
|
|
49
|
+
/**
|
|
50
|
+
* Most idle buffers one {@link update} looks at.
|
|
51
|
+
*
|
|
52
|
+
* @type {number}
|
|
53
|
+
*/
|
|
54
|
+
param_scan_budget: number;
|
|
16
55
|
/**
|
|
17
56
|
* Bytes in every buffer this pool has made and not destroyed: free, handed out, or on the way
|
|
18
57
|
* back.
|
|
@@ -33,6 +72,18 @@ export class GPUBufferStagingAllocator {
|
|
|
33
72
|
* this pool did not hand out
|
|
34
73
|
*/
|
|
35
74
|
release(buffer: GPUBuffer): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Advance the pool's frame clock; once a frame, with the other pools'.
|
|
77
|
+
*/
|
|
78
|
+
increment_time(): void;
|
|
79
|
+
/**
|
|
80
|
+
* Once-a-frame housekeeping: destroy the buffers nothing has asked for in a while — idle past
|
|
81
|
+
* {@link param_backstop_frames}, or, while more than {@link param_soft_byte_budget} bytes sit
|
|
82
|
+
* idle, past {@link param_over_budget_idle_frames}. A bounded sweep from where the last one
|
|
83
|
+
* stopped, at most {@link param_scan_budget} buffers looked at and
|
|
84
|
+
* {@link param_max_reclaim_per_frame} destroyed, as {@link TransientResourcePool#update}.
|
|
85
|
+
*/
|
|
86
|
+
update(): void;
|
|
36
87
|
destroy(): void;
|
|
37
88
|
#private;
|
|
38
89
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GPUBufferStagingAllocator.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/buffer/GPUBufferStagingAllocator.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"GPUBufferStagingAllocator.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/buffer/GPUBufferStagingAllocator.js"],"names":[],"mappings":"AAwFA;;;;;;;;;;;;;;;GAeG;AACH;IA0GI;;;OAGG;IACH,+BAIC;IA9GD;;;;;OAKG;IACH,wBAFU,MAAM,CAEuB;IAEvC;;;;;OAKG;IACH,+BAFU,MAAM,CAEmB;IAEnC;;;;OAIG;IACH,uBAFU,MAAM,CAEY;IAE5B;;;;OAIG;IACH,6BAFU,MAAM,CAEgB;IAEhC;;;;OAIG;IACH,mBAFU,MAAM,CAEO;IA6EvB;;;;;OAKG;IACH,+BAEC;IAGD;;;;OAIG;IACH,oBAHW,MAAM,aA2ChB;IAsBD;;;;;OAKG;IACH,4BAHa,OAAO,CAwBnB;IAkCD;;OAEG;IACH,uBAEC;IAED;;;;;;OAMG;IACH,eA+BC;IAED,gBAUC;;CACJ"}
|