@aethervtt/shard-gpu-webgl2 0.0.0-stage → 0.1.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/dist/copies.js ADDED
@@ -0,0 +1,621 @@
1
+ import { BufferUsage, TextureUsage } from "./constants.js";
2
+ import { unsupported } from "./errors.js";
3
+ import { Attachments } from "./fbo.js";
4
+ import { GL } from "./gl.js";
5
+ import { readFormatOf, repack } from "./readback.js";
6
+ import { Webgl2Texture } from "./resources.js";
7
+ function arrayFor(type) {
8
+ switch (type) {
9
+ case GL.UNSIGNED_BYTE:
10
+ return Uint8Array;
11
+ case GL.BYTE:
12
+ return Int8Array;
13
+ case GL.UNSIGNED_SHORT:
14
+ case GL.HALF_FLOAT:
15
+ return Uint16Array;
16
+ case GL.INT:
17
+ return Int32Array;
18
+ case GL.FLOAT:
19
+ return Float32Array;
20
+ default:
21
+ return Uint32Array;
22
+ }
23
+ }
24
+ const DEPTH_VS = `#version 300 es
25
+ void main() {
26
+ vec2 p = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));
27
+ gl_Position = vec4(p * 2.0 - 1.0, 0.0, 1.0);
28
+ }
29
+ `;
30
+ const depthFs = (array) => `#version 300 es
31
+ precision highp float;
32
+ precision highp int;
33
+ uniform highp ${array ? "sampler2DArray" : "sampler2D"} depth_texture;
34
+ uniform ivec3 origin;
35
+ uniform int level;
36
+ out vec4 color;
37
+ void main() {
38
+ ivec2 p = ivec2(gl_FragCoord.xy) + origin.xy;
39
+ color = vec4(texelFetch(depth_texture, ${array ? "ivec3(p, origin.z)" : "p"}, level).r, 0.0, 0.0, 1.0);
40
+ }
41
+ `;
42
+ class Copier {
43
+ device;
44
+ gl;
45
+ read = new Attachments();
46
+ draw = new Attachments();
47
+ rowLength = 0;
48
+ imageHeight = 0;
49
+ views = /* @__PURE__ */ new Map();
50
+ viewOffset = 0;
51
+ zeros = new Uint8Array(0);
52
+ packs = [];
53
+ depthPrograms = [
54
+ void 0,
55
+ void 0
56
+ ];
57
+ depthTarget;
58
+ constructor(device) {
59
+ this.device = device;
60
+ this.gl = device.gl;
61
+ }
62
+ // --- queue writes -----------------------------------------------------------------
63
+ writeTexture(texture, mip, x, y, z, data, offset, bytesPerRow, rowsPerImage, width, height, depth) {
64
+ if (!texture.gl) throw unsupported(`write into "${texture.label}" (it's only an attachment)`);
65
+ const gl = this.gl;
66
+ const info = texture.info;
67
+ const buffer = ArrayBuffer.isView(data) ? data.buffer : data;
68
+ const base = (ArrayBuffer.isView(data) ? data.byteOffset : 0) + offset;
69
+ this.device.state.bindTexture(texture.target, texture.gl);
70
+ if (info.compressed) {
71
+ const blocksWide = Math.ceil(width / 4);
72
+ const blocksHigh = Math.ceil(height / 4);
73
+ const tight = blocksWide * info.bytes;
74
+ const stride = bytesPerRow ?? tight;
75
+ const image = (rowsPerImage ?? height) / 4;
76
+ for (let layer = 0; layer < depth; layer++) {
77
+ const start = base + layer * image * stride;
78
+ if (stride === tight) {
79
+ this.compressed(
80
+ texture,
81
+ mip,
82
+ x,
83
+ y,
84
+ z + layer,
85
+ width,
86
+ height,
87
+ new Uint8Array(buffer, start, tight * blocksHigh)
88
+ );
89
+ } else {
90
+ for (let row = 0; row < blocksHigh; row++) {
91
+ const h = Math.min(4, height - row * 4);
92
+ this.compressed(
93
+ texture,
94
+ mip,
95
+ x,
96
+ y + row * 4,
97
+ z + layer,
98
+ width,
99
+ h,
100
+ new Uint8Array(buffer, start + row * stride, tight)
101
+ );
102
+ }
103
+ }
104
+ }
105
+ return;
106
+ }
107
+ const rowBytes = bytesPerRow ?? width * info.bytes;
108
+ if (rowBytes % info.bytes !== 0) {
109
+ for (let layer = 0; layer < depth; layer++) {
110
+ for (let row = 0; row < height; row++) {
111
+ const start = base + (layer * (rowsPerImage ?? height) + row) * rowBytes;
112
+ const bytes = new Uint8Array(buffer, start, width * info.bytes).slice();
113
+ this.writeTexture(
114
+ texture,
115
+ mip,
116
+ x,
117
+ y + row,
118
+ z + layer,
119
+ bytes,
120
+ 0,
121
+ void 0,
122
+ void 0,
123
+ width,
124
+ 1,
125
+ 1
126
+ );
127
+ }
128
+ }
129
+ return;
130
+ }
131
+ const view = this.view(info.type, buffer, base);
132
+ const rows = rowsPerImage ?? height;
133
+ this.unpack(rowBytes / info.bytes, rows);
134
+ const at = this.viewOffset;
135
+ if (texture.target === GL.TEXTURE_2D) {
136
+ gl.texSubImage2D(GL.TEXTURE_2D, mip, x, y, width, height, info.format, info.type, view, at);
137
+ } else if (texture.target === GL.TEXTURE_CUBE_MAP) {
138
+ const step = rows * rowBytes / view.BYTES_PER_ELEMENT;
139
+ for (let layer = 0; layer < depth; layer++) {
140
+ gl.texSubImage2D(
141
+ GL.TEXTURE_CUBE_MAP_POSITIVE_X + z + layer,
142
+ mip,
143
+ x,
144
+ y,
145
+ width,
146
+ height,
147
+ info.format,
148
+ info.type,
149
+ view,
150
+ at + layer * step
151
+ );
152
+ }
153
+ } else {
154
+ gl.texSubImage3D(
155
+ texture.target,
156
+ mip,
157
+ x,
158
+ y,
159
+ z,
160
+ width,
161
+ height,
162
+ depth,
163
+ info.format,
164
+ info.type,
165
+ view,
166
+ at
167
+ );
168
+ }
169
+ }
170
+ copyExternal(source, destination, width, height) {
171
+ const texture = destination.texture;
172
+ if (!texture.gl)
173
+ throw unsupported(`copy an image into "${texture.label}" (it's only an attachment)`);
174
+ const gl = this.gl;
175
+ const info = texture.info;
176
+ const o = destination.origin;
177
+ const dx = (Array.isArray(o) ? o[0] : o?.x) ?? 0;
178
+ const dy = (Array.isArray(o) ? o[1] : o?.y) ?? 0;
179
+ const dz = (Array.isArray(o) ? o[2] : o?.z) ?? 0;
180
+ const so = source.origin;
181
+ const sx = (Array.isArray(so) ? so[0] : so?.x) ?? 0;
182
+ const sy = (Array.isArray(so) ? so[1] : so?.y) ?? 0;
183
+ this.device.state.bindTexture(texture.target, texture.gl);
184
+ this.unpack(0, 0);
185
+ gl.pixelStorei(GL.UNPACK_FLIP_Y_WEBGL, source.flipY === true);
186
+ gl.pixelStorei(GL.UNPACK_PREMULTIPLY_ALPHA_WEBGL, destination.premultipliedAlpha === true);
187
+ gl.pixelStorei(GL.UNPACK_SKIP_PIXELS, sx);
188
+ gl.pixelStorei(GL.UNPACK_SKIP_ROWS, sy);
189
+ const image = source.source;
190
+ const mip = destination.mipLevel ?? 0;
191
+ try {
192
+ if (texture.target === GL.TEXTURE_2D) {
193
+ gl.texSubImage2D(GL.TEXTURE_2D, mip, dx, dy, width, height, info.format, info.type, image);
194
+ } else if (texture.target === GL.TEXTURE_CUBE_MAP) {
195
+ gl.texSubImage2D(
196
+ GL.TEXTURE_CUBE_MAP_POSITIVE_X + dz,
197
+ mip,
198
+ dx,
199
+ dy,
200
+ width,
201
+ height,
202
+ info.format,
203
+ info.type,
204
+ image
205
+ );
206
+ } else {
207
+ gl.texSubImage3D(
208
+ texture.target,
209
+ mip,
210
+ dx,
211
+ dy,
212
+ dz,
213
+ width,
214
+ height,
215
+ 1,
216
+ info.format,
217
+ info.type,
218
+ image
219
+ );
220
+ }
221
+ } finally {
222
+ gl.pixelStorei(GL.UNPACK_FLIP_Y_WEBGL, false);
223
+ gl.pixelStorei(GL.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
224
+ gl.pixelStorei(GL.UNPACK_SKIP_PIXELS, 0);
225
+ gl.pixelStorei(GL.UNPACK_SKIP_ROWS, 0);
226
+ }
227
+ this.device.touch(texture);
228
+ }
229
+ // --- encoder copies ------------------------------------------------------------------
230
+ copyBuffer(src, srcOffset, dst, dstOffset, size) {
231
+ const gl = this.gl;
232
+ gl.bindBuffer(GL.COPY_READ_BUFFER, src.gl);
233
+ gl.bindBuffer(GL.COPY_WRITE_BUFFER, dst.gl);
234
+ gl.copyBufferSubData(GL.COPY_READ_BUFFER, GL.COPY_WRITE_BUFFER, srcOffset, dstOffset, size);
235
+ if (dst.shadow) {
236
+ if (src.shadow) dst.shadow.set(src.shadow.subarray(srcOffset, srcOffset + size), dstOffset);
237
+ else gl.getBufferSubData(GL.COPY_WRITE_BUFFER, dstOffset, dst.shadow, dstOffset, size);
238
+ dst.version++;
239
+ }
240
+ gl.bindBuffer(GL.COPY_READ_BUFFER, null);
241
+ gl.bindBuffer(GL.COPY_WRITE_BUFFER, null);
242
+ }
243
+ clearBuffer(buffer, offset, size) {
244
+ if (size <= 0) return;
245
+ if (this.zeros.length < size) this.zeros = new Uint8Array(size);
246
+ buffer.write(offset, this.zeros, 0, size);
247
+ }
248
+ copyBufferToTexture(buffer, offset, bytesPerRow, rowsPerImage, texture, mip, x, y, z, width, height, depth) {
249
+ if (!texture.gl) throw unsupported(`copy into "${texture.label}" (it's only an attachment)`);
250
+ const gl = this.gl;
251
+ const info = texture.info;
252
+ this.device.state.bindTexture(texture.target, texture.gl);
253
+ gl.bindBuffer(GL.PIXEL_UNPACK_BUFFER, buffer.gl);
254
+ try {
255
+ if (info.compressed) {
256
+ const blocksWide = Math.ceil(width / 4);
257
+ const blocksHigh = Math.ceil(height / 4);
258
+ const tight = blocksWide * info.bytes;
259
+ for (let layer = 0; layer < depth; layer++) {
260
+ const start = offset + layer * (rowsPerImage / 4) * bytesPerRow;
261
+ if (bytesPerRow === tight) {
262
+ this.compressedAt(
263
+ texture,
264
+ mip,
265
+ x,
266
+ y,
267
+ z + layer,
268
+ width,
269
+ height,
270
+ tight * blocksHigh,
271
+ start
272
+ );
273
+ } else {
274
+ for (let row = 0; row < blocksHigh; row++) {
275
+ const h = Math.min(4, height - row * 4);
276
+ this.compressedAt(
277
+ texture,
278
+ mip,
279
+ x,
280
+ y + row * 4,
281
+ z + layer,
282
+ width,
283
+ h,
284
+ tight,
285
+ start + row * bytesPerRow
286
+ );
287
+ }
288
+ }
289
+ }
290
+ return;
291
+ }
292
+ this.unpack(bytesPerRow / info.bytes, rowsPerImage);
293
+ if (texture.target === GL.TEXTURE_2D) {
294
+ gl.texSubImage2D(GL.TEXTURE_2D, mip, x, y, width, height, info.format, info.type, offset);
295
+ } else if (texture.target === GL.TEXTURE_CUBE_MAP) {
296
+ for (let layer = 0; layer < depth; layer++) {
297
+ const face = GL.TEXTURE_CUBE_MAP_POSITIVE_X + z + layer;
298
+ const at = offset + layer * rowsPerImage * bytesPerRow;
299
+ gl.texSubImage2D(face, mip, x, y, width, height, info.format, info.type, at);
300
+ }
301
+ } else {
302
+ gl.texSubImage3D(
303
+ texture.target,
304
+ mip,
305
+ x,
306
+ y,
307
+ z,
308
+ width,
309
+ height,
310
+ depth,
311
+ info.format,
312
+ info.type,
313
+ offset
314
+ );
315
+ }
316
+ } finally {
317
+ gl.bindBuffer(GL.PIXEL_UNPACK_BUFFER, null);
318
+ }
319
+ this.device.touch(texture);
320
+ }
321
+ copyTextureToBuffer(texture, mip, x, y, z, buffer, offset, bytesPerRow, rowsPerImage, width, height, depth) {
322
+ const gl = this.gl;
323
+ const info = texture.info;
324
+ if (info.compressed) throw unsupported(`read back the compressed texture "${texture.label}"`);
325
+ if (info.kind === "stencil" || texture.sampleCount > 1) {
326
+ throw unsupported(`read back "${texture.label}" (${texture.format}, ${texture.sampleCount}\xD7)`);
327
+ }
328
+ const read = readFormatOf(info);
329
+ const direct = read.direct && bytesPerRow % read.bytes === 0;
330
+ const tight = width * height * read.bytes;
331
+ const mapped = (buffer.usage & BufferUsage.MAP_READ) !== 0;
332
+ let pack;
333
+ if (!direct) pack = this.pack(tight * depth);
334
+ gl.bindBuffer(GL.PIXEL_PACK_BUFFER, direct ? buffer.gl : pack.gl);
335
+ gl.pixelStorei(GL.PACK_ROW_LENGTH, direct ? bytesPerRow / read.bytes : 0);
336
+ for (let layer = 0; layer < depth; layer++) {
337
+ const at = direct ? offset + layer * rowsPerImage * bytesPerRow : layer * tight;
338
+ if (info.depth) {
339
+ this.readDepth(texture, mip, x, y, z + layer, width, height, at);
340
+ } else {
341
+ this.bindRead(texture, mip, z + layer);
342
+ gl.readPixels(x, y, width, height, read.format, read.type, at);
343
+ }
344
+ }
345
+ gl.pixelStorei(GL.PACK_ROW_LENGTH, 0);
346
+ gl.bindBuffer(GL.PIXEL_PACK_BUFFER, null);
347
+ if (direct) return;
348
+ const job = {
349
+ pack: pack.gl,
350
+ packSize: pack.size,
351
+ read,
352
+ info,
353
+ width,
354
+ rows: height,
355
+ layers: depth,
356
+ offset,
357
+ bytesPerRow,
358
+ rowsPerImage
359
+ };
360
+ if (mapped) {
361
+ buffer.reads.push(job);
362
+ return;
363
+ }
364
+ const bytes = new Uint8Array(rowsPerImage * bytesPerRow * (depth - 1) + bytesPerRow * height);
365
+ this.finishRead(job, bytes, -offset);
366
+ buffer.write(offset, bytes, 0, bytes.length);
367
+ }
368
+ /** Repacks a finished read into `out`, at the job's offset plus `shift`, and frees its pack buffer. */
369
+ finishRead(job, out, shift) {
370
+ const gl = this.gl;
371
+ const size = job.width * job.rows * job.read.bytes * job.layers;
372
+ const src = new Uint8Array(size);
373
+ gl.bindBuffer(GL.COPY_READ_BUFFER, job.pack);
374
+ gl.getBufferSubData(GL.COPY_READ_BUFFER, 0, src);
375
+ gl.bindBuffer(GL.COPY_READ_BUFFER, null);
376
+ const layerBytes = job.width * job.rows * job.read.bytes;
377
+ for (let layer = 0; layer < job.layers; layer++) {
378
+ repack(
379
+ src.subarray(layer * layerBytes, (layer + 1) * layerBytes),
380
+ job.read,
381
+ job.info,
382
+ job.width,
383
+ job.rows,
384
+ out,
385
+ job.offset + shift + layer * job.rowsPerImage * job.bytesPerRow,
386
+ job.bytesPerRow
387
+ );
388
+ }
389
+ this.releaseRead(job);
390
+ }
391
+ releaseRead(job) {
392
+ if (!job.pack) return;
393
+ this.packs.push({ gl: job.pack, size: job.packSize });
394
+ job.pack = null;
395
+ }
396
+ copyTexture(src, srcMip, sx, sy, sz, dst, dstMip, dx, dy, dz, width, height, depth) {
397
+ const gl = this.gl;
398
+ const info = src.info;
399
+ if (info.compressed || dst.info.compressed) {
400
+ throw unsupported(`copy the compressed texture "${src.label}" on the GPU`);
401
+ }
402
+ const mask = info.depth ? GL.DEPTH_BUFFER_BIT | (info.stencil ? GL.STENCIL_BUFFER_BIT : 0) : info.kind === "stencil" ? GL.STENCIL_BUFFER_BIT : GL.COLOR_BUFFER_BIT;
403
+ for (let layer = 0; layer < depth; layer++) {
404
+ this.read.reset();
405
+ this.draw.reset();
406
+ if (mask === GL.COLOR_BUFFER_BIT) {
407
+ this.read.color(0, src, srcMip, sz + layer);
408
+ this.draw.color(0, dst, dstMip, dz + layer);
409
+ } else {
410
+ this.read.setDepth(src, srcMip, sz + layer);
411
+ this.draw.setDepth(dst, dstMip, dz + layer);
412
+ }
413
+ const from = this.device.fbos.get(this.read);
414
+ const to = this.device.fbos.get(this.draw);
415
+ gl.bindFramebuffer(GL.READ_FRAMEBUFFER, from);
416
+ gl.bindFramebuffer(GL.DRAW_FRAMEBUFFER, to);
417
+ this.device.state.enable(GL.SCISSOR_TEST, false);
418
+ gl.blitFramebuffer(
419
+ sx,
420
+ sy,
421
+ sx + width,
422
+ sy + height,
423
+ dx,
424
+ dy,
425
+ dx + width,
426
+ dy + height,
427
+ mask,
428
+ GL.NEAREST
429
+ );
430
+ }
431
+ this.device.touch(dst);
432
+ }
433
+ /** Binds `texture`'s mip and layer as the read framebuffer. */
434
+ bindRead(texture, mip, layer) {
435
+ this.read.reset();
436
+ if (texture.info.depth || texture.info.kind === "stencil")
437
+ this.read.setDepth(texture, mip, layer);
438
+ else this.read.color(0, texture, mip, layer);
439
+ this.gl.bindFramebuffer(GL.READ_FRAMEBUFFER, this.device.fbos.get(this.read));
440
+ }
441
+ /** Forgets GL objects of a lost or destroyed device. */
442
+ clear() {
443
+ for (const p of this.packs) this.gl.deleteBuffer(p.gl);
444
+ this.packs.length = 0;
445
+ this.depthTarget?.destroy();
446
+ this.depthTarget = void 0;
447
+ for (const p of this.depthPrograms) if (p) this.gl.deleteProgram(p.program);
448
+ this.depthPrograms = [void 0, void 0];
449
+ }
450
+ // --- helpers ---------------------------------------------------------------------
451
+ unpack(rowLength, imageHeight) {
452
+ if (this.rowLength !== rowLength) {
453
+ this.rowLength = rowLength;
454
+ this.gl.pixelStorei(GL.UNPACK_ROW_LENGTH, rowLength);
455
+ }
456
+ if (this.imageHeight !== imageHeight) {
457
+ this.imageHeight = imageHeight;
458
+ this.gl.pixelStorei(GL.UNPACK_IMAGE_HEIGHT, imageHeight);
459
+ }
460
+ }
461
+ /**
462
+ * A typed array of the upload type over all of `buffer`, kept while uploads come from the same
463
+ * buffer; `viewOffset` is where `byteOffset` falls in it, in elements.
464
+ */
465
+ view(type, buffer, byteOffset) {
466
+ const Array_ = arrayFor(type);
467
+ const size = Array_.BYTES_PER_ELEMENT;
468
+ if (byteOffset % size !== 0) {
469
+ const copy = new Uint8Array(buffer, byteOffset).slice();
470
+ this.viewOffset = 0;
471
+ return new Array_(copy.buffer, 0, Math.floor(copy.byteLength / size));
472
+ }
473
+ let view = this.views.get(Array_);
474
+ if (!view || view.buffer !== buffer) {
475
+ view = new Array_(buffer, 0, Math.floor(buffer.byteLength / size));
476
+ this.views.set(Array_, view);
477
+ }
478
+ this.viewOffset = byteOffset / size;
479
+ return view;
480
+ }
481
+ compressed(texture, mip, x, y, z, width, height, bytes) {
482
+ const gl = this.gl;
483
+ const internal = texture.info.internal;
484
+ if (texture.target === GL.TEXTURE_2D) {
485
+ gl.compressedTexSubImage2D(GL.TEXTURE_2D, mip, x, y, width, height, internal, bytes);
486
+ } else if (texture.target === GL.TEXTURE_CUBE_MAP) {
487
+ gl.compressedTexSubImage2D(
488
+ GL.TEXTURE_CUBE_MAP_POSITIVE_X + z,
489
+ mip,
490
+ x,
491
+ y,
492
+ width,
493
+ height,
494
+ internal,
495
+ bytes
496
+ );
497
+ } else {
498
+ gl.compressedTexSubImage3D(texture.target, mip, x, y, z, width, height, 1, internal, bytes);
499
+ }
500
+ }
501
+ compressedAt(texture, mip, x, y, z, width, height, size, offset) {
502
+ const gl = this.gl;
503
+ const internal = texture.info.internal;
504
+ if (texture.target === GL.TEXTURE_2D) {
505
+ gl.compressedTexSubImage2D(GL.TEXTURE_2D, mip, x, y, width, height, internal, size, offset);
506
+ } else if (texture.target === GL.TEXTURE_CUBE_MAP) {
507
+ gl.compressedTexSubImage2D(
508
+ GL.TEXTURE_CUBE_MAP_POSITIVE_X + z,
509
+ mip,
510
+ x,
511
+ y,
512
+ width,
513
+ height,
514
+ internal,
515
+ size,
516
+ offset
517
+ );
518
+ } else {
519
+ gl.compressedTexSubImage3D(
520
+ texture.target,
521
+ mip,
522
+ x,
523
+ y,
524
+ z,
525
+ width,
526
+ height,
527
+ 1,
528
+ internal,
529
+ size,
530
+ offset
531
+ );
532
+ }
533
+ }
534
+ pack(size) {
535
+ for (let i = 0; i < this.packs.length; i++) {
536
+ if (this.packs[i].size >= size) return this.packs.splice(i, 1)[0];
537
+ }
538
+ const gl = this.gl;
539
+ const buffer = gl.createBuffer();
540
+ gl.bindBuffer(GL.PIXEL_PACK_BUFFER, buffer);
541
+ gl.bufferData(GL.PIXEL_PACK_BUFFER, size, GL.STREAM_READ);
542
+ gl.bindBuffer(GL.PIXEL_PACK_BUFFER, null);
543
+ return { gl: buffer, size };
544
+ }
545
+ /**
546
+ * Reads depth (which readPixels can't) by drawing it into an R32F target with texelFetch, then
547
+ * reading that as floats into the bound pack buffer at `at`.
548
+ */
549
+ readDepth(texture, mip, x, y, layer, width, height, at) {
550
+ const gl = this.gl;
551
+ const device = this.device;
552
+ const state = device.state;
553
+ if (!texture.gl) throw unsupported(`read back the depth attachment "${texture.label}"`);
554
+ const array = texture.target !== GL.TEXTURE_2D;
555
+ const program = this.depthProgram(array);
556
+ let target = this.depthTarget;
557
+ if (!target || target.width < width || target.height < height) {
558
+ target?.destroy();
559
+ target = new Webgl2Texture(device, {
560
+ label: "shard/depth-readback",
561
+ size: [Math.max(width, target?.width ?? 0), Math.max(height, target?.height ?? 0)],
562
+ format: "r32float",
563
+ usage: TextureUsage.RENDER_ATTACHMENT
564
+ });
565
+ this.depthTarget = target;
566
+ }
567
+ this.draw.reset();
568
+ this.draw.color(0, target, 0, 0);
569
+ gl.bindFramebuffer(GL.FRAMEBUFFER, device.fbos.get(this.draw));
570
+ state.prepareClear();
571
+ state.enable(GL.DEPTH_TEST, false);
572
+ state.enable(GL.CULL_FACE, false);
573
+ state.enable(GL.BLEND, false);
574
+ state.enable(GL.STENCIL_TEST, false);
575
+ state.useProgram(program.program);
576
+ state.bindVertexArray(null);
577
+ state.bindUnit(0, texture.target, texture.gl);
578
+ state.bindSampler(0, null);
579
+ device.levels(texture, 0, texture.mipLevelCount - 1);
580
+ gl.uniform1i(program.texture, 0);
581
+ gl.uniform3i(program.origin, x, y, layer);
582
+ gl.uniform1i(program.level, mip);
583
+ gl.viewport(0, 0, width, height);
584
+ gl.drawArrays(GL.TRIANGLES, 0, 3);
585
+ gl.bindFramebuffer(GL.READ_FRAMEBUFFER, device.fbos.get(this.draw));
586
+ gl.readPixels(0, 0, width, height, GL.RGBA, GL.FLOAT, at);
587
+ }
588
+ depthProgram(array) {
589
+ const i = array ? 1 : 0;
590
+ const cached = this.depthPrograms[i];
591
+ if (cached) return cached;
592
+ const gl = this.gl;
593
+ const program = gl.createProgram();
594
+ const compile = (type, source) => {
595
+ const s = gl.createShader(type);
596
+ gl.shaderSource(s, source);
597
+ gl.compileShader(s);
598
+ gl.attachShader(program, s);
599
+ return s;
600
+ };
601
+ const vs = compile(GL.VERTEX_SHADER, DEPTH_VS);
602
+ const fs = compile(GL.FRAGMENT_SHADER, depthFs(array));
603
+ gl.linkProgram(program);
604
+ if (!gl.getProgramParameter(program, GL.LINK_STATUS)) {
605
+ throw new Error(`The depth readback program didn't link: ${gl.getProgramInfoLog(program)}`);
606
+ }
607
+ gl.deleteShader(vs);
608
+ gl.deleteShader(fs);
609
+ const made = {
610
+ program,
611
+ origin: gl.getUniformLocation(program, "origin"),
612
+ level: gl.getUniformLocation(program, "level"),
613
+ texture: gl.getUniformLocation(program, "depth_texture")
614
+ };
615
+ this.depthPrograms[i] = made;
616
+ return made;
617
+ }
618
+ }
619
+ export {
620
+ Copier
621
+ };