@woosh/meep-engine 3.10.0 → 3.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/README.md +168 -152
- package/package.json +1 -1
- package/src/REVIEW_2026_08_06.md +610 -610
- package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/ConvexHullShape3D.js +28 -4
- package/src/engine/graphics/render/frame_graph/FrameGraph.d.ts +26 -0
- package/src/engine/graphics/render/frame_graph/FrameGraph.d.ts.map +1 -1
- package/src/engine/graphics/render/frame_graph/FrameGraph.js +47 -0
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +0 -3
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +0 -3
- package/src/shade/device/ShadeGPUCommandContext.d.ts +21 -0
- package/src/shade/device/ShadeGPUCommandContext.d.ts.map +1 -1
- package/src/shade/device/ShadeGPUCommandContext.js +940 -749
- package/src/shade/device/timing/GPUTimerArray.d.ts +22 -4
- package/src/shade/device/timing/GPUTimerArray.d.ts.map +1 -1
- package/src/shade/device/timing/GPUTimerArray.js +88 -14
- package/src/shade/device/timing/GPU_PROFILER_PROPOSAL_2026_08_28.md +349 -98
- package/src/shade/device/timing/profile/GPUFrameRecorder.d.ts +93 -0
- package/src/shade/device/timing/profile/GPUFrameRecorder.d.ts.map +1 -0
- package/src/shade/device/timing/profile/GPUFrameRecorder.js +297 -0
- package/src/shade/device/timing/profile/GPUProfileCapture.d.ts +62 -0
- package/src/shade/device/timing/profile/GPUProfileCapture.d.ts.map +1 -0
- package/src/shade/device/timing/profile/GPUProfileCapture.js +77 -0
- package/src/shade/device/timing/profile/GPUProfileFrame.d.ts +73 -0
- package/src/shade/device/timing/profile/GPUProfileFrame.d.ts.map +1 -0
- package/src/shade/device/timing/profile/GPUProfileFrame.js +88 -0
- package/src/shade/device/timing/profile/GPUProfileLevel.d.ts +20 -0
- package/src/shade/device/timing/profile/GPUProfileLevel.d.ts.map +1 -0
- package/src/shade/device/timing/profile/GPUProfileLevel.js +50 -0
- package/src/shade/device/timing/profile/GPUProfileMeta.d.ts +63 -0
- package/src/shade/device/timing/profile/GPUProfileMeta.d.ts.map +1 -0
- package/src/shade/device/timing/profile/GPUProfileMeta.js +73 -0
- package/src/shade/device/timing/profile/GPUProfileSession.d.ts +173 -0
- package/src/shade/device/timing/profile/GPUProfileSession.d.ts.map +1 -0
- package/src/shade/device/timing/profile/GPUProfileSession.js +470 -0
- package/src/shade/device/timing/profile/GPUProfileSpan.d.ts +60 -0
- package/src/shade/device/timing/profile/GPUProfileSpan.d.ts.map +1 -0
- package/src/shade/device/timing/profile/GPUProfileSpan.js +65 -0
- package/src/shade/device/timing/profile/GPUProfileSpanKind.d.ts +19 -0
- package/src/shade/device/timing/profile/GPUProfileSpanKind.d.ts.map +1 -0
- package/src/shade/device/timing/profile/GPUProfileSpanKind.js +25 -0
- package/src/shade/device/timing/profile/GPUProfileTopology.d.ts +159 -0
- package/src/shade/device/timing/profile/GPUProfileTopology.d.ts.map +1 -0
- package/src/shade/device/timing/profile/GPUProfileTopology.js +202 -0
- package/src/shade/device/timing/profile/GPUProfileWork.d.ts +90 -0
- package/src/shade/device/timing/profile/GPUProfileWork.d.ts.map +1 -0
- package/src/shade/device/timing/profile/GPUProfileWork.js +115 -0
- package/src/shade/device/timing/profile/SGPTDefect.d.ts +43 -0
- package/src/shade/device/timing/profile/SGPTDefect.d.ts.map +1 -0
- package/src/shade/device/timing/profile/SGPTDefect.js +65 -0
- package/src/shade/device/timing/profile/SGPTHeader.d.ts +38 -0
- package/src/shade/device/timing/profile/SGPTHeader.d.ts.map +1 -0
- package/src/shade/device/timing/profile/SGPTHeader.js +46 -0
- package/src/shade/device/timing/profile/SGPTRecordRef.d.ts +37 -0
- package/src/shade/device/timing/profile/SGPTRecordRef.d.ts.map +1 -0
- package/src/shade/device/timing/profile/SGPTRecordRef.js +45 -0
- package/src/shade/device/timing/profile/SGPTWriteStream.d.ts +103 -0
- package/src/shade/device/timing/profile/SGPTWriteStream.d.ts.map +1 -0
- package/src/shade/device/timing/profile/SGPTWriteStream.js +287 -0
- package/src/shade/device/timing/profile/SGPT_DEFECT.d.ts +19 -0
- package/src/shade/device/timing/profile/SGPT_DEFECT.d.ts.map +1 -0
- package/src/shade/device/timing/profile/SGPT_DEFECT.js +26 -0
- package/src/shade/device/timing/profile/SGPT_FLAG.d.ts +8 -0
- package/src/shade/device/timing/profile/SGPT_FLAG.d.ts.map +1 -0
- package/src/shade/device/timing/profile/SGPT_FLAG.js +15 -0
- package/src/shade/device/timing/profile/SGPT_FORMAT.md +345 -0
- package/src/shade/device/timing/profile/SGPT_FORMAT_VERSION.d.ts +10 -0
- package/src/shade/device/timing/profile/SGPT_FORMAT_VERSION.d.ts.map +1 -0
- package/src/shade/device/timing/profile/SGPT_FORMAT_VERSION.js +9 -0
- package/src/shade/device/timing/profile/SGPT_FRAME_SLACK.d.ts +17 -0
- package/src/shade/device/timing/profile/SGPT_FRAME_SLACK.d.ts.map +1 -0
- package/src/shade/device/timing/profile/SGPT_FRAME_SLACK.js +16 -0
- package/src/shade/device/timing/profile/SGPT_HEADER_CHECKSUM_COVERAGE.d.ts +16 -0
- package/src/shade/device/timing/profile/SGPT_HEADER_CHECKSUM_COVERAGE.d.ts.map +1 -0
- package/src/shade/device/timing/profile/SGPT_HEADER_CHECKSUM_COVERAGE.js +15 -0
- package/src/shade/device/timing/profile/SGPT_HEADER_OFFSET.d.ts +15 -0
- package/src/shade/device/timing/profile/SGPT_HEADER_OFFSET.d.ts.map +1 -0
- package/src/shade/device/timing/profile/SGPT_HEADER_OFFSET.js +29 -0
- package/src/shade/device/timing/profile/SGPT_HEADER_SIZE.d.ts +7 -0
- package/src/shade/device/timing/profile/SGPT_HEADER_SIZE.d.ts.map +1 -0
- package/src/shade/device/timing/profile/SGPT_HEADER_SIZE.js +6 -0
- package/src/shade/device/timing/profile/SGPT_MAGIC.d.ts +7 -0
- package/src/shade/device/timing/profile/SGPT_MAGIC.d.ts.map +1 -0
- package/src/shade/device/timing/profile/SGPT_MAGIC.js +6 -0
- package/src/shade/device/timing/profile/SGPT_MIN_READER_VERSION.d.ts +16 -0
- package/src/shade/device/timing/profile/SGPT_MIN_READER_VERSION.d.ts.map +1 -0
- package/src/shade/device/timing/profile/SGPT_MIN_READER_VERSION.js +15 -0
- package/src/shade/device/timing/profile/SGPT_RECORD_HEADER_SIZE.d.ts +7 -0
- package/src/shade/device/timing/profile/SGPT_RECORD_HEADER_SIZE.d.ts.map +1 -0
- package/src/shade/device/timing/profile/SGPT_RECORD_HEADER_SIZE.js +6 -0
- package/src/shade/device/timing/profile/SGPT_RECORD_OFFSET.d.ts +13 -0
- package/src/shade/device/timing/profile/SGPT_RECORD_OFFSET.d.ts.map +1 -0
- package/src/shade/device/timing/profile/SGPT_RECORD_OFFSET.js +23 -0
- package/src/shade/device/timing/profile/SGPT_RECORD_SYNC.d.ts +12 -0
- package/src/shade/device/timing/profile/SGPT_RECORD_SYNC.d.ts.map +1 -0
- package/src/shade/device/timing/profile/SGPT_RECORD_SYNC.js +11 -0
- package/src/shade/device/timing/profile/SGPT_RECORD_TYPE.d.ts +18 -0
- package/src/shade/device/timing/profile/SGPT_RECORD_TYPE.d.ts.map +1 -0
- package/src/shade/device/timing/profile/SGPT_RECORD_TYPE.js +46 -0
- package/src/shade/device/timing/profile/frame_graph_extract_topology.d.ts +20 -0
- package/src/shade/device/timing/profile/frame_graph_extract_topology.d.ts.map +1 -0
- package/src/shade/device/timing/profile/frame_graph_extract_topology.js +174 -0
- package/src/shade/device/timing/profile/make_profiling_pass_encoder.d.ts +21 -0
- package/src/shade/device/timing/profile/make_profiling_pass_encoder.d.ts.map +1 -0
- package/src/shade/device/timing/profile/make_profiling_pass_encoder.js +114 -0
- package/src/shade/device/timing/profile/parse_workgroup_size.d.ts +20 -0
- package/src/shade/device/timing/profile/parse_workgroup_size.d.ts.map +1 -0
- package/src/shade/device/timing/profile/parse_workgroup_size.js +43 -0
- package/src/shade/device/timing/profile/sgpt_fourcc.d.ts +18 -0
- package/src/shade/device/timing/profile/sgpt_fourcc.d.ts.map +1 -0
- package/src/shade/device/timing/profile/sgpt_fourcc.js +34 -0
- package/src/shade/device/timing/profile/sgpt_frame_codec.d.ts +42 -0
- package/src/shade/device/timing/profile/sgpt_frame_codec.d.ts.map +1 -0
- package/src/shade/device/timing/profile/sgpt_frame_codec.js +172 -0
- package/src/shade/device/timing/profile/sgpt_meta_codec.d.ts +20 -0
- package/src/shade/device/timing/profile/sgpt_meta_codec.d.ts.map +1 -0
- package/src/shade/device/timing/profile/sgpt_meta_codec.js +65 -0
- package/src/shade/device/timing/profile/sgpt_read_capture.d.ts +19 -0
- package/src/shade/device/timing/profile/sgpt_read_capture.d.ts.map +1 -0
- package/src/shade/device/timing/profile/sgpt_read_capture.js +134 -0
- package/src/shade/device/timing/profile/sgpt_read_header.d.ts +22 -0
- package/src/shade/device/timing/profile/sgpt_read_header.d.ts.map +1 -0
- package/src/shade/device/timing/profile/sgpt_read_header.js +94 -0
- package/src/shade/device/timing/profile/sgpt_read_records.d.ts +27 -0
- package/src/shade/device/timing/profile/sgpt_read_records.d.ts.map +1 -0
- package/src/shade/device/timing/profile/sgpt_read_records.js +153 -0
- package/src/shade/device/timing/profile/sgpt_topology_codec.d.ts +54 -0
- package/src/shade/device/timing/profile/sgpt_topology_codec.d.ts.map +1 -0
- package/src/shade/device/timing/profile/sgpt_topology_codec.js +329 -0
- package/src/shade/device/timing/profile/sgpt_write_header.d.ts +22 -0
- package/src/shade/device/timing/profile/sgpt_write_header.d.ts.map +1 -0
- package/src/shade/device/timing/profile/sgpt_write_header.js +47 -0
- package/src/shade/device/timing/profile/sgpt_write_record.d.ts +23 -0
- package/src/shade/device/timing/profile/sgpt_write_record.d.ts.map +1 -0
- package/src/shade/device/timing/profile/sgpt_write_record.js +62 -0
- package/src/shade/renderer/Renderer.d.ts +21 -0
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +2513 -2455
- package/src/engine/save/GameStateLoader.d.ts +0 -49
- package/src/engine/save/GameStateLoader.d.ts.map +0 -1
- package/src/engine/save/GameStateLoader.js +0 -168
|
@@ -1,750 +1,941 @@
|
|
|
1
|
-
import { assert } from "../../core/assert.js";
|
|
2
|
-
import { array_push_if_unique } from "../../core/collection/array/array_push_if_unique.js";
|
|
3
|
-
import { array_remove_first } from "../../core/collection/array/array_remove_first.js";
|
|
4
|
-
import { array_buffer_copy } from "../../core/collection/array/typed/array_buffer_copy.js";
|
|
5
|
-
import Signal from "../../core/events/signal/Signal.js";
|
|
6
|
-
import { noop } from "../../core/function/noop.js";
|
|
7
|
-
import { GPUTimerArray } from "./timing/GPUTimerArray.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
*
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
*
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
*
|
|
181
|
-
* @param {
|
|
182
|
-
* @
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
-
return
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
* @
|
|
243
|
-
* @
|
|
244
|
-
*/
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
*
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
if (
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
/**
|
|
505
|
-
*
|
|
506
|
-
*
|
|
507
|
-
*
|
|
508
|
-
* @param {
|
|
509
|
-
* @
|
|
510
|
-
*/
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
this.#
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
assert.
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
* @
|
|
615
|
-
* @
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
this.#
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
this.
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
1
|
+
import { assert } from "../../core/assert.js";
|
|
2
|
+
import { array_push_if_unique } from "../../core/collection/array/array_push_if_unique.js";
|
|
3
|
+
import { array_remove_first } from "../../core/collection/array/array_remove_first.js";
|
|
4
|
+
import { array_buffer_copy } from "../../core/collection/array/typed/array_buffer_copy.js";
|
|
5
|
+
import Signal from "../../core/events/signal/Signal.js";
|
|
6
|
+
import { noop } from "../../core/function/noop.js";
|
|
7
|
+
import { GPUTimerArray } from "./timing/GPUTimerArray.js";
|
|
8
|
+
import { make_profiling_pass_encoder } from "./timing/profile/make_profiling_pass_encoder.js";
|
|
9
|
+
import { parse_workgroup_size } from "./timing/profile/parse_workgroup_size.js";
|
|
10
|
+
import { warn_limited } from "../util/warn_limited.js";
|
|
11
|
+
import { write_gpu_typed_buffer } from "../renderer/shader/type/serde/write_gpu_typed_buffer.js";
|
|
12
|
+
import { BufferResourceDescriptor } from "./graph/BufferResourceDescriptor.js";
|
|
13
|
+
import { GraphicsExecutionContext } from "./graph/GraphicsExecutionContext.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Keep track of how many contexts are currently open (unfinished)
|
|
17
|
+
* Important because contexts can have transient resources allocated, and those are not cleaned up until the context is `finished`
|
|
18
|
+
* @type {number}
|
|
19
|
+
*/
|
|
20
|
+
let debug_open_context_count = 0;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
*
|
|
24
|
+
* @type {ShadeGPUCommandContext[]}
|
|
25
|
+
*/
|
|
26
|
+
const debug_open_contexts = [];
|
|
27
|
+
|
|
28
|
+
const scratch_buffer_descriptor = new BufferResourceDescriptor();
|
|
29
|
+
|
|
30
|
+
let id_counter = 0;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Abstraction on top of {@link GPUCommandEncoder}.
|
|
34
|
+
*
|
|
35
|
+
* Inspired by Intel's "MiniEngine" CommandContext
|
|
36
|
+
* @see https://github.com/GameTechDev/TAA/blob/39786709cf70a1e0906196c600f6079571a33ceb/MiniEngine/Core/CommandContext.h
|
|
37
|
+
* @see https://www.w3.org/TR/webgpu/#gpucommandencoder
|
|
38
|
+
*/
|
|
39
|
+
export class ShadeGPUCommandContext {
|
|
40
|
+
|
|
41
|
+
#id = id_counter++;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Unique identifier for this context, useful for debugging
|
|
45
|
+
*
|
|
46
|
+
* @return {number}
|
|
47
|
+
*/
|
|
48
|
+
get id() {
|
|
49
|
+
return this.#id;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
*
|
|
54
|
+
* @returns {boolean}
|
|
55
|
+
*/
|
|
56
|
+
get isGPUCommandContext() {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Actual underlying encoder
|
|
63
|
+
* @type {GPUCommandEncoder}
|
|
64
|
+
*/
|
|
65
|
+
#gpu_encoder
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Don't circumvent {@link ShadeGPUCommandContext} unless you know what you're doing.
|
|
69
|
+
* {@link ShadeGPUCommandContext} is a wrapper around {@link GPUCommandEncoder} that provides additional spec-compliance asserts to help find errors sooner.
|
|
70
|
+
* @returns {GPUCommandEncoder}
|
|
71
|
+
*/
|
|
72
|
+
get gpu_encoder() {
|
|
73
|
+
return this.#gpu_encoder;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @type {GPUTimerArray|undefined}
|
|
78
|
+
*/
|
|
79
|
+
#debug_timers;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
*
|
|
83
|
+
* @type {function}
|
|
84
|
+
*/
|
|
85
|
+
#debug_timers_callback = noop;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
*
|
|
89
|
+
* @param {function} debug_timers_callback
|
|
90
|
+
*/
|
|
91
|
+
enable_debug_timers(debug_timers_callback) {
|
|
92
|
+
if (this.#debug_timers !== undefined) {
|
|
93
|
+
// already enabled
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
this.#debug_timers = new GPUTimerArray(this.device);
|
|
98
|
+
this.#debug_timers_callback = debug_timers_callback;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The frame recorder collecting this context's spans, or null.
|
|
103
|
+
*
|
|
104
|
+
* @type {GPUFrameRecorder|null}
|
|
105
|
+
*/
|
|
106
|
+
#profile_recorder = null;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Which query set this context is, within its frame. Carried into every span it records —
|
|
110
|
+
* timestamps from two query sets are not specified to share a clock domain, and a frame has
|
|
111
|
+
* one set per context.
|
|
112
|
+
*
|
|
113
|
+
* @type {number}
|
|
114
|
+
*/
|
|
115
|
+
#profile_query_set_id = 0;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The work record for the pass currently being opened.
|
|
119
|
+
*
|
|
120
|
+
* Set by {@link #with_timestamp_writes} and consumed immediately by the `begin*Pass` that asked
|
|
121
|
+
* for it — the two are one operation, split only because the descriptor has to be built before
|
|
122
|
+
* the encoder exists.
|
|
123
|
+
*
|
|
124
|
+
* @type {GPUProfileWork|null}
|
|
125
|
+
*/
|
|
126
|
+
#pending_work = null;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Wrap `encoder` so its dispatches and draws are recorded, when a capture asked for them.
|
|
130
|
+
*
|
|
131
|
+
* @param {GPUComputePassEncoder|GPURenderPassEncoder} encoder
|
|
132
|
+
* @returns {GPUComputePassEncoder|GPURenderPassEncoder}
|
|
133
|
+
*/
|
|
134
|
+
#instrument_pass(encoder) {
|
|
135
|
+
const work = this.#pending_work;
|
|
136
|
+
|
|
137
|
+
if (work === null) {
|
|
138
|
+
this.#last_work = null;
|
|
139
|
+
|
|
140
|
+
return encoder;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
this.#pending_work = null;
|
|
144
|
+
this.#last_work = work;
|
|
145
|
+
|
|
146
|
+
return make_profiling_pass_encoder(encoder, work);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Resolves once this context's timings have reached its recorder.
|
|
151
|
+
*
|
|
152
|
+
* @type {Promise<void>}
|
|
153
|
+
*/
|
|
154
|
+
#profiling_absorbed = Promise.resolve();
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* @type {function}
|
|
158
|
+
*/
|
|
159
|
+
#profiling_absorbed_resolve = noop;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Resolves after this context's timings have been read back and handed to its recorder.
|
|
163
|
+
*
|
|
164
|
+
* **Not the same moment as {@link done}.** `done` resolves from `onFinished`, which fires
|
|
165
|
+
* synchronously at submit — before any timestamp has been read. A caller that closes its frame
|
|
166
|
+
* on `done` commits an empty one, every time. Already-resolved when nothing is profiling.
|
|
167
|
+
*
|
|
168
|
+
* @returns {Promise<void>}
|
|
169
|
+
*/
|
|
170
|
+
get profiling_absorbed() {
|
|
171
|
+
return this.#profiling_absorbed;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Record this context's pass timings into `recorder`.
|
|
176
|
+
*
|
|
177
|
+
* Independent of {@link enable_debug_timers}, which is the console-shaped one-off; both share
|
|
178
|
+
* the underlying {@link GPUTimerArray} and either may enable it.
|
|
179
|
+
*
|
|
180
|
+
* @param {GPUFrameRecorder} recorder
|
|
181
|
+
* @param {number} query_set_id this context's index within the frame
|
|
182
|
+
* @returns {void}
|
|
183
|
+
*/
|
|
184
|
+
enable_profiling(recorder, query_set_id) {
|
|
185
|
+
assert.defined(recorder, 'recorder');
|
|
186
|
+
assert.isNonNegativeInteger(query_set_id, 'query_set_id');
|
|
187
|
+
assert.notOk(this.#finished, 'already finished');
|
|
188
|
+
|
|
189
|
+
if (this.#debug_timers === undefined) {
|
|
190
|
+
this.#debug_timers = new GPUTimerArray(this.device);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
this.#profile_recorder = recorder;
|
|
194
|
+
this.#profile_query_set_id = query_set_id;
|
|
195
|
+
|
|
196
|
+
this.#profiling_absorbed = new Promise(resolve => {
|
|
197
|
+
this.#profiling_absorbed_resolve = resolve;
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* @type {GraphicsContext}
|
|
203
|
+
*/
|
|
204
|
+
#graphics
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
*
|
|
208
|
+
* @return {GPUDevice}
|
|
209
|
+
*/
|
|
210
|
+
get device() {
|
|
211
|
+
return this.#graphics.device;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
*
|
|
216
|
+
* @return {TextureManager}
|
|
217
|
+
*/
|
|
218
|
+
get textures() {
|
|
219
|
+
return this.#graphics.textures;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* These are to be destroyed at the end of the context's lifetime.
|
|
224
|
+
* Need to be released at the end of the lifetime.
|
|
225
|
+
* @type {GPUBuffer[]}
|
|
226
|
+
*/
|
|
227
|
+
#transient_buffers = [];
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
*
|
|
231
|
+
* @type {GPUBuffer[]}
|
|
232
|
+
*/
|
|
233
|
+
#staging_buffers = [];
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Once finished, the encoder becomes inactive, we use this flag to track potential misuse and inform the user
|
|
237
|
+
* @type {boolean}
|
|
238
|
+
*/
|
|
239
|
+
#finished = false;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* @readonly
|
|
243
|
+
* @type {Signal<this>}
|
|
244
|
+
*/
|
|
245
|
+
onFinished = new Signal();
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* @readonly
|
|
249
|
+
* @type {Signal<this>}
|
|
250
|
+
*/
|
|
251
|
+
onBeforeFinish = new Signal();
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
*
|
|
255
|
+
* @return {Promise<void>}
|
|
256
|
+
*/
|
|
257
|
+
get done() {
|
|
258
|
+
if (this.#finished) {
|
|
259
|
+
return Promise.resolve();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return new Promise(resolve => {
|
|
263
|
+
this.onFinished.addOne(resolve);
|
|
264
|
+
|
|
265
|
+
if (this.#finished) {
|
|
266
|
+
// special case, double-checking to avoid race conditions
|
|
267
|
+
resolve();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Do not call directly, always use {@link create}
|
|
275
|
+
* @private
|
|
276
|
+
*/
|
|
277
|
+
constructor() {
|
|
278
|
+
// private, do not call from the outside
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
*
|
|
283
|
+
* @param {GraphicsContext} graphics
|
|
284
|
+
* @param {string} [label]
|
|
285
|
+
* @returns {ShadeGPUCommandContext}
|
|
286
|
+
*/
|
|
287
|
+
static create(
|
|
288
|
+
graphics,
|
|
289
|
+
label = ""
|
|
290
|
+
) {
|
|
291
|
+
|
|
292
|
+
assert.defined(graphics, 'graphics');
|
|
293
|
+
assert.equal(graphics.isGraphicsContext, true, "graphics.isGraphicsContext !== true");
|
|
294
|
+
|
|
295
|
+
assert.isString(label, 'label');
|
|
296
|
+
|
|
297
|
+
const r = new ShadeGPUCommandContext();
|
|
298
|
+
|
|
299
|
+
r.#graphics = graphics;
|
|
300
|
+
r.#gpu_encoder = graphics.device.createCommandEncoder({
|
|
301
|
+
label
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// track how many we have open
|
|
305
|
+
debug_open_context_count++;
|
|
306
|
+
array_push_if_unique(debug_open_contexts, r);
|
|
307
|
+
|
|
308
|
+
if (debug_open_context_count > 1024) {
|
|
309
|
+
warn_limited(`Too many open GPU contexts`, 20);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return r;
|
|
313
|
+
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
*
|
|
318
|
+
* @returns {GraphicsExecutionContext}
|
|
319
|
+
*/
|
|
320
|
+
createFrameGraphContext() {
|
|
321
|
+
return new GraphicsExecutionContext(this.#graphics, this);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
*
|
|
326
|
+
* @param {FrameGraph} graph
|
|
327
|
+
*/
|
|
328
|
+
encodeGraph(graph) {
|
|
329
|
+
assert.defined(graph, 'graph');
|
|
330
|
+
assert.isObject(graph, 'graph');
|
|
331
|
+
assert.equal(graph.isFrameGraph, true, "graph.isFrameGraph !== true");
|
|
332
|
+
|
|
333
|
+
// TODO check if the graph is empty, if so, we can skip the whole thing
|
|
334
|
+
|
|
335
|
+
const ctx = this.createFrameGraphContext();
|
|
336
|
+
|
|
337
|
+
graph.compile();
|
|
338
|
+
graph.execute(ctx);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* @see https://www.w3.org/TR/webgpu/#dom-gpucommandencoder-clearbuffer
|
|
343
|
+
* @param {GPUBuffer} buffer
|
|
344
|
+
* @param {number} [offset=0]
|
|
345
|
+
* @param {number} [size]
|
|
346
|
+
*/
|
|
347
|
+
clearBuffer(buffer, offset = 0, size) {
|
|
348
|
+
assert.defined(buffer, 'buffer');
|
|
349
|
+
assert.equal(buffer.usage & GPUBufferUsage.COPY_DST, GPUBufferUsage.COPY_DST, 'buffer must include COPY_DST usage flag');
|
|
350
|
+
assert.isNonNegativeInteger(offset, 'offset');
|
|
351
|
+
assert.equal(offset % 4, 0, 'offset must be a multiple of 4');
|
|
352
|
+
|
|
353
|
+
// console.log(`cmd.clearBuffer(${buffer.label}, ${offset}, ${size})`);
|
|
354
|
+
|
|
355
|
+
this.#gpu_encoder.clearBuffer(buffer, offset, size);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Not supported, only here to offer documentation hint
|
|
360
|
+
* see {@link texture_fill_rectangle}
|
|
361
|
+
* @throws {Error} Always, documentation hint only
|
|
362
|
+
*/
|
|
363
|
+
clearTexture() {
|
|
364
|
+
throw new Error(`Documentation hint only, use texture_fill_rectangle instead`);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* NOTE: only the explicit verbose form of the API is supported, shorthand such as `copyBufferToBuffer(source, destination, size)` are not supported.
|
|
369
|
+
* see https://www.w3.org/TR/webgpu/#gpucommandencoder-copybuffertobuffer
|
|
370
|
+
* @see GPUCommandEncoder.copyBufferToBuffer
|
|
371
|
+
* @param {GPUBuffer} source The GPUBuffer to copy from.
|
|
372
|
+
* @param {number} sourceOffset Offset in bytes into the source to begin copying from.
|
|
373
|
+
* @param {GPUBuffer} destination The GPUBuffer to copy to.
|
|
374
|
+
* @param {number} destinationOffset Offset in bytes into destination to place the copied data.
|
|
375
|
+
* @param {number} [size] Bytes to copy. In not specified, defaults to `source.size - sourceOffset`
|
|
376
|
+
*/
|
|
377
|
+
copyBufferToBuffer(
|
|
378
|
+
source,
|
|
379
|
+
sourceOffset,
|
|
380
|
+
destination,
|
|
381
|
+
destinationOffset,
|
|
382
|
+
size
|
|
383
|
+
) {
|
|
384
|
+
|
|
385
|
+
assert.notEqual(source.usage & GPUBufferUsage.COPY_SRC, 0, 'source must include COPY_SRC usage flag');
|
|
386
|
+
assert.isNonNegativeInteger(sourceOffset, 'sourceOffset');
|
|
387
|
+
assert.equal(sourceOffset % 4, 0, 'sourceOffset must be a multiple of 4');
|
|
388
|
+
|
|
389
|
+
assert.notEqual(destination.usage & GPUBufferUsage.COPY_DST, 0, 'destination must include COPY_DST usage flag');
|
|
390
|
+
assert.isNonNegativeInteger(destinationOffset, 'destinationOffset');
|
|
391
|
+
assert.equal(destinationOffset % 4, 0, 'destinationOffset must be a multiple of 4');
|
|
392
|
+
|
|
393
|
+
if (size !== undefined) {
|
|
394
|
+
assert.isNonNegativeInteger(size, 'size');
|
|
395
|
+
assert.equal(size % 4, 0, 'size must be a multiple of 4');
|
|
396
|
+
|
|
397
|
+
assert.greaterThanOrEqual(source.size, sourceOffset + size, 'source underflow');
|
|
398
|
+
assert.greaterThanOrEqual(destination.size, destinationOffset + size, 'destination underflow');
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
assert.notEqual(source, destination, 'source and destination must be different');
|
|
402
|
+
|
|
403
|
+
// console.log(`cmd.copyBufferToBuffer("${source.label}", ${sourceOffset}, "${destination.label}", ${destinationOffset}, ${size})`);
|
|
404
|
+
|
|
405
|
+
this.#gpu_encoder.copyBufferToBuffer(
|
|
406
|
+
source,
|
|
407
|
+
sourceOffset,
|
|
408
|
+
destination,
|
|
409
|
+
destinationOffset,
|
|
410
|
+
size
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
*
|
|
417
|
+
* @param {GPUImageCopyTexture} source
|
|
418
|
+
* @param {GPUImageCopyTexture} destination
|
|
419
|
+
* @param {GPUExtent3DStrict} copySize
|
|
420
|
+
*/
|
|
421
|
+
copyTextureToTexture(source,
|
|
422
|
+
destination,
|
|
423
|
+
copySize
|
|
424
|
+
) {
|
|
425
|
+
this.#gpu_encoder.copyTextureToTexture(source, destination, copySize);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
*
|
|
430
|
+
* @param {GPUComputePassDescriptor} [descriptor]
|
|
431
|
+
* @returns {GPUComputePassEncoder}
|
|
432
|
+
*/
|
|
433
|
+
beginComputePass(descriptor) {
|
|
434
|
+
const encoder = this.#gpu_encoder;
|
|
435
|
+
|
|
436
|
+
const _descriptor = this.#with_timestamp_writes(descriptor, 'compute');
|
|
437
|
+
|
|
438
|
+
// console.log(`cmd.beginComputePass(${descriptor.label})`);
|
|
439
|
+
|
|
440
|
+
return this.#instrument_pass(encoder.beginComputePass(_descriptor))
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* The descriptor a pass should actually be begun with, once timing has had its say.
|
|
445
|
+
*
|
|
446
|
+
* Copies rather than writing through. Several call sites hand in a descriptor they reuse across
|
|
447
|
+
* passes, and stamping `timestampWrites` onto it left the next pass carrying the previous
|
|
448
|
+
* pass's query indices. The copy is one shallow spread per pass and only when timers are on.
|
|
449
|
+
*
|
|
450
|
+
* Returns the descriptor unchanged when timing is off, when the device withheld the feature, or
|
|
451
|
+
* when the timer array has no slot left ({@link GPUTimerArray#dropped_count}). An untimed pass
|
|
452
|
+
* is the correct outcome in all three; a pass carrying indices the query set does not have is a
|
|
453
|
+
* validation error.
|
|
454
|
+
*
|
|
455
|
+
* @param {GPUComputePassDescriptor|GPURenderPassDescriptor} [descriptor]
|
|
456
|
+
* @param {string} kind one of 'compute', 'render'
|
|
457
|
+
* @returns {GPUComputePassDescriptor|GPURenderPassDescriptor}
|
|
458
|
+
*/
|
|
459
|
+
#with_timestamp_writes(descriptor, kind) {
|
|
460
|
+
const timers = this.#debug_timers;
|
|
461
|
+
|
|
462
|
+
if (timers === undefined) {
|
|
463
|
+
return descriptor;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (!this.#graphics.device.features.has('timestamp-query')) {
|
|
467
|
+
return descriptor;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// `beginComputePass()` is legal with no descriptor at all; reading `.label` off nothing is
|
|
471
|
+
// not, and used to throw here the moment timers were switched on
|
|
472
|
+
const source = descriptor !== undefined ? descriptor : {};
|
|
473
|
+
|
|
474
|
+
const writes = kind === 'compute'
|
|
475
|
+
? timers.getComputeWrites(source.label)
|
|
476
|
+
: timers.getRenderWrites(source.label);
|
|
477
|
+
|
|
478
|
+
if (writes === undefined) {
|
|
479
|
+
return descriptor;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const recorder = this.#profile_recorder;
|
|
483
|
+
|
|
484
|
+
if (recorder !== null) {
|
|
485
|
+
/*
|
|
486
|
+
The one moment the join can be made. The recorder knows which frame graph pass is
|
|
487
|
+
executing right now; the slot is what the timings will come back keyed by. Neither
|
|
488
|
+
fact is recoverable later — see GPUFrameRecorder.
|
|
489
|
+
*/
|
|
490
|
+
this.#pending_work = recorder.note_pass_slot(
|
|
491
|
+
this.#profile_query_set_id,
|
|
492
|
+
writes.beginningOfPassWriteIndex / 2
|
|
493
|
+
);
|
|
494
|
+
} else {
|
|
495
|
+
this.#pending_work = null;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
return {
|
|
499
|
+
...source,
|
|
500
|
+
timestampWrites: writes
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
*
|
|
506
|
+
* @param {ComputePipelineDescriptor} pipeline
|
|
507
|
+
* @param {string} [label]
|
|
508
|
+
* @param {GPUBindingResource[][]} [bindings]
|
|
509
|
+
* @returns {GPUComputePassEncoder}
|
|
510
|
+
*/
|
|
511
|
+
constructComputePass({
|
|
512
|
+
pipeline,
|
|
513
|
+
label,
|
|
514
|
+
bindings = []
|
|
515
|
+
}) {
|
|
516
|
+
|
|
517
|
+
const pass_descriptor = {
|
|
518
|
+
label
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
if (label === undefined) {
|
|
522
|
+
pass_descriptor.label = pipeline.label;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const graphics = this.#graphics;
|
|
526
|
+
|
|
527
|
+
const pass = this.beginComputePass(pass_descriptor);
|
|
528
|
+
|
|
529
|
+
const gpu_pipeline = graphics.compute_pipelines.obtain(pipeline);
|
|
530
|
+
|
|
531
|
+
pass.setPipeline(gpu_pipeline);
|
|
532
|
+
|
|
533
|
+
this.#note_pipeline(pipeline, true);
|
|
534
|
+
|
|
535
|
+
graphics.setPipelineBindings(pass, pipeline, bindings);
|
|
536
|
+
|
|
537
|
+
// console.warn(`cmd.constructComputePass(${pass_descriptor.label})`)
|
|
538
|
+
|
|
539
|
+
return pass;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Record which pipeline a pass ran, and for a compute pipeline its workgroup size.
|
|
544
|
+
*
|
|
545
|
+
* Only reachable from `constructComputePass` / `constructRenderPass`, which is the only place a
|
|
546
|
+
* *descriptor* is in hand. `setPipeline` receives the compiled `GPUComputePipeline`, and the
|
|
547
|
+
* WGSL that carries `@workgroup_size` is not on it — so a pass begun the long way records its
|
|
548
|
+
* counts but not what ran them, which the model represents as an unknown rather than a zero.
|
|
549
|
+
*
|
|
550
|
+
* @param {ComputePipelineDescriptor|RenderPipelineDescriptor} pipeline
|
|
551
|
+
* @param {boolean} is_compute
|
|
552
|
+
* @returns {void}
|
|
553
|
+
*/
|
|
554
|
+
#note_pipeline(pipeline, is_compute) {
|
|
555
|
+
const work = this.#last_work;
|
|
556
|
+
|
|
557
|
+
if (work === null) {
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
work.pipeline = pipeline.label ?? "";
|
|
562
|
+
|
|
563
|
+
if (is_compute) {
|
|
564
|
+
work.workgroup_size = parse_workgroup_size(pipeline.compute?.module?.code);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* The work record of the most recently opened pass, so `construct*Pass` can annotate it after
|
|
570
|
+
* the encoder has been made.
|
|
571
|
+
*
|
|
572
|
+
* @type {GPUProfileWork|null}
|
|
573
|
+
*/
|
|
574
|
+
#last_work = null;
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Begins a labeled debug group containing subsequent commands.
|
|
578
|
+
* @see https://gpuweb.github.io/gpuweb/#debug-markers
|
|
579
|
+
* @param {string} label
|
|
580
|
+
*/
|
|
581
|
+
pushDebugGroup(label) {
|
|
582
|
+
assert.isString(label, 'label');
|
|
583
|
+
this.#gpu_encoder.pushDebugGroup(label);
|
|
584
|
+
|
|
585
|
+
// console.group("cmd/debug_group=" + label)
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Ends the labeled debug group most recently started by pushDebugGroup().
|
|
590
|
+
* @see https://gpuweb.github.io/gpuweb/#debug-markers
|
|
591
|
+
*/
|
|
592
|
+
popDebugGroup() {
|
|
593
|
+
this.#gpu_encoder.popDebugGroup();
|
|
594
|
+
|
|
595
|
+
// console.groupEnd();
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Marks a point in a stream of commands with a label.
|
|
600
|
+
* @see https://gpuweb.github.io/gpuweb/#debug-markers
|
|
601
|
+
* @param {string} label
|
|
602
|
+
*/
|
|
603
|
+
insertDebugMarker(label) {
|
|
604
|
+
assert.isString(label, 'label');
|
|
605
|
+
this.#gpu_encoder.insertDebugMarker(label);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
*
|
|
610
|
+
* @param {string} [label]
|
|
611
|
+
* @param {RenderPipelineDescriptor} pipeline
|
|
612
|
+
* @param {GPUBindingResource[][]} [bindings]
|
|
613
|
+
* @param {GPURenderPassColorAttachment[]} colorAttachments
|
|
614
|
+
* @param {GPURenderPassDepthStencilAttachment} [depthStencilAttachment]
|
|
615
|
+
* @returns {GPURenderPassEncoder}
|
|
616
|
+
*/
|
|
617
|
+
constructRenderPass({
|
|
618
|
+
label,
|
|
619
|
+
pipeline,
|
|
620
|
+
bindings,
|
|
621
|
+
colorAttachments,
|
|
622
|
+
depthStencilAttachment,
|
|
623
|
+
}) {
|
|
624
|
+
assert.defined(pipeline, 'pipeline');
|
|
625
|
+
assert.equal(pipeline.isRenderPipelineDescriptor, true, "pipeline.isRenderPipelineDescriptor !== true");
|
|
626
|
+
|
|
627
|
+
const pass_descriptor = {
|
|
628
|
+
label,
|
|
629
|
+
colorAttachments,
|
|
630
|
+
depthStencilAttachment
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
if (label === undefined) {
|
|
634
|
+
pass_descriptor.label = pipeline.label;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
const graphics = this.#graphics;
|
|
638
|
+
|
|
639
|
+
// TODO rewrite the pipeline if necessary to achieve compatibility with colorAttachment formats
|
|
640
|
+
const gpu_pipeline = graphics.render_pipelines.obtain(pipeline);
|
|
641
|
+
|
|
642
|
+
const pass = this.beginRenderPass(pass_descriptor);
|
|
643
|
+
|
|
644
|
+
pass.setPipeline(gpu_pipeline);
|
|
645
|
+
|
|
646
|
+
this.#note_pipeline(pipeline, false);
|
|
647
|
+
|
|
648
|
+
graphics.setPipelineBindings(pass, pipeline, bindings);
|
|
649
|
+
|
|
650
|
+
// console.warn(`cmd.constructRenderPass(${pass_descriptor.label})`)
|
|
651
|
+
|
|
652
|
+
return pass;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
*
|
|
657
|
+
* @param {GPURenderPassDescriptor} descriptor
|
|
658
|
+
* @returns {GPURenderPassEncoder}
|
|
659
|
+
*/
|
|
660
|
+
beginRenderPass(descriptor) {
|
|
661
|
+
|
|
662
|
+
const _descriptor = this.#with_timestamp_writes(descriptor, 'render');
|
|
663
|
+
|
|
664
|
+
// console.log(`cmd.beginRenderPass(${descriptor.label})`);
|
|
665
|
+
|
|
666
|
+
return this.#instrument_pass(this.#gpu_encoder.beginRenderPass(_descriptor))
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
*
|
|
671
|
+
* @param {GPUQuerySet} querySet
|
|
672
|
+
* @param {number} firstQuery
|
|
673
|
+
* @param {number} queryCount
|
|
674
|
+
* @param {GPUBuffer} destination
|
|
675
|
+
* @param {number} destinationOffset
|
|
676
|
+
*/
|
|
677
|
+
resolveQuerySet(querySet, firstQuery, queryCount, destination, destinationOffset) {
|
|
678
|
+
this.#gpu_encoder.resolveQuerySet(querySet, firstQuery, queryCount, destination, destinationOffset);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Transient buffers are automatically released at the end of the context's lifetime;
|
|
683
|
+
* however, we can explicitly release it earlier to allow for reuse.
|
|
684
|
+
*
|
|
685
|
+
* @param {GPUBuffer} buffer
|
|
686
|
+
* @return {boolean} true if buffer was released, false if it was not found
|
|
687
|
+
*/
|
|
688
|
+
releaseTransientBuffer(buffer) {
|
|
689
|
+
assert.defined(buffer, 'buffer');
|
|
690
|
+
|
|
691
|
+
if (!array_remove_first(this.#transient_buffers, buffer)) {
|
|
692
|
+
return false;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
const allocator = this.#transient_allocator;
|
|
696
|
+
|
|
697
|
+
return allocator.release(buffer);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
*
|
|
702
|
+
* @return {GPUBufferAllocator}
|
|
703
|
+
*/
|
|
704
|
+
get #transient_allocator() {
|
|
705
|
+
return this.#graphics.buffer_allocator_main;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* Allocated buffer will only be valid until the context is finished.
|
|
710
|
+
*
|
|
711
|
+
* @param {number|GPUBufferUsage} usage
|
|
712
|
+
* @param {number} size
|
|
713
|
+
* @returns {GPUBuffer} managed buffer
|
|
714
|
+
*/
|
|
715
|
+
allocateTransientBuffer(
|
|
716
|
+
usage = GPUBufferUsage.UNIFORM,
|
|
717
|
+
size
|
|
718
|
+
) {
|
|
719
|
+
assert.isNonNegativeInteger(size, 'size');
|
|
720
|
+
assert.isInteger(usage, 'usage');
|
|
721
|
+
|
|
722
|
+
assert.notOk(this.#finished, 'already finished');
|
|
723
|
+
|
|
724
|
+
scratch_buffer_descriptor.size = size;
|
|
725
|
+
// COPY_DST is needed to write the data
|
|
726
|
+
scratch_buffer_descriptor.usage = usage | GPUBufferUsage.COPY_DST;
|
|
727
|
+
|
|
728
|
+
const allocator = this.#transient_allocator;
|
|
729
|
+
const buffer = allocator.get(scratch_buffer_descriptor, this);
|
|
730
|
+
|
|
731
|
+
// remember so we can clean up at the end
|
|
732
|
+
this.#transient_buffers.push(buffer);
|
|
733
|
+
|
|
734
|
+
return buffer;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
*
|
|
739
|
+
* @param {ArrayBuffer} data CPU-side data to place into the GPU buffer
|
|
740
|
+
* @param {number|GPUBufferUsage} [usage=GPUBufferUsage.UNIFORM]
|
|
741
|
+
* @param {number} [offset] offset into source data where to start copying from
|
|
742
|
+
* @param {number} [size] in bytes
|
|
743
|
+
* @returns {GPUBuffer} managed buffer, will be automatically destroyed when context is finished
|
|
744
|
+
*/
|
|
745
|
+
allocateTransientBufferAndLoad(
|
|
746
|
+
data,
|
|
747
|
+
usage = GPUBufferUsage.UNIFORM,
|
|
748
|
+
offset = 0,
|
|
749
|
+
size = data.byteLength
|
|
750
|
+
) {
|
|
751
|
+
assert.isInstanceOf(data, ArrayBuffer, 'data');
|
|
752
|
+
assert.isInteger(usage, 'usage');
|
|
753
|
+
assert.isNonNegativeInteger(offset, 'offset');
|
|
754
|
+
assert.isNonNegativeInteger(size, 'size');
|
|
755
|
+
|
|
756
|
+
assert.notOk(this.#finished, 'already finished');
|
|
757
|
+
|
|
758
|
+
const buffer = this.allocateTransientBuffer(
|
|
759
|
+
usage,
|
|
760
|
+
size
|
|
761
|
+
);
|
|
762
|
+
|
|
763
|
+
this.writeBuffer(buffer, 0, data, offset, size);
|
|
764
|
+
|
|
765
|
+
return buffer;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* @template T
|
|
770
|
+
* @param {WebGPUType} type
|
|
771
|
+
* @param {T} value
|
|
772
|
+
* @param {number|GPUBufferUsage} [usage]
|
|
773
|
+
* @return {GPUBuffer}
|
|
774
|
+
*/
|
|
775
|
+
allocateTransientValueBuffer(
|
|
776
|
+
type,
|
|
777
|
+
value,
|
|
778
|
+
usage
|
|
779
|
+
) {
|
|
780
|
+
assert.defined(type, 'type');
|
|
781
|
+
assert.notNull(type, 'type');
|
|
782
|
+
|
|
783
|
+
const buffer = this.allocateTransientBuffer(usage, type.aligned_size);
|
|
784
|
+
|
|
785
|
+
this.writeValueBuffer(buffer, 0, type, value);
|
|
786
|
+
|
|
787
|
+
return buffer;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
* @template T
|
|
792
|
+
* @param {GPUBuffer} buffer
|
|
793
|
+
* @param {number} buffer_offset
|
|
794
|
+
* @param {WebGPUType} type
|
|
795
|
+
* @param {T} value
|
|
796
|
+
*/
|
|
797
|
+
writeValueBuffer(buffer, buffer_offset, type, value) {
|
|
798
|
+
|
|
799
|
+
assert.notOk(this.#finished, 'already finished');
|
|
800
|
+
|
|
801
|
+
const gfx = this.#graphics;
|
|
802
|
+
|
|
803
|
+
const size = type.aligned_size;
|
|
804
|
+
|
|
805
|
+
const stage = gfx.buffer_allocator_staging.get(size);
|
|
806
|
+
|
|
807
|
+
const mapped = stage.getMappedRange(0, size);
|
|
808
|
+
|
|
809
|
+
write_gpu_typed_buffer(value, type, mapped, 0);
|
|
810
|
+
|
|
811
|
+
stage.unmap();
|
|
812
|
+
|
|
813
|
+
this.#staging_buffers.push(stage);
|
|
814
|
+
|
|
815
|
+
this.copyBufferToBuffer(stage, 0, buffer, buffer_offset, size);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
/**
|
|
819
|
+
* Very similar to {@link GPUQueue.writeBuffer}, but instead of doing the operation direction on the queue - uses a staging buffer under the hood and performs the actual write operation as part of the commmand buffer
|
|
820
|
+
* @param {GPUBuffer} buffer
|
|
821
|
+
* @param {number} buffer_offset
|
|
822
|
+
* @param {ArrayBuffer} data
|
|
823
|
+
* @param {number} data_offset
|
|
824
|
+
* @param {number} size
|
|
825
|
+
*/
|
|
826
|
+
writeBuffer(buffer, buffer_offset, data, data_offset, size) {
|
|
827
|
+
assert.defined(buffer, 'buffer');
|
|
828
|
+
assert.isNonNegativeInteger(buffer_offset, 'buffer_offset');
|
|
829
|
+
assert.defined(data, 'data');
|
|
830
|
+
assert.isNonNegativeInteger(data_offset, 'data_offset');
|
|
831
|
+
assert.isNonNegativeInteger(size, 'size');
|
|
832
|
+
|
|
833
|
+
const gfx = this.#graphics;
|
|
834
|
+
|
|
835
|
+
const stage = gfx.buffer_allocator_staging.get(size);
|
|
836
|
+
|
|
837
|
+
const mapped = stage.getMappedRange(0, size);
|
|
838
|
+
|
|
839
|
+
array_buffer_copy(data, data_offset, mapped, 0, size);
|
|
840
|
+
|
|
841
|
+
stage.unmap();
|
|
842
|
+
|
|
843
|
+
this.#staging_buffers.push(stage);
|
|
844
|
+
|
|
845
|
+
this.copyBufferToBuffer(stage, 0, buffer, buffer_offset, size);
|
|
846
|
+
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
#cleanup() {
|
|
850
|
+
|
|
851
|
+
// console.warn('cmd.cleanup()');
|
|
852
|
+
|
|
853
|
+
const transient_buffers = this.#transient_buffers;
|
|
854
|
+
const transient_buffer_count = transient_buffers.length;
|
|
855
|
+
|
|
856
|
+
const buffer_allocator_transient = this.#transient_allocator;
|
|
857
|
+
|
|
858
|
+
// release in reverse order to make it more likely that we'd end up in the same acquisition order next time, which is desirable for caching
|
|
859
|
+
for (let i = transient_buffer_count - 1; i >= 0; i--) {
|
|
860
|
+
const buffer = transient_buffers[i];
|
|
861
|
+
|
|
862
|
+
buffer_allocator_transient.release(buffer);
|
|
863
|
+
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// staging buffers
|
|
867
|
+
const buffer_allocator_staging = this.#graphics.buffer_allocator_staging;
|
|
868
|
+
for (const staging of this.#staging_buffers) {
|
|
869
|
+
buffer_allocator_staging.release(staging);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
/**
|
|
874
|
+
* Performs both {@link GPUCommandEncoder.finish} and flushed command buffer
|
|
875
|
+
*/
|
|
876
|
+
finish() {
|
|
877
|
+
if (this.#finished) {
|
|
878
|
+
console.warn('Context already finished');
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
this.onBeforeFinish.send1(this);
|
|
883
|
+
|
|
884
|
+
debug_open_context_count--;
|
|
885
|
+
array_remove_first(debug_open_contexts, this);
|
|
886
|
+
|
|
887
|
+
const encoder = this.#gpu_encoder;
|
|
888
|
+
|
|
889
|
+
const debug_timers = this.#debug_timers;
|
|
890
|
+
if (debug_timers !== undefined) {
|
|
891
|
+
debug_timers.resolve(encoder);
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
this.#finished = true;
|
|
895
|
+
|
|
896
|
+
// release encoder reference
|
|
897
|
+
this.#gpu_encoder = undefined;
|
|
898
|
+
|
|
899
|
+
const command_buffer = encoder.finish();
|
|
900
|
+
|
|
901
|
+
const graphics = this.#graphics;
|
|
902
|
+
const queue = graphics.device.queue;
|
|
903
|
+
|
|
904
|
+
queue.submit([command_buffer]);
|
|
905
|
+
|
|
906
|
+
// cleanup allocated memory
|
|
907
|
+
this.#cleanup();
|
|
908
|
+
|
|
909
|
+
if (debug_timers !== undefined) {
|
|
910
|
+
const recorder = this.#profile_recorder;
|
|
911
|
+
const query_set_id = this.#profile_query_set_id;
|
|
912
|
+
|
|
913
|
+
debug_timers
|
|
914
|
+
.download_results()
|
|
915
|
+
.then(() => {
|
|
916
|
+
if (recorder !== null) {
|
|
917
|
+
recorder.absorb(debug_timers, query_set_id);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// The console-shaped path stays exactly as it was; a context can be doing both.
|
|
921
|
+
const table = debug_timers.results_to_console_table();
|
|
922
|
+
|
|
923
|
+
this.#debug_timers_callback(table);
|
|
924
|
+
})
|
|
925
|
+
.finally(() => {
|
|
926
|
+
debug_timers.destroy();
|
|
927
|
+
|
|
928
|
+
// In `finally` so a failed readback still releases whoever is waiting on the
|
|
929
|
+
// frame. A profile session that hangs because one download rejected is worse
|
|
930
|
+
// than one missing a frame.
|
|
931
|
+
this.#profiling_absorbed_resolve();
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
this.#debug_timers = undefined;
|
|
935
|
+
this.#profile_recorder = null;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
this.onFinished.send1(this);
|
|
939
|
+
|
|
940
|
+
}
|
|
750
941
|
}
|