@swf2es/context3d 0.1.20 → 0.1.21
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 +8 -5
- package/dist/webgl/back-buffer.d.ts +6 -0
- package/dist/webgl/back-buffer.d.ts.map +1 -0
- package/dist/webgl/back-buffer.js +197 -0
- package/dist/webgl/back-buffer.js.map +1 -0
- package/dist/webgl/buffers.d.ts +2 -2
- package/dist/webgl/buffers.d.ts.map +1 -1
- package/dist/webgl/buffers.js +105 -2
- package/dist/webgl/buffers.js.map +1 -1
- package/dist/webgl/context.d.ts +5 -45
- package/dist/webgl/context.d.ts.map +1 -1
- package/dist/webgl/context.js +44 -322
- package/dist/webgl/context.js.map +1 -1
- package/dist/webgl/readback.d.ts +3 -0
- package/dist/webgl/readback.d.ts.map +1 -0
- package/dist/webgl/readback.js +40 -0
- package/dist/webgl/readback.js.map +1 -0
- package/package.json +1 -1
- package/src/webgl/back-buffer.ts +287 -0
- package/src/webgl/buffers.ts +114 -2
- package/src/webgl/context.ts +73 -451
- package/src/webgl/readback.ts +54 -0
package/dist/webgl/context.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { INDIRECT_BANK } from "../agal.js";
|
|
2
|
-
import { compressedFormat, FRAGMENT_CONSTANTS,
|
|
2
|
+
import { compressedFormat, FRAGMENT_CONSTANTS, MOST_CUBE, profileLimits, SAMPLER_FILTERS, SAMPLER_MIPS, SAMPLER_SLOTS, SAMPLER_WRAPS, ScissorBoundsError, screenPixels, TEXEL_BYTES, TextureBindingError, VERTEX_CONSTANTS, VERTEX_FORMATS, VERTEX_SLOTS, VertexOffsetError, } from "../backend.js";
|
|
3
3
|
import { GlState } from "../device/state.js";
|
|
4
4
|
import { blockBytes } from "../dxt.js";
|
|
5
5
|
import { Context3DError } from "../errors.js";
|
|
6
|
-
import {
|
|
6
|
+
import { configureBackBuffer, presentBackBuffer, unconfigureBackBuffer } from "./back-buffer.js";
|
|
7
|
+
import { allocateBuffer } from "./buffers.js";
|
|
7
8
|
import { resourceLimits } from "./limits.js";
|
|
8
9
|
import { createCopyProgram, uploadProgram, } from "./programs.js";
|
|
10
|
+
import { readBackBuffer } from "./readback.js";
|
|
9
11
|
import { BLEND_FUNCTIONS, DEPTH_FUNCTIONS, range, STENCIL_FUNCTIONS, StencilState, } from "./state.js";
|
|
10
12
|
import { allocateMultisample, deleteMultisample, resolveTarget, selectSamples, targetFramebuffer, } from "./targets.js";
|
|
11
13
|
import { storageFormat, uploadCompressedLevel, uploadTexture, uploadTextureBytes, } from "./textures.js";
|
|
@@ -41,9 +43,14 @@ function constants(registers) {
|
|
|
41
43
|
writing: 0,
|
|
42
44
|
};
|
|
43
45
|
}
|
|
44
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* An owned WebGL2 context, deliberately independent of the 2D renderer. Its members that are
|
|
48
|
+
* neither private nor Context3D's are for back-buffer.ts, buffers.ts and readback.ts, which
|
|
49
|
+
* hold parts of it as functions over it.
|
|
50
|
+
*/
|
|
45
51
|
export class WebGLContext3D {
|
|
46
52
|
gl;
|
|
53
|
+
/** @internal */
|
|
47
54
|
drawState;
|
|
48
55
|
width = 0;
|
|
49
56
|
height = 0;
|
|
@@ -51,15 +58,22 @@ export class WebGLContext3D {
|
|
|
51
58
|
* The back buffer's storage: its size, or less where the GPU allows less. The SWF still
|
|
52
59
|
* sees the size it asked for, drawn at this one and stretched to it when presented.
|
|
53
60
|
*/
|
|
61
|
+
/** @internal */
|
|
54
62
|
drawnWidth = 0;
|
|
63
|
+
/** @internal */
|
|
55
64
|
drawnHeight = 0;
|
|
65
|
+
/** @internal */
|
|
56
66
|
gpuMost = null;
|
|
57
67
|
disposed = false;
|
|
58
68
|
samplerFormats = true;
|
|
59
69
|
profile = "baseline";
|
|
70
|
+
/** @internal */
|
|
60
71
|
buffers = new Map();
|
|
72
|
+
/** @internal */
|
|
61
73
|
vertexBuffers = 0;
|
|
74
|
+
/** @internal */
|
|
62
75
|
bufferBytes = 0;
|
|
76
|
+
/** @internal */
|
|
63
77
|
vertexBufferBytes = 0;
|
|
64
78
|
programs = new Map();
|
|
65
79
|
textures = new Map();
|
|
@@ -72,6 +86,7 @@ export class WebGLContext3D {
|
|
|
72
86
|
copy2D = null;
|
|
73
87
|
copyCube = null;
|
|
74
88
|
targetSamples = new Int8Array(10).fill(-1);
|
|
89
|
+
/** @internal */
|
|
75
90
|
renderTexture = null;
|
|
76
91
|
renderDepth = false;
|
|
77
92
|
renderFace = 0;
|
|
@@ -90,19 +105,30 @@ export class WebGLContext3D {
|
|
|
90
105
|
anisotropy;
|
|
91
106
|
s3tc;
|
|
92
107
|
maxAnisotropy = 1;
|
|
108
|
+
/** @internal */
|
|
93
109
|
vertices = new Array(VERTEX_SLOTS).fill(null);
|
|
94
110
|
enabledAttributes = 0;
|
|
111
|
+
/** @internal */
|
|
95
112
|
indexBuffer = null;
|
|
113
|
+
/** @internal */
|
|
96
114
|
texturesApplied = new Uint8Array(SAMPLER_SLOTS);
|
|
97
115
|
selected = null;
|
|
116
|
+
/** @internal */
|
|
98
117
|
framebuffer = null;
|
|
118
|
+
/** @internal */
|
|
99
119
|
texture = null;
|
|
120
|
+
/** @internal */
|
|
100
121
|
resolveFramebuffer = null;
|
|
122
|
+
/** @internal */
|
|
101
123
|
colorBuffer = null;
|
|
124
|
+
/** @internal */
|
|
102
125
|
resolveDirty = false;
|
|
126
|
+
/** @internal */
|
|
103
127
|
depthBuffer = null;
|
|
128
|
+
/** @internal */
|
|
104
129
|
antiAlias = 0;
|
|
105
130
|
/** The back buffer's pixels to a CSS pixel and to a scissor rectangle's unit. */
|
|
131
|
+
/** @internal */
|
|
106
132
|
scale = 1;
|
|
107
133
|
depthWrite = true;
|
|
108
134
|
depthCompare = "less";
|
|
@@ -117,6 +143,7 @@ export class WebGLContext3D {
|
|
|
117
143
|
stencilReference = 0;
|
|
118
144
|
stencilRead = 255;
|
|
119
145
|
stencilWrite = 255;
|
|
146
|
+
/** @internal */
|
|
120
147
|
scissor = false;
|
|
121
148
|
scissorLeft = 0;
|
|
122
149
|
scissorTop = 0;
|
|
@@ -125,9 +152,13 @@ export class WebGLContext3D {
|
|
|
125
152
|
scissorOutside = false;
|
|
126
153
|
vertexConstants = constants(VERTEX_CONSTANTS);
|
|
127
154
|
fragmentConstants = constants(FRAGMENT_CONSTANTS);
|
|
155
|
+
/** @internal */
|
|
128
156
|
pixels = new Uint8Array(0);
|
|
157
|
+
/** @internal */
|
|
129
158
|
topDown = new Uint8Array(0);
|
|
159
|
+
/** @internal */
|
|
130
160
|
vao;
|
|
161
|
+
/** @internal */
|
|
131
162
|
limits;
|
|
132
163
|
dxtEncoder = null;
|
|
133
164
|
/** The host's whole-level encoder; a refusal retains the TypeScript path. */
|
|
@@ -162,11 +193,13 @@ export class WebGLContext3D {
|
|
|
162
193
|
gl.frontFace(gl.CW);
|
|
163
194
|
gl.disable(gl.DITHER);
|
|
164
195
|
}
|
|
196
|
+
/** @internal */
|
|
165
197
|
alive() {
|
|
166
198
|
if (this.disposed || this.gl.isContextLost()) {
|
|
167
199
|
throw new Context3DError("disposed", "Context3D experiment: context disposed or lost");
|
|
168
200
|
}
|
|
169
201
|
}
|
|
202
|
+
/** @internal */
|
|
170
203
|
target(back = false, discardColor = false) {
|
|
171
204
|
this.alive();
|
|
172
205
|
if (!this.framebuffer) {
|
|
@@ -189,164 +222,16 @@ export class WebGLContext3D {
|
|
|
189
222
|
this.drawState.viewport(0, 0, texture?.width ?? this.drawnWidth, texture?.height ?? this.drawnHeight);
|
|
190
223
|
}
|
|
191
224
|
/** Temporary attachments and separate READ/DRAW bindings bypass the hot target cache. */
|
|
225
|
+
/** @internal */
|
|
192
226
|
bindFramebuffer(target, framebuffer) {
|
|
193
227
|
this.drawState.forget();
|
|
194
228
|
this.gl.bindFramebuffer(target, framebuffer);
|
|
195
229
|
}
|
|
196
230
|
configure(width, height, depth = false, antiAlias = 0, scale = 1) {
|
|
197
|
-
this
|
|
198
|
-
range(32, width - 32, MOST_BACK_BUFFER);
|
|
199
|
-
range(32, height - 32, MOST_BACK_BUFFER);
|
|
200
|
-
if (this.renderTexture) {
|
|
201
|
-
throw new Context3DError("operation", "Context3D experiment: resize while rendering to texture");
|
|
202
|
-
}
|
|
203
|
-
// AIR preserves color, depth and bindings when the request is unchanged.
|
|
204
|
-
// Shadow passes can issue this every frame when returning to the back buffer.
|
|
205
|
-
if (this.framebuffer &&
|
|
206
|
-
width === this.width &&
|
|
207
|
-
height === this.height &&
|
|
208
|
-
depth === (this.depthBuffer !== null) &&
|
|
209
|
-
antiAlias === this.antiAlias) {
|
|
210
|
-
if (scale !== this.scale) {
|
|
211
|
-
this.scale = scale;
|
|
212
|
-
this.showAt();
|
|
213
|
-
this.applyScissor();
|
|
214
|
-
}
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
const samples = selectSamples(this.gl, antiAlias, depth);
|
|
218
|
-
const gl = this.gl;
|
|
219
|
-
const most = this.storageLimit();
|
|
220
|
-
const drawnWidth = Math.min(width, most[0]);
|
|
221
|
-
const drawnHeight = Math.min(height, most[1]);
|
|
222
|
-
const texture = gl.createTexture();
|
|
223
|
-
const framebuffer = gl.createFramebuffer();
|
|
224
|
-
const depthBuffer = depth ? gl.createRenderbuffer() : null;
|
|
225
|
-
const resolveFramebuffer = samples ? gl.createFramebuffer() : null;
|
|
226
|
-
const colorBuffer = samples ? gl.createRenderbuffer() : null;
|
|
227
|
-
try {
|
|
228
|
-
if (!texture ||
|
|
229
|
-
!framebuffer ||
|
|
230
|
-
(depth && !depthBuffer) ||
|
|
231
|
-
(samples && (!resolveFramebuffer || !colorBuffer))) {
|
|
232
|
-
throw new Context3DError("operation", "Context3D experiment: cannot allocate back buffer");
|
|
233
|
-
}
|
|
234
|
-
this.texturesApplied[0] = 0;
|
|
235
|
-
gl.activeTexture(gl.TEXTURE0);
|
|
236
|
-
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
237
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
238
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
239
|
-
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, drawnWidth, drawnHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
240
|
-
this.bindFramebuffer(gl.FRAMEBUFFER, resolveFramebuffer ?? framebuffer);
|
|
241
|
-
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
|
|
242
|
-
if (resolveFramebuffer) {
|
|
243
|
-
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
|
|
244
|
-
throw new Context3DError("operation", "Context3D experiment: incomplete resolve buffer");
|
|
245
|
-
}
|
|
246
|
-
this.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
|
247
|
-
gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
|
|
248
|
-
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples, gl.RGBA8, drawnWidth, drawnHeight);
|
|
249
|
-
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
|
|
250
|
-
}
|
|
251
|
-
if (depthBuffer) {
|
|
252
|
-
gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
|
|
253
|
-
if (samples) {
|
|
254
|
-
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples, gl.DEPTH24_STENCIL8, drawnWidth, drawnHeight);
|
|
255
|
-
}
|
|
256
|
-
else {
|
|
257
|
-
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH24_STENCIL8, drawnWidth, drawnHeight);
|
|
258
|
-
}
|
|
259
|
-
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
|
|
260
|
-
}
|
|
261
|
-
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
|
|
262
|
-
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
|
|
263
|
-
throw new Context3DError("operation", "Context3D experiment: incomplete back buffer");
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
catch (error) {
|
|
267
|
-
gl.deleteTexture(texture);
|
|
268
|
-
gl.deleteFramebuffer(framebuffer);
|
|
269
|
-
gl.deleteFramebuffer(resolveFramebuffer);
|
|
270
|
-
gl.deleteRenderbuffer(depthBuffer);
|
|
271
|
-
gl.deleteRenderbuffer(colorBuffer);
|
|
272
|
-
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
|
|
273
|
-
this.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
|
|
274
|
-
throw error;
|
|
275
|
-
}
|
|
276
|
-
gl.deleteTexture(this.texture);
|
|
277
|
-
gl.deleteFramebuffer(this.framebuffer);
|
|
278
|
-
gl.deleteFramebuffer(this.resolveFramebuffer);
|
|
279
|
-
gl.deleteRenderbuffer(this.depthBuffer);
|
|
280
|
-
gl.deleteRenderbuffer(this.colorBuffer);
|
|
281
|
-
this.texture = texture;
|
|
282
|
-
this.framebuffer = framebuffer;
|
|
283
|
-
this.resolveFramebuffer = resolveFramebuffer;
|
|
284
|
-
this.colorBuffer = colorBuffer;
|
|
285
|
-
this.depthBuffer = depthBuffer;
|
|
286
|
-
this.antiAlias = antiAlias;
|
|
287
|
-
this.width = width;
|
|
288
|
-
this.height = height;
|
|
289
|
-
this.drawnWidth = drawnWidth;
|
|
290
|
-
this.drawnHeight = drawnHeight;
|
|
291
|
-
this.scale = scale;
|
|
292
|
-
gl.canvas.width = drawnWidth;
|
|
293
|
-
gl.canvas.height = drawnHeight;
|
|
294
|
-
this.showAt();
|
|
295
|
-
// Readback's storage waits for a read: a back buffer as large as a 4K screen is 33 MB each.
|
|
296
|
-
this.pixels = new Uint8Array(0);
|
|
297
|
-
this.topDown = new Uint8Array(0);
|
|
298
|
-
this.applyScissor();
|
|
299
|
-
this.clear(0, 0, 0, 1);
|
|
300
|
-
}
|
|
301
|
-
/**
|
|
302
|
-
* Shown at the size asked for, in CSS pixels, as an unstyled canvas of that size would be,
|
|
303
|
-
* divided by its scale: a best-resolution back buffer shows its pixels at the screen's.
|
|
304
|
-
*/
|
|
305
|
-
showAt() {
|
|
306
|
-
const style = this.gl.canvas.style;
|
|
307
|
-
if (!style) {
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
310
|
-
const plain = this.scale === 1;
|
|
311
|
-
style.width = plain && this.drawnWidth === this.width ? "" : `${this.width / this.scale}px`;
|
|
312
|
-
style.height = plain && this.drawnHeight === this.height ? "" : `${this.height / this.scale}px`;
|
|
313
|
-
}
|
|
314
|
-
/** The largest back buffer storage the GPU gives: a texture, a renderbuffer and a viewport. */
|
|
315
|
-
storageLimit() {
|
|
316
|
-
if (!this.gpuMost) {
|
|
317
|
-
const gl = this.gl;
|
|
318
|
-
const viewport = gl.getParameter(gl.MAX_VIEWPORT_DIMS);
|
|
319
|
-
const side = Math.min(gl.getParameter(gl.MAX_TEXTURE_SIZE), gl.getParameter(gl.MAX_RENDERBUFFER_SIZE));
|
|
320
|
-
this.gpuMost = Int32Array.of(Math.min(side, viewport[0]), Math.min(side, viewport[1]));
|
|
321
|
-
}
|
|
322
|
-
return this.gpuMost;
|
|
231
|
+
configureBackBuffer(this, width, height, depth, antiAlias, scale);
|
|
323
232
|
}
|
|
324
233
|
unconfigure() {
|
|
325
|
-
this
|
|
326
|
-
if (this.renderTexture) {
|
|
327
|
-
throw new Context3DError("operation", "Context3D experiment: resize while rendering to texture");
|
|
328
|
-
}
|
|
329
|
-
const gl = this.gl;
|
|
330
|
-
gl.deleteTexture(this.texture);
|
|
331
|
-
gl.deleteFramebuffer(this.framebuffer);
|
|
332
|
-
gl.deleteFramebuffer(this.resolveFramebuffer);
|
|
333
|
-
gl.deleteRenderbuffer(this.depthBuffer);
|
|
334
|
-
gl.deleteRenderbuffer(this.colorBuffer);
|
|
335
|
-
this.drawState.forget();
|
|
336
|
-
this.texture = null;
|
|
337
|
-
this.framebuffer = null;
|
|
338
|
-
this.resolveFramebuffer = null;
|
|
339
|
-
this.colorBuffer = null;
|
|
340
|
-
this.depthBuffer = null;
|
|
341
|
-
this.resolveDirty = false;
|
|
342
|
-
this.antiAlias = 0;
|
|
343
|
-
this.scale = 1;
|
|
344
|
-
this.width = 0;
|
|
345
|
-
this.height = 0;
|
|
346
|
-
this.drawnWidth = 0;
|
|
347
|
-
this.drawnHeight = 0;
|
|
348
|
-
this.pixels = new Uint8Array(0);
|
|
349
|
-
this.topDown = new Uint8Array(0);
|
|
234
|
+
unconfigureBackBuffer(this);
|
|
350
235
|
}
|
|
351
236
|
clear(red, green, blue, alpha, depth = 1, mask = 0xffffffff, stencil = 0) {
|
|
352
237
|
this.target(false, (mask & 1) !== 0);
|
|
@@ -492,6 +377,7 @@ export class WebGLContext3D {
|
|
|
492
377
|
}
|
|
493
378
|
this.applyScissor();
|
|
494
379
|
}
|
|
380
|
+
/** @internal */
|
|
495
381
|
applyScissor() {
|
|
496
382
|
const gl = this.gl;
|
|
497
383
|
if (!this.scissor) {
|
|
@@ -577,107 +463,9 @@ export class WebGLContext3D {
|
|
|
577
463
|
}
|
|
578
464
|
}
|
|
579
465
|
createBuffer(count, stride, dynamic = false) {
|
|
580
|
-
this
|
|
581
|
-
range(1, count - 1, stride === 0 ? 524287 : 65535);
|
|
582
|
-
range(0, stride, 64);
|
|
583
|
-
const countLimit = stride === 0 ? this.limits.maxIndexBuffers : this.limits.maxVertexBuffers;
|
|
584
|
-
const used = stride === 0 ? this.buffers.size - this.vertexBuffers : this.vertexBuffers;
|
|
585
|
-
if (this.buffers.size >= this.limits.maxBuffers || used >= countLimit) {
|
|
586
|
-
throw new Context3DError("limit", "Context3D experiment: buffer limit");
|
|
587
|
-
}
|
|
588
|
-
const bytes = count * (stride === 0 ? 2 : stride * 4);
|
|
589
|
-
const usedBytes = stride === 0 ? this.bufferBytes - this.vertexBufferBytes : this.vertexBufferBytes;
|
|
590
|
-
const byteLimit = stride === 0 ? this.limits.maxIndexBufferBytes : this.limits.maxVertexBufferBytes;
|
|
591
|
-
if (this.bufferBytes + bytes > this.limits.maxBufferBytes || usedBytes + bytes > byteLimit) {
|
|
592
|
-
throw new Context3DError("limit", "Context3D experiment: buffer memory limit");
|
|
593
|
-
}
|
|
594
|
-
const gl = this.gl;
|
|
595
|
-
const handle = gl.createBuffer();
|
|
596
|
-
if (!handle) {
|
|
597
|
-
throw new Context3DError("bufferAllocation", "Context3D experiment: cannot allocate buffer");
|
|
598
|
-
}
|
|
599
|
-
const target = stride === 0 ? gl.ELEMENT_ARRAY_BUFFER : gl.ARRAY_BUFFER;
|
|
600
|
-
if (stride === 0) {
|
|
601
|
-
this.indexBuffer = null;
|
|
602
|
-
}
|
|
603
|
-
gl.bindVertexArray(this.vao);
|
|
604
|
-
gl.bindBuffer(target, handle);
|
|
605
|
-
gl.bufferData(target, bytes, dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW);
|
|
606
|
-
const state = {
|
|
607
|
-
handle,
|
|
608
|
-
indices: stride === 0 ? new Uint16Array(count) : null,
|
|
609
|
-
uploaded: new Uint8Array(count),
|
|
610
|
-
prefix: 0,
|
|
611
|
-
version: 0,
|
|
612
|
-
checkedVersion: -1,
|
|
613
|
-
checkedFirst: 0,
|
|
614
|
-
checkedCount: 0,
|
|
615
|
-
maximum: -1,
|
|
616
|
-
ranges: null,
|
|
617
|
-
};
|
|
618
|
-
const buffer = {
|
|
619
|
-
count,
|
|
620
|
-
stride,
|
|
621
|
-
upload: (values, start, amount) => {
|
|
622
|
-
this.alive();
|
|
623
|
-
this.buffer(buffer);
|
|
624
|
-
try {
|
|
625
|
-
uploadBuffer(gl, this.vao, buffer, state, values, start, amount);
|
|
626
|
-
}
|
|
627
|
-
finally {
|
|
628
|
-
// Source getters can draw before the upload replaces the VAO's index binding.
|
|
629
|
-
if (stride === 0) {
|
|
630
|
-
this.indexBuffer = null;
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
},
|
|
634
|
-
uploadBytes: (data, offset, start, amount) => {
|
|
635
|
-
this.alive();
|
|
636
|
-
this.buffer(buffer);
|
|
637
|
-
try {
|
|
638
|
-
uploadBufferBytes(gl, this.vao, buffer, state, data, offset, start, amount);
|
|
639
|
-
}
|
|
640
|
-
finally {
|
|
641
|
-
if (stride === 0) {
|
|
642
|
-
this.indexBuffer = null;
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
},
|
|
646
|
-
dispose: () => {
|
|
647
|
-
if (!this.buffers.delete(buffer)) {
|
|
648
|
-
return;
|
|
649
|
-
}
|
|
650
|
-
if (this.indexBuffer === handle) {
|
|
651
|
-
this.indexBuffer = null;
|
|
652
|
-
}
|
|
653
|
-
gl.deleteBuffer(handle);
|
|
654
|
-
this.bufferBytes -= bytes;
|
|
655
|
-
if (stride !== 0) {
|
|
656
|
-
this.vertexBuffers--;
|
|
657
|
-
this.vertexBufferBytes -= bytes;
|
|
658
|
-
}
|
|
659
|
-
state.indices = null;
|
|
660
|
-
state.ranges = null;
|
|
661
|
-
state.uploaded = new Uint8Array(0);
|
|
662
|
-
for (let i = 0; i < this.vertices.length; i++) {
|
|
663
|
-
const binding = this.vertices[i];
|
|
664
|
-
if (binding?.buffer === buffer) {
|
|
665
|
-
this.vertices[i] = null;
|
|
666
|
-
}
|
|
667
|
-
else if (binding?.checkedIndex === state) {
|
|
668
|
-
binding.checkedIndex = null;
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
},
|
|
672
|
-
};
|
|
673
|
-
this.buffers.set(buffer, state);
|
|
674
|
-
if (stride !== 0) {
|
|
675
|
-
this.vertexBuffers++;
|
|
676
|
-
this.vertexBufferBytes += bytes;
|
|
677
|
-
}
|
|
678
|
-
this.bufferBytes += bytes;
|
|
679
|
-
return buffer;
|
|
466
|
+
return allocateBuffer(this, count, stride, dynamic);
|
|
680
467
|
}
|
|
468
|
+
/** @internal */
|
|
681
469
|
buffer(buffer) {
|
|
682
470
|
const state = this.buffers.get(buffer);
|
|
683
471
|
if (!state) {
|
|
@@ -1734,77 +1522,11 @@ export class WebGLContext3D {
|
|
|
1734
1522
|
}
|
|
1735
1523
|
}
|
|
1736
1524
|
}
|
|
1737
|
-
resolve() {
|
|
1738
|
-
if (!this.resolveFramebuffer) {
|
|
1739
|
-
return;
|
|
1740
|
-
}
|
|
1741
|
-
const gl = this.gl;
|
|
1742
|
-
if (this.resolveDirty) {
|
|
1743
|
-
this.bindFramebuffer(gl.READ_FRAMEBUFFER, this.framebuffer);
|
|
1744
|
-
this.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.resolveFramebuffer);
|
|
1745
|
-
if (this.scissor) {
|
|
1746
|
-
gl.disable(gl.SCISSOR_TEST);
|
|
1747
|
-
}
|
|
1748
|
-
gl.blitFramebuffer(0, 0, this.drawnWidth, this.drawnHeight, 0, 0, this.drawnWidth, this.drawnHeight, gl.COLOR_BUFFER_BIT, gl.NEAREST);
|
|
1749
|
-
if (this.scissor) {
|
|
1750
|
-
gl.enable(gl.SCISSOR_TEST);
|
|
1751
|
-
}
|
|
1752
|
-
this.resolveDirty = false;
|
|
1753
|
-
}
|
|
1754
|
-
this.bindFramebuffer(gl.READ_FRAMEBUFFER, this.resolveFramebuffer);
|
|
1755
|
-
}
|
|
1756
1525
|
read(width = this.width, height = this.height) {
|
|
1757
|
-
|
|
1758
|
-
height = Math.max(0, Math.min(this.height, Math.floor(height) || 0));
|
|
1759
|
-
this.target(true);
|
|
1760
|
-
this.resolve();
|
|
1761
|
-
const gl = this.gl;
|
|
1762
|
-
const scaleX = this.drawnWidth / this.width;
|
|
1763
|
-
const scaleY = this.drawnHeight / this.height;
|
|
1764
|
-
const drawnWidth = Math.min(this.drawnWidth, Math.ceil(width * scaleX));
|
|
1765
|
-
const drawnHeight = Math.min(this.drawnHeight, Math.ceil(height * scaleY));
|
|
1766
|
-
if (this.pixels.length < drawnWidth * drawnHeight * 4) {
|
|
1767
|
-
this.pixels = new Uint8Array(drawnWidth * drawnHeight * 4);
|
|
1768
|
-
}
|
|
1769
|
-
if (this.topDown.length < width * height * 4) {
|
|
1770
|
-
this.topDown = new Uint8Array(width * height * 4);
|
|
1771
|
-
}
|
|
1772
|
-
// GL's rows run bottom-up, so the top-left region starts height rows below the top.
|
|
1773
|
-
gl.readPixels(0, this.drawnHeight - drawnHeight, drawnWidth, drawnHeight, gl.RGBA, gl.UNSIGNED_BYTE, this.pixels);
|
|
1774
|
-
const row = width * 4;
|
|
1775
|
-
if (drawnWidth === width && drawnHeight === height) {
|
|
1776
|
-
for (let y = 0; y < height; y++) {
|
|
1777
|
-
const from = (height - y - 1) * row;
|
|
1778
|
-
this.topDown.set(this.pixels.subarray(from, from + row), y * row);
|
|
1779
|
-
}
|
|
1780
|
-
return this.topDown;
|
|
1781
|
-
}
|
|
1782
|
-
// Storage smaller than the back buffer: each pixel is the stored one it falls on.
|
|
1783
|
-
const source = new Uint32Array(this.pixels.buffer, 0, drawnWidth * drawnHeight);
|
|
1784
|
-
const target = new Uint32Array(this.topDown.buffer, 0, width * height);
|
|
1785
|
-
for (let y = 0; y < height; y++) {
|
|
1786
|
-
const from = (drawnHeight - 1 - Math.min(drawnHeight - 1, Math.floor(y * scaleY))) * drawnWidth;
|
|
1787
|
-
for (let x = 0; x < width; x++) {
|
|
1788
|
-
target[y * width + x] = source[from + Math.min(drawnWidth - 1, Math.floor(x * scaleX))];
|
|
1789
|
-
}
|
|
1790
|
-
}
|
|
1791
|
-
return this.topDown;
|
|
1526
|
+
return readBackBuffer(this, width, height);
|
|
1792
1527
|
}
|
|
1793
1528
|
present() {
|
|
1794
|
-
this
|
|
1795
|
-
this.target();
|
|
1796
|
-
this.resolve();
|
|
1797
|
-
const gl = this.gl;
|
|
1798
|
-
this.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
|
|
1799
|
-
if (this.scissor) {
|
|
1800
|
-
gl.disable(gl.SCISSOR_TEST);
|
|
1801
|
-
}
|
|
1802
|
-
// To the drawing buffer the browser gave, which it may have made smaller than the canvas.
|
|
1803
|
-
gl.blitFramebuffer(0, 0, this.drawnWidth, this.drawnHeight, 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight, gl.COLOR_BUFFER_BIT, gl.NEAREST);
|
|
1804
|
-
if (this.scissor) {
|
|
1805
|
-
gl.enable(gl.SCISSOR_TEST);
|
|
1806
|
-
}
|
|
1807
|
-
gl.flush();
|
|
1529
|
+
presentBackBuffer(this);
|
|
1808
1530
|
}
|
|
1809
1531
|
dispose() {
|
|
1810
1532
|
if (this.disposed) {
|