@swf2es/context3d 0.1.10 → 0.1.11
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 +36 -0
- package/dist/webgl/buffers.d.ts +2 -0
- package/dist/webgl/buffers.d.ts.map +1 -1
- package/dist/webgl/buffers.js.map +1 -1
- package/dist/webgl/context.d.ts +11 -1
- package/dist/webgl/context.d.ts.map +1 -1
- package/dist/webgl/context.js +218 -59
- package/dist/webgl/context.js.map +1 -1
- package/dist/webgl/programs.d.ts +6 -0
- package/dist/webgl/programs.d.ts.map +1 -1
- package/dist/webgl/programs.js +10 -0
- package/dist/webgl/programs.js.map +1 -1
- package/package.json +1 -1
- package/src/webgl/buffers.ts +2 -0
- package/src/webgl/context.ts +254 -79
- package/src/webgl/programs.ts +18 -0
package/dist/webgl/context.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SAMPLER_FILTERS, SAMPLER_MIPS, SAMPLER_WRAPS, ScissorBoundsError, TextureBindingError, VERTEX_FORMATS, VertexOffsetError, } from "../backend.js";
|
|
2
|
+
import { GlState } from "../device/state.js";
|
|
2
3
|
import { blockBytes } from "../dxt.js";
|
|
3
4
|
import { Context3DError } from "../errors.js";
|
|
4
5
|
import { uploadBuffer, uploadBufferBytes, } from "./buffers.js";
|
|
@@ -17,6 +18,7 @@ const SAMPLER_FORMATS = {
|
|
|
17
18
|
/** An owned WebGL2 context, deliberately independent of the 2D renderer. */
|
|
18
19
|
export class WebGLContext3D {
|
|
19
20
|
gl;
|
|
21
|
+
drawState;
|
|
20
22
|
width = 0;
|
|
21
23
|
height = 0;
|
|
22
24
|
disposed = false;
|
|
@@ -48,6 +50,9 @@ export class WebGLContext3D {
|
|
|
48
50
|
s3tc;
|
|
49
51
|
maxAnisotropy = 1;
|
|
50
52
|
vertices = new Array(8).fill(null);
|
|
53
|
+
enabledAttributes = 0;
|
|
54
|
+
indexBuffer = null;
|
|
55
|
+
texturesApplied = new Uint8Array(8);
|
|
51
56
|
selected = null;
|
|
52
57
|
framebuffer = null;
|
|
53
58
|
texture = null;
|
|
@@ -57,8 +62,11 @@ export class WebGLContext3D {
|
|
|
57
62
|
depthBuffer = null;
|
|
58
63
|
antiAlias = 0;
|
|
59
64
|
depthWrite = true;
|
|
65
|
+
depthCompare = "less";
|
|
60
66
|
blendEnabled = false;
|
|
61
|
-
|
|
67
|
+
blendSource = "one";
|
|
68
|
+
blendDestination = "zero";
|
|
69
|
+
cullFace = "none";
|
|
62
70
|
colorWrite = 15;
|
|
63
71
|
stencilFront = new StencilState();
|
|
64
72
|
stencilBack = new StencilState();
|
|
@@ -72,8 +80,18 @@ export class WebGLContext3D {
|
|
|
72
80
|
scissorRight = 0;
|
|
73
81
|
scissorBottom = 0;
|
|
74
82
|
scissorOutside = false;
|
|
75
|
-
vertexConstants =
|
|
76
|
-
|
|
83
|
+
vertexConstants = {
|
|
84
|
+
data: new Float32Array(128 * 4),
|
|
85
|
+
versions: new Float64Array(128),
|
|
86
|
+
version: 0,
|
|
87
|
+
writing: 0,
|
|
88
|
+
};
|
|
89
|
+
fragmentConstants = {
|
|
90
|
+
data: new Float32Array(28 * 4),
|
|
91
|
+
versions: new Float64Array(28),
|
|
92
|
+
version: 0,
|
|
93
|
+
writing: 0,
|
|
94
|
+
};
|
|
77
95
|
pixels = new Uint8Array(0);
|
|
78
96
|
topDown = new Uint8Array(0);
|
|
79
97
|
vao;
|
|
@@ -89,6 +107,7 @@ export class WebGLContext3D {
|
|
|
89
107
|
if (gl.getContextAttributes()?.antialias) {
|
|
90
108
|
throw new Context3DError("unsupported", "Context3D requires a WebGL2 context with antialias: false");
|
|
91
109
|
}
|
|
110
|
+
this.drawState = new GlState(gl);
|
|
92
111
|
const vao = gl.createVertexArray();
|
|
93
112
|
if (!vao) {
|
|
94
113
|
throw new Context3DError("operation", "Context3D experiment: cannot create vertex array");
|
|
@@ -130,8 +149,13 @@ export class WebGLContext3D {
|
|
|
130
149
|
this.seedTarget(texture, state);
|
|
131
150
|
}
|
|
132
151
|
}
|
|
133
|
-
this.
|
|
134
|
-
this.
|
|
152
|
+
this.drawState.bindFramebuffer(state ? targetFramebuffer(state) : this.framebuffer);
|
|
153
|
+
this.drawState.viewport(0, 0, texture?.width ?? this.width, texture?.height ?? this.height);
|
|
154
|
+
}
|
|
155
|
+
/** Temporary attachments and separate READ/DRAW bindings bypass the hot target cache. */
|
|
156
|
+
bindFramebuffer(target, framebuffer) {
|
|
157
|
+
this.drawState.forget();
|
|
158
|
+
this.gl.bindFramebuffer(target, framebuffer);
|
|
135
159
|
}
|
|
136
160
|
configure(width, height, depth = false, antiAlias = 0) {
|
|
137
161
|
this.alive();
|
|
@@ -163,17 +187,19 @@ export class WebGLContext3D {
|
|
|
163
187
|
(samples && (!resolveFramebuffer || !colorBuffer))) {
|
|
164
188
|
throw new Context3DError("operation", "Context3D experiment: cannot allocate back buffer");
|
|
165
189
|
}
|
|
190
|
+
this.texturesApplied[0] = 0;
|
|
191
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
166
192
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
167
193
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
168
194
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
169
195
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
170
|
-
|
|
196
|
+
this.bindFramebuffer(gl.FRAMEBUFFER, resolveFramebuffer ?? framebuffer);
|
|
171
197
|
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
|
|
172
198
|
if (resolveFramebuffer) {
|
|
173
199
|
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
|
|
174
200
|
throw new Context3DError("operation", "Context3D experiment: incomplete resolve buffer");
|
|
175
201
|
}
|
|
176
|
-
|
|
202
|
+
this.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
|
177
203
|
gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
|
|
178
204
|
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples, gl.RGBA8, width, height);
|
|
179
205
|
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
|
|
@@ -200,7 +226,7 @@ export class WebGLContext3D {
|
|
|
200
226
|
gl.deleteRenderbuffer(depthBuffer);
|
|
201
227
|
gl.deleteRenderbuffer(colorBuffer);
|
|
202
228
|
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
|
|
203
|
-
|
|
229
|
+
this.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
|
|
204
230
|
throw error;
|
|
205
231
|
}
|
|
206
232
|
gl.deleteTexture(this.texture);
|
|
@@ -337,9 +363,14 @@ export class WebGLContext3D {
|
|
|
337
363
|
}
|
|
338
364
|
setDepth(write, compare) {
|
|
339
365
|
this.alive();
|
|
340
|
-
this.depthWrite
|
|
341
|
-
|
|
342
|
-
|
|
366
|
+
if (this.depthWrite !== write) {
|
|
367
|
+
this.depthWrite = write;
|
|
368
|
+
this.gl.depthMask(write);
|
|
369
|
+
}
|
|
370
|
+
if (this.depthCompare !== compare) {
|
|
371
|
+
this.depthCompare = compare;
|
|
372
|
+
this.gl.depthFunc(this.gl[DEPTH_FUNCTIONS[compare]]);
|
|
373
|
+
}
|
|
343
374
|
}
|
|
344
375
|
setScissor(x, y = 0, width = 0, height = 0) {
|
|
345
376
|
this.alive();
|
|
@@ -380,25 +411,39 @@ export class WebGLContext3D {
|
|
|
380
411
|
}
|
|
381
412
|
setBlend(source, destination) {
|
|
382
413
|
this.alive();
|
|
414
|
+
if (this.blendSource === source && this.blendDestination === destination) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
383
417
|
const gl = this.gl;
|
|
418
|
+
const enabled = this.blendEnabled;
|
|
419
|
+
this.blendSource = source;
|
|
420
|
+
this.blendDestination = destination;
|
|
384
421
|
this.blendEnabled = source !== "one" || destination !== "zero";
|
|
385
|
-
if (
|
|
422
|
+
if (!this.blendEnabled) {
|
|
386
423
|
gl.disable(gl.BLEND);
|
|
387
424
|
}
|
|
388
425
|
else {
|
|
389
426
|
gl.blendFunc(gl[BLEND_FUNCTIONS[source]], gl[BLEND_FUNCTIONS[destination]]);
|
|
390
|
-
|
|
427
|
+
if (!enabled) {
|
|
428
|
+
gl.enable(gl.BLEND);
|
|
429
|
+
}
|
|
391
430
|
}
|
|
392
431
|
}
|
|
393
432
|
setCulling(face) {
|
|
394
433
|
this.alive();
|
|
434
|
+
const previous = this.cullFace;
|
|
435
|
+
if (previous === face) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
395
438
|
const gl = this.gl;
|
|
396
|
-
this.
|
|
439
|
+
this.cullFace = face;
|
|
397
440
|
if (face === "none") {
|
|
398
441
|
gl.disable(gl.CULL_FACE);
|
|
399
442
|
return;
|
|
400
443
|
}
|
|
401
|
-
|
|
444
|
+
if (previous === "none") {
|
|
445
|
+
gl.enable(gl.CULL_FACE);
|
|
446
|
+
}
|
|
402
447
|
switch (face) {
|
|
403
448
|
case "back":
|
|
404
449
|
gl.cullFace(gl.BACK);
|
|
@@ -432,6 +477,9 @@ export class WebGLContext3D {
|
|
|
432
477
|
throw new Context3DError("bufferAllocation", "Context3D experiment: cannot allocate buffer");
|
|
433
478
|
}
|
|
434
479
|
const target = stride === 0 ? gl.ELEMENT_ARRAY_BUFFER : gl.ARRAY_BUFFER;
|
|
480
|
+
if (stride === 0) {
|
|
481
|
+
this.indexBuffer = null;
|
|
482
|
+
}
|
|
435
483
|
gl.bindVertexArray(this.vao);
|
|
436
484
|
gl.bindBuffer(target, handle);
|
|
437
485
|
gl.bufferData(target, bytes, gl.STATIC_DRAW);
|
|
@@ -452,17 +500,35 @@ export class WebGLContext3D {
|
|
|
452
500
|
upload: (values, start, amount) => {
|
|
453
501
|
this.alive();
|
|
454
502
|
this.buffer(buffer);
|
|
455
|
-
|
|
503
|
+
try {
|
|
504
|
+
uploadBuffer(gl, this.vao, buffer, state, values, start, amount);
|
|
505
|
+
}
|
|
506
|
+
finally {
|
|
507
|
+
// Source getters can draw before the upload replaces the VAO's index binding.
|
|
508
|
+
if (stride === 0) {
|
|
509
|
+
this.indexBuffer = null;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
456
512
|
},
|
|
457
513
|
uploadBytes: (data, offset, start, amount) => {
|
|
458
514
|
this.alive();
|
|
459
515
|
this.buffer(buffer);
|
|
460
|
-
|
|
516
|
+
try {
|
|
517
|
+
uploadBufferBytes(gl, this.vao, buffer, state, data, offset, start, amount);
|
|
518
|
+
}
|
|
519
|
+
finally {
|
|
520
|
+
if (stride === 0) {
|
|
521
|
+
this.indexBuffer = null;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
461
524
|
},
|
|
462
525
|
dispose: () => {
|
|
463
526
|
if (!this.buffers.delete(buffer)) {
|
|
464
527
|
return;
|
|
465
528
|
}
|
|
529
|
+
if (this.indexBuffer === handle) {
|
|
530
|
+
this.indexBuffer = null;
|
|
531
|
+
}
|
|
466
532
|
gl.deleteBuffer(handle);
|
|
467
533
|
this.bufferBytes -= bytes;
|
|
468
534
|
if (stride !== 0) {
|
|
@@ -505,17 +571,24 @@ export class WebGLContext3D {
|
|
|
505
571
|
const state = {
|
|
506
572
|
handle: null,
|
|
507
573
|
targetY: null,
|
|
574
|
+
targetYValue: Number.NaN,
|
|
508
575
|
attributes: [],
|
|
576
|
+
attributeMask: 0,
|
|
509
577
|
vertex: [],
|
|
510
578
|
vertexBank: null,
|
|
511
579
|
fragment: [],
|
|
512
580
|
samplers: [],
|
|
513
581
|
samplerMask: 0,
|
|
582
|
+
samplersSet: false,
|
|
583
|
+
vertexVersions: new Float64Array(0),
|
|
584
|
+
fragmentVersions: new Float64Array(0),
|
|
585
|
+
vertexVersion: -1,
|
|
514
586
|
};
|
|
515
587
|
const program = {
|
|
516
588
|
upload: (vertex, fragment) => {
|
|
517
589
|
this.alive();
|
|
518
590
|
this.program(program);
|
|
591
|
+
this.drawState.forget();
|
|
519
592
|
uploadProgram(this.gl, state, vertex, fragment);
|
|
520
593
|
if (this.selected === program) {
|
|
521
594
|
this.setProgram(program);
|
|
@@ -526,6 +599,7 @@ export class WebGLContext3D {
|
|
|
526
599
|
return;
|
|
527
600
|
}
|
|
528
601
|
// WebGL keeps a deleted program alive while it is still current.
|
|
602
|
+
this.drawState.forget();
|
|
529
603
|
if (this.gl.getParameter(this.gl.CURRENT_PROGRAM) === state.handle) {
|
|
530
604
|
this.gl.useProgram(null);
|
|
531
605
|
}
|
|
@@ -533,11 +607,14 @@ export class WebGLContext3D {
|
|
|
533
607
|
state.handle = null;
|
|
534
608
|
state.targetY = null;
|
|
535
609
|
state.attributes.length = 0;
|
|
610
|
+
state.attributeMask = 0;
|
|
536
611
|
state.vertex.length = 0;
|
|
537
612
|
state.vertexBank = null;
|
|
538
613
|
state.fragment.length = 0;
|
|
539
614
|
state.samplers.length = 0;
|
|
540
615
|
state.samplerMask = 0;
|
|
616
|
+
state.vertexVersions = new Float64Array(0);
|
|
617
|
+
state.fragmentVersions = new Float64Array(0);
|
|
541
618
|
if (this.selected === program) {
|
|
542
619
|
this.selected = null;
|
|
543
620
|
}
|
|
@@ -635,6 +712,7 @@ export class WebGLContext3D {
|
|
|
635
712
|
if (!handle) {
|
|
636
713
|
throw new Context3DError("textureAllocation", "Context3D experiment: cannot create texture");
|
|
637
714
|
}
|
|
715
|
+
this.texturesApplied[0] = 0;
|
|
638
716
|
gl.activeTexture(gl.TEXTURE0);
|
|
639
717
|
const target = cube ? gl.TEXTURE_CUBE_MAP : gl.TEXTURE_2D;
|
|
640
718
|
gl.bindTexture(target, handle);
|
|
@@ -664,12 +742,23 @@ export class WebGLContext3D {
|
|
|
664
742
|
upload: (pixels, level = 0, face = 0, sourceWidth, sourceHeight) => {
|
|
665
743
|
this.alive();
|
|
666
744
|
this.textureState(texture);
|
|
667
|
-
|
|
745
|
+
try {
|
|
746
|
+
uploadTexture(gl, texture, state, pixels, level, face, sourceWidth, sourceHeight, this.dxtEncoder);
|
|
747
|
+
}
|
|
748
|
+
finally {
|
|
749
|
+
// An encoder can draw before the upload replaces unit zero's binding.
|
|
750
|
+
this.texturesApplied[0] = 0;
|
|
751
|
+
}
|
|
668
752
|
},
|
|
669
753
|
uploadBytes: (data, offset, level = 0, face = 0) => {
|
|
670
754
|
this.alive();
|
|
671
755
|
this.textureState(texture);
|
|
672
|
-
|
|
756
|
+
try {
|
|
757
|
+
uploadTextureBytes(gl, texture, state, data, offset, level, face, this.dxtEncoder);
|
|
758
|
+
}
|
|
759
|
+
finally {
|
|
760
|
+
this.texturesApplied[0] = 0;
|
|
761
|
+
}
|
|
673
762
|
},
|
|
674
763
|
uploadCompressed: (blocks, level = 0, face = 0) => {
|
|
675
764
|
this.alive();
|
|
@@ -677,18 +766,24 @@ export class WebGLContext3D {
|
|
|
677
766
|
if (format === "bgra") {
|
|
678
767
|
throw new Context3DError("textureUpload", "Context3D experiment: BGRA texture given blocks");
|
|
679
768
|
}
|
|
680
|
-
|
|
769
|
+
try {
|
|
770
|
+
uploadCompressedLevel(gl, texture, state, blocks, level, face);
|
|
771
|
+
}
|
|
772
|
+
finally {
|
|
773
|
+
this.texturesApplied[0] = 0;
|
|
774
|
+
}
|
|
681
775
|
},
|
|
682
776
|
dispose: () => {
|
|
683
777
|
if (!this.textures.delete(texture)) {
|
|
684
778
|
return;
|
|
685
779
|
}
|
|
686
780
|
this.textureCounts[kind]--;
|
|
781
|
+
this.drawState.forget();
|
|
687
782
|
this.textureQuotaBytes[kind] -= quotaBytes;
|
|
688
783
|
if (this.renderTexture === texture) {
|
|
689
784
|
this.renderTexture = null;
|
|
690
785
|
this.renderDepth = false;
|
|
691
|
-
|
|
786
|
+
this.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
|
|
692
787
|
gl.frontFace(gl.CW);
|
|
693
788
|
this.applyScissor();
|
|
694
789
|
}
|
|
@@ -707,6 +802,7 @@ export class WebGLContext3D {
|
|
|
707
802
|
state.depthBuffer = null;
|
|
708
803
|
for (let i = 0; i < this.textureSlots.length; i++) {
|
|
709
804
|
if (this.textureSlots[i] === texture) {
|
|
805
|
+
this.texturesApplied[i] = 0;
|
|
710
806
|
this.textureSlots[i] = null;
|
|
711
807
|
this.textureMask &= ~(1 << i);
|
|
712
808
|
gl.activeTexture(gl.TEXTURE0 + i);
|
|
@@ -754,7 +850,7 @@ export class WebGLContext3D {
|
|
|
754
850
|
}
|
|
755
851
|
const gl = this.gl;
|
|
756
852
|
const program = this.copyProgram(texture.cube);
|
|
757
|
-
|
|
853
|
+
this.bindFramebuffer(gl.FRAMEBUFFER, target.framebuffer);
|
|
758
854
|
gl.viewport(0, 0, texture.width, texture.height);
|
|
759
855
|
gl.disable(gl.DEPTH_TEST);
|
|
760
856
|
gl.disable(gl.STENCIL_TEST);
|
|
@@ -762,6 +858,7 @@ export class WebGLContext3D {
|
|
|
762
858
|
gl.disable(gl.CULL_FACE);
|
|
763
859
|
gl.disable(gl.SCISSOR_TEST);
|
|
764
860
|
gl.colorMask(true, true, true, true);
|
|
861
|
+
this.texturesApplied[0] = 0;
|
|
765
862
|
gl.activeTexture(gl.TEXTURE0);
|
|
766
863
|
gl.bindTexture(texture.cube ? gl.TEXTURE_CUBE_MAP : gl.TEXTURE_2D, state.handle);
|
|
767
864
|
gl.bindSampler(0, this.sampler(0));
|
|
@@ -780,7 +877,7 @@ export class WebGLContext3D {
|
|
|
780
877
|
if (this.blendEnabled) {
|
|
781
878
|
gl.enable(gl.BLEND);
|
|
782
879
|
}
|
|
783
|
-
if (this.
|
|
880
|
+
if (this.cullFace !== "none") {
|
|
784
881
|
gl.enable(gl.CULL_FACE);
|
|
785
882
|
}
|
|
786
883
|
if (this.scissor) {
|
|
@@ -800,6 +897,7 @@ export class WebGLContext3D {
|
|
|
800
897
|
return this.targetSamples[key];
|
|
801
898
|
}
|
|
802
899
|
deleteMultisample(texture, target) {
|
|
900
|
+
this.drawState.forget();
|
|
803
901
|
deleteMultisample(this.gl, target);
|
|
804
902
|
const bytes = texture.width * texture.height * 4 * target.samples;
|
|
805
903
|
this.targetColorBytes -= bytes + (target.texture ? texture.width * texture.height * 4 : 0);
|
|
@@ -822,6 +920,8 @@ export class WebGLContext3D {
|
|
|
822
920
|
}
|
|
823
921
|
this.copyProgram(texture.cube);
|
|
824
922
|
this.sampler(0);
|
|
923
|
+
this.drawState.forget();
|
|
924
|
+
this.texturesApplied[0] = 0;
|
|
825
925
|
const target = allocateMultisample(this.gl, texture, old, samples, depth, this.depthWrite, this.stencilWrite);
|
|
826
926
|
if (same) {
|
|
827
927
|
if (newDepth) {
|
|
@@ -877,7 +977,7 @@ export class WebGLContext3D {
|
|
|
877
977
|
throw new Context3DError("operation", "Context3D experiment: cannot create cube mip framebuffer");
|
|
878
978
|
}
|
|
879
979
|
}
|
|
880
|
-
|
|
980
|
+
this.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
|
881
981
|
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, texture.cube ? gl.TEXTURE_CUBE_MAP_POSITIVE_X + face : gl.TEXTURE_2D, state.handle, 0);
|
|
882
982
|
if (singleDepth && !depthBuffer) {
|
|
883
983
|
if (this.targetDepthBytes + texture.width * texture.height * 4 >
|
|
@@ -905,7 +1005,7 @@ export class WebGLContext3D {
|
|
|
905
1005
|
gl.deleteFramebuffer(framebuffer);
|
|
906
1006
|
}
|
|
907
1007
|
else if (framebuffer) {
|
|
908
|
-
|
|
1008
|
+
this.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
|
909
1009
|
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, texture.cube ? gl.TEXTURE_CUBE_MAP_POSITIVE_X + state.face : gl.TEXTURE_2D, state.handle, 0);
|
|
910
1010
|
// Restore the attachment if this was the currently selected target.
|
|
911
1011
|
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, state.depthAttached ? state.depthBuffer : null);
|
|
@@ -917,7 +1017,7 @@ export class WebGLContext3D {
|
|
|
917
1017
|
gl.deleteRenderbuffer(depthBuffer);
|
|
918
1018
|
}
|
|
919
1019
|
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
|
|
920
|
-
|
|
1020
|
+
this.bindFramebuffer(gl.FRAMEBUFFER, this.renderTexture
|
|
921
1021
|
? targetFramebuffer(this.textureState(this.renderTexture))
|
|
922
1022
|
: this.framebuffer);
|
|
923
1023
|
this.applyScissor();
|
|
@@ -978,6 +1078,8 @@ export class WebGLContext3D {
|
|
|
978
1078
|
const state = this.textureState(texture);
|
|
979
1079
|
if (state.dirty) {
|
|
980
1080
|
const gl = this.gl;
|
|
1081
|
+
this.drawState.forget();
|
|
1082
|
+
this.texturesApplied[0] = 0;
|
|
981
1083
|
resolveTarget(this.gl, texture, state, this.renderFace, this.scissor);
|
|
982
1084
|
gl.activeTexture(gl.TEXTURE0);
|
|
983
1085
|
if (texture.cube) {
|
|
@@ -986,19 +1088,19 @@ export class WebGLContext3D {
|
|
|
986
1088
|
gl.disable(gl.SCISSOR_TEST);
|
|
987
1089
|
const face = gl.TEXTURE_CUBE_MAP_POSITIVE_X + this.renderFace;
|
|
988
1090
|
// The base-size depth attachment cannot accompany smaller mip images.
|
|
989
|
-
|
|
1091
|
+
this.bindFramebuffer(gl.FRAMEBUFFER, state.framebuffer);
|
|
990
1092
|
if (state.depthAttached) {
|
|
991
1093
|
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, null);
|
|
992
1094
|
}
|
|
993
1095
|
for (let level = 1; level < texture.levels; level++) {
|
|
994
|
-
|
|
1096
|
+
this.bindFramebuffer(gl.READ_FRAMEBUFFER, state.framebuffer);
|
|
995
1097
|
gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, face, state.handle, level - 1);
|
|
996
|
-
|
|
1098
|
+
this.bindFramebuffer(gl.DRAW_FRAMEBUFFER, state.mipFramebuffer);
|
|
997
1099
|
gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, face, state.handle, level);
|
|
998
1100
|
const size = texture.width >> level;
|
|
999
1101
|
gl.blitFramebuffer(0, 0, size * 2, size * 2, 0, 0, size, size, gl.COLOR_BUFFER_BIT, gl.LINEAR);
|
|
1000
1102
|
}
|
|
1001
|
-
|
|
1103
|
+
this.bindFramebuffer(gl.FRAMEBUFFER, state.framebuffer);
|
|
1002
1104
|
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + state.face, state.handle, 0);
|
|
1003
1105
|
if (state.depthAttached) {
|
|
1004
1106
|
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, state.depthBuffer);
|
|
@@ -1019,9 +1121,13 @@ export class WebGLContext3D {
|
|
|
1019
1121
|
throw new Context3DError("disposed", "Context3D experiment: texture from another context");
|
|
1020
1122
|
}
|
|
1021
1123
|
// AIR accepts a disposed texture binding and reports 3663 when drawing.
|
|
1124
|
+
if (this.textureSlots[index] !== texture) {
|
|
1125
|
+
this.texturesApplied[index] = 0;
|
|
1126
|
+
}
|
|
1022
1127
|
this.textureSlots[index] = texture;
|
|
1023
1128
|
this.textureMask = (this.textureMask & ~(1 << index)) | (texture ? 1 << index : 0);
|
|
1024
1129
|
if (!texture) {
|
|
1130
|
+
this.texturesApplied[index] = 0;
|
|
1025
1131
|
this.gl.activeTexture(this.gl.TEXTURE0 + index);
|
|
1026
1132
|
this.gl.bindTexture(this.gl.TEXTURE_2D, null);
|
|
1027
1133
|
this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, null);
|
|
@@ -1037,7 +1143,13 @@ export class WebGLContext3D {
|
|
|
1037
1143
|
if (w < 0 || f < 0 || m < 0) {
|
|
1038
1144
|
throw new Context3DError("operation", "Context3D experiment: invalid sampler state");
|
|
1039
1145
|
}
|
|
1040
|
-
this.
|
|
1146
|
+
this.setSamplerState(index, w | (f << 2) | (m << 5));
|
|
1147
|
+
}
|
|
1148
|
+
setSamplerState(index, state) {
|
|
1149
|
+
if (this.samplerStates[index] !== state) {
|
|
1150
|
+
this.samplerStates[index] = state;
|
|
1151
|
+
this.texturesApplied[index] = 0;
|
|
1152
|
+
}
|
|
1041
1153
|
}
|
|
1042
1154
|
sampler(index) {
|
|
1043
1155
|
let sampler = this.samplers[index];
|
|
@@ -1089,8 +1201,7 @@ export class WebGLContext3D {
|
|
|
1089
1201
|
for (let i = 0; i < state.samplers.length; i++) {
|
|
1090
1202
|
const source = state.samplers[i].source;
|
|
1091
1203
|
if (!source.ignore) {
|
|
1092
|
-
this.
|
|
1093
|
-
(source.repeat ? 3 : 0) | (source.filter << 2) | (source.mip << 5);
|
|
1204
|
+
this.setSamplerState(source.index, (source.repeat ? 3 : 0) | (source.filter << 2) | (source.mip << 5));
|
|
1094
1205
|
}
|
|
1095
1206
|
}
|
|
1096
1207
|
}
|
|
@@ -1112,6 +1223,12 @@ export class WebGLContext3D {
|
|
|
1112
1223
|
const valid = offset >= 0 && offset <= buffer.stride - (packed ? 1 : size);
|
|
1113
1224
|
const previous = this.vertices[index];
|
|
1114
1225
|
if (previous) {
|
|
1226
|
+
if (previous.buffer !== buffer ||
|
|
1227
|
+
previous.offset !== offset ||
|
|
1228
|
+
previous.size !== size ||
|
|
1229
|
+
previous.packed !== packed) {
|
|
1230
|
+
previous.applied = false;
|
|
1231
|
+
}
|
|
1115
1232
|
if (previous.buffer !== buffer) {
|
|
1116
1233
|
previous.checkedIndex = null;
|
|
1117
1234
|
}
|
|
@@ -1128,6 +1245,7 @@ export class WebGLContext3D {
|
|
|
1128
1245
|
size,
|
|
1129
1246
|
packed,
|
|
1130
1247
|
valid,
|
|
1248
|
+
applied: false,
|
|
1131
1249
|
checkedIndex: null,
|
|
1132
1250
|
checkedIndexVersion: -1,
|
|
1133
1251
|
checkedVertexVersion: -1,
|
|
@@ -1139,10 +1257,20 @@ export class WebGLContext3D {
|
|
|
1139
1257
|
setConstants(stage, first, values, count) {
|
|
1140
1258
|
this.alive();
|
|
1141
1259
|
const dest = stage === "vertex" ? this.vertexConstants : this.fragmentConstants;
|
|
1142
|
-
range(first, count, dest.length / 4);
|
|
1260
|
+
range(first, count, dest.data.length / 4);
|
|
1143
1261
|
range(0, count * 4, values.length);
|
|
1144
|
-
|
|
1145
|
-
|
|
1262
|
+
dest.writing++;
|
|
1263
|
+
try {
|
|
1264
|
+
for (let i = 0; i < count * 4; i++) {
|
|
1265
|
+
dest.data[first * 4 + i] = values[i];
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
finally {
|
|
1269
|
+
// Source getters can draw, nest setters or throw after partially writing the bank.
|
|
1270
|
+
dest.writing--;
|
|
1271
|
+
if (count) {
|
|
1272
|
+
dest.versions.fill(++dest.version, first, first + count);
|
|
1273
|
+
}
|
|
1146
1274
|
}
|
|
1147
1275
|
}
|
|
1148
1276
|
draw(indices, first, triangles) {
|
|
@@ -1184,9 +1312,13 @@ export class WebGLContext3D {
|
|
|
1184
1312
|
}
|
|
1185
1313
|
const maximum = validateIndices(index, first, count);
|
|
1186
1314
|
const gl = this.gl;
|
|
1187
|
-
|
|
1315
|
+
this.drawState.bindVertexArray(this.vao);
|
|
1188
1316
|
for (let i = 0; i < 8; i++) {
|
|
1189
|
-
|
|
1317
|
+
const bit = 1 << i;
|
|
1318
|
+
if (this.enabledAttributes & bit & ~state.attributeMask) {
|
|
1319
|
+
gl.disableVertexAttribArray(i);
|
|
1320
|
+
this.enabledAttributes &= ~bit;
|
|
1321
|
+
}
|
|
1190
1322
|
}
|
|
1191
1323
|
for (let i = 0; i < state.attributes.length; i++) {
|
|
1192
1324
|
const slot = state.attributes[i];
|
|
@@ -1199,12 +1331,23 @@ export class WebGLContext3D {
|
|
|
1199
1331
|
throw new VertexOffsetError("Vertex attribute exceeds its stride");
|
|
1200
1332
|
}
|
|
1201
1333
|
validateVertices(buffer, index, binding, first, count, maximum);
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1334
|
+
if (!binding.applied) {
|
|
1335
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer.handle);
|
|
1336
|
+
gl.vertexAttribPointer(slot, binding.size, binding.packed ? gl.UNSIGNED_BYTE : gl.FLOAT, binding.packed, binding.buffer.stride * 4, binding.offset * 4);
|
|
1337
|
+
binding.applied = true;
|
|
1338
|
+
}
|
|
1339
|
+
const bit = 1 << slot;
|
|
1340
|
+
if (!(this.enabledAttributes & bit)) {
|
|
1341
|
+
gl.enableVertexAttribArray(slot);
|
|
1342
|
+
this.enabledAttributes |= bit;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
this.drawState.useProgram(state.handle);
|
|
1346
|
+
const targetY = this.renderTexture ? -1 : 1;
|
|
1347
|
+
if (state.targetYValue !== targetY) {
|
|
1348
|
+
gl.uniform1f(state.targetY, targetY);
|
|
1349
|
+
state.targetYValue = targetY;
|
|
1205
1350
|
}
|
|
1206
|
-
gl.useProgram(state.handle);
|
|
1207
|
-
gl.uniform1f(state.targetY, this.renderTexture ? -1 : 1);
|
|
1208
1351
|
for (let i = 0; i < state.samplers.length; i++) {
|
|
1209
1352
|
const binding = state.samplers[i];
|
|
1210
1353
|
const slot = binding.source.index;
|
|
@@ -1248,26 +1391,39 @@ export class WebGLContext3D {
|
|
|
1248
1391
|
if (this.samplerFormats && binding.source.format !== SAMPLER_FORMATS[texture.format]) {
|
|
1249
1392
|
throw new TextureBindingError("format", slot);
|
|
1250
1393
|
}
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1394
|
+
if (!this.texturesApplied[slot]) {
|
|
1395
|
+
gl.activeTexture(gl.TEXTURE0 + slot);
|
|
1396
|
+
gl.bindTexture(texture.cube ? gl.TEXTURE_CUBE_MAP : gl.TEXTURE_2D, resource.handle);
|
|
1397
|
+
gl.bindSampler(slot, this.sampler(this.samplerStates[slot]));
|
|
1398
|
+
this.texturesApplied[slot] = 1;
|
|
1399
|
+
}
|
|
1400
|
+
if (!state.samplersSet) {
|
|
1401
|
+
gl.uniform1i(binding.location, slot);
|
|
1402
|
+
}
|
|
1255
1403
|
}
|
|
1404
|
+
state.samplersSet = true;
|
|
1256
1405
|
if (state.vertexBank) {
|
|
1257
|
-
|
|
1406
|
+
if (this.vertexConstants.writing || state.vertexVersion !== this.vertexConstants.version) {
|
|
1407
|
+
gl.uniform4fv(state.vertexBank, this.vertexConstants.data);
|
|
1408
|
+
state.vertexVersion = this.vertexConstants.version;
|
|
1409
|
+
}
|
|
1258
1410
|
}
|
|
1259
1411
|
else {
|
|
1260
|
-
this.uniforms(state.vertex, this.vertexConstants);
|
|
1412
|
+
this.uniforms(state.vertex, this.vertexConstants, state.vertexVersions);
|
|
1413
|
+
}
|
|
1414
|
+
this.uniforms(state.fragment, this.fragmentConstants, state.fragmentVersions);
|
|
1415
|
+
if (this.indexBuffer !== index.handle) {
|
|
1416
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index.handle);
|
|
1417
|
+
this.indexBuffer = index.handle;
|
|
1261
1418
|
}
|
|
1262
|
-
this.uniforms(state.fragment, this.fragmentConstants);
|
|
1263
|
-
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index.handle);
|
|
1264
1419
|
gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, first * 2);
|
|
1265
1420
|
this.changedTarget();
|
|
1266
1421
|
}
|
|
1267
|
-
uniforms(locations,
|
|
1422
|
+
uniforms(locations, bank, previous) {
|
|
1268
1423
|
for (let i = 0; i < locations.length; i++) {
|
|
1269
|
-
if (locations[i]) {
|
|
1270
|
-
this.gl.uniform4fv(locations[i], data, i * 4, 4);
|
|
1424
|
+
if (locations[i] && (bank.writing || previous[i] !== bank.versions[i])) {
|
|
1425
|
+
this.gl.uniform4fv(locations[i], bank.data, i * 4, 4);
|
|
1426
|
+
previous[i] = bank.versions[i];
|
|
1271
1427
|
}
|
|
1272
1428
|
}
|
|
1273
1429
|
}
|
|
@@ -1277,8 +1433,8 @@ export class WebGLContext3D {
|
|
|
1277
1433
|
}
|
|
1278
1434
|
const gl = this.gl;
|
|
1279
1435
|
if (this.resolveDirty) {
|
|
1280
|
-
|
|
1281
|
-
|
|
1436
|
+
this.bindFramebuffer(gl.READ_FRAMEBUFFER, this.framebuffer);
|
|
1437
|
+
this.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.resolveFramebuffer);
|
|
1282
1438
|
if (this.scissor) {
|
|
1283
1439
|
gl.disable(gl.SCISSOR_TEST);
|
|
1284
1440
|
}
|
|
@@ -1288,7 +1444,7 @@ export class WebGLContext3D {
|
|
|
1288
1444
|
}
|
|
1289
1445
|
this.resolveDirty = false;
|
|
1290
1446
|
}
|
|
1291
|
-
|
|
1447
|
+
this.bindFramebuffer(gl.READ_FRAMEBUFFER, this.resolveFramebuffer);
|
|
1292
1448
|
}
|
|
1293
1449
|
read(width = this.width, height = this.height) {
|
|
1294
1450
|
width = Math.max(0, Math.min(this.width, Math.floor(width) || 0));
|
|
@@ -1310,7 +1466,7 @@ export class WebGLContext3D {
|
|
|
1310
1466
|
this.target();
|
|
1311
1467
|
this.resolve();
|
|
1312
1468
|
const gl = this.gl;
|
|
1313
|
-
|
|
1469
|
+
this.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
|
|
1314
1470
|
if (this.scissor) {
|
|
1315
1471
|
gl.disable(gl.SCISSOR_TEST);
|
|
1316
1472
|
}
|
|
@@ -1344,7 +1500,7 @@ export class WebGLContext3D {
|
|
|
1344
1500
|
this.gl.useProgram(null);
|
|
1345
1501
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);
|
|
1346
1502
|
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, null);
|
|
1347
|
-
this.
|
|
1503
|
+
this.bindFramebuffer(this.gl.FRAMEBUFFER, null);
|
|
1348
1504
|
this.gl.bindTexture(this.gl.TEXTURE_2D, null);
|
|
1349
1505
|
this.gl.deleteVertexArray(this.vao);
|
|
1350
1506
|
this.gl.deleteFramebuffer(this.framebuffer);
|
|
@@ -1363,6 +1519,9 @@ export class WebGLContext3D {
|
|
|
1363
1519
|
this.depthBuffer = null;
|
|
1364
1520
|
this.pixels = new Uint8Array(0);
|
|
1365
1521
|
this.topDown = new Uint8Array(0);
|
|
1522
|
+
this.texturesApplied.fill(0);
|
|
1523
|
+
this.indexBuffer = null;
|
|
1524
|
+
this.drawState.forget();
|
|
1366
1525
|
}
|
|
1367
1526
|
}
|
|
1368
1527
|
//# sourceMappingURL=context.js.map
|