@gjsify/webgl 0.1.2 → 0.1.3

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.
Files changed (59) hide show
  1. package/lib/esm/canvas-webgl-widget.js +12 -0
  2. package/lib/esm/conformance/attribs.spec.js +296 -0
  3. package/lib/esm/conformance/buffers.spec.js +203 -0
  4. package/lib/esm/conformance/context.spec.js +302 -0
  5. package/lib/esm/conformance/programs.spec.js +468 -0
  6. package/lib/esm/conformance/rendering-basic.spec.js +136 -0
  7. package/lib/esm/conformance/rendering.spec.js +424 -0
  8. package/lib/esm/conformance/setup.js +36 -0
  9. package/lib/esm/conformance/state.spec.js +348 -0
  10. package/lib/esm/conformance/textures.spec.js +354 -0
  11. package/lib/esm/conformance/uniforms.spec.js +305 -0
  12. package/lib/esm/conformance-test.js +23 -0
  13. package/lib/esm/extensions/ext-color-buffer-float.js +13 -0
  14. package/lib/esm/extensions/ext-color-buffer-half-float.js +13 -0
  15. package/lib/esm/extensions/oes-texture-half-float.js +19 -0
  16. package/lib/esm/index.js +5 -0
  17. package/lib/esm/test-utils.js +65 -0
  18. package/lib/esm/test.js +2 -2
  19. package/lib/esm/webgl-buffer.js +1 -1
  20. package/lib/esm/webgl-context-base.js +3371 -0
  21. package/lib/esm/webgl-framebuffer.js +1 -1
  22. package/lib/esm/webgl-program.js +1 -1
  23. package/lib/esm/webgl-renderbuffer.js +1 -1
  24. package/lib/esm/webgl-rendering-context.js +95 -3253
  25. package/lib/esm/webgl-shader.js +2 -1
  26. package/lib/esm/webgl-texture.js +1 -1
  27. package/lib/esm/{index.spec.js → webgl1.spec.js} +2 -2
  28. package/lib/esm/webgl2-rendering-context.js +617 -27
  29. package/lib/esm/webgl2.spec.js +573 -1
  30. package/lib/types/conformance/setup.d.ts +14 -0
  31. package/lib/types/extensions/ext-blend-minmax.d.ts +2 -2
  32. package/lib/types/extensions/ext-color-buffer-float.d.ts +4 -0
  33. package/lib/types/extensions/ext-color-buffer-half-float.d.ts +4 -0
  34. package/lib/types/extensions/ext-texture-filter-anisotropic.d.ts +2 -2
  35. package/lib/types/extensions/oes-element-index-unit.d.ts +2 -2
  36. package/lib/types/extensions/oes-standard-derivatives.d.ts +2 -2
  37. package/lib/types/extensions/oes-texture-float-linear.d.ts +2 -2
  38. package/lib/types/extensions/oes-texture-float.d.ts +2 -2
  39. package/lib/types/extensions/oes-texture-half-float.d.ts +5 -0
  40. package/lib/types/extensions/stackgl-destroy-context.d.ts +3 -3
  41. package/lib/types/extensions/stackgl-resize-drawing-buffer.d.ts +3 -3
  42. package/lib/types/index.d.ts +1 -0
  43. package/lib/types/test-utils.d.ts +24 -0
  44. package/lib/types/types/extension.d.ts +2 -2
  45. package/lib/types/utils.d.ts +9 -10
  46. package/lib/types/webgl-buffer.d.ts +3 -3
  47. package/lib/types/webgl-context-base.d.ts +267 -0
  48. package/lib/types/webgl-framebuffer.d.ts +3 -3
  49. package/lib/types/webgl-program.d.ts +3 -3
  50. package/lib/types/webgl-renderbuffer.d.ts +3 -3
  51. package/lib/types/webgl-rendering-context.d.ts +5 -250
  52. package/lib/types/webgl-shader.d.ts +4 -3
  53. package/lib/types/webgl-texture-unit.d.ts +3 -3
  54. package/lib/types/webgl-texture.d.ts +3 -3
  55. package/lib/types/webgl-vertex-attribute.d.ts +5 -5
  56. package/lib/types/webgl2-rendering-context.d.ts +95 -6
  57. package/package.json +13 -11
  58. package/prebuilds/linux-x86_64/Gwebgl-0.1.typelib +0 -0
  59. package/prebuilds/linux-x86_64/libgwebgl.so +0 -0
@@ -0,0 +1,354 @@
1
+ import { describe, it, expect, beforeEach, on } from "@gjsify/unit";
2
+ import { makeProgram, drawTriangle, makeTestFBO, destroyTestFBO } from "../test-utils.js";
3
+ import { createGLSetup } from "./setup.js";
4
+ const VS_TEX = `
5
+ precision mediump float;
6
+ attribute vec2 position;
7
+ varying vec2 vTexCoord;
8
+ void main() {
9
+ vTexCoord = 0.5 * (position + 1.0);
10
+ gl_Position = vec4(position, 0.0, 1.0);
11
+ }`;
12
+ const FS_TEX = `
13
+ precision mediump float;
14
+ uniform sampler2D uTex;
15
+ varying vec2 vTexCoord;
16
+ void main() { gl_FragColor = texture2D(uTex, vTexCoord); }`;
17
+ var textures_spec_default = async () => {
18
+ await on("Display", async () => {
19
+ const setup = createGLSetup();
20
+ if (!setup) {
21
+ console.warn("WebGL context not available \u2014 skipping conformance/textures tests");
22
+ return;
23
+ }
24
+ const { gl, glArea } = setup;
25
+ glArea.make_current();
26
+ await describe("textures/texImage2D-rgba", async () => {
27
+ beforeEach(async () => {
28
+ glArea.make_current();
29
+ });
30
+ await it("upload 2\xD72 RGBA Uint8Array and sample it back", async () => {
31
+ const W = 2, H = 2;
32
+ const fbo = makeTestFBO(gl, W, H);
33
+ const data = new Uint8Array([
34
+ 255,
35
+ 0,
36
+ 0,
37
+ 255,
38
+ // red
39
+ 0,
40
+ 255,
41
+ 0,
42
+ 255,
43
+ // green
44
+ 0,
45
+ 0,
46
+ 255,
47
+ 255,
48
+ // blue
49
+ 255,
50
+ 255,
51
+ 0,
52
+ 255
53
+ // yellow
54
+ ]);
55
+ const tex = gl.createTexture();
56
+ gl.bindTexture(gl.TEXTURE_2D, tex);
57
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
58
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
59
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
60
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
61
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, W, H, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
62
+ const prog = makeProgram(gl, VS_TEX, FS_TEX);
63
+ gl.useProgram(prog);
64
+ gl.uniform1i(gl.getUniformLocation(prog, "uTex"), 0);
65
+ drawTriangle(gl);
66
+ expect(gl.getError()).toBe(gl.NO_ERROR);
67
+ gl.deleteTexture(tex);
68
+ destroyTestFBO(gl, fbo);
69
+ });
70
+ await it("createTexture returns non-null; deleteTexture cleans up", async () => {
71
+ const tex = gl.createTexture();
72
+ expect(tex).toBeDefined();
73
+ gl.bindTexture(gl.TEXTURE_2D, tex);
74
+ gl.texImage2D(
75
+ gl.TEXTURE_2D,
76
+ 0,
77
+ gl.RGBA,
78
+ 1,
79
+ 1,
80
+ 0,
81
+ gl.RGBA,
82
+ gl.UNSIGNED_BYTE,
83
+ new Uint8Array([128, 128, 128, 255])
84
+ );
85
+ expect(gl.getError()).toBe(gl.NO_ERROR);
86
+ gl.bindTexture(gl.TEXTURE_2D, null);
87
+ gl.deleteTexture(tex);
88
+ expect(gl.getError()).toBe(gl.NO_ERROR);
89
+ });
90
+ });
91
+ await describe("textures/alpha-texture", async () => {
92
+ beforeEach(async () => {
93
+ glArea.make_current();
94
+ });
95
+ await it("ALPHA format texImage2D: RGB channels zero, alpha matches upload data", async () => {
96
+ const W = 4, H = 4;
97
+ const fbo = makeTestFBO(gl, W, H);
98
+ const data = new Uint8Array(W * H);
99
+ for (let i = 0; i < H; i++) {
100
+ for (let j = 0; j < W; j++) {
101
+ data[i * W + j] = (i + j) % 255;
102
+ }
103
+ }
104
+ const tex = gl.createTexture();
105
+ gl.bindTexture(gl.TEXTURE_2D, tex);
106
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
107
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
108
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
109
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
110
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.ALPHA, W, H, 0, gl.ALPHA, gl.UNSIGNED_BYTE, data);
111
+ expect(gl.getError()).toBe(gl.NO_ERROR);
112
+ gl.clearColor(0, 0, 0, 0);
113
+ gl.clear(gl.COLOR_BUFFER_BIT);
114
+ const prog = makeProgram(gl, VS_TEX, FS_TEX);
115
+ gl.useProgram(prog);
116
+ gl.uniform1i(gl.getUniformLocation(prog, "uTex"), 0);
117
+ drawTriangle(gl);
118
+ expect(gl.getError()).toBe(gl.NO_ERROR);
119
+ const pixels = new Uint8Array(W * H * 4);
120
+ gl.readPixels(0, 0, W, H, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
121
+ let ok = true;
122
+ for (let k = 0; k < W * H; k++) {
123
+ const i = k * 4;
124
+ if (pixels[i] !== 0 || pixels[i + 1] !== 0 || pixels[i + 2] !== 0) {
125
+ ok = false;
126
+ break;
127
+ }
128
+ if (Math.abs(pixels[i + 3] - data[k]) > 3) {
129
+ ok = false;
130
+ break;
131
+ }
132
+ }
133
+ expect(ok).toBe(true);
134
+ gl.deleteTexture(tex);
135
+ destroyTestFBO(gl, fbo);
136
+ });
137
+ });
138
+ await describe("textures/texSubImage2D", async () => {
139
+ beforeEach(async () => {
140
+ glArea.make_current();
141
+ });
142
+ await it("texSubImage2D overwrites a sub-region of an RGBA texture", async () => {
143
+ const W = 4, H = 4;
144
+ const fbo = makeTestFBO(gl, W, H);
145
+ const redData = new Uint8Array(W * H * 4).fill(0);
146
+ for (let i = 0; i < W * H; i++) {
147
+ redData[i * 4] = 255;
148
+ redData[i * 4 + 3] = 255;
149
+ }
150
+ const tex = gl.createTexture();
151
+ gl.bindTexture(gl.TEXTURE_2D, tex);
152
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
153
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
154
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
155
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
156
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, W, H, 0, gl.RGBA, gl.UNSIGNED_BYTE, redData);
157
+ const greenPatch = new Uint8Array([
158
+ 0,
159
+ 255,
160
+ 0,
161
+ 255,
162
+ 0,
163
+ 255,
164
+ 0,
165
+ 255,
166
+ 0,
167
+ 255,
168
+ 0,
169
+ 255,
170
+ 0,
171
+ 255,
172
+ 0,
173
+ 255
174
+ ]);
175
+ gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 2, 2, gl.RGBA, gl.UNSIGNED_BYTE, greenPatch);
176
+ expect(gl.getError()).toBe(gl.NO_ERROR);
177
+ gl.deleteTexture(tex);
178
+ destroyTestFBO(gl, fbo);
179
+ });
180
+ });
181
+ await describe("textures/parameters", async () => {
182
+ beforeEach(async () => {
183
+ glArea.make_current();
184
+ });
185
+ await it("getTexParameter returns set WRAP and FILTER values", async () => {
186
+ const tex = gl.createTexture();
187
+ gl.bindTexture(gl.TEXTURE_2D, tex);
188
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
189
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
190
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
191
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
192
+ expect(gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S)).toBe(gl.CLAMP_TO_EDGE);
193
+ expect(gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T)).toBe(gl.REPEAT);
194
+ expect(gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER)).toBe(gl.LINEAR);
195
+ expect(gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER)).toBe(gl.NEAREST);
196
+ expect(gl.getError()).toBe(gl.NO_ERROR);
197
+ gl.bindTexture(gl.TEXTURE_2D, null);
198
+ gl.deleteTexture(tex);
199
+ });
200
+ await it("MIRRORED_REPEAT wrap mode is accepted", async () => {
201
+ const tex = gl.createTexture();
202
+ gl.bindTexture(gl.TEXTURE_2D, tex);
203
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.MIRRORED_REPEAT);
204
+ expect(gl.getError()).toBe(gl.NO_ERROR);
205
+ expect(gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S)).toBe(gl.MIRRORED_REPEAT);
206
+ gl.bindTexture(gl.TEXTURE_2D, null);
207
+ gl.deleteTexture(tex);
208
+ });
209
+ await it("LINEAR_MIPMAP_LINEAR min filter is accepted", async () => {
210
+ const tex = gl.createTexture();
211
+ gl.bindTexture(gl.TEXTURE_2D, tex);
212
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
213
+ expect(gl.getError()).toBe(gl.NO_ERROR);
214
+ expect(gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER)).toBe(gl.LINEAR_MIPMAP_LINEAR);
215
+ gl.bindTexture(gl.TEXTURE_2D, null);
216
+ gl.deleteTexture(tex);
217
+ });
218
+ });
219
+ await describe("textures/active-texture", async () => {
220
+ beforeEach(async () => {
221
+ glArea.make_current();
222
+ });
223
+ await it("activeTexture switches binding slot; each unit holds independent binding", async () => {
224
+ const tex0 = gl.createTexture();
225
+ const tex1 = gl.createTexture();
226
+ gl.activeTexture(gl.TEXTURE0);
227
+ gl.bindTexture(gl.TEXTURE_2D, tex0);
228
+ gl.activeTexture(gl.TEXTURE1);
229
+ gl.bindTexture(gl.TEXTURE_2D, tex1);
230
+ gl.activeTexture(gl.TEXTURE0);
231
+ expect(gl.getParameter(gl.TEXTURE_BINDING_2D)).toBe(tex0);
232
+ gl.activeTexture(gl.TEXTURE1);
233
+ expect(gl.getParameter(gl.TEXTURE_BINDING_2D)).toBe(tex1);
234
+ expect(gl.getError()).toBe(gl.NO_ERROR);
235
+ gl.activeTexture(gl.TEXTURE0);
236
+ gl.bindTexture(gl.TEXTURE_2D, null);
237
+ gl.activeTexture(gl.TEXTURE1);
238
+ gl.bindTexture(gl.TEXTURE_2D, null);
239
+ gl.deleteTexture(tex0);
240
+ gl.deleteTexture(tex1);
241
+ });
242
+ await it("getParameter ACTIVE_TEXTURE reflects activeTexture call", async () => {
243
+ gl.activeTexture(gl.TEXTURE3);
244
+ expect(gl.getParameter(gl.ACTIVE_TEXTURE)).toBe(gl.TEXTURE3);
245
+ gl.activeTexture(gl.TEXTURE0);
246
+ expect(gl.getParameter(gl.ACTIVE_TEXTURE)).toBe(gl.TEXTURE0);
247
+ expect(gl.getError()).toBe(gl.NO_ERROR);
248
+ });
249
+ });
250
+ await describe("textures/cubemap", async () => {
251
+ beforeEach(async () => {
252
+ glArea.make_current();
253
+ });
254
+ await it("attach all 6 cubemap faces to FBO \u2014 no GL error", async () => {
255
+ const tex = gl.createTexture();
256
+ const fb = gl.createFramebuffer();
257
+ const whitePixel = new Uint8Array([255, 255, 255, 255]);
258
+ gl.activeTexture(gl.TEXTURE1);
259
+ gl.bindTexture(gl.TEXTURE_CUBE_MAP, tex);
260
+ gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
261
+ gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
262
+ gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
263
+ gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
264
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
265
+ for (let i = 0; i < 6; i++) {
266
+ gl.texImage2D(
267
+ gl.TEXTURE_CUBE_MAP_POSITIVE_X + i,
268
+ 0,
269
+ gl.RGBA,
270
+ 1,
271
+ 1,
272
+ 0,
273
+ gl.RGBA,
274
+ gl.UNSIGNED_BYTE,
275
+ whitePixel
276
+ );
277
+ gl.framebufferTexture2D(
278
+ gl.FRAMEBUFFER,
279
+ gl.COLOR_ATTACHMENT0,
280
+ gl.TEXTURE_CUBE_MAP_POSITIVE_X + i,
281
+ tex,
282
+ 0
283
+ );
284
+ }
285
+ expect(gl.getError()).toBe(gl.NO_ERROR);
286
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
287
+ gl.activeTexture(gl.TEXTURE1);
288
+ gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
289
+ gl.activeTexture(gl.TEXTURE0);
290
+ gl.deleteTexture(tex);
291
+ gl.deleteFramebuffer(fb);
292
+ });
293
+ await it("createTexture with TEXTURE_CUBE_MAP binding \u2014 no error", async () => {
294
+ const tex = gl.createTexture();
295
+ gl.bindTexture(gl.TEXTURE_CUBE_MAP, tex);
296
+ gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
297
+ gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
298
+ gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
299
+ gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
300
+ expect(gl.getError()).toBe(gl.NO_ERROR);
301
+ gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
302
+ gl.deleteTexture(tex);
303
+ });
304
+ });
305
+ await describe("textures/luminance", async () => {
306
+ beforeEach(async () => {
307
+ glArea.make_current();
308
+ });
309
+ await it("LUMINANCE format texImage2D \u2014 no error", async () => {
310
+ const tex = gl.createTexture();
311
+ gl.bindTexture(gl.TEXTURE_2D, tex);
312
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
313
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
314
+ const data = new Uint8Array([128, 64, 192, 32]);
315
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, 4, 1, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, data);
316
+ expect(gl.getError()).toBe(gl.NO_ERROR);
317
+ gl.bindTexture(gl.TEXTURE_2D, null);
318
+ gl.deleteTexture(tex);
319
+ });
320
+ await it("LUMINANCE_ALPHA format texImage2D \u2014 no error", async () => {
321
+ const tex = gl.createTexture();
322
+ gl.bindTexture(gl.TEXTURE_2D, tex);
323
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
324
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
325
+ const data = new Uint8Array([128, 255, 64, 128]);
326
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE_ALPHA, 2, 1, 0, gl.LUMINANCE_ALPHA, gl.UNSIGNED_BYTE, data);
327
+ expect(gl.getError()).toBe(gl.NO_ERROR);
328
+ gl.bindTexture(gl.TEXTURE_2D, null);
329
+ gl.deleteTexture(tex);
330
+ });
331
+ });
332
+ await describe("textures/generateMipmap", async () => {
333
+ beforeEach(async () => {
334
+ glArea.make_current();
335
+ });
336
+ await it("generateMipmap on power-of-two RGBA texture \u2014 no error", async () => {
337
+ const tex = gl.createTexture();
338
+ gl.bindTexture(gl.TEXTURE_2D, tex);
339
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
340
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
341
+ const W = 4, H = 4;
342
+ const data = new Uint8Array(W * H * 4).fill(255);
343
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, W, H, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
344
+ gl.generateMipmap(gl.TEXTURE_2D);
345
+ expect(gl.getError()).toBe(gl.NO_ERROR);
346
+ gl.bindTexture(gl.TEXTURE_2D, null);
347
+ gl.deleteTexture(tex);
348
+ });
349
+ });
350
+ });
351
+ };
352
+ export {
353
+ textures_spec_default as default
354
+ };
@@ -0,0 +1,305 @@
1
+ import { describe, it, expect, beforeEach, on } from "@gjsify/unit";
2
+ import { makeProgram } from "../test-utils.js";
3
+ import { createGLSetup } from "./setup.js";
4
+ const VS = `attribute vec4 a_position; void main() { gl_Position = a_position; }`;
5
+ function makeFS(body) {
6
+ return `precision mediump float; ${body}`;
7
+ }
8
+ const FS_FLOAT = makeFS("uniform float u_f; void main() { gl_FragColor = vec4(u_f,0,0,1); }");
9
+ const FS_INT = makeFS("uniform int u_i; void main() { gl_FragColor = vec4(float(u_i),0,0,1); }");
10
+ const FS_BOOL = makeFS("uniform bool u_b; void main() { gl_FragColor = vec4(u_b ? 1.0 : 0.0,0,0,1); }");
11
+ const FS_VEC2 = makeFS("uniform vec2 u_v2; void main() { gl_FragColor = vec4(u_v2,0,1); }");
12
+ const FS_VEC3 = makeFS("uniform vec3 u_v3; void main() { gl_FragColor = vec4(u_v3,1); }");
13
+ const FS_VEC4 = makeFS("uniform vec4 u_v4; void main() { gl_FragColor = u_v4; }");
14
+ const FS_MAT2 = makeFS("uniform mat2 u_m2; void main() { gl_FragColor = vec4(u_m2[0],u_m2[1]); }");
15
+ const FS_MAT3 = makeFS("uniform mat3 u_m3; void main() { gl_FragColor = vec4(u_m3[0],1); }");
16
+ const FS_MAT4 = makeFS("uniform mat4 u_m4; void main() { gl_FragColor = u_m4[0]; }");
17
+ const FS_ARR = makeFS("uniform float u_arr[3]; void main() { gl_FragColor = vec4(u_arr[0]+u_arr[1]+u_arr[2],0,0,1); }");
18
+ const FS_VEC4ARR = makeFS("uniform vec4 u_v4arr[3]; void main() { gl_FragColor = u_v4arr[0]+u_v4arr[1]+u_v4arr[2]; }");
19
+ function floatArrayClose(a, b, tol = 1e-3) {
20
+ if (a.length !== b.length) return false;
21
+ for (let i = 0; i < b.length; i++) {
22
+ if (Math.abs(a[i] - b[i]) > tol) return false;
23
+ }
24
+ return true;
25
+ }
26
+ var uniforms_spec_default = async () => {
27
+ await on("Display", async () => {
28
+ const setup = createGLSetup();
29
+ if (!setup) {
30
+ console.warn("WebGL context not available \u2014 skipping conformance/uniforms tests");
31
+ return;
32
+ }
33
+ const { gl, glArea } = setup;
34
+ glArea.make_current();
35
+ await describe("conformance/uniforms/null-uniform-location", async () => {
36
+ beforeEach(async () => {
37
+ glArea.make_current();
38
+ });
39
+ await it("uniform* with null location generates NO_ERROR", async () => {
40
+ const prog = makeProgram(gl, VS, FS_FLOAT);
41
+ gl.useProgram(prog);
42
+ gl.uniform1f(null, 1);
43
+ gl.getError();
44
+ gl.uniform1fv(null, [1]);
45
+ gl.getError();
46
+ gl.uniform1i(null, 1);
47
+ gl.getError();
48
+ gl.uniform1iv(null, [1]);
49
+ gl.getError();
50
+ gl.uniform2f(null, 1, 2);
51
+ gl.getError();
52
+ gl.uniform2fv(null, [1, 2]);
53
+ gl.getError();
54
+ gl.uniform2i(null, 1, 2);
55
+ gl.getError();
56
+ gl.uniform2iv(null, [1, 2]);
57
+ gl.getError();
58
+ gl.uniform3f(null, 1, 2, 3);
59
+ gl.getError();
60
+ gl.uniform3fv(null, [1, 2, 3]);
61
+ gl.getError();
62
+ gl.uniform3i(null, 1, 2, 3);
63
+ gl.getError();
64
+ gl.uniform3iv(null, [1, 2, 3]);
65
+ gl.getError();
66
+ gl.uniform4f(null, 1, 2, 3, 4);
67
+ gl.getError();
68
+ gl.uniform4fv(null, [1, 2, 3, 4]);
69
+ gl.getError();
70
+ gl.uniform4i(null, 1, 2, 3, 4);
71
+ gl.getError();
72
+ gl.uniform4iv(null, [1, 2, 3, 4]);
73
+ gl.getError();
74
+ gl.uniformMatrix2fv(null, false, [1, 0, 0, 1]);
75
+ gl.getError();
76
+ gl.uniformMatrix3fv(null, false, [1, 0, 0, 0, 1, 0, 0, 0, 1]);
77
+ gl.getError();
78
+ gl.uniformMatrix4fv(null, false, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
79
+ gl.getError();
80
+ expect(gl.getError()).toBe(gl.NO_ERROR);
81
+ });
82
+ });
83
+ await describe("conformance/uniforms/gl-unknown-uniform", async () => {
84
+ beforeEach(async () => {
85
+ glArea.make_current();
86
+ });
87
+ await it("getUniformLocation for unknown uniform returns null", async () => {
88
+ const prog = makeProgram(gl, VS, FS_FLOAT);
89
+ gl.useProgram(prog);
90
+ const loc = gl.getUniformLocation(prog, "someUnknownUniform");
91
+ expect(loc).toBeNull();
92
+ expect(gl.getError()).toBe(gl.NO_ERROR);
93
+ });
94
+ await it("uniform1f with null location (unknown) generates NO_ERROR", async () => {
95
+ const prog = makeProgram(gl, VS, FS_FLOAT);
96
+ gl.useProgram(prog);
97
+ gl.uniform1f(null, 42);
98
+ expect(gl.getError()).toBe(gl.NO_ERROR);
99
+ });
100
+ });
101
+ await describe("conformance/uniforms/gl-uniform-bool", async () => {
102
+ beforeEach(async () => {
103
+ glArea.make_current();
104
+ });
105
+ await it("setting a bool uniform via uniform1i succeeds", async () => {
106
+ const prog = makeProgram(gl, VS, FS_BOOL);
107
+ gl.useProgram(prog);
108
+ const loc = gl.getUniformLocation(prog, "u_b");
109
+ expect(loc).not.toBeNull();
110
+ gl.uniform1i(loc, 1);
111
+ expect(gl.getError()).toBe(gl.NO_ERROR);
112
+ });
113
+ });
114
+ await describe("conformance/uniforms/scalar-vector-round-trip", async () => {
115
+ beforeEach(async () => {
116
+ glArea.make_current();
117
+ });
118
+ await it("uniform1f / getUniform round-trips a float", async () => {
119
+ const prog = makeProgram(gl, VS, FS_FLOAT);
120
+ gl.useProgram(prog);
121
+ const loc = gl.getUniformLocation(prog, "u_f");
122
+ gl.uniform1f(loc, 0.5);
123
+ expect(gl.getError()).toBe(gl.NO_ERROR);
124
+ const val = gl.getUniform(prog, loc);
125
+ expect(Math.abs(val - 0.5) < 1e-3).toBe(true);
126
+ });
127
+ await it("uniform1i / getUniform round-trips an int", async () => {
128
+ const prog = makeProgram(gl, VS, FS_INT);
129
+ gl.useProgram(prog);
130
+ const loc = gl.getUniformLocation(prog, "u_i");
131
+ gl.uniform1i(loc, 7);
132
+ expect(gl.getError()).toBe(gl.NO_ERROR);
133
+ const val = gl.getUniform(prog, loc);
134
+ expect(val).toBe(7);
135
+ });
136
+ await it("uniform2fv / getUniform round-trips a vec2", async () => {
137
+ const prog = makeProgram(gl, VS, FS_VEC2);
138
+ gl.useProgram(prog);
139
+ const loc = gl.getUniformLocation(prog, "u_v2");
140
+ gl.uniform2fv(loc, [0.25, 0.75]);
141
+ expect(gl.getError()).toBe(gl.NO_ERROR);
142
+ const val = gl.getUniform(prog, loc);
143
+ expect(floatArrayClose(val, [0.25, 0.75])).toBe(true);
144
+ });
145
+ await it("uniform3fv / getUniform round-trips a vec3", async () => {
146
+ const prog = makeProgram(gl, VS, FS_VEC3);
147
+ gl.useProgram(prog);
148
+ const loc = gl.getUniformLocation(prog, "u_v3");
149
+ gl.uniform3fv(loc, [0.1, 0.2, 0.3]);
150
+ expect(gl.getError()).toBe(gl.NO_ERROR);
151
+ const val = gl.getUniform(prog, loc);
152
+ expect(floatArrayClose(val, [0.1, 0.2, 0.3])).toBe(true);
153
+ });
154
+ await it("uniform4fv / getUniform round-trips a vec4", async () => {
155
+ const prog = makeProgram(gl, VS, FS_VEC4);
156
+ gl.useProgram(prog);
157
+ const loc = gl.getUniformLocation(prog, "u_v4");
158
+ gl.uniform4fv(loc, [0.1, 0.2, 0.3, 0.4]);
159
+ expect(gl.getError()).toBe(gl.NO_ERROR);
160
+ const val = gl.getUniform(prog, loc);
161
+ expect(floatArrayClose(val, [0.1, 0.2, 0.3, 0.4])).toBe(true);
162
+ });
163
+ });
164
+ await describe("conformance/uniforms/gl-uniformmatrix4fv", async () => {
165
+ beforeEach(async () => {
166
+ glArea.make_current();
167
+ });
168
+ await it("uniformMatrix2fv with correct size (4 elements) succeeds", async () => {
169
+ const prog = makeProgram(gl, VS, FS_MAT2);
170
+ gl.useProgram(prog);
171
+ const loc = gl.getUniformLocation(prog, "u_m2");
172
+ gl.uniformMatrix2fv(loc, false, [1, 0, 0, 1]);
173
+ expect(gl.getError()).toBe(gl.NO_ERROR);
174
+ });
175
+ await it("uniformMatrix2fv with too few elements generates INVALID_VALUE", async () => {
176
+ const prog = makeProgram(gl, VS, FS_MAT2);
177
+ gl.useProgram(prog);
178
+ const loc = gl.getUniformLocation(prog, "u_m2");
179
+ gl.uniformMatrix2fv(loc, false, [1, 0, 0]);
180
+ expect(gl.getError()).toBe(gl.INVALID_VALUE);
181
+ });
182
+ await it("uniformMatrix3fv with correct size (9 elements) succeeds", async () => {
183
+ const prog = makeProgram(gl, VS, FS_MAT3);
184
+ gl.useProgram(prog);
185
+ const loc = gl.getUniformLocation(prog, "u_m3");
186
+ gl.uniformMatrix3fv(loc, false, [1, 0, 0, 0, 1, 0, 0, 0, 1]);
187
+ expect(gl.getError()).toBe(gl.NO_ERROR);
188
+ });
189
+ await it("uniformMatrix4fv with correct size (16 elements) succeeds", async () => {
190
+ const prog = makeProgram(gl, VS, FS_MAT4);
191
+ gl.useProgram(prog);
192
+ const loc = gl.getUniformLocation(prog, "u_m4");
193
+ gl.uniformMatrix4fv(loc, false, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
194
+ expect(gl.getError()).toBe(gl.NO_ERROR);
195
+ });
196
+ await it("uniformMatrix4fv with transpose=true generates INVALID_VALUE in WebGL1", async () => {
197
+ const prog = makeProgram(gl, VS, FS_MAT4);
198
+ gl.useProgram(prog);
199
+ const loc = gl.getUniformLocation(prog, "u_m4");
200
+ gl.uniformMatrix4fv(loc, true, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
201
+ expect(gl.getError()).toBe(gl.INVALID_VALUE);
202
+ });
203
+ await it("uniformMatrix4fv / getUniform round-trips a mat4", async () => {
204
+ const prog = makeProgram(gl, VS, FS_MAT4);
205
+ gl.useProgram(prog);
206
+ const loc = gl.getUniformLocation(prog, "u_m4");
207
+ const identity = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
208
+ gl.uniformMatrix4fv(loc, false, identity);
209
+ expect(gl.getError()).toBe(gl.NO_ERROR);
210
+ const val = gl.getUniform(prog, loc);
211
+ expect(floatArrayClose(val, identity)).toBe(true);
212
+ });
213
+ });
214
+ await describe("conformance/uniforms/gl-uniform-arrays", async () => {
215
+ beforeEach(async () => {
216
+ glArea.make_current();
217
+ });
218
+ await it("uniform1fv sets a float array and getUniform retrieves element [0]", async () => {
219
+ const prog = makeProgram(gl, VS, FS_ARR);
220
+ gl.useProgram(prog);
221
+ const loc = gl.getUniformLocation(prog, "u_arr[0]");
222
+ expect(loc).not.toBeNull();
223
+ gl.uniform1fv(loc, [0.1, 0.2, 0.3]);
224
+ expect(gl.getError()).toBe(gl.NO_ERROR);
225
+ const val = gl.getUniform(prog, loc);
226
+ expect(Math.abs(val - 0.1) < 1e-3).toBe(true);
227
+ });
228
+ await it("uniform1fv with fewer values than array size (partial update) succeeds", async () => {
229
+ const prog = makeProgram(gl, VS, FS_ARR);
230
+ gl.useProgram(prog);
231
+ const loc = gl.getUniformLocation(prog, "u_arr[0]");
232
+ gl.uniform1fv(loc, [0.5, 0.6]);
233
+ expect(gl.getError()).toBe(gl.NO_ERROR);
234
+ });
235
+ await it("uniform4fv sets a vec4 array", async () => {
236
+ const prog = makeProgram(gl, VS, FS_VEC4ARR);
237
+ gl.useProgram(prog);
238
+ const loc = gl.getUniformLocation(prog, "u_v4arr[0]");
239
+ expect(loc).not.toBeNull();
240
+ gl.uniform4fv(loc, [
241
+ 0.1,
242
+ 0.2,
243
+ 0.3,
244
+ 0.4,
245
+ 0.5,
246
+ 0.6,
247
+ 0.7,
248
+ 0.8,
249
+ 0.9,
250
+ 1,
251
+ 0,
252
+ 0
253
+ ]);
254
+ expect(gl.getError()).toBe(gl.NO_ERROR);
255
+ });
256
+ await it("calling uniform* before useProgram generates INVALID_OPERATION", async () => {
257
+ const prog = makeProgram(gl, VS, FS_ARR);
258
+ gl.useProgram(null);
259
+ const loc = gl.getUniformLocation(prog, "u_arr[0]");
260
+ if (loc !== null) {
261
+ gl.uniform1fv(loc, [1, 2, 3]);
262
+ expect(gl.getError()).toBe(gl.INVALID_OPERATION);
263
+ } else {
264
+ expect(gl.getError()).toBe(gl.NO_ERROR);
265
+ }
266
+ });
267
+ });
268
+ await describe("conformance/uniforms/uniform-location", async () => {
269
+ beforeEach(async () => {
270
+ glArea.make_current();
271
+ });
272
+ await it("getUniformLocation returns non-null for known uniform", async () => {
273
+ const prog = makeProgram(gl, VS, FS_FLOAT);
274
+ gl.useProgram(prog);
275
+ const loc = gl.getUniformLocation(prog, "u_f");
276
+ expect(loc).not.toBeNull();
277
+ expect(gl.getError()).toBe(gl.NO_ERROR);
278
+ });
279
+ await it("location from different program generates INVALID_OPERATION", async () => {
280
+ const prog1 = makeProgram(gl, VS, FS_FLOAT);
281
+ const prog2 = makeProgram(gl, VS, FS_FLOAT);
282
+ const loc = gl.getUniformLocation(prog1, "u_f");
283
+ gl.useProgram(prog2);
284
+ gl.uniform1f(loc, 1);
285
+ expect(gl.getError()).toBe(gl.INVALID_OPERATION);
286
+ });
287
+ await it("location becomes invalid after relinkProgram", async () => {
288
+ const prog = makeProgram(gl, VS, FS_FLOAT);
289
+ gl.useProgram(prog);
290
+ const locBefore = gl.getUniformLocation(prog, "u_f");
291
+ expect(locBefore).not.toBeNull();
292
+ gl.linkProgram(prog);
293
+ gl.uniform1f(locBefore, 1);
294
+ expect(gl.getError()).toBe(gl.INVALID_OPERATION);
295
+ const locAfter = gl.getUniformLocation(prog, "u_f");
296
+ expect(locAfter).not.toBeNull();
297
+ gl.uniform1f(locAfter, 1);
298
+ expect(gl.getError()).toBe(gl.NO_ERROR);
299
+ });
300
+ });
301
+ });
302
+ };
303
+ export {
304
+ uniforms_spec_default as default
305
+ };
@@ -0,0 +1,23 @@
1
+ import { run } from "@gjsify/unit";
2
+ import buffersTests from "./conformance/buffers.spec.js";
3
+ import programsTests from "./conformance/programs.spec.js";
4
+ import attribsTests from "./conformance/attribs.spec.js";
5
+ import contextTests from "./conformance/context.spec.js";
6
+ import renderingBasicTests from "./conformance/rendering-basic.spec.js";
7
+ import uniformsTests from "./conformance/uniforms.spec.js";
8
+ import stateTests from "./conformance/state.spec.js";
9
+ import renderingTests from "./conformance/rendering.spec.js";
10
+ import texturesTests from "./conformance/textures.spec.js";
11
+ run({
12
+ testSuite: async () => {
13
+ await buffersTests();
14
+ await programsTests();
15
+ await attribsTests();
16
+ await contextTests();
17
+ await renderingBasicTests();
18
+ await uniformsTests();
19
+ await stateTests();
20
+ await renderingTests();
21
+ await texturesTests();
22
+ }
23
+ });
@@ -0,0 +1,13 @@
1
+ class EXTColorBufferFloat {
2
+ }
3
+ function getEXTColorBufferFloat(context) {
4
+ const exts = context.getSupportedExtensions();
5
+ if (exts && exts.indexOf("EXT_color_buffer_float") >= 0) {
6
+ return new EXTColorBufferFloat();
7
+ }
8
+ return null;
9
+ }
10
+ export {
11
+ EXTColorBufferFloat,
12
+ getEXTColorBufferFloat
13
+ };