@aethervtt/shard-gpu-webgl2 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2 -2
  3. package/dist/binding.d.ts +32 -0
  4. package/dist/binding.js +81 -0
  5. package/dist/cache.d.ts +73 -0
  6. package/dist/cache.js +184 -0
  7. package/dist/commands.d.ts +101 -0
  8. package/dist/commands.js +391 -0
  9. package/dist/constants.d.ts +41 -0
  10. package/dist/constants.js +38 -0
  11. package/dist/copies.d.ts +61 -0
  12. package/dist/copies.js +629 -0
  13. package/dist/device.d.ts +168 -0
  14. package/dist/device.js +401 -0
  15. package/dist/errors.d.ts +8 -0
  16. package/dist/errors.js +18 -0
  17. package/dist/fake-gl.d.ts +338 -0
  18. package/dist/fake-gl.js +980 -0
  19. package/dist/fbo.d.ts +30 -0
  20. package/dist/fbo.js +142 -0
  21. package/dist/formats.d.ts +25 -0
  22. package/dist/formats.js +106 -0
  23. package/dist/gl.d.ts +216 -0
  24. package/dist/gl.js +232 -0
  25. package/dist/gpu.d.ts +58 -0
  26. package/dist/gpu.js +215 -0
  27. package/dist/index.d.ts +8 -0
  28. package/dist/index.js +42 -0
  29. package/dist/naga.d.ts +37 -0
  30. package/dist/naga.js +99 -0
  31. package/dist/pipeline.d.ts +113 -0
  32. package/dist/pipeline.js +294 -0
  33. package/dist/present.d.ts +18 -0
  34. package/dist/present.js +108 -0
  35. package/dist/queries.d.ts +41 -0
  36. package/dist/queries.js +110 -0
  37. package/dist/queue.d.ts +13 -0
  38. package/dist/queue.js +50 -0
  39. package/dist/readback.d.ts +17 -0
  40. package/dist/readback.js +104 -0
  41. package/dist/replay.d.ts +78 -0
  42. package/dist/replay.js +678 -0
  43. package/dist/resources.d.ts +96 -0
  44. package/dist/resources.js +355 -0
  45. package/dist/state.d.ts +43 -0
  46. package/dist/state.js +169 -0
  47. package/package.json +36 -4
  48. package/wasm/shard_naga.wasm +0 -0
package/dist/replay.js ADDED
@@ -0,0 +1,678 @@
1
+ import { layerOf, mipSize, OP } from "./commands.js";
2
+ import { TextureUsage } from "./constants.js";
3
+ import { Attachments } from "./fbo.js";
4
+ import { GL } from "./gl.js";
5
+ import { Webgl2Texture } from "./resources.js";
6
+ class Replayer {
7
+ device;
8
+ gl;
9
+ vao;
10
+ attachments = new Attachments();
11
+ single = new Attachments();
12
+ stream;
13
+ i = 0;
14
+ r = 0;
15
+ /** The query set timing the pass being replayed. */
16
+ timed;
17
+ // The pass.
18
+ passFbo = null;
19
+ passWidth = 0;
20
+ passHeight = 0;
21
+ colorCount = 0;
22
+ colors = [];
23
+ resolves = [];
24
+ colorLayers = [];
25
+ colorDiscard = [];
26
+ depthView = null;
27
+ depthDiscard = false;
28
+ stencilDiscard = false;
29
+ depthReadOnly = false;
30
+ depthCopied = false;
31
+ scratchDepth = /* @__PURE__ */ new Map();
32
+ discards = [];
33
+ clearFloat = new Float32Array(4);
34
+ clearUint = new Uint32Array(4);
35
+ clearInt = new Int32Array(4);
36
+ // Bindings.
37
+ pipeline = null;
38
+ groups = [null, null, null, null];
39
+ offsets = [[], [], [], []];
40
+ bindingsDirty = true;
41
+ vertexBuffers = [];
42
+ vertexOffsets = [];
43
+ attributesDirty = true;
44
+ attributePipeline = null;
45
+ baseVertex = 0;
46
+ firstInstance = 0;
47
+ indexBuffer = null;
48
+ indexBytes = 2;
49
+ indexOffset = 0;
50
+ stencilReference = 0;
51
+ // Vertex attribute state, by location.
52
+ attribBuffer = [];
53
+ attribOffset = [];
54
+ attribStride = [];
55
+ attribFormat = [];
56
+ attribDivisor = [];
57
+ attribEnabled = [];
58
+ attribStamp = [];
59
+ stamp = 0;
60
+ element = null;
61
+ constructor(device) {
62
+ this.device = device;
63
+ this.gl = device.gl;
64
+ this.vao = this.gl.createVertexArray();
65
+ }
66
+ /** Forgets cached bindings: after a context loss, or when GL state was changed elsewhere. */
67
+ reset() {
68
+ this.attribBuffer.length = 0;
69
+ this.attribOffset.length = 0;
70
+ this.attribStride.length = 0;
71
+ this.attribFormat.length = 0;
72
+ this.attribDivisor.length = 0;
73
+ this.attribEnabled.length = 0;
74
+ this.attribStamp.length = 0;
75
+ this.element = null;
76
+ }
77
+ /** Drops the depth copies made for `texture`. */
78
+ forget(texture) {
79
+ const copy = this.scratchDepth.get(texture);
80
+ if (copy) {
81
+ copy.destroy();
82
+ this.scratchDepth.delete(texture);
83
+ }
84
+ }
85
+ run(stream) {
86
+ this.stream = stream;
87
+ this.i = 0;
88
+ this.r = 0;
89
+ const nums = stream.nums;
90
+ const n = stream.n;
91
+ try {
92
+ while (this.i < n) {
93
+ const op = nums[this.i++];
94
+ switch (op) {
95
+ case OP.BEGIN_PASS:
96
+ this.beginPass();
97
+ break;
98
+ case OP.END_PASS:
99
+ this.endPass();
100
+ break;
101
+ case OP.SET_PIPELINE:
102
+ this.pipeline = this.ref();
103
+ this.bindingsDirty = true;
104
+ this.attributesDirty = true;
105
+ break;
106
+ case OP.SET_BIND_GROUP: {
107
+ const index = this.num();
108
+ const count = this.num();
109
+ const offsets = this.offsets[index];
110
+ offsets.length = count;
111
+ for (let k = 0; k < count; k++) offsets[k] = this.num();
112
+ this.groups[index] = this.ref();
113
+ this.bindingsDirty = true;
114
+ break;
115
+ }
116
+ case OP.SET_VERTEX_BUFFER: {
117
+ const slot = this.num();
118
+ this.vertexOffsets[slot] = this.num();
119
+ this.num();
120
+ this.vertexBuffers[slot] = this.ref();
121
+ this.attributesDirty = true;
122
+ break;
123
+ }
124
+ case OP.SET_INDEX_BUFFER:
125
+ this.indexBytes = this.num();
126
+ this.indexOffset = this.num();
127
+ this.num();
128
+ this.indexBuffer = this.ref();
129
+ break;
130
+ case OP.DRAW:
131
+ this.draw();
132
+ break;
133
+ case OP.DRAW_INDEXED:
134
+ this.drawIndexed();
135
+ break;
136
+ case OP.VIEWPORT: {
137
+ const x = this.num();
138
+ const y = this.num();
139
+ const w = this.num();
140
+ const h = this.num();
141
+ this.gl.viewport(x, y, w, h);
142
+ this.gl.depthRange(this.num(), this.num());
143
+ break;
144
+ }
145
+ case OP.SCISSOR: {
146
+ const x = this.num();
147
+ const y = this.num();
148
+ const w = this.num();
149
+ const h = this.num();
150
+ this.device.state.enable(GL.SCISSOR_TEST, true);
151
+ this.gl.scissor(x, y, w, h);
152
+ break;
153
+ }
154
+ case OP.STENCIL_REFERENCE:
155
+ this.stencilReference = this.num();
156
+ break;
157
+ case OP.BLEND_CONSTANT:
158
+ this.gl.blendColor(this.num(), this.num(), this.num(), this.num());
159
+ break;
160
+ case OP.COPY_BUFFER: {
161
+ const so = this.num();
162
+ const dO = this.num();
163
+ const size = this.num();
164
+ const src = this.ref();
165
+ const dst = this.ref();
166
+ this.device.copier.copyBuffer(src, so, dst, dO, size);
167
+ break;
168
+ }
169
+ case OP.COPY_BUFFER_TO_TEXTURE: {
170
+ const offset = this.num();
171
+ const bytesPerRow = this.num();
172
+ const rowsPerImage = this.num();
173
+ const mip = this.num();
174
+ const x = this.num();
175
+ const y = this.num();
176
+ const z = this.num();
177
+ const w = this.num();
178
+ const h = this.num();
179
+ const d = this.num();
180
+ const buffer = this.ref();
181
+ const texture = this.ref();
182
+ this.device.copier.copyBufferToTexture(
183
+ buffer,
184
+ offset,
185
+ bytesPerRow,
186
+ rowsPerImage,
187
+ texture,
188
+ mip,
189
+ x,
190
+ y,
191
+ z,
192
+ w,
193
+ h,
194
+ d
195
+ );
196
+ break;
197
+ }
198
+ case OP.COPY_TEXTURE_TO_BUFFER: {
199
+ const mip = this.num();
200
+ const x = this.num();
201
+ const y = this.num();
202
+ const z = this.num();
203
+ const offset = this.num();
204
+ const bytesPerRow = this.num();
205
+ const rowsPerImage = this.num();
206
+ const w = this.num();
207
+ const h = this.num();
208
+ const d = this.num();
209
+ const texture = this.ref();
210
+ const buffer = this.ref();
211
+ this.device.copier.copyTextureToBuffer(
212
+ texture,
213
+ mip,
214
+ x,
215
+ y,
216
+ z,
217
+ buffer,
218
+ offset,
219
+ bytesPerRow,
220
+ rowsPerImage,
221
+ w,
222
+ h,
223
+ d
224
+ );
225
+ break;
226
+ }
227
+ case OP.COPY_TEXTURE: {
228
+ const sm = this.num();
229
+ const sx = this.num();
230
+ const sy = this.num();
231
+ const sz = this.num();
232
+ const dm = this.num();
233
+ const dx = this.num();
234
+ const dy = this.num();
235
+ const dz = this.num();
236
+ const w = this.num();
237
+ const h = this.num();
238
+ const d = this.num();
239
+ const src = this.ref();
240
+ const dst = this.ref();
241
+ this.device.copier.copyTexture(src, sm, sx, sy, sz, dst, dm, dx, dy, dz, w, h, d);
242
+ break;
243
+ }
244
+ case OP.CLEAR_BUFFER: {
245
+ const offset = this.num();
246
+ const size = this.num();
247
+ this.device.copier.clearBuffer(this.ref(), offset, size);
248
+ break;
249
+ }
250
+ case OP.BEGIN_QUERY: {
251
+ const pair = this.num();
252
+ const set = this.ref();
253
+ set.begin(pair);
254
+ this.timed = set;
255
+ break;
256
+ }
257
+ case OP.END_QUERY:
258
+ this.timed?.end();
259
+ this.timed = void 0;
260
+ break;
261
+ case OP.RESOLVE_QUERIES: {
262
+ const first = this.num();
263
+ const count = this.num();
264
+ const offset = this.num();
265
+ const set = this.ref();
266
+ set.resolve(first, count, this.ref(), offset);
267
+ break;
268
+ }
269
+ default:
270
+ throw new Error(`Unknown recorded command ${op}`);
271
+ }
272
+ }
273
+ } finally {
274
+ this.stream = void 0;
275
+ this.clearBindings();
276
+ }
277
+ }
278
+ num() {
279
+ return this.stream.nums[this.i++];
280
+ }
281
+ ref() {
282
+ return this.stream.refs[this.r++];
283
+ }
284
+ clearBindings() {
285
+ this.pipeline = null;
286
+ for (let g = 0; g < 4; g++) this.groups[g] = null;
287
+ for (let s = 0; s < this.vertexBuffers.length; s++) this.vertexBuffers[s] = null;
288
+ this.indexBuffer = null;
289
+ for (let c = 0; c < this.colorCount; c++) {
290
+ this.colors[c] = null;
291
+ this.resolves[c] = null;
292
+ }
293
+ this.depthView = null;
294
+ this.bindingsDirty = true;
295
+ this.attributesDirty = true;
296
+ }
297
+ // --- passes ----------------------------------------------------------------------
298
+ beginPass() {
299
+ const gl = this.gl;
300
+ const state = this.device.state;
301
+ const a = this.attachments;
302
+ a.reset();
303
+ const count = this.num();
304
+ this.colorCount = count;
305
+ let width = 0;
306
+ let height = 0;
307
+ let clears = 0;
308
+ for (let c = 0; c < count; c++) {
309
+ if (this.num() === 0) {
310
+ this.colors[c] = null;
311
+ this.resolves[c] = null;
312
+ a.color(c, null, 0, 0);
313
+ continue;
314
+ }
315
+ const view = this.ref();
316
+ const resolve = this.ref();
317
+ const clear = this.num() === 1;
318
+ this.colorDiscard[c] = this.num() === 1;
319
+ const layer = layerOf(view, this.num());
320
+ this.colors[c] = view;
321
+ this.resolves[c] = resolve;
322
+ this.colorLayers[c] = layer;
323
+ a.color(c, view.texture, view.baseMipLevel, layer);
324
+ width = mipSize(view.texture.width, view.baseMipLevel);
325
+ height = mipSize(view.texture.height, view.baseMipLevel);
326
+ const r = this.num();
327
+ const g = this.num();
328
+ const b = this.num();
329
+ const al = this.num();
330
+ if (clear) clears |= 1 << c;
331
+ this.pending[c * 4] = r;
332
+ this.pending[c * 4 + 1] = g;
333
+ this.pending[c * 4 + 2] = b;
334
+ this.pending[c * 4 + 3] = al;
335
+ }
336
+ let depthClear = false;
337
+ let stencilClear = false;
338
+ let depthValue = 0;
339
+ let stencilValue = 0;
340
+ if (this.num() === 1) {
341
+ const view = this.ref();
342
+ depthClear = this.num() === 1;
343
+ this.depthDiscard = this.num() === 1;
344
+ depthValue = this.num();
345
+ this.depthReadOnly = this.num() === 1;
346
+ stencilClear = this.num() === 1;
347
+ this.stencilDiscard = this.num() === 1;
348
+ stencilValue = this.num();
349
+ this.num();
350
+ this.depthView = view;
351
+ a.setDepth(view.texture, view.baseMipLevel, view.baseArrayLayer);
352
+ if (width === 0) {
353
+ width = mipSize(view.texture.width, view.baseMipLevel);
354
+ height = mipSize(view.texture.height, view.baseMipLevel);
355
+ }
356
+ } else {
357
+ this.depthView = null;
358
+ this.depthReadOnly = false;
359
+ }
360
+ this.depthCopied = false;
361
+ this.passWidth = width;
362
+ this.passHeight = height;
363
+ this.passFbo = this.device.fbos.get(a);
364
+ gl.bindFramebuffer(GL.FRAMEBUFFER, this.passFbo);
365
+ if (clears !== 0 || depthClear || stencilClear) {
366
+ state.prepareClear();
367
+ for (let c = 0; c < count; c++) {
368
+ if ((clears & 1 << c) === 0) continue;
369
+ const kind = this.colors[c].texture.info.kind;
370
+ const p = this.pending;
371
+ if (kind === "uint") {
372
+ const v = this.clearUint;
373
+ v[0] = p[c * 4];
374
+ v[1] = p[c * 4 + 1];
375
+ v[2] = p[c * 4 + 2];
376
+ v[3] = p[c * 4 + 3];
377
+ gl.clearBufferuiv(GL.COLOR, c, v);
378
+ } else if (kind === "sint") {
379
+ const v = this.clearInt;
380
+ v[0] = p[c * 4];
381
+ v[1] = p[c * 4 + 1];
382
+ v[2] = p[c * 4 + 2];
383
+ v[3] = p[c * 4 + 3];
384
+ gl.clearBufferiv(GL.COLOR, c, v);
385
+ } else {
386
+ const v = this.clearFloat;
387
+ v[0] = p[c * 4];
388
+ v[1] = p[c * 4 + 1];
389
+ v[2] = p[c * 4 + 2];
390
+ v[3] = p[c * 4 + 3];
391
+ gl.clearBufferfv(GL.COLOR, c, v);
392
+ }
393
+ }
394
+ const info = this.depthView?.texture.info;
395
+ if (info?.depth && info.stencil && depthClear && stencilClear) {
396
+ gl.clearBufferfi(GL.DEPTH_STENCIL, 0, depthValue, stencilValue);
397
+ } else {
398
+ if (depthClear && info?.depth) {
399
+ this.clearFloat[0] = depthValue;
400
+ gl.clearBufferfv(GL.DEPTH, 0, this.clearFloat);
401
+ }
402
+ if (stencilClear && info?.stencil) {
403
+ this.clearInt[0] = stencilValue;
404
+ gl.clearBufferiv(GL.STENCIL, 0, this.clearInt);
405
+ }
406
+ }
407
+ }
408
+ gl.viewport(0, 0, width, height);
409
+ gl.depthRange(0, 1);
410
+ state.enable(GL.SCISSOR_TEST, false);
411
+ this.stencilReference = 0;
412
+ this.bindingsDirty = true;
413
+ this.attributesDirty = true;
414
+ }
415
+ pending = [];
416
+ endPass() {
417
+ const gl = this.gl;
418
+ const device = this.device;
419
+ const w = this.passWidth;
420
+ const h = this.passHeight;
421
+ let resolved = false;
422
+ for (let c = 0; c < this.colorCount; c++) {
423
+ const target = this.resolves[c];
424
+ if (!target) continue;
425
+ const s = this.single;
426
+ s.reset();
427
+ s.color(0, target.texture, target.baseMipLevel, layerOf(target, -1));
428
+ const to = device.fbos.get(s);
429
+ gl.bindFramebuffer(GL.READ_FRAMEBUFFER, this.passFbo);
430
+ gl.readBuffer(GL.COLOR_ATTACHMENT0 + c);
431
+ gl.bindFramebuffer(GL.DRAW_FRAMEBUFFER, to);
432
+ device.state.enable(GL.SCISSOR_TEST, false);
433
+ gl.blitFramebuffer(0, 0, w, h, 0, 0, w, h, GL.COLOR_BUFFER_BIT, GL.NEAREST);
434
+ device.touch(target.texture);
435
+ resolved = true;
436
+ }
437
+ if (resolved) gl.bindFramebuffer(GL.FRAMEBUFFER, this.passFbo);
438
+ const discards = this.discards;
439
+ discards.length = 0;
440
+ for (let c = 0; c < this.colorCount; c++) {
441
+ const view = this.colors[c];
442
+ if (!view) continue;
443
+ if (this.colorDiscard[c]) discards.push(GL.COLOR_ATTACHMENT0 + c);
444
+ else device.touch(view.texture);
445
+ }
446
+ const depth = this.depthView;
447
+ if (depth && !this.depthReadOnly) {
448
+ const info = depth.texture.info;
449
+ if (info.depth && info.stencil && this.depthDiscard && this.stencilDiscard) {
450
+ discards.push(GL.DEPTH_STENCIL_ATTACHMENT);
451
+ } else if (info.depth && this.depthDiscard && !info.stencil) {
452
+ discards.push(GL.DEPTH_ATTACHMENT);
453
+ } else if (!info.depth && info.stencil && this.stencilDiscard) {
454
+ discards.push(GL.STENCIL_ATTACHMENT);
455
+ }
456
+ }
457
+ if (discards.length > 0) gl.invalidateFramebuffer(GL.FRAMEBUFFER, discards);
458
+ }
459
+ // --- draws ----------------------------------------------------------------------
460
+ draw() {
461
+ const count = this.num();
462
+ const instances = this.num();
463
+ const first = this.num();
464
+ const firstInstance = this.num();
465
+ if (count === 0 || instances === 0) return;
466
+ const p = this.prepare(0, firstInstance);
467
+ if (!p) return;
468
+ this.gl.drawArraysInstanced(p.topology, first, count, instances);
469
+ }
470
+ drawIndexed() {
471
+ const count = this.num();
472
+ const instances = this.num();
473
+ const firstIndex = this.num();
474
+ const baseVertex = this.num();
475
+ const firstInstance = this.num();
476
+ if (count === 0 || instances === 0) return;
477
+ const p = this.pipeline;
478
+ if (p && baseVertex !== 0 && p.vertexIndex) {
479
+ this.device.raise(
480
+ "validation",
481
+ `"${p.label}" reads vertex_index with a base vertex of ${baseVertex}: WebGL2 can't offset vertex_index`
482
+ );
483
+ return;
484
+ }
485
+ const ib = this.indexBuffer;
486
+ if (!p || !ib) return;
487
+ let buffer = ib.gl;
488
+ let type = this.indexBytes === 4 ? GL.UNSIGNED_INT : GL.UNSIGNED_SHORT;
489
+ let offset = this.indexOffset + firstIndex * this.indexBytes;
490
+ if (type === GL.UNSIGNED_SHORT && p.topology !== GL.TRIANGLE_STRIP && p.topology !== GL.LINE_STRIP) {
491
+ const promoted = promote(this.device, ib);
492
+ if (promoted) {
493
+ buffer = promoted;
494
+ type = GL.UNSIGNED_INT;
495
+ offset *= 2;
496
+ }
497
+ }
498
+ if (!this.prepare(baseVertex, firstInstance)) return;
499
+ if (this.element !== buffer) {
500
+ this.element = buffer;
501
+ this.gl.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, buffer);
502
+ }
503
+ this.gl.drawElementsInstanced(p.topology, count, type, offset, instances);
504
+ }
505
+ /** Binds what the draw needs. Undefined when there's nothing valid to draw with. */
506
+ prepare(baseVertex, firstInstance) {
507
+ const p = this.pipeline;
508
+ if (!p?.program) return void 0;
509
+ const state = this.device.state;
510
+ state.applyPipeline(p);
511
+ state.applyStencil(p, this.stencilReference);
512
+ if (this.bindingsDirty) {
513
+ if (!this.bind(p)) return void 0;
514
+ this.bindingsDirty = false;
515
+ }
516
+ state.bindVertexArray(this.vao);
517
+ if (this.attributesDirty || this.attributePipeline !== p || this.baseVertex !== baseVertex || this.firstInstance !== firstInstance) {
518
+ this.attributes(p, baseVertex, firstInstance);
519
+ }
520
+ if (p.firstInstance && p.firstInstanceValue !== firstInstance) {
521
+ p.firstInstanceValue = firstInstance;
522
+ this.gl.uniform1ui(p.firstInstance, firstInstance);
523
+ }
524
+ return p;
525
+ }
526
+ /** Uniform blocks and textures from the bound groups. False if a group the pipeline reads is missing. */
527
+ bind(p) {
528
+ const device = this.device;
529
+ const state = device.state;
530
+ const blocks = p.blocks;
531
+ for (let i = 0; i < blocks.length; i++) {
532
+ const b = blocks[i];
533
+ const group = this.groups[b.group];
534
+ const bound = group?.buffers[b.binding];
535
+ if (!group || !bound) {
536
+ device.raise(
537
+ "validation",
538
+ `"${p.label}" draws without bind group ${b.group} binding ${b.binding}`
539
+ );
540
+ return false;
541
+ }
542
+ let offset = bound.offset;
543
+ const dynamic = group.dynamic;
544
+ for (let k = 0; k < dynamic.length; k++) {
545
+ if (dynamic[k] === b.binding) {
546
+ offset += this.offsets[b.group][k] ?? 0;
547
+ break;
548
+ }
549
+ }
550
+ const size = Math.min(Math.max(bound.size, b.size), bound.buffer.allocated - offset);
551
+ state.bindUniformBuffer(b.point, bound.buffer.gl, offset, size);
552
+ }
553
+ const samplers = p.samplers;
554
+ for (let i = 0; i < samplers.length; i++) {
555
+ const s = samplers[i];
556
+ const group = this.groups[s.group];
557
+ const view = group?.views[s.binding];
558
+ if (!group || !view) {
559
+ device.raise(
560
+ "validation",
561
+ `"${p.label}" draws without bind group ${s.group} binding ${s.binding}`
562
+ );
563
+ return false;
564
+ }
565
+ let texture = view.texture;
566
+ if (this.depthReadOnly && this.depthView?.texture === texture)
567
+ texture = this.depthCopy(texture);
568
+ state.bindUnit(s.unit, texture.target, texture.gl);
569
+ device.levels(texture, view.baseMipLevel, view.baseMipLevel + view.mipLevelCount - 1);
570
+ const sampler = s.samplerGroup >= 0 ? this.groups[s.samplerGroup]?.samplers[s.samplerBinding] : void 0;
571
+ state.bindSampler(s.unit, sampler ? sampler.gl : null);
572
+ }
573
+ return true;
574
+ }
575
+ /** A copy of the read-only depth attachment for sampling, refreshed once per pass. */
576
+ depthCopy(texture) {
577
+ let copy = this.scratchDepth.get(texture);
578
+ if (!copy) {
579
+ copy = new Webgl2Texture(this.device, {
580
+ label: `${texture.label} (sampled copy)`,
581
+ size: [texture.width, texture.height],
582
+ format: texture.format,
583
+ usage: TextureUsage.TEXTURE_BINDING | TextureUsage.RENDER_ATTACHMENT
584
+ });
585
+ this.scratchDepth.set(texture, copy);
586
+ }
587
+ if (!this.depthCopied) {
588
+ const gl = this.gl;
589
+ const s = this.single;
590
+ s.reset();
591
+ s.setDepth(copy, 0, 0);
592
+ const to = this.device.fbos.get(s);
593
+ gl.bindFramebuffer(GL.READ_FRAMEBUFFER, this.passFbo);
594
+ gl.bindFramebuffer(GL.DRAW_FRAMEBUFFER, to);
595
+ this.device.state.enable(GL.SCISSOR_TEST, false);
596
+ const w = texture.width;
597
+ const h = texture.height;
598
+ gl.blitFramebuffer(0, 0, w, h, 0, 0, w, h, GL.DEPTH_BUFFER_BIT, GL.NEAREST);
599
+ gl.bindFramebuffer(GL.FRAMEBUFFER, this.passFbo);
600
+ this.depthCopied = true;
601
+ }
602
+ return copy;
603
+ }
604
+ /** Points each attribute of `p` at its buffer, moved by the base vertex or first instance. */
605
+ attributes(p, baseVertex, firstInstance) {
606
+ const gl = this.gl;
607
+ const stamp = ++this.stamp;
608
+ const layouts = p.vertexBuffers;
609
+ let bound;
610
+ for (let slot = 0; slot < layouts.length; slot++) {
611
+ const layout = layouts[slot];
612
+ const buffer = this.vertexBuffers[slot];
613
+ if (!buffer) continue;
614
+ const base = (this.vertexOffsets[slot] ?? 0) + (layout.instanced ? firstInstance : baseVertex) * layout.stride;
615
+ const attributes = layout.attributes;
616
+ for (let k = 0; k < attributes.length; k++) {
617
+ const a = attributes[k];
618
+ const loc = a.location;
619
+ this.attribStamp[loc] = stamp;
620
+ if (!this.attribEnabled[loc]) {
621
+ gl.enableVertexAttribArray(loc);
622
+ this.attribEnabled[loc] = true;
623
+ }
624
+ const offset = base + a.offset;
625
+ const format = a.size | a.type << 3 | (a.normalized ? 1 << 20 : 0) | (a.integer ? 1 << 21 : 0);
626
+ if (this.attribBuffer[loc] !== buffer.gl || this.attribOffset[loc] !== offset || this.attribStride[loc] !== layout.stride || this.attribFormat[loc] !== format) {
627
+ if (bound !== buffer.gl) {
628
+ gl.bindBuffer(GL.ARRAY_BUFFER, buffer.gl);
629
+ bound = buffer.gl;
630
+ }
631
+ if (a.integer) gl.vertexAttribIPointer(loc, a.size, a.type, layout.stride, offset);
632
+ else gl.vertexAttribPointer(loc, a.size, a.type, a.normalized, layout.stride, offset);
633
+ this.attribBuffer[loc] = buffer.gl;
634
+ this.attribOffset[loc] = offset;
635
+ this.attribStride[loc] = layout.stride;
636
+ this.attribFormat[loc] = format;
637
+ }
638
+ const divisor = layout.instanced ? 1 : 0;
639
+ if (this.attribDivisor[loc] !== divisor) {
640
+ gl.vertexAttribDivisor(loc, divisor);
641
+ this.attribDivisor[loc] = divisor;
642
+ }
643
+ }
644
+ }
645
+ for (let loc = 0; loc < this.attribEnabled.length; loc++) {
646
+ if (this.attribEnabled[loc] && this.attribStamp[loc] !== stamp) {
647
+ gl.disableVertexAttribArray(loc);
648
+ this.attribEnabled[loc] = false;
649
+ }
650
+ }
651
+ this.attributesDirty = false;
652
+ this.attributePipeline = p;
653
+ this.baseVertex = baseVertex;
654
+ this.firstInstance = firstInstance;
655
+ }
656
+ }
657
+ function promote(device, buffer) {
658
+ if (buffer.restartVersion !== buffer.version) {
659
+ buffer.restartVersion = buffer.version;
660
+ const shadow = buffer.shadow;
661
+ const indices = new Uint16Array(shadow.buffer, shadow.byteOffset, shadow.byteLength >> 1);
662
+ buffer.restart = indices.includes(65535);
663
+ if (buffer.restart) {
664
+ const gl = device.gl;
665
+ const wide = new Uint32Array(indices);
666
+ device.state.bindVertexArray(null);
667
+ const target = buffer.promoted ?? gl.createBuffer();
668
+ gl.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, target);
669
+ gl.bufferData(GL.ELEMENT_ARRAY_BUFFER, wide, GL.STATIC_DRAW);
670
+ gl.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, null);
671
+ buffer.promoted = target;
672
+ }
673
+ }
674
+ return buffer.restart ? buffer.promoted : null;
675
+ }
676
+ export {
677
+ Replayer
678
+ };