@swf2es/context3d 0.1.10 → 0.1.12
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/src/webgl/context.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
type VertexFormat,
|
|
22
22
|
VertexOffsetError,
|
|
23
23
|
} from "../backend.js";
|
|
24
|
+
import { GlState } from "../device/state.js";
|
|
24
25
|
import { blockBytes, type DxtEncoder } from "../dxt.js";
|
|
25
26
|
import { Context3DError } from "../errors.js";
|
|
26
27
|
|
|
@@ -67,9 +68,17 @@ const SAMPLER_FORMATS: Record<TextureFormat, number> = {
|
|
|
67
68
|
compressedAlpha: 2,
|
|
68
69
|
};
|
|
69
70
|
|
|
71
|
+
interface Constants {
|
|
72
|
+
data: Float32Array;
|
|
73
|
+
versions: Float64Array;
|
|
74
|
+
version: number;
|
|
75
|
+
writing: number;
|
|
76
|
+
}
|
|
77
|
+
|
|
70
78
|
/** An owned WebGL2 context, deliberately independent of the 2D renderer. */
|
|
71
79
|
export class WebGLContext3D implements Context3D {
|
|
72
80
|
readonly gl: WebGL2RenderingContext;
|
|
81
|
+
private readonly drawState: GlState;
|
|
73
82
|
width = 0;
|
|
74
83
|
height = 0;
|
|
75
84
|
disposed = false;
|
|
@@ -101,6 +110,9 @@ export class WebGLContext3D implements Context3D {
|
|
|
101
110
|
private s3tc: WEBGL_compressed_texture_s3tc | null | undefined;
|
|
102
111
|
private maxAnisotropy = 1;
|
|
103
112
|
private readonly vertices: (VertexBinding | null)[] = new Array(8).fill(null);
|
|
113
|
+
private enabledAttributes = 0;
|
|
114
|
+
private indexBuffer: WebGLBuffer | null = null;
|
|
115
|
+
private readonly texturesApplied = new Uint8Array(8);
|
|
104
116
|
private selected: Program3D | null = null;
|
|
105
117
|
private framebuffer: WebGLFramebuffer | null = null;
|
|
106
118
|
private texture: WebGLTexture | null = null;
|
|
@@ -110,8 +122,11 @@ export class WebGLContext3D implements Context3D {
|
|
|
110
122
|
private depthBuffer: WebGLRenderbuffer | null = null;
|
|
111
123
|
private antiAlias = 0;
|
|
112
124
|
private depthWrite = true;
|
|
125
|
+
private depthCompare: CompareMode = "less";
|
|
113
126
|
private blendEnabled = false;
|
|
114
|
-
private
|
|
127
|
+
private blendSource: BlendFactor = "one";
|
|
128
|
+
private blendDestination: BlendFactor = "zero";
|
|
129
|
+
private cullFace: CullFace = "none";
|
|
115
130
|
private colorWrite = 15;
|
|
116
131
|
private readonly stencilFront = new StencilState();
|
|
117
132
|
private readonly stencilBack = new StencilState();
|
|
@@ -125,8 +140,18 @@ export class WebGLContext3D implements Context3D {
|
|
|
125
140
|
private scissorRight = 0;
|
|
126
141
|
private scissorBottom = 0;
|
|
127
142
|
private scissorOutside = false;
|
|
128
|
-
private readonly vertexConstants =
|
|
129
|
-
|
|
143
|
+
private readonly vertexConstants: Constants = {
|
|
144
|
+
data: new Float32Array(128 * 4),
|
|
145
|
+
versions: new Float64Array(128),
|
|
146
|
+
version: 0,
|
|
147
|
+
writing: 0,
|
|
148
|
+
};
|
|
149
|
+
private readonly fragmentConstants: Constants = {
|
|
150
|
+
data: new Float32Array(28 * 4),
|
|
151
|
+
versions: new Float64Array(28),
|
|
152
|
+
version: 0,
|
|
153
|
+
writing: 0,
|
|
154
|
+
};
|
|
130
155
|
private pixels = new Uint8Array(0);
|
|
131
156
|
private topDown = new Uint8Array(0);
|
|
132
157
|
private readonly vao: WebGLVertexArrayObject;
|
|
@@ -148,6 +173,7 @@ export class WebGLContext3D implements Context3D {
|
|
|
148
173
|
);
|
|
149
174
|
}
|
|
150
175
|
|
|
176
|
+
this.drawState = new GlState(gl);
|
|
151
177
|
const vao = gl.createVertexArray();
|
|
152
178
|
if (!vao) {
|
|
153
179
|
throw new Context3DError("operation", "Context3D experiment: cannot create vertex array");
|
|
@@ -195,11 +221,14 @@ export class WebGLContext3D implements Context3D {
|
|
|
195
221
|
this.seedTarget(texture, state);
|
|
196
222
|
}
|
|
197
223
|
}
|
|
198
|
-
this.
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
224
|
+
this.drawState.bindFramebuffer(state ? targetFramebuffer(state) : this.framebuffer);
|
|
225
|
+
this.drawState.viewport(0, 0, texture?.width ?? this.width, texture?.height ?? this.height);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** Temporary attachments and separate READ/DRAW bindings bypass the hot target cache. */
|
|
229
|
+
private bindFramebuffer(target: number, framebuffer: WebGLFramebuffer | null): void {
|
|
230
|
+
this.drawState.forget();
|
|
231
|
+
this.gl.bindFramebuffer(target, framebuffer);
|
|
203
232
|
}
|
|
204
233
|
|
|
205
234
|
configure(width: number, height: number, depth = false, antiAlias = 0): void {
|
|
@@ -242,18 +271,20 @@ export class WebGLContext3D implements Context3D {
|
|
|
242
271
|
throw new Context3DError("operation", "Context3D experiment: cannot allocate back buffer");
|
|
243
272
|
}
|
|
244
273
|
|
|
274
|
+
this.texturesApplied[0] = 0;
|
|
275
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
245
276
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
246
277
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
247
278
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
248
279
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
249
|
-
|
|
280
|
+
this.bindFramebuffer(gl.FRAMEBUFFER, resolveFramebuffer ?? framebuffer);
|
|
250
281
|
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
|
|
251
282
|
if (resolveFramebuffer) {
|
|
252
283
|
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
|
|
253
284
|
throw new Context3DError("operation", "Context3D experiment: incomplete resolve buffer");
|
|
254
285
|
}
|
|
255
286
|
|
|
256
|
-
|
|
287
|
+
this.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
|
257
288
|
gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
|
|
258
289
|
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples, gl.RGBA8, width, height);
|
|
259
290
|
gl.framebufferRenderbuffer(
|
|
@@ -294,7 +325,7 @@ export class WebGLContext3D implements Context3D {
|
|
|
294
325
|
gl.deleteRenderbuffer(depthBuffer);
|
|
295
326
|
gl.deleteRenderbuffer(colorBuffer);
|
|
296
327
|
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
|
|
297
|
-
|
|
328
|
+
this.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
|
|
298
329
|
throw error;
|
|
299
330
|
}
|
|
300
331
|
|
|
@@ -485,9 +516,15 @@ export class WebGLContext3D implements Context3D {
|
|
|
485
516
|
|
|
486
517
|
setDepth(write: boolean, compare: CompareMode): void {
|
|
487
518
|
this.alive();
|
|
488
|
-
this.depthWrite
|
|
489
|
-
|
|
490
|
-
|
|
519
|
+
if (this.depthWrite !== write) {
|
|
520
|
+
this.depthWrite = write;
|
|
521
|
+
this.gl.depthMask(write);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
if (this.depthCompare !== compare) {
|
|
525
|
+
this.depthCompare = compare;
|
|
526
|
+
this.gl.depthFunc(this.gl[DEPTH_FUNCTIONS[compare]]);
|
|
527
|
+
}
|
|
491
528
|
}
|
|
492
529
|
|
|
493
530
|
setScissor(x: number | null, y = 0, width = 0, height = 0): void {
|
|
@@ -541,26 +578,42 @@ export class WebGLContext3D implements Context3D {
|
|
|
541
578
|
|
|
542
579
|
setBlend(source: BlendFactor, destination: BlendFactor): void {
|
|
543
580
|
this.alive();
|
|
581
|
+
if (this.blendSource === source && this.blendDestination === destination) {
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
|
|
544
585
|
const gl = this.gl;
|
|
586
|
+
const enabled = this.blendEnabled;
|
|
587
|
+
this.blendSource = source;
|
|
588
|
+
this.blendDestination = destination;
|
|
545
589
|
this.blendEnabled = source !== "one" || destination !== "zero";
|
|
546
|
-
if (
|
|
590
|
+
if (!this.blendEnabled) {
|
|
547
591
|
gl.disable(gl.BLEND);
|
|
548
592
|
} else {
|
|
549
593
|
gl.blendFunc(gl[BLEND_FUNCTIONS[source]], gl[BLEND_FUNCTIONS[destination]]);
|
|
550
|
-
|
|
594
|
+
if (!enabled) {
|
|
595
|
+
gl.enable(gl.BLEND);
|
|
596
|
+
}
|
|
551
597
|
}
|
|
552
598
|
}
|
|
553
599
|
|
|
554
600
|
setCulling(face: CullFace): void {
|
|
555
601
|
this.alive();
|
|
602
|
+
const previous = this.cullFace;
|
|
603
|
+
if (previous === face) {
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
|
|
556
607
|
const gl = this.gl;
|
|
557
|
-
this.
|
|
608
|
+
this.cullFace = face;
|
|
558
609
|
if (face === "none") {
|
|
559
610
|
gl.disable(gl.CULL_FACE);
|
|
560
611
|
return;
|
|
561
612
|
}
|
|
562
613
|
|
|
563
|
-
|
|
614
|
+
if (previous === "none") {
|
|
615
|
+
gl.enable(gl.CULL_FACE);
|
|
616
|
+
}
|
|
564
617
|
switch (face) {
|
|
565
618
|
case "back":
|
|
566
619
|
gl.cullFace(gl.BACK);
|
|
@@ -600,6 +653,9 @@ export class WebGLContext3D implements Context3D {
|
|
|
600
653
|
}
|
|
601
654
|
|
|
602
655
|
const target = stride === 0 ? gl.ELEMENT_ARRAY_BUFFER : gl.ARRAY_BUFFER;
|
|
656
|
+
if (stride === 0) {
|
|
657
|
+
this.indexBuffer = null;
|
|
658
|
+
}
|
|
603
659
|
gl.bindVertexArray(this.vao);
|
|
604
660
|
gl.bindBuffer(target, handle);
|
|
605
661
|
gl.bufferData(target, bytes, gl.STATIC_DRAW);
|
|
@@ -620,18 +676,34 @@ export class WebGLContext3D implements Context3D {
|
|
|
620
676
|
upload: (values, start, amount) => {
|
|
621
677
|
this.alive();
|
|
622
678
|
this.buffer(buffer);
|
|
623
|
-
|
|
679
|
+
try {
|
|
680
|
+
uploadBuffer(gl, this.vao, buffer, state, values, start, amount);
|
|
681
|
+
} finally {
|
|
682
|
+
// Source getters can draw before the upload replaces the VAO's index binding.
|
|
683
|
+
if (stride === 0) {
|
|
684
|
+
this.indexBuffer = null;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
624
687
|
},
|
|
625
688
|
uploadBytes: (data, offset, start, amount) => {
|
|
626
689
|
this.alive();
|
|
627
690
|
this.buffer(buffer);
|
|
628
|
-
|
|
691
|
+
try {
|
|
692
|
+
uploadBufferBytes(gl, this.vao, buffer, state, data, offset, start, amount);
|
|
693
|
+
} finally {
|
|
694
|
+
if (stride === 0) {
|
|
695
|
+
this.indexBuffer = null;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
629
698
|
},
|
|
630
699
|
dispose: () => {
|
|
631
700
|
if (!this.buffers.delete(buffer)) {
|
|
632
701
|
return;
|
|
633
702
|
}
|
|
634
703
|
|
|
704
|
+
if (this.indexBuffer === handle) {
|
|
705
|
+
this.indexBuffer = null;
|
|
706
|
+
}
|
|
635
707
|
gl.deleteBuffer(handle);
|
|
636
708
|
this.bufferBytes -= bytes;
|
|
637
709
|
if (stride !== 0) {
|
|
@@ -680,17 +752,24 @@ export class WebGLContext3D implements Context3D {
|
|
|
680
752
|
const state: ProgramState = {
|
|
681
753
|
handle: null,
|
|
682
754
|
targetY: null,
|
|
755
|
+
targetYValue: Number.NaN,
|
|
683
756
|
attributes: [],
|
|
757
|
+
attributeMask: 0,
|
|
684
758
|
vertex: [],
|
|
685
759
|
vertexBank: null,
|
|
686
760
|
fragment: [],
|
|
687
761
|
samplers: [],
|
|
688
762
|
samplerMask: 0,
|
|
763
|
+
samplersSet: false,
|
|
764
|
+
vertexVersions: new Float64Array(0),
|
|
765
|
+
fragmentVersions: new Float64Array(0),
|
|
766
|
+
vertexVersion: -1,
|
|
689
767
|
};
|
|
690
768
|
const program: Program3D = {
|
|
691
769
|
upload: (vertex, fragment) => {
|
|
692
770
|
this.alive();
|
|
693
771
|
this.program(program);
|
|
772
|
+
this.drawState.forget();
|
|
694
773
|
uploadProgram(this.gl, state, vertex, fragment);
|
|
695
774
|
|
|
696
775
|
if (this.selected === program) {
|
|
@@ -703,6 +782,7 @@ export class WebGLContext3D implements Context3D {
|
|
|
703
782
|
}
|
|
704
783
|
|
|
705
784
|
// WebGL keeps a deleted program alive while it is still current.
|
|
785
|
+
this.drawState.forget();
|
|
706
786
|
if (this.gl.getParameter(this.gl.CURRENT_PROGRAM) === state.handle) {
|
|
707
787
|
this.gl.useProgram(null);
|
|
708
788
|
}
|
|
@@ -711,11 +791,14 @@ export class WebGLContext3D implements Context3D {
|
|
|
711
791
|
state.handle = null;
|
|
712
792
|
state.targetY = null;
|
|
713
793
|
state.attributes.length = 0;
|
|
794
|
+
state.attributeMask = 0;
|
|
714
795
|
state.vertex.length = 0;
|
|
715
796
|
state.vertexBank = null;
|
|
716
797
|
state.fragment.length = 0;
|
|
717
798
|
state.samplers.length = 0;
|
|
718
799
|
state.samplerMask = 0;
|
|
800
|
+
state.vertexVersions = new Float64Array(0);
|
|
801
|
+
state.fragmentVersions = new Float64Array(0);
|
|
719
802
|
if (this.selected === program) {
|
|
720
803
|
this.selected = null;
|
|
721
804
|
}
|
|
@@ -852,6 +935,7 @@ export class WebGLContext3D implements Context3D {
|
|
|
852
935
|
throw new Context3DError("textureAllocation", "Context3D experiment: cannot create texture");
|
|
853
936
|
}
|
|
854
937
|
|
|
938
|
+
this.texturesApplied[0] = 0;
|
|
855
939
|
gl.activeTexture(gl.TEXTURE0);
|
|
856
940
|
const target = cube ? gl.TEXTURE_CUBE_MAP : gl.TEXTURE_2D;
|
|
857
941
|
gl.bindTexture(target, handle);
|
|
@@ -881,22 +965,31 @@ export class WebGLContext3D implements Context3D {
|
|
|
881
965
|
upload: (pixels, level = 0, face = 0, sourceWidth, sourceHeight) => {
|
|
882
966
|
this.alive();
|
|
883
967
|
this.textureState(texture);
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
968
|
+
try {
|
|
969
|
+
uploadTexture(
|
|
970
|
+
gl,
|
|
971
|
+
texture,
|
|
972
|
+
state,
|
|
973
|
+
pixels,
|
|
974
|
+
level,
|
|
975
|
+
face,
|
|
976
|
+
sourceWidth,
|
|
977
|
+
sourceHeight,
|
|
978
|
+
this.dxtEncoder,
|
|
979
|
+
);
|
|
980
|
+
} finally {
|
|
981
|
+
// An encoder can draw before the upload replaces unit zero's binding.
|
|
982
|
+
this.texturesApplied[0] = 0;
|
|
983
|
+
}
|
|
895
984
|
},
|
|
896
985
|
uploadBytes: (data, offset, level = 0, face = 0) => {
|
|
897
986
|
this.alive();
|
|
898
987
|
this.textureState(texture);
|
|
899
|
-
|
|
988
|
+
try {
|
|
989
|
+
uploadTextureBytes(gl, texture, state, data, offset, level, face, this.dxtEncoder);
|
|
990
|
+
} finally {
|
|
991
|
+
this.texturesApplied[0] = 0;
|
|
992
|
+
}
|
|
900
993
|
},
|
|
901
994
|
uploadCompressed: (blocks, level = 0, face = 0) => {
|
|
902
995
|
this.alive();
|
|
@@ -908,7 +1001,11 @@ export class WebGLContext3D implements Context3D {
|
|
|
908
1001
|
);
|
|
909
1002
|
}
|
|
910
1003
|
|
|
911
|
-
|
|
1004
|
+
try {
|
|
1005
|
+
uploadCompressedLevel(gl, texture, state, blocks, level, face);
|
|
1006
|
+
} finally {
|
|
1007
|
+
this.texturesApplied[0] = 0;
|
|
1008
|
+
}
|
|
912
1009
|
},
|
|
913
1010
|
dispose: () => {
|
|
914
1011
|
if (!this.textures.delete(texture)) {
|
|
@@ -916,12 +1013,13 @@ export class WebGLContext3D implements Context3D {
|
|
|
916
1013
|
}
|
|
917
1014
|
|
|
918
1015
|
this.textureCounts[kind]--;
|
|
1016
|
+
this.drawState.forget();
|
|
919
1017
|
this.textureQuotaBytes[kind] -= quotaBytes;
|
|
920
1018
|
|
|
921
1019
|
if (this.renderTexture === texture) {
|
|
922
1020
|
this.renderTexture = null;
|
|
923
1021
|
this.renderDepth = false;
|
|
924
|
-
|
|
1022
|
+
this.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
|
|
925
1023
|
gl.frontFace(gl.CW);
|
|
926
1024
|
this.applyScissor();
|
|
927
1025
|
}
|
|
@@ -941,6 +1039,7 @@ export class WebGLContext3D implements Context3D {
|
|
|
941
1039
|
|
|
942
1040
|
for (let i = 0; i < this.textureSlots.length; i++) {
|
|
943
1041
|
if (this.textureSlots[i] === texture) {
|
|
1042
|
+
this.texturesApplied[i] = 0;
|
|
944
1043
|
this.textureSlots[i] = null;
|
|
945
1044
|
this.textureMask &= ~(1 << i);
|
|
946
1045
|
gl.activeTexture(gl.TEXTURE0 + i);
|
|
@@ -999,7 +1098,7 @@ export class WebGLContext3D implements Context3D {
|
|
|
999
1098
|
|
|
1000
1099
|
const gl = this.gl;
|
|
1001
1100
|
const program = this.copyProgram(texture.cube);
|
|
1002
|
-
|
|
1101
|
+
this.bindFramebuffer(gl.FRAMEBUFFER, target.framebuffer);
|
|
1003
1102
|
gl.viewport(0, 0, texture.width, texture.height);
|
|
1004
1103
|
gl.disable(gl.DEPTH_TEST);
|
|
1005
1104
|
gl.disable(gl.STENCIL_TEST);
|
|
@@ -1007,6 +1106,7 @@ export class WebGLContext3D implements Context3D {
|
|
|
1007
1106
|
gl.disable(gl.CULL_FACE);
|
|
1008
1107
|
gl.disable(gl.SCISSOR_TEST);
|
|
1009
1108
|
gl.colorMask(true, true, true, true);
|
|
1109
|
+
this.texturesApplied[0] = 0;
|
|
1010
1110
|
gl.activeTexture(gl.TEXTURE0);
|
|
1011
1111
|
gl.bindTexture(texture.cube ? gl.TEXTURE_CUBE_MAP : gl.TEXTURE_2D, state.handle);
|
|
1012
1112
|
gl.bindSampler(0, this.sampler(0));
|
|
@@ -1025,7 +1125,7 @@ export class WebGLContext3D implements Context3D {
|
|
|
1025
1125
|
if (this.blendEnabled) {
|
|
1026
1126
|
gl.enable(gl.BLEND);
|
|
1027
1127
|
}
|
|
1028
|
-
if (this.
|
|
1128
|
+
if (this.cullFace !== "none") {
|
|
1029
1129
|
gl.enable(gl.CULL_FACE);
|
|
1030
1130
|
}
|
|
1031
1131
|
if (this.scissor) {
|
|
@@ -1049,6 +1149,7 @@ export class WebGLContext3D implements Context3D {
|
|
|
1049
1149
|
}
|
|
1050
1150
|
|
|
1051
1151
|
private deleteMultisample(texture: Texture3D, target: MultisampleTarget): void {
|
|
1152
|
+
this.drawState.forget();
|
|
1052
1153
|
deleteMultisample(this.gl, target);
|
|
1053
1154
|
|
|
1054
1155
|
const bytes = texture.width * texture.height * 4 * target.samples;
|
|
@@ -1081,6 +1182,8 @@ export class WebGLContext3D implements Context3D {
|
|
|
1081
1182
|
|
|
1082
1183
|
this.copyProgram(texture.cube);
|
|
1083
1184
|
this.sampler(0);
|
|
1185
|
+
this.drawState.forget();
|
|
1186
|
+
this.texturesApplied[0] = 0;
|
|
1084
1187
|
const target = allocateMultisample(
|
|
1085
1188
|
this.gl,
|
|
1086
1189
|
texture,
|
|
@@ -1157,7 +1260,7 @@ export class WebGLContext3D implements Context3D {
|
|
|
1157
1260
|
);
|
|
1158
1261
|
}
|
|
1159
1262
|
}
|
|
1160
|
-
|
|
1263
|
+
this.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
|
1161
1264
|
gl.framebufferTexture2D(
|
|
1162
1265
|
gl.FRAMEBUFFER,
|
|
1163
1266
|
gl.COLOR_ATTACHMENT0,
|
|
@@ -1209,7 +1312,7 @@ export class WebGLContext3D implements Context3D {
|
|
|
1209
1312
|
if (framebuffer !== state.framebuffer) {
|
|
1210
1313
|
gl.deleteFramebuffer(framebuffer);
|
|
1211
1314
|
} else if (framebuffer) {
|
|
1212
|
-
|
|
1315
|
+
this.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
|
1213
1316
|
gl.framebufferTexture2D(
|
|
1214
1317
|
gl.FRAMEBUFFER,
|
|
1215
1318
|
gl.COLOR_ATTACHMENT0,
|
|
@@ -1232,7 +1335,7 @@ export class WebGLContext3D implements Context3D {
|
|
|
1232
1335
|
gl.deleteRenderbuffer(depthBuffer);
|
|
1233
1336
|
}
|
|
1234
1337
|
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
|
|
1235
|
-
|
|
1338
|
+
this.bindFramebuffer(
|
|
1236
1339
|
gl.FRAMEBUFFER,
|
|
1237
1340
|
this.renderTexture
|
|
1238
1341
|
? targetFramebuffer(this.textureState(this.renderTexture))
|
|
@@ -1298,6 +1401,8 @@ export class WebGLContext3D implements Context3D {
|
|
|
1298
1401
|
const state = this.textureState(texture);
|
|
1299
1402
|
if (state.dirty) {
|
|
1300
1403
|
const gl = this.gl;
|
|
1404
|
+
this.drawState.forget();
|
|
1405
|
+
this.texturesApplied[0] = 0;
|
|
1301
1406
|
resolveTarget(this.gl, texture, state, this.renderFace, this.scissor);
|
|
1302
1407
|
gl.activeTexture(gl.TEXTURE0);
|
|
1303
1408
|
if (texture.cube) {
|
|
@@ -1306,7 +1411,7 @@ export class WebGLContext3D implements Context3D {
|
|
|
1306
1411
|
gl.disable(gl.SCISSOR_TEST);
|
|
1307
1412
|
const face = gl.TEXTURE_CUBE_MAP_POSITIVE_X + this.renderFace;
|
|
1308
1413
|
// The base-size depth attachment cannot accompany smaller mip images.
|
|
1309
|
-
|
|
1414
|
+
this.bindFramebuffer(gl.FRAMEBUFFER, state.framebuffer);
|
|
1310
1415
|
if (state.depthAttached) {
|
|
1311
1416
|
gl.framebufferRenderbuffer(
|
|
1312
1417
|
gl.FRAMEBUFFER,
|
|
@@ -1316,7 +1421,7 @@ export class WebGLContext3D implements Context3D {
|
|
|
1316
1421
|
);
|
|
1317
1422
|
}
|
|
1318
1423
|
for (let level = 1; level < texture.levels; level++) {
|
|
1319
|
-
|
|
1424
|
+
this.bindFramebuffer(gl.READ_FRAMEBUFFER, state.framebuffer);
|
|
1320
1425
|
gl.framebufferTexture2D(
|
|
1321
1426
|
gl.READ_FRAMEBUFFER,
|
|
1322
1427
|
gl.COLOR_ATTACHMENT0,
|
|
@@ -1324,7 +1429,7 @@ export class WebGLContext3D implements Context3D {
|
|
|
1324
1429
|
state.handle,
|
|
1325
1430
|
level - 1,
|
|
1326
1431
|
);
|
|
1327
|
-
|
|
1432
|
+
this.bindFramebuffer(gl.DRAW_FRAMEBUFFER, state.mipFramebuffer);
|
|
1328
1433
|
gl.framebufferTexture2D(
|
|
1329
1434
|
gl.DRAW_FRAMEBUFFER,
|
|
1330
1435
|
gl.COLOR_ATTACHMENT0,
|
|
@@ -1346,7 +1451,7 @@ export class WebGLContext3D implements Context3D {
|
|
|
1346
1451
|
gl.LINEAR,
|
|
1347
1452
|
);
|
|
1348
1453
|
}
|
|
1349
|
-
|
|
1454
|
+
this.bindFramebuffer(gl.FRAMEBUFFER, state.framebuffer);
|
|
1350
1455
|
gl.framebufferTexture2D(
|
|
1351
1456
|
gl.FRAMEBUFFER,
|
|
1352
1457
|
gl.COLOR_ATTACHMENT0,
|
|
@@ -1379,9 +1484,13 @@ export class WebGLContext3D implements Context3D {
|
|
|
1379
1484
|
}
|
|
1380
1485
|
|
|
1381
1486
|
// AIR accepts a disposed texture binding and reports 3663 when drawing.
|
|
1487
|
+
if (this.textureSlots[index] !== texture) {
|
|
1488
|
+
this.texturesApplied[index] = 0;
|
|
1489
|
+
}
|
|
1382
1490
|
this.textureSlots[index] = texture;
|
|
1383
1491
|
this.textureMask = (this.textureMask & ~(1 << index)) | (texture ? 1 << index : 0);
|
|
1384
1492
|
if (!texture) {
|
|
1493
|
+
this.texturesApplied[index] = 0;
|
|
1385
1494
|
this.gl.activeTexture(this.gl.TEXTURE0 + index);
|
|
1386
1495
|
this.gl.bindTexture(this.gl.TEXTURE_2D, null);
|
|
1387
1496
|
this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, null);
|
|
@@ -1399,7 +1508,14 @@ export class WebGLContext3D implements Context3D {
|
|
|
1399
1508
|
throw new Context3DError("operation", "Context3D experiment: invalid sampler state");
|
|
1400
1509
|
}
|
|
1401
1510
|
|
|
1402
|
-
this.
|
|
1511
|
+
this.setSamplerState(index, w | (f << 2) | (m << 5));
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
private setSamplerState(index: number, state: number): void {
|
|
1515
|
+
if (this.samplerStates[index] !== state) {
|
|
1516
|
+
this.samplerStates[index] = state;
|
|
1517
|
+
this.texturesApplied[index] = 0;
|
|
1518
|
+
}
|
|
1403
1519
|
}
|
|
1404
1520
|
|
|
1405
1521
|
private sampler(index: number): WebGLSampler {
|
|
@@ -1465,8 +1581,10 @@ export class WebGLContext3D implements Context3D {
|
|
|
1465
1581
|
for (let i = 0; i < state.samplers.length; i++) {
|
|
1466
1582
|
const source = state.samplers[i].source;
|
|
1467
1583
|
if (!source.ignore) {
|
|
1468
|
-
this.
|
|
1469
|
-
|
|
1584
|
+
this.setSamplerState(
|
|
1585
|
+
source.index,
|
|
1586
|
+
(source.repeat ? 3 : 0) | (source.filter << 2) | (source.mip << 5),
|
|
1587
|
+
);
|
|
1470
1588
|
}
|
|
1471
1589
|
}
|
|
1472
1590
|
}
|
|
@@ -1494,6 +1612,15 @@ export class WebGLContext3D implements Context3D {
|
|
|
1494
1612
|
const valid = offset >= 0 && offset <= buffer.stride - (packed ? 1 : size);
|
|
1495
1613
|
const previous = this.vertices[index];
|
|
1496
1614
|
if (previous) {
|
|
1615
|
+
if (
|
|
1616
|
+
previous.buffer !== buffer ||
|
|
1617
|
+
previous.offset !== offset ||
|
|
1618
|
+
previous.size !== size ||
|
|
1619
|
+
previous.packed !== packed
|
|
1620
|
+
) {
|
|
1621
|
+
previous.applied = false;
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1497
1624
|
if (previous.buffer !== buffer) {
|
|
1498
1625
|
previous.checkedIndex = null;
|
|
1499
1626
|
}
|
|
@@ -1509,6 +1636,7 @@ export class WebGLContext3D implements Context3D {
|
|
|
1509
1636
|
size,
|
|
1510
1637
|
packed,
|
|
1511
1638
|
valid,
|
|
1639
|
+
applied: false,
|
|
1512
1640
|
checkedIndex: null,
|
|
1513
1641
|
checkedIndexVersion: -1,
|
|
1514
1642
|
checkedVertexVersion: -1,
|
|
@@ -1521,10 +1649,20 @@ export class WebGLContext3D implements Context3D {
|
|
|
1521
1649
|
setConstants(stage: ProgramType, first: number, values: ArrayLike<number>, count: number): void {
|
|
1522
1650
|
this.alive();
|
|
1523
1651
|
const dest = stage === "vertex" ? this.vertexConstants : this.fragmentConstants;
|
|
1524
|
-
range(first, count, dest.length / 4);
|
|
1652
|
+
range(first, count, dest.data.length / 4);
|
|
1525
1653
|
range(0, count * 4, values.length);
|
|
1526
|
-
|
|
1527
|
-
|
|
1654
|
+
|
|
1655
|
+
dest.writing++;
|
|
1656
|
+
try {
|
|
1657
|
+
for (let i = 0; i < count * 4; i++) {
|
|
1658
|
+
dest.data[first * 4 + i] = values[i];
|
|
1659
|
+
}
|
|
1660
|
+
} finally {
|
|
1661
|
+
// Source getters can draw, nest setters or throw after partially writing the bank.
|
|
1662
|
+
dest.writing--;
|
|
1663
|
+
if (count) {
|
|
1664
|
+
dest.versions.fill(++dest.version, first, first + count);
|
|
1665
|
+
}
|
|
1528
1666
|
}
|
|
1529
1667
|
}
|
|
1530
1668
|
|
|
@@ -1575,9 +1713,13 @@ export class WebGLContext3D implements Context3D {
|
|
|
1575
1713
|
const maximum = validateIndices(index, first, count);
|
|
1576
1714
|
|
|
1577
1715
|
const gl = this.gl;
|
|
1578
|
-
|
|
1716
|
+
this.drawState.bindVertexArray(this.vao);
|
|
1579
1717
|
for (let i = 0; i < 8; i++) {
|
|
1580
|
-
|
|
1718
|
+
const bit = 1 << i;
|
|
1719
|
+
if (this.enabledAttributes & bit & ~state.attributeMask) {
|
|
1720
|
+
gl.disableVertexAttribArray(i);
|
|
1721
|
+
this.enabledAttributes &= ~bit;
|
|
1722
|
+
}
|
|
1581
1723
|
}
|
|
1582
1724
|
|
|
1583
1725
|
for (let i = 0; i < state.attributes.length; i++) {
|
|
@@ -1594,20 +1736,32 @@ export class WebGLContext3D implements Context3D {
|
|
|
1594
1736
|
|
|
1595
1737
|
validateVertices(buffer, index, binding, first, count, maximum);
|
|
1596
1738
|
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1739
|
+
if (!binding.applied) {
|
|
1740
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer.handle);
|
|
1741
|
+
gl.vertexAttribPointer(
|
|
1742
|
+
slot,
|
|
1743
|
+
binding.size,
|
|
1744
|
+
binding.packed ? gl.UNSIGNED_BYTE : gl.FLOAT,
|
|
1745
|
+
binding.packed,
|
|
1746
|
+
binding.buffer.stride * 4,
|
|
1747
|
+
binding.offset * 4,
|
|
1748
|
+
);
|
|
1749
|
+
binding.applied = true;
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
const bit = 1 << slot;
|
|
1753
|
+
if (!(this.enabledAttributes & bit)) {
|
|
1754
|
+
gl.enableVertexAttribArray(slot);
|
|
1755
|
+
this.enabledAttributes |= bit;
|
|
1756
|
+
}
|
|
1607
1757
|
}
|
|
1608
1758
|
|
|
1609
|
-
|
|
1610
|
-
|
|
1759
|
+
this.drawState.useProgram(state.handle);
|
|
1760
|
+
const targetY = this.renderTexture ? -1 : 1;
|
|
1761
|
+
if (state.targetYValue !== targetY) {
|
|
1762
|
+
gl.uniform1f(state.targetY, targetY);
|
|
1763
|
+
state.targetYValue = targetY;
|
|
1764
|
+
}
|
|
1611
1765
|
for (let i = 0; i < state.samplers.length; i++) {
|
|
1612
1766
|
const binding = state.samplers[i];
|
|
1613
1767
|
const slot = binding.source.index;
|
|
@@ -1654,27 +1808,45 @@ export class WebGLContext3D implements Context3D {
|
|
|
1654
1808
|
throw new TextureBindingError("format", slot);
|
|
1655
1809
|
}
|
|
1656
1810
|
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1811
|
+
if (!this.texturesApplied[slot]) {
|
|
1812
|
+
gl.activeTexture(gl.TEXTURE0 + slot);
|
|
1813
|
+
gl.bindTexture(texture.cube ? gl.TEXTURE_CUBE_MAP : gl.TEXTURE_2D, resource.handle);
|
|
1814
|
+
gl.bindSampler(slot, this.sampler(this.samplerStates[slot]));
|
|
1815
|
+
this.texturesApplied[slot] = 1;
|
|
1816
|
+
}
|
|
1817
|
+
if (!state.samplersSet) {
|
|
1818
|
+
gl.uniform1i(binding.location, slot);
|
|
1819
|
+
}
|
|
1661
1820
|
}
|
|
1662
1821
|
|
|
1822
|
+
state.samplersSet = true;
|
|
1823
|
+
|
|
1663
1824
|
if (state.vertexBank) {
|
|
1664
|
-
|
|
1825
|
+
if (this.vertexConstants.writing || state.vertexVersion !== this.vertexConstants.version) {
|
|
1826
|
+
gl.uniform4fv(state.vertexBank, this.vertexConstants.data);
|
|
1827
|
+
state.vertexVersion = this.vertexConstants.version;
|
|
1828
|
+
}
|
|
1665
1829
|
} else {
|
|
1666
|
-
this.uniforms(state.vertex, this.vertexConstants);
|
|
1830
|
+
this.uniforms(state.vertex, this.vertexConstants, state.vertexVersions);
|
|
1831
|
+
}
|
|
1832
|
+
this.uniforms(state.fragment, this.fragmentConstants, state.fragmentVersions);
|
|
1833
|
+
if (this.indexBuffer !== index.handle) {
|
|
1834
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index.handle);
|
|
1835
|
+
this.indexBuffer = index.handle;
|
|
1667
1836
|
}
|
|
1668
|
-
this.uniforms(state.fragment, this.fragmentConstants);
|
|
1669
|
-
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index.handle);
|
|
1670
1837
|
gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, first * 2);
|
|
1671
1838
|
this.changedTarget();
|
|
1672
1839
|
}
|
|
1673
1840
|
|
|
1674
|
-
private uniforms(
|
|
1841
|
+
private uniforms(
|
|
1842
|
+
locations: (WebGLUniformLocation | null)[],
|
|
1843
|
+
bank: Constants,
|
|
1844
|
+
previous: Float64Array,
|
|
1845
|
+
): void {
|
|
1675
1846
|
for (let i = 0; i < locations.length; i++) {
|
|
1676
|
-
if (locations[i]) {
|
|
1677
|
-
this.gl.uniform4fv(locations[i], data, i * 4, 4);
|
|
1847
|
+
if (locations[i] && (bank.writing || previous[i] !== bank.versions[i])) {
|
|
1848
|
+
this.gl.uniform4fv(locations[i], bank.data, i * 4, 4);
|
|
1849
|
+
previous[i] = bank.versions[i];
|
|
1678
1850
|
}
|
|
1679
1851
|
}
|
|
1680
1852
|
}
|
|
@@ -1686,8 +1858,8 @@ export class WebGLContext3D implements Context3D {
|
|
|
1686
1858
|
|
|
1687
1859
|
const gl = this.gl;
|
|
1688
1860
|
if (this.resolveDirty) {
|
|
1689
|
-
|
|
1690
|
-
|
|
1861
|
+
this.bindFramebuffer(gl.READ_FRAMEBUFFER, this.framebuffer);
|
|
1862
|
+
this.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.resolveFramebuffer);
|
|
1691
1863
|
if (this.scissor) {
|
|
1692
1864
|
gl.disable(gl.SCISSOR_TEST);
|
|
1693
1865
|
}
|
|
@@ -1708,7 +1880,7 @@ export class WebGLContext3D implements Context3D {
|
|
|
1708
1880
|
}
|
|
1709
1881
|
this.resolveDirty = false;
|
|
1710
1882
|
}
|
|
1711
|
-
|
|
1883
|
+
this.bindFramebuffer(gl.READ_FRAMEBUFFER, this.resolveFramebuffer);
|
|
1712
1884
|
}
|
|
1713
1885
|
|
|
1714
1886
|
read(width = this.width, height = this.height): Uint8Array {
|
|
@@ -1733,7 +1905,7 @@ export class WebGLContext3D implements Context3D {
|
|
|
1733
1905
|
this.target();
|
|
1734
1906
|
this.resolve();
|
|
1735
1907
|
const gl = this.gl;
|
|
1736
|
-
|
|
1908
|
+
this.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
|
|
1737
1909
|
if (this.scissor) {
|
|
1738
1910
|
gl.disable(gl.SCISSOR_TEST);
|
|
1739
1911
|
}
|
|
@@ -1780,7 +1952,7 @@ export class WebGLContext3D implements Context3D {
|
|
|
1780
1952
|
this.gl.useProgram(null);
|
|
1781
1953
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);
|
|
1782
1954
|
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, null);
|
|
1783
|
-
this.
|
|
1955
|
+
this.bindFramebuffer(this.gl.FRAMEBUFFER, null);
|
|
1784
1956
|
this.gl.bindTexture(this.gl.TEXTURE_2D, null);
|
|
1785
1957
|
this.gl.deleteVertexArray(this.vao);
|
|
1786
1958
|
this.gl.deleteFramebuffer(this.framebuffer);
|
|
@@ -1799,5 +1971,8 @@ export class WebGLContext3D implements Context3D {
|
|
|
1799
1971
|
this.depthBuffer = null;
|
|
1800
1972
|
this.pixels = new Uint8Array(0);
|
|
1801
1973
|
this.topDown = new Uint8Array(0);
|
|
1974
|
+
this.texturesApplied.fill(0);
|
|
1975
|
+
this.indexBuffer = null;
|
|
1976
|
+
this.drawState.forget();
|
|
1802
1977
|
}
|
|
1803
1978
|
}
|