@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/LICENSE +21 -0
- package/README.md +2 -2
- package/dist/binding.d.ts +32 -0
- package/dist/binding.js +81 -0
- package/dist/cache.d.ts +73 -0
- package/dist/cache.js +184 -0
- package/dist/commands.d.ts +94 -0
- package/dist/commands.js +366 -0
- package/dist/constants.d.ts +41 -0
- package/dist/constants.js +38 -0
- package/dist/copies.d.ts +61 -0
- package/dist/copies.js +621 -0
- package/dist/device.d.ts +160 -0
- package/dist/device.js +384 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +18 -0
- package/dist/fake-gl.d.ts +316 -0
- package/dist/fake-gl.js +949 -0
- package/dist/fbo.d.ts +30 -0
- package/dist/fbo.js +142 -0
- package/dist/formats.d.ts +25 -0
- package/dist/formats.js +106 -0
- package/dist/gl.d.ts +216 -0
- package/dist/gl.js +232 -0
- package/dist/gpu.d.ts +58 -0
- package/dist/gpu.js +213 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +42 -0
- package/dist/naga.d.ts +37 -0
- package/dist/naga.js +99 -0
- package/dist/pipeline.d.ts +113 -0
- package/dist/pipeline.js +294 -0
- package/dist/present.d.ts +18 -0
- package/dist/present.js +108 -0
- package/dist/queue.d.ts +13 -0
- package/dist/queue.js +50 -0
- package/dist/readback.d.ts +17 -0
- package/dist/readback.js +104 -0
- package/dist/replay.d.ts +76 -0
- package/dist/replay.js +657 -0
- package/dist/resources.d.ts +93 -0
- package/dist/resources.js +353 -0
- package/dist/state.d.ts +43 -0
- package/dist/state.js +169 -0
- package/package.json +36 -4
- package/wasm/shard_naga.wasm +0 -0
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
|
+
};
|