@gjsify/webgl 0.3.16 → 0.3.18

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 (55) hide show
  1. package/lib/esm/conformance/attribs.spec.js +3 -315
  2. package/lib/esm/conformance/buffers.spec.js +1 -220
  3. package/lib/esm/conformance/context.spec.js +3 -302
  4. package/lib/esm/conformance/programs.spec.js +3 -477
  5. package/lib/esm/conformance/rendering-basic.spec.js +3 -141
  6. package/lib/esm/conformance/rendering.spec.js +7 -514
  7. package/lib/esm/conformance/setup.js +1 -47
  8. package/lib/esm/conformance/state.spec.js +1 -365
  9. package/lib/esm/conformance/textures.spec.js +3 -337
  10. package/lib/esm/conformance/uniforms.spec.js +1 -484
  11. package/lib/esm/conformance-test.js +1 -25
  12. package/lib/esm/extensions/ext-blend-minmax.js +1 -18
  13. package/lib/esm/extensions/ext-color-buffer-float.js +1 -12
  14. package/lib/esm/extensions/ext-color-buffer-half-float.js +1 -12
  15. package/lib/esm/extensions/ext-texture-filter-anisotropic.js +1 -18
  16. package/lib/esm/extensions/oes-element-index-unit.js +1 -13
  17. package/lib/esm/extensions/oes-standard-derivatives.js +1 -17
  18. package/lib/esm/extensions/oes-texture-float-linear.js +1 -13
  19. package/lib/esm/extensions/oes-texture-float.js +1 -13
  20. package/lib/esm/extensions/oes-texture-half-float.js +1 -19
  21. package/lib/esm/extensions/stackgl-destroy-context.js +1 -12
  22. package/lib/esm/extensions/stackgl-resize-drawing-buffer.js +1 -12
  23. package/lib/esm/html-canvas-element.js +1 -65
  24. package/lib/esm/index.js +1 -33
  25. package/lib/esm/linkable.js +1 -50
  26. package/lib/esm/test-utils.js +4 -186
  27. package/lib/esm/test.js +1 -11
  28. package/lib/esm/types/index.js +1 -5
  29. package/lib/esm/utils.js +1 -201
  30. package/lib/esm/webgl-active-info.js +1 -11
  31. package/lib/esm/webgl-bridge.js +1 -167
  32. package/lib/esm/webgl-buffer.js +1 -19
  33. package/lib/esm/webgl-context-attributes.js +1 -24
  34. package/lib/esm/webgl-context-base.js +8 -3069
  35. package/lib/esm/webgl-drawing-buffer-wrapper.js +1 -11
  36. package/lib/esm/webgl-framebuffer.js +1 -110
  37. package/lib/esm/webgl-program.js +1 -27
  38. package/lib/esm/webgl-query.js +1 -17
  39. package/lib/esm/webgl-renderbuffer.js +1 -25
  40. package/lib/esm/webgl-rendering-context.js +1 -175
  41. package/lib/esm/webgl-sampler.js +1 -17
  42. package/lib/esm/webgl-shader-precision-format.js +1 -11
  43. package/lib/esm/webgl-shader.js +1 -25
  44. package/lib/esm/webgl-sync.js +1 -17
  45. package/lib/esm/webgl-texture-unit.js +1 -13
  46. package/lib/esm/webgl-texture.js +1 -23
  47. package/lib/esm/webgl-transform-feedback.js +1 -17
  48. package/lib/esm/webgl-uniform-location.js +1 -15
  49. package/lib/esm/webgl-vertex-array-object.js +1 -23
  50. package/lib/esm/webgl-vertex-attribute.js +1 -151
  51. package/lib/esm/webgl1.spec.js +10 -1044
  52. package/lib/esm/webgl2-rendering-context.js +1 -1218
  53. package/lib/esm/webgl2.spec.js +45 -1288
  54. package/lib/types/webgl-bridge.d.ts +9 -9
  55. package/package.json +9 -9
@@ -1,1288 +1,45 @@
1
- import { TEXTURE_FS_300, TEXTURE_VS_300, destroyTestFBO, drawTriangle, makeProgram, makeTestFBO, makeTestFBOFloat, makeTestFBOWithDepthTexture, readPixel } from "./test-utils.js";
2
- import { beforeEach, describe, expect, it, on } from "@gjsify/unit";
3
- import { WebGL2RenderingContext, WebGLBridge } from "@gjsify/webgl";
4
- import GLib from "@girs/glib-2.0";
5
- import Gtk from "@girs/gtk-4.0";
6
-
7
- //#region src/ts/webgl2.spec.ts
8
- var webgl2_spec_default = async () => {
9
- await on("Display", async () => {
10
- Gtk.init();
11
- let glArea;
12
- let gl2;
13
- const readyLoop = new GLib.MainLoop(null, false);
14
- const win = new Gtk.Window({});
15
- win.set_default_size(200, 200);
16
- glArea = new WebGLBridge();
17
- glArea.onReady((c, _g) => {
18
- gl2 = c.getContext("webgl2");
19
- readyLoop.quit();
20
- });
21
- win.set_child(glArea);
22
- win.present();
23
- const giveUpId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1e4, () => {
24
- readyLoop.quit();
25
- return GLib.SOURCE_REMOVE;
26
- });
27
- readyLoop.run();
28
- GLib.source_remove(giveUpId);
29
- if (!gl2) {
30
- console.warn("WebGL2 context not available — skipping WebGL2 tests");
31
- win.destroy();
32
- return;
33
- }
34
- glArea.make_current();
35
- await describe("WebGL2 context", async () => {
36
- beforeEach(async () => {
37
- glArea.make_current();
38
- });
39
- await it("getContext(\"webgl2\") returns a WebGL2RenderingContext", async () => {
40
- expect(gl2).toBeInstanceOf(WebGL2RenderingContext);
41
- });
42
- await it("VERSION is \"WebGL 2.0\"", async () => {
43
- expect(gl2.getParameter(gl2.VERSION)).toBe("WebGL 2.0");
44
- });
45
- await it("SHADING_LANGUAGE_VERSION is \"WebGL GLSL ES 3.00\"", async () => {
46
- expect(gl2.getParameter(gl2.SHADING_LANGUAGE_VERSION)).toBe("WebGL GLSL ES 3.00");
47
- });
48
- await it("drawingBufferWidth and drawingBufferHeight are positive", async () => {
49
- expect(gl2.drawingBufferWidth > 0).toBeTruthy();
50
- expect(gl2.drawingBufferHeight > 0).toBeTruthy();
51
- });
52
- await it("getError() returns NO_ERROR initially", async () => {
53
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
54
- });
55
- });
56
- await describe("GLSL ES 3.00 compilation", async () => {
57
- beforeEach(async () => {
58
- glArea.make_current();
59
- });
60
- const VS300 = [
61
- "#version 300 es",
62
- "in vec2 position;",
63
- "void main() { gl_Position = vec4(position, 0.0, 1.0); }"
64
- ].join("\n");
65
- const FS300 = [
66
- "#version 300 es",
67
- "precision mediump float;",
68
- "out vec4 fragColor;",
69
- "void main() { fragColor = vec4(0.0, 1.0, 0.0, 1.0); }"
70
- ].join("\n");
71
- await it("#version 300 es vertex shader compiles", async () => {
72
- const sh = gl2.createShader(gl2.VERTEX_SHADER);
73
- gl2.shaderSource(sh, VS300);
74
- gl2.compileShader(sh);
75
- if (!gl2.getShaderParameter(sh, gl2.COMPILE_STATUS)) {
76
- console.error("VS300 log:", gl2.getShaderInfoLog(sh));
77
- }
78
- expect(gl2.getShaderParameter(sh, gl2.COMPILE_STATUS)).toBeTruthy();
79
- gl2.deleteShader(sh);
80
- });
81
- await it("#version 300 es fragment shader compiles", async () => {
82
- const sh = gl2.createShader(gl2.FRAGMENT_SHADER);
83
- gl2.shaderSource(sh, FS300);
84
- gl2.compileShader(sh);
85
- if (!gl2.getShaderParameter(sh, gl2.COMPILE_STATUS)) {
86
- console.error("FS300 log:", gl2.getShaderInfoLog(sh));
87
- }
88
- expect(gl2.getShaderParameter(sh, gl2.COMPILE_STATUS)).toBeTruthy();
89
- gl2.deleteShader(sh);
90
- });
91
- await it("#version 300 es program links and renders green", async () => {
92
- const prog = makeProgram(gl2, VS300, FS300);
93
- if (!gl2.getProgramParameter(prog, gl2.LINK_STATUS)) {
94
- console.error("Program300 log:", gl2.getProgramInfoLog(prog));
95
- }
96
- expect(gl2.getProgramParameter(prog, gl2.LINK_STATUS)).toBeTruthy();
97
- const fbo = makeTestFBO(gl2, 4, 4);
98
- gl2.clearColor(0, 0, 0, 1);
99
- gl2.clear(gl2.COLOR_BUFFER_BIT);
100
- gl2.useProgram(prog);
101
- drawTriangle(gl2);
102
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
103
- const p = readPixel(gl2, 0, 0);
104
- destroyTestFBO(gl2, fbo);
105
- expect(p[0]).toBe(0);
106
- expect(p[1]).toBe(255);
107
- expect(p[2]).toBe(0);
108
- expect(p[3]).toBe(255);
109
- gl2.deleteProgram(prog);
110
- });
111
- });
112
- await describe("VAO", async () => {
113
- beforeEach(async () => {
114
- glArea.make_current();
115
- });
116
- await it("createVertexArray returns a non-null object", async () => {
117
- const vao = gl2.createVertexArray();
118
- expect(vao).not.toBeNull();
119
- expect(vao).toBeDefined();
120
- gl2.deleteVertexArray(vao);
121
- });
122
- await it("isVertexArray is true after creation, false after deletion", async () => {
123
- const vao = gl2.createVertexArray();
124
- gl2.bindVertexArray(vao);
125
- expect(gl2.isVertexArray(vao)).toBeTruthy();
126
- gl2.bindVertexArray(null);
127
- gl2.deleteVertexArray(vao);
128
- expect(gl2.isVertexArray(vao)).toBeFalsy();
129
- });
130
- await it("VAO preserves vertex attribute state", async () => {
131
- const VS = [
132
- "#version 300 es",
133
- "in vec2 position;",
134
- "void main() { gl_Position = vec4(position,0.0,1.0); }"
135
- ].join("\n");
136
- const FS = [
137
- "#version 300 es",
138
- "precision mediump float;",
139
- "out vec4 c;",
140
- "void main() { c = vec4(0.0,1.0,0.0,1.0); }"
141
- ].join("\n");
142
- const prog = makeProgram(gl2, VS, FS);
143
- gl2.useProgram(prog);
144
- const fbo = makeTestFBO(gl2, 4, 4);
145
- const vao = gl2.createVertexArray();
146
- gl2.bindVertexArray(vao);
147
- const buf = gl2.createBuffer();
148
- gl2.bindBuffer(gl2.ARRAY_BUFFER, buf);
149
- gl2.bufferData(gl2.ARRAY_BUFFER, new Float32Array([
150
- -2,
151
- -2,
152
- -2,
153
- 4,
154
- 4,
155
- -2
156
- ]), gl2.STREAM_DRAW);
157
- gl2.enableVertexAttribArray(0);
158
- gl2.vertexAttribPointer(0, 2, gl2.FLOAT, false, 0, 0);
159
- gl2.bindVertexArray(null);
160
- gl2.bindVertexArray(vao);
161
- gl2.clearColor(0, 0, 0, 1);
162
- gl2.clear(gl2.COLOR_BUFFER_BIT);
163
- gl2.drawArrays(gl2.TRIANGLES, 0, 3);
164
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
165
- const p = readPixel(gl2, 0, 0);
166
- destroyTestFBO(gl2, fbo);
167
- expect(p[1]).toBe(255);
168
- gl2.bindVertexArray(null);
169
- gl2.deleteVertexArray(vao);
170
- gl2.deleteBuffer(buf);
171
- gl2.deleteProgram(prog);
172
- });
173
- });
174
- await describe("getBufferSubData", async () => {
175
- beforeEach(async () => {
176
- glArea.make_current();
177
- });
178
- await it("reads back float data written with bufferData", async () => {
179
- const src = new Float32Array([
180
- 1,
181
- 2,
182
- 3,
183
- 4
184
- ]);
185
- const buf = gl2.createBuffer();
186
- gl2.bindBuffer(gl2.ARRAY_BUFFER, buf);
187
- gl2.bufferData(gl2.ARRAY_BUFFER, src, gl2.STATIC_READ);
188
- const dst = new Float32Array(4);
189
- gl2.getBufferSubData(gl2.ARRAY_BUFFER, 0, dst);
190
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
191
- expect(dst[0]).toBe(1);
192
- expect(dst[1]).toBe(2);
193
- expect(dst[2]).toBe(3);
194
- expect(dst[3]).toBe(4);
195
- gl2.deleteBuffer(buf);
196
- });
197
- await it("reads a sub-range correctly", async () => {
198
- const src = new Float32Array([
199
- 10,
200
- 20,
201
- 30,
202
- 40
203
- ]);
204
- const buf = gl2.createBuffer();
205
- gl2.bindBuffer(gl2.ARRAY_BUFFER, buf);
206
- gl2.bufferData(gl2.ARRAY_BUFFER, src, gl2.STATIC_READ);
207
- const dst = new Float32Array(2);
208
- gl2.getBufferSubData(gl2.ARRAY_BUFFER, 8, dst);
209
- expect(dst[0]).toBe(30);
210
- expect(dst[1]).toBe(40);
211
- gl2.deleteBuffer(buf);
212
- });
213
- });
214
- await describe("sync objects", async () => {
215
- beforeEach(async () => {
216
- glArea.make_current();
217
- });
218
- await it("fenceSync returns a non-null sync", async () => {
219
- const sync = gl2.fenceSync(gl2.SYNC_GPU_COMMANDS_COMPLETE, 0);
220
- expect(sync).not.toBeNull();
221
- gl2.deleteSync(sync);
222
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
223
- });
224
- await it("isSync is true before deletion, false after", async () => {
225
- const sync = gl2.fenceSync(gl2.SYNC_GPU_COMMANDS_COMPLETE, 0);
226
- expect(gl2.isSync(sync)).toBeTruthy();
227
- gl2.deleteSync(sync);
228
- expect(gl2.isSync(sync)).toBeFalsy();
229
- });
230
- await it("clientWaitSync returns TIMEOUT_EXPIRED or CONDITION_SATISFIED", async () => {
231
- const sync = gl2.fenceSync(gl2.SYNC_GPU_COMMANDS_COMPLETE, 0);
232
- gl2.flush();
233
- const result = gl2.clientWaitSync(sync, 0, 0);
234
- const valid = result === gl2.TIMEOUT_EXPIRED || result === gl2.CONDITION_SATISFIED || result === gl2.ALREADY_SIGNALED;
235
- expect(valid).toBeTruthy();
236
- gl2.deleteSync(sync);
237
- });
238
- });
239
- await describe("uint uniforms", async () => {
240
- beforeEach(async () => {
241
- glArea.make_current();
242
- });
243
- const VS_U = [
244
- "#version 300 es",
245
- "in vec2 position;",
246
- "void main() { gl_Position = vec4(position,0.0,1.0); }"
247
- ].join("\n");
248
- const FS_U = [
249
- "#version 300 es",
250
- "precision mediump float;",
251
- "uniform uint uR; uniform uint uG; uniform uint uB;",
252
- "out vec4 c;",
253
- "void main() { c = vec4(float(uR)/255.0, float(uG)/255.0, float(uB)/255.0, 1.0); }"
254
- ].join("\n");
255
- await it("uniform1ui sets an unsigned int uniform", async () => {
256
- const prog = makeProgram(gl2, VS_U, FS_U);
257
- expect(gl2.getProgramParameter(prog, gl2.LINK_STATUS)).toBeTruthy();
258
- gl2.useProgram(prog);
259
- const locR = gl2.getUniformLocation(prog, "uR");
260
- const locG = gl2.getUniformLocation(prog, "uG");
261
- const locB = gl2.getUniformLocation(prog, "uB");
262
- gl2.uniform1ui(locR, 0);
263
- gl2.uniform1ui(locG, 255);
264
- gl2.uniform1ui(locB, 0);
265
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
266
- expect(gl2.getUniform(prog, locR)).toBe(0);
267
- expect(gl2.getUniform(prog, locG)).toBe(255);
268
- gl2.deleteProgram(prog);
269
- });
270
- await it("uniform2ui, uniform3ui, uniform4ui set without error", async () => {
271
- const FS2 = [
272
- "#version 300 es",
273
- "precision mediump float;",
274
- "uniform uvec2 u2; uniform uvec3 u3; uniform uvec4 u4;",
275
- "out vec4 c;",
276
- "void main() { c = vec4(float(u2.x), float(u3.x), float(u4.x), 1.0)/255.0; }"
277
- ].join("\n");
278
- const prog = makeProgram(gl2, VS_U, FS2);
279
- gl2.useProgram(prog);
280
- gl2.uniform2ui(gl2.getUniformLocation(prog, "u2"), 1, 2);
281
- gl2.uniform3ui(gl2.getUniformLocation(prog, "u3"), 1, 2, 3);
282
- gl2.uniform4ui(gl2.getUniformLocation(prog, "u4"), 1, 2, 3, 4);
283
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
284
- gl2.deleteProgram(prog);
285
- });
286
- });
287
- await describe("non-square matrix uniforms", async () => {
288
- beforeEach(async () => {
289
- glArea.make_current();
290
- });
291
- const VS_M = [
292
- "#version 300 es",
293
- "in vec2 position;",
294
- "void main() { gl_Position = vec4(position,0.0,1.0); }"
295
- ].join("\n");
296
- await it("uniformMatrix2x3fv sets a mat2x3 without error", async () => {
297
- const FS = [
298
- "#version 300 es",
299
- "precision mediump float;",
300
- "uniform mat2x3 m; out vec4 c;",
301
- "void main() { c = vec4(m[0][0], m[0][1], m[0][2], 1.0); }"
302
- ].join("\n");
303
- const prog = makeProgram(gl2, VS_M, FS);
304
- gl2.useProgram(prog);
305
- gl2.uniformMatrix2x3fv(gl2.getUniformLocation(prog, "m"), false, new Float32Array([
306
- 1,
307
- 0,
308
- 0,
309
- 0,
310
- 1,
311
- 0
312
- ]));
313
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
314
- gl2.deleteProgram(prog);
315
- });
316
- await it("uniformMatrix3x2fv sets a mat3x2 without error", async () => {
317
- const FS = [
318
- "#version 300 es",
319
- "precision mediump float;",
320
- "uniform mat3x2 m; out vec4 c;",
321
- "void main() { c = vec4(m[0][0], m[0][1], 0.0, 1.0); }"
322
- ].join("\n");
323
- const prog = makeProgram(gl2, VS_M, FS);
324
- gl2.useProgram(prog);
325
- gl2.uniformMatrix3x2fv(gl2.getUniformLocation(prog, "m"), false, new Float32Array([
326
- 1,
327
- 0,
328
- 0,
329
- 1,
330
- 0,
331
- 0
332
- ]));
333
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
334
- gl2.deleteProgram(prog);
335
- });
336
- });
337
- await describe("Uniform Buffer Objects", async () => {
338
- beforeEach(async () => {
339
- glArea.make_current();
340
- });
341
- await it("getUniformBlockIndex + uniformBlockBinding works", async () => {
342
- const VS = [
343
- "#version 300 es",
344
- "layout(std140) uniform Params { vec4 color; };",
345
- "in vec2 position;",
346
- "out vec4 vColor;",
347
- "void main() { vColor = color; gl_Position = vec4(position,0.0,1.0); }"
348
- ].join("\n");
349
- const FS = [
350
- "#version 300 es",
351
- "precision mediump float;",
352
- "in vec4 vColor; out vec4 c;",
353
- "void main() { c = vColor; }"
354
- ].join("\n");
355
- const prog = makeProgram(gl2, VS, FS);
356
- expect(gl2.getProgramParameter(prog, gl2.LINK_STATUS)).toBeTruthy();
357
- const idx = gl2.getUniformBlockIndex(prog, "Params");
358
- expect(idx).not.toBe(gl2.INVALID_INDEX);
359
- gl2.uniformBlockBinding(prog, idx, 0);
360
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
361
- const ubo = gl2.createBuffer();
362
- gl2.bindBuffer(gl2.UNIFORM_BUFFER, ubo);
363
- gl2.bufferData(gl2.UNIFORM_BUFFER, new Float32Array([
364
- 0,
365
- 1,
366
- 0,
367
- 1
368
- ]), gl2.STATIC_DRAW);
369
- gl2.bindBufferBase(gl2.UNIFORM_BUFFER, 0, ubo);
370
- const fbo = makeTestFBO(gl2, 4, 4);
371
- gl2.useProgram(prog);
372
- gl2.clearColor(0, 0, 0, 1);
373
- gl2.clear(gl2.COLOR_BUFFER_BIT);
374
- drawTriangle(gl2);
375
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
376
- const p = readPixel(gl2, 0, 0);
377
- destroyTestFBO(gl2, fbo);
378
- expect(p[1]).toBe(255);
379
- gl2.deleteBuffer(ubo);
380
- gl2.deleteProgram(prog);
381
- });
382
- });
383
- await describe("instanced drawing", async () => {
384
- beforeEach(async () => {
385
- glArea.make_current();
386
- });
387
- await it("drawArraysInstanced renders without error", async () => {
388
- const VS = [
389
- "#version 300 es",
390
- "in vec2 position;",
391
- "void main() { gl_Position = vec4(position,0.0,1.0); }"
392
- ].join("\n");
393
- const FS = [
394
- "#version 300 es",
395
- "precision mediump float;",
396
- "out vec4 c;",
397
- "void main() { c = vec4(0.0,1.0,0.0,1.0); }"
398
- ].join("\n");
399
- const prog = makeProgram(gl2, VS, FS);
400
- gl2.useProgram(prog);
401
- const fbo = makeTestFBO(gl2, 4, 4);
402
- const buf = gl2.createBuffer();
403
- gl2.bindBuffer(gl2.ARRAY_BUFFER, buf);
404
- gl2.bufferData(gl2.ARRAY_BUFFER, new Float32Array([
405
- -2,
406
- -2,
407
- -2,
408
- 4,
409
- 4,
410
- -2
411
- ]), gl2.STREAM_DRAW);
412
- gl2.enableVertexAttribArray(0);
413
- gl2.vertexAttribPointer(0, 2, gl2.FLOAT, false, 0, 0);
414
- gl2.vertexAttribDivisor(0, 0);
415
- gl2.clearColor(0, 0, 0, 1);
416
- gl2.clear(gl2.COLOR_BUFFER_BIT);
417
- gl2.drawArraysInstanced(gl2.TRIANGLES, 0, 3, 1);
418
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
419
- const p = readPixel(gl2, 0, 0);
420
- destroyTestFBO(gl2, fbo);
421
- expect(p[1]).toBe(255);
422
- gl2.disableVertexAttribArray(0);
423
- gl2.deleteBuffer(buf);
424
- gl2.deleteProgram(prog);
425
- });
426
- });
427
- await describe("drawBuffers (MRT)", async () => {
428
- beforeEach(async () => {
429
- glArea.make_current();
430
- });
431
- await it("drawBuffers with 2 COLOR_ATTACHMENTs renders to both", async () => {
432
- const VS = [
433
- "#version 300 es",
434
- "in vec2 position;",
435
- "void main() { gl_Position = vec4(position,0.0,1.0); }"
436
- ].join("\n");
437
- const FS = [
438
- "#version 300 es",
439
- "precision mediump float;",
440
- "layout(location=0) out vec4 c0;",
441
- "layout(location=1) out vec4 c1;",
442
- "void main() { c0 = vec4(1.0,0.0,0.0,1.0); c1 = vec4(0.0,0.0,1.0,1.0); }"
443
- ].join("\n");
444
- const prog = makeProgram(gl2, VS, FS);
445
- expect(gl2.getProgramParameter(prog, gl2.LINK_STATUS)).toBeTruthy();
446
- const fb = gl2.createFramebuffer();
447
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, fb);
448
- const tex0 = gl2.createTexture();
449
- gl2.bindTexture(gl2.TEXTURE_2D, tex0);
450
- gl2.texImage2D(gl2.TEXTURE_2D, 0, gl2.RGBA, 4, 4, 0, gl2.RGBA, gl2.UNSIGNED_BYTE, null);
451
- gl2.framebufferTexture2D(gl2.FRAMEBUFFER, gl2.COLOR_ATTACHMENT0, gl2.TEXTURE_2D, tex0, 0);
452
- const tex1 = gl2.createTexture();
453
- gl2.bindTexture(gl2.TEXTURE_2D, tex1);
454
- gl2.texImage2D(gl2.TEXTURE_2D, 0, gl2.RGBA, 4, 4, 0, gl2.RGBA, gl2.UNSIGNED_BYTE, null);
455
- gl2.framebufferTexture2D(gl2.FRAMEBUFFER, gl2.COLOR_ATTACHMENT1, gl2.TEXTURE_2D, tex1, 0);
456
- const status = gl2.checkFramebufferStatus(gl2.FRAMEBUFFER);
457
- if (status !== gl2.FRAMEBUFFER_COMPLETE) {
458
- console.warn("MRT framebuffer not complete:", status);
459
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, null);
460
- gl2.deleteFramebuffer(fb);
461
- gl2.deleteTexture(tex0);
462
- gl2.deleteTexture(tex1);
463
- gl2.deleteProgram(prog);
464
- return;
465
- }
466
- gl2.drawBuffers([gl2.COLOR_ATTACHMENT0, gl2.COLOR_ATTACHMENT1]);
467
- gl2.viewport(0, 0, 4, 4);
468
- gl2.clearColor(0, 0, 0, 1);
469
- gl2.clear(gl2.COLOR_BUFFER_BIT);
470
- gl2.useProgram(prog);
471
- drawTriangle(gl2);
472
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
473
- gl2.readBuffer(gl2.COLOR_ATTACHMENT0);
474
- const p0 = readPixel(gl2, 0, 0);
475
- expect(p0[0]).toBe(255);
476
- expect(p0[1]).toBe(0);
477
- gl2.readBuffer(gl2.COLOR_ATTACHMENT1);
478
- const p1 = readPixel(gl2, 0, 0);
479
- expect(p1[0]).toBe(0);
480
- expect(p1[2]).toBe(255);
481
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, null);
482
- gl2.deleteFramebuffer(fb);
483
- gl2.deleteTexture(tex0);
484
- gl2.deleteTexture(tex1);
485
- gl2.deleteProgram(prog);
486
- });
487
- });
488
- await describe("3D texture", async () => {
489
- beforeEach(async () => {
490
- glArea.make_current();
491
- });
492
- await it("createTexture + TEXTURE_3D binding works", async () => {
493
- const tex = gl2.createTexture();
494
- expect(tex).not.toBeNull();
495
- gl2.bindTexture(gl2.TEXTURE_3D, tex);
496
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
497
- gl2.deleteTexture(tex);
498
- });
499
- await it("texImage3D uploads a 2×2×2 RGBA texture without error", async () => {
500
- const tex = gl2.createTexture();
501
- gl2.bindTexture(gl2.TEXTURE_3D, tex);
502
- gl2.texParameteri(gl2.TEXTURE_3D, gl2.TEXTURE_MIN_FILTER, gl2.NEAREST);
503
- gl2.texParameteri(gl2.TEXTURE_3D, gl2.TEXTURE_MAG_FILTER, gl2.NEAREST);
504
- gl2.texParameteri(gl2.TEXTURE_3D, gl2.TEXTURE_WRAP_S, gl2.CLAMP_TO_EDGE);
505
- gl2.texParameteri(gl2.TEXTURE_3D, gl2.TEXTURE_WRAP_T, gl2.CLAMP_TO_EDGE);
506
- gl2.texParameteri(gl2.TEXTURE_3D, gl2.TEXTURE_WRAP_R, gl2.CLAMP_TO_EDGE);
507
- const data = new Uint8Array(2 * 2 * 2 * 4).fill(128);
508
- gl2.texImage3D(gl2.TEXTURE_3D, 0, gl2.RGBA, 2, 2, 2, 0, gl2.RGBA, gl2.UNSIGNED_BYTE, data);
509
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
510
- gl2.deleteTexture(tex);
511
- });
512
- await it("texStorage3D allocates storage without error", async () => {
513
- const tex = gl2.createTexture();
514
- gl2.bindTexture(gl2.TEXTURE_3D, tex);
515
- gl2.texStorage3D(gl2.TEXTURE_3D, 1, gl2.RGBA8, 4, 4, 4);
516
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
517
- gl2.deleteTexture(tex);
518
- });
519
- });
520
- await describe("transform feedback", async () => {
521
- beforeEach(async () => {
522
- glArea.make_current();
523
- });
524
- await it("captures vertex shader output via transform feedback", async () => {
525
- const VS_TF = [
526
- "#version 300 es",
527
- "in float inVal;",
528
- "out float outVal;",
529
- "void main() { outVal = inVal * 2.0; gl_Position = vec4(0.0); }"
530
- ].join("\n");
531
- const FS_TF = [
532
- "#version 300 es",
533
- "precision mediump float;",
534
- "out vec4 c;",
535
- "void main() { c = vec4(0.0); }"
536
- ].join("\n");
537
- const vert = gl2.createShader(gl2.VERTEX_SHADER);
538
- gl2.shaderSource(vert, VS_TF);
539
- gl2.compileShader(vert);
540
- if (!gl2.getShaderParameter(vert, gl2.COMPILE_STATUS)) {
541
- console.error("TF VS:", gl2.getShaderInfoLog(vert));
542
- }
543
- const frag = gl2.createShader(gl2.FRAGMENT_SHADER);
544
- gl2.shaderSource(frag, FS_TF);
545
- gl2.compileShader(frag);
546
- const prog = gl2.createProgram();
547
- gl2.attachShader(prog, vert);
548
- gl2.attachShader(prog, frag);
549
- gl2.transformFeedbackVaryings(prog, ["outVal"], gl2.SEPARATE_ATTRIBS);
550
- gl2.linkProgram(prog);
551
- if (!gl2.getProgramParameter(prog, gl2.LINK_STATUS)) {
552
- console.error("TF prog:", gl2.getProgramInfoLog(prog));
553
- }
554
- expect(gl2.getProgramParameter(prog, gl2.LINK_STATUS)).toBeTruthy();
555
- const inBuf = gl2.createBuffer();
556
- gl2.bindBuffer(gl2.ARRAY_BUFFER, inBuf);
557
- gl2.bufferData(gl2.ARRAY_BUFFER, new Float32Array([
558
- 1,
559
- 2,
560
- 3,
561
- 4
562
- ]), gl2.STATIC_DRAW);
563
- const attribLoc = gl2.getAttribLocation(prog, "inVal");
564
- gl2.enableVertexAttribArray(attribLoc);
565
- gl2.vertexAttribPointer(attribLoc, 1, gl2.FLOAT, false, 0, 0);
566
- const outBuf = gl2.createBuffer();
567
- gl2.bindBuffer(gl2.TRANSFORM_FEEDBACK_BUFFER, outBuf);
568
- gl2.bufferData(gl2.TRANSFORM_FEEDBACK_BUFFER, 4 * 4, gl2.STATIC_READ);
569
- const tf = gl2.createTransformFeedback();
570
- gl2.bindTransformFeedback(gl2.TRANSFORM_FEEDBACK, tf);
571
- gl2.bindBufferBase(gl2.TRANSFORM_FEEDBACK_BUFFER, 0, outBuf);
572
- const fbo = makeTestFBO(gl2, 1, 1);
573
- gl2.useProgram(prog);
574
- gl2.enable(gl2.RASTERIZER_DISCARD);
575
- gl2.beginTransformFeedback(gl2.POINTS);
576
- gl2.drawArrays(gl2.POINTS, 0, 4);
577
- gl2.endTransformFeedback();
578
- gl2.disable(gl2.RASTERIZER_DISCARD);
579
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
580
- gl2.bindTransformFeedback(gl2.TRANSFORM_FEEDBACK, null);
581
- destroyTestFBO(gl2, fbo);
582
- const result = new Float32Array(4);
583
- gl2.bindBuffer(gl2.TRANSFORM_FEEDBACK_BUFFER, outBuf);
584
- gl2.getBufferSubData(gl2.TRANSFORM_FEEDBACK_BUFFER, 0, result);
585
- expect(result[0]).toBe(2);
586
- expect(result[1]).toBe(4);
587
- expect(result[2]).toBe(6);
588
- expect(result[3]).toBe(8);
589
- gl2.disableVertexAttribArray(attribLoc);
590
- gl2.deleteBuffer(inBuf);
591
- gl2.deleteBuffer(outBuf);
592
- gl2.deleteTransformFeedback(tf);
593
- gl2.deleteProgram(prog);
594
- });
595
- });
596
- await describe("getStringi", async () => {
597
- beforeEach(async () => {
598
- glArea.make_current();
599
- });
600
- await it("getStringi(EXTENSIONS, 0) returns a string", async () => {
601
- const numExts = gl2.getParameter(gl2.NUM_EXTENSIONS);
602
- if (numExts > 0) {
603
- const ext = gl2.getStringi(gl2.EXTENSIONS, 0);
604
- expect(typeof ext).toBe("string");
605
- expect(ext.length > 0).toBeTruthy();
606
- }
607
- });
608
- });
609
- await describe("WebGL2 extensions", async () => {
610
- beforeEach(async () => {
611
- glArea.make_current();
612
- });
613
- await it("supports EXT_color_buffer_float", async () => {
614
- const ext = gl2.getExtension("EXT_color_buffer_float");
615
- expect(ext).toBeTruthy();
616
- });
617
- await it("supports EXT_color_buffer_half_float", async () => {
618
- const ext = gl2.getExtension("EXT_color_buffer_half_float");
619
- expect(ext).toBeTruthy();
620
- });
621
- await it("supports OES_texture_half_float", async () => {
622
- const ext = gl2.getExtension("OES_texture_half_float");
623
- expect(ext).toBeTruthy();
624
- });
625
- await it("OES_texture_half_float exposes HALF_FLOAT_OES constant", async () => {
626
- const ext = gl2.getExtension("OES_texture_half_float");
627
- expect(ext).toBeTruthy();
628
- expect(ext.HALF_FLOAT_OES).toBe(36193);
629
- });
630
- });
631
- await describe("WebGL2 renderbufferStorage formats", async () => {
632
- beforeEach(async () => {
633
- glArea.make_current();
634
- });
635
- async function testRbFormat(internalFormat) {
636
- const rb = gl2.createRenderbuffer();
637
- gl2.bindRenderbuffer(gl2.RENDERBUFFER, rb);
638
- gl2.renderbufferStorage(gl2.RENDERBUFFER, internalFormat, 64, 64);
639
- const err = gl2.getError();
640
- gl2.deleteRenderbuffer(rb);
641
- return err;
642
- }
643
- await it("accepts RGBA8 (0x8058)", async () => {
644
- expect(await testRbFormat(32856)).toBe(gl2.NO_ERROR);
645
- });
646
- await it("accepts DEPTH_COMPONENT24 (0x81A6)", async () => {
647
- expect(await testRbFormat(33190)).toBe(gl2.NO_ERROR);
648
- });
649
- await it("accepts DEPTH24_STENCIL8 (0x88F0)", async () => {
650
- expect(await testRbFormat(35056)).toBe(gl2.NO_ERROR);
651
- });
652
- });
653
- await describe("WebGL2 float render target", async () => {
654
- beforeEach(async () => {
655
- glArea.make_current();
656
- });
657
- await it("can create RGBA16F texture and attach to FBO", async () => {
658
- const RGBA16F = 34842;
659
- const HALF_FLOAT = 5131;
660
- const tex = gl2.createTexture();
661
- gl2.bindTexture(gl2.TEXTURE_2D, tex);
662
- gl2.texImage2D(gl2.TEXTURE_2D, 0, RGBA16F, 64, 64, 0, gl2.RGBA, HALF_FLOAT, null);
663
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
664
- const fbo = gl2.createFramebuffer();
665
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, fbo);
666
- gl2.framebufferTexture2D(gl2.FRAMEBUFFER, gl2.COLOR_ATTACHMENT0, gl2.TEXTURE_2D, tex, 0);
667
- const status = gl2.checkFramebufferStatus(gl2.FRAMEBUFFER);
668
- expect(status).toBe(gl2.FRAMEBUFFER_COMPLETE);
669
- gl2.clearColor(1, 0, 0, 1);
670
- gl2.clear(gl2.COLOR_BUFFER_BIT);
671
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
672
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, null);
673
- gl2.deleteFramebuffer(fbo);
674
- gl2.deleteTexture(tex);
675
- });
676
- await it("can create RGBA8 renderbuffer FBO", async () => {
677
- const RGBA8 = 32856;
678
- const rb = gl2.createRenderbuffer();
679
- gl2.bindRenderbuffer(gl2.RENDERBUFFER, rb);
680
- gl2.renderbufferStorage(gl2.RENDERBUFFER, RGBA8, 64, 64);
681
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
682
- const fbo = gl2.createFramebuffer();
683
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, fbo);
684
- gl2.framebufferRenderbuffer(gl2.FRAMEBUFFER, gl2.COLOR_ATTACHMENT0, gl2.RENDERBUFFER, rb);
685
- const status = gl2.checkFramebufferStatus(gl2.FRAMEBUFFER);
686
- expect(status).toBe(gl2.FRAMEBUFFER_COMPLETE);
687
- gl2.clearColor(0, 1, 0, 1);
688
- gl2.clear(gl2.COLOR_BUFFER_BIT);
689
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
690
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, null);
691
- gl2.deleteFramebuffer(fbo);
692
- gl2.deleteRenderbuffer(rb);
693
- });
694
- });
695
- await describe("WebGL2 GLSL 1.0 compatibility", async () => {
696
- beforeEach(async () => {
697
- glArea.make_current();
698
- });
699
- await it("compiles versionless shaders with attribute/varying", async () => {
700
- const vs = "attribute vec2 position;\nvarying vec2 vUv;\nvoid main() { vUv = position; gl_Position = vec4(position, 0.0, 1.0); }";
701
- const fs = "precision mediump float;\nvarying vec2 vUv;\nvoid main() { gl_FragColor = vec4(vUv, 0.0, 1.0); }";
702
- const prog = makeProgram(gl2, vs, fs);
703
- expect(prog).toBeTruthy();
704
- if (prog) {
705
- expect(gl2.getProgramParameter(prog, gl2.LINK_STATUS)).toBeTruthy();
706
- gl2.deleteProgram(prog);
707
- }
708
- });
709
- });
710
- await describe("WebGL2 textured rendering", async () => {
711
- beforeEach(async () => {
712
- glArea.make_current();
713
- });
714
- await it("samples a 2x2 RGBA texture to produce red output", async () => {
715
- const fbo = makeTestFBO(gl2, 4, 4);
716
- const tex = gl2.createTexture();
717
- gl2.bindTexture(gl2.TEXTURE_2D, tex);
718
- const pixels = new Uint8Array([
719
- 255,
720
- 0,
721
- 0,
722
- 255,
723
- 255,
724
- 0,
725
- 0,
726
- 255,
727
- 255,
728
- 0,
729
- 0,
730
- 255,
731
- 255,
732
- 0,
733
- 0,
734
- 255
735
- ]);
736
- gl2.texImage2D(gl2.TEXTURE_2D, 0, gl2.RGBA, 2, 2, 0, gl2.RGBA, gl2.UNSIGNED_BYTE, pixels);
737
- gl2.texParameteri(gl2.TEXTURE_2D, gl2.TEXTURE_MIN_FILTER, gl2.NEAREST);
738
- gl2.texParameteri(gl2.TEXTURE_2D, gl2.TEXTURE_MAG_FILTER, gl2.NEAREST);
739
- const prog = makeProgram(gl2, TEXTURE_VS_300, TEXTURE_FS_300);
740
- expect(gl2.getProgramParameter(prog, gl2.LINK_STATUS)).toBeTruthy();
741
- gl2.useProgram(prog);
742
- const loc = gl2.getUniformLocation(prog, "uTexture");
743
- gl2.uniform1i(loc, 0);
744
- gl2.activeTexture(gl2.TEXTURE0);
745
- gl2.bindTexture(gl2.TEXTURE_2D, tex);
746
- drawTriangle(gl2);
747
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
748
- const p = readPixel(gl2, 0, 0);
749
- expect(p[0]).toBe(255);
750
- expect(p[1]).toBe(0);
751
- expect(p[2]).toBe(0);
752
- expect(p[3]).toBe(255);
753
- gl2.deleteTexture(tex);
754
- gl2.deleteProgram(prog);
755
- destroyTestFBO(gl2, fbo);
756
- });
757
- });
758
- await describe("WebGL2 FBO chain (post-processing)", async () => {
759
- beforeEach(async () => {
760
- glArea.make_current();
761
- });
762
- await it("renders green to FBO1, samples FBO1 texture into FBO2", async () => {
763
- const VS = [
764
- "#version 300 es",
765
- "in vec2 position;",
766
- "void main() { gl_Position = vec4(position,0.0,1.0); }"
767
- ].join("\n");
768
- const FS_GREEN = [
769
- "#version 300 es",
770
- "precision mediump float;",
771
- "out vec4 c;",
772
- "void main() { c = vec4(0.0,1.0,0.0,1.0); }"
773
- ].join("\n");
774
- const fbo1 = makeTestFBO(gl2, 4, 4);
775
- const status1 = gl2.checkFramebufferStatus(gl2.FRAMEBUFFER);
776
- expect(status1).toBe(gl2.FRAMEBUFFER_COMPLETE);
777
- const progGreen = makeProgram(gl2, VS, FS_GREEN);
778
- gl2.useProgram(progGreen);
779
- gl2.clearColor(0, 0, 0, 1);
780
- gl2.clear(gl2.COLOR_BUFFER_BIT);
781
- drawTriangle(gl2);
782
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
783
- const p1 = readPixel(gl2, 0, 0);
784
- expect(p1[1]).toBe(255);
785
- const fbo2 = makeTestFBO(gl2, 4, 4);
786
- const status2 = gl2.checkFramebufferStatus(gl2.FRAMEBUFFER);
787
- expect(status2).toBe(gl2.FRAMEBUFFER_COMPLETE);
788
- const progTex = makeProgram(gl2, TEXTURE_VS_300, TEXTURE_FS_300);
789
- gl2.useProgram(progTex);
790
- const texLoc = gl2.getUniformLocation(progTex, "uTexture");
791
- gl2.uniform1i(texLoc, 0);
792
- gl2.activeTexture(gl2.TEXTURE0);
793
- gl2.bindTexture(gl2.TEXTURE_2D, fbo1.colorTex);
794
- gl2.clearColor(0, 0, 0, 1);
795
- gl2.clear(gl2.COLOR_BUFFER_BIT);
796
- drawTriangle(gl2);
797
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
798
- const p2 = readPixel(gl2, 0, 0);
799
- expect(p2[1]).toBe(255);
800
- gl2.deleteProgram(progGreen);
801
- gl2.deleteProgram(progTex);
802
- destroyTestFBO(gl2, fbo2);
803
- destroyTestFBO(gl2, fbo1);
804
- });
805
- });
806
- await describe("WebGL2 half-float FBO chain", async () => {
807
- beforeEach(async () => {
808
- glArea.make_current();
809
- });
810
- await it("renders green to RGBA16F FBO, samples into RGBA8 FBO", async () => {
811
- const VS = [
812
- "#version 300 es",
813
- "in vec2 position;",
814
- "void main() { gl_Position = vec4(position,0.0,1.0); }"
815
- ].join("\n");
816
- const FS_GREEN = [
817
- "#version 300 es",
818
- "precision mediump float;",
819
- "out vec4 c;",
820
- "void main() { c = vec4(0.0,1.0,0.0,1.0); }"
821
- ].join("\n");
822
- const fboFloat = makeTestFBOFloat(gl2, 4, 4);
823
- const status1 = gl2.checkFramebufferStatus(gl2.FRAMEBUFFER);
824
- expect(status1).toBe(gl2.FRAMEBUFFER_COMPLETE);
825
- const progGreen = makeProgram(gl2, VS, FS_GREEN);
826
- gl2.useProgram(progGreen);
827
- gl2.clearColor(0, 0, 0, 1);
828
- gl2.clear(gl2.COLOR_BUFFER_BIT);
829
- drawTriangle(gl2);
830
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
831
- const fbo2 = makeTestFBO(gl2, 4, 4);
832
- const progTex = makeProgram(gl2, TEXTURE_VS_300, TEXTURE_FS_300);
833
- gl2.useProgram(progTex);
834
- const texLoc = gl2.getUniformLocation(progTex, "uTexture");
835
- gl2.uniform1i(texLoc, 0);
836
- gl2.activeTexture(gl2.TEXTURE0);
837
- gl2.bindTexture(gl2.TEXTURE_2D, fboFloat.colorTex);
838
- gl2.clearColor(0, 0, 0, 1);
839
- gl2.clear(gl2.COLOR_BUFFER_BIT);
840
- drawTriangle(gl2);
841
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
842
- const p = readPixel(gl2, 0, 0);
843
- expect(p[1]).toBe(255);
844
- gl2.deleteProgram(progGreen);
845
- gl2.deleteProgram(progTex);
846
- destroyTestFBO(gl2, fbo2);
847
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, fboFloat.fb);
848
- gl2.framebufferTexture2D(gl2.FRAMEBUFFER, gl2.COLOR_ATTACHMENT0, gl2.TEXTURE_2D, null, 0);
849
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, null);
850
- gl2.deleteTexture(fboFloat.colorTex);
851
- gl2.deleteFramebuffer(fboFloat.fb);
852
- });
853
- });
854
- await describe("WebGL2 depth texture FBO", async () => {
855
- beforeEach(async () => {
856
- glArea.make_current();
857
- });
858
- await it("creates FBO with depth texture and renders with depth test", async () => {
859
- const fbo = makeTestFBOWithDepthTexture(gl2, 4, 4);
860
- const status = gl2.checkFramebufferStatus(gl2.FRAMEBUFFER);
861
- expect(status).toBe(gl2.FRAMEBUFFER_COMPLETE);
862
- const VS = [
863
- "#version 300 es",
864
- "in vec2 position;",
865
- "uniform float uDepth;",
866
- "void main() { gl_Position = vec4(position, uDepth, 1.0); }"
867
- ].join("\n");
868
- const FS = [
869
- "#version 300 es",
870
- "precision mediump float;",
871
- "uniform vec4 uColor;",
872
- "out vec4 c;",
873
- "void main() { c = uColor; }"
874
- ].join("\n");
875
- const prog = makeProgram(gl2, VS, FS);
876
- gl2.useProgram(prog);
877
- gl2.enable(gl2.DEPTH_TEST);
878
- gl2.depthFunc(gl2.LESS);
879
- gl2.clearColor(0, 0, 0, 1);
880
- gl2.clearDepth(1);
881
- gl2.clear(gl2.COLOR_BUFFER_BIT | gl2.DEPTH_BUFFER_BIT);
882
- const colorLoc = gl2.getUniformLocation(prog, "uColor");
883
- const depthLoc = gl2.getUniformLocation(prog, "uDepth");
884
- gl2.uniform4f(colorLoc, 1, 0, 0, 1);
885
- gl2.uniform1f(depthLoc, .5);
886
- drawTriangle(gl2);
887
- gl2.uniform4f(colorLoc, 0, 1, 0, 1);
888
- gl2.uniform1f(depthLoc, 0);
889
- drawTriangle(gl2);
890
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
891
- const p = readPixel(gl2, 0, 0);
892
- expect(p[1]).toBe(255);
893
- gl2.disable(gl2.DEPTH_TEST);
894
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, fbo.fb);
895
- gl2.framebufferTexture2D(gl2.FRAMEBUFFER, gl2.COLOR_ATTACHMENT0, gl2.TEXTURE_2D, null, 0);
896
- gl2.framebufferTexture2D(gl2.FRAMEBUFFER, gl2.DEPTH_ATTACHMENT, gl2.TEXTURE_2D, null, 0);
897
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, null);
898
- gl2.deleteTexture(fbo.colorTex);
899
- gl2.deleteTexture(fbo.depthTex);
900
- gl2.deleteFramebuffer(fbo.fb);
901
- gl2.deleteProgram(prog);
902
- });
903
- });
904
- await describe("WebGL2 generateMipmap", async () => {
905
- beforeEach(async () => {
906
- glArea.make_current();
907
- });
908
- await it("generates mipmaps for a 4x4 texture without error", async () => {
909
- const tex = gl2.createTexture();
910
- gl2.bindTexture(gl2.TEXTURE_2D, tex);
911
- const data = new Uint8Array(4 * 4 * 4);
912
- for (let i = 0; i < data.length; i += 4) {
913
- data[i] = 255;
914
- data[i + 3] = 255;
915
- }
916
- gl2.texImage2D(gl2.TEXTURE_2D, 0, gl2.RGBA, 4, 4, 0, gl2.RGBA, gl2.UNSIGNED_BYTE, data);
917
- gl2.generateMipmap(gl2.TEXTURE_2D);
918
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
919
- gl2.texParameteri(gl2.TEXTURE_2D, gl2.TEXTURE_MIN_FILTER, gl2.LINEAR_MIPMAP_LINEAR);
920
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
921
- const fbo = makeTestFBO(gl2, 4, 4);
922
- const prog = makeProgram(gl2, TEXTURE_VS_300, TEXTURE_FS_300);
923
- gl2.useProgram(prog);
924
- gl2.bindTexture(gl2.TEXTURE_2D, tex);
925
- gl2.uniform1i(gl2.getUniformLocation(prog, "uTexture"), 0);
926
- drawTriangle(gl2);
927
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
928
- const p = readPixel(gl2, 0, 0);
929
- expect(p[0]).toBeGreaterThan(200);
930
- gl2.deleteTexture(tex);
931
- gl2.deleteProgram(prog);
932
- destroyTestFBO(gl2, fbo);
933
- });
934
- });
935
- await describe("WebGL2 cubemap texture", async () => {
936
- beforeEach(async () => {
937
- glArea.make_current();
938
- });
939
- await it("creates cubemap and verifies FBO attachment", async () => {
940
- const tex = gl2.createTexture();
941
- gl2.bindTexture(gl2.TEXTURE_CUBE_MAP, tex);
942
- const faces = [
943
- gl2.TEXTURE_CUBE_MAP_POSITIVE_X,
944
- gl2.TEXTURE_CUBE_MAP_NEGATIVE_X,
945
- gl2.TEXTURE_CUBE_MAP_POSITIVE_Y,
946
- gl2.TEXTURE_CUBE_MAP_NEGATIVE_Y,
947
- gl2.TEXTURE_CUBE_MAP_POSITIVE_Z,
948
- gl2.TEXTURE_CUBE_MAP_NEGATIVE_Z
949
- ];
950
- const colors = [
951
- [
952
- 255,
953
- 0,
954
- 0,
955
- 255
956
- ],
957
- [
958
- 0,
959
- 255,
960
- 0,
961
- 255
962
- ],
963
- [
964
- 0,
965
- 0,
966
- 255,
967
- 255
968
- ],
969
- [
970
- 255,
971
- 255,
972
- 0,
973
- 255
974
- ],
975
- [
976
- 255,
977
- 0,
978
- 255,
979
- 255
980
- ],
981
- [
982
- 0,
983
- 255,
984
- 255,
985
- 255
986
- ]
987
- ];
988
- for (let i = 0; i < 6; i++) {
989
- gl2.texImage2D(faces[i], 0, gl2.RGBA, 1, 1, 0, gl2.RGBA, gl2.UNSIGNED_BYTE, new Uint8Array(colors[i]));
990
- }
991
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
992
- gl2.texParameteri(gl2.TEXTURE_CUBE_MAP, gl2.TEXTURE_MIN_FILTER, gl2.NEAREST);
993
- gl2.texParameteri(gl2.TEXTURE_CUBE_MAP, gl2.TEXTURE_MAG_FILTER, gl2.NEAREST);
994
- const fb = gl2.createFramebuffer();
995
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, fb);
996
- gl2.framebufferTexture2D(gl2.FRAMEBUFFER, gl2.COLOR_ATTACHMENT0, gl2.TEXTURE_CUBE_MAP_POSITIVE_X, tex, 0);
997
- const status = gl2.checkFramebufferStatus(gl2.FRAMEBUFFER);
998
- expect(status).toBe(gl2.FRAMEBUFFER_COMPLETE);
999
- gl2.viewport(0, 0, 1, 1);
1000
- gl2.clearColor(0, 1, 1, 1);
1001
- gl2.clear(gl2.COLOR_BUFFER_BIT);
1002
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
1003
- const p = readPixel(gl2, 0, 0);
1004
- expect(p[0]).toBe(0);
1005
- expect(p[1]).toBe(255);
1006
- expect(p[2]).toBe(255);
1007
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, null);
1008
- gl2.deleteFramebuffer(fb);
1009
- gl2.deleteTexture(tex);
1010
- });
1011
- });
1012
- await describe("WebGL2 READ_FRAMEBUFFER / DRAW_FRAMEBUFFER", async () => {
1013
- beforeEach(async () => {
1014
- glArea.make_current();
1015
- });
1016
- await it("bindFramebuffer accepts READ_FRAMEBUFFER and DRAW_FRAMEBUFFER", async () => {
1017
- const fb1 = gl2.createFramebuffer();
1018
- const fb2 = gl2.createFramebuffer();
1019
- gl2.bindFramebuffer(36008, fb1);
1020
- gl2.bindFramebuffer(36009, fb2);
1021
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
1022
- expect(gl2.getParameter(36010)).toBe(fb1);
1023
- expect(gl2.getParameter(36006)).toBe(fb2);
1024
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, null);
1025
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
1026
- gl2.deleteFramebuffer(fb1);
1027
- gl2.deleteFramebuffer(fb2);
1028
- });
1029
- });
1030
- await describe("WebGL2 RGBA8 FBO (sized internal format)", async () => {
1031
- beforeEach(async () => {
1032
- glArea.make_current();
1033
- });
1034
- await it("renders solid color to RGBA8 FBO, readPixels confirms result", async () => {
1035
- const RGBA8 = 32856;
1036
- const w = 4;
1037
- const h = 4;
1038
- const fb = gl2.createFramebuffer();
1039
- const tex = gl2.createTexture();
1040
- gl2.bindTexture(gl2.TEXTURE_2D, tex);
1041
- gl2.texImage2D(gl2.TEXTURE_2D, 0, RGBA8, w, h, 0, gl2.RGBA, gl2.UNSIGNED_BYTE, null);
1042
- gl2.texParameteri(gl2.TEXTURE_2D, gl2.TEXTURE_MIN_FILTER, gl2.NEAREST);
1043
- gl2.texParameteri(gl2.TEXTURE_2D, gl2.TEXTURE_MAG_FILTER, gl2.NEAREST);
1044
- gl2.bindTexture(gl2.TEXTURE_2D, null);
1045
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, fb);
1046
- gl2.framebufferTexture2D(gl2.FRAMEBUFFER, gl2.COLOR_ATTACHMENT0, gl2.TEXTURE_2D, tex, 0);
1047
- expect(gl2.checkFramebufferStatus(gl2.FRAMEBUFFER)).toBe(gl2.FRAMEBUFFER_COMPLETE);
1048
- gl2.viewport(0, 0, w, h);
1049
- gl2.clearColor(0, 1, 0, 1);
1050
- gl2.clear(gl2.COLOR_BUFFER_BIT);
1051
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
1052
- const pixels = new Uint8Array(4);
1053
- gl2.readPixels(0, 0, 1, 1, gl2.RGBA, gl2.UNSIGNED_BYTE, pixels);
1054
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
1055
- expect(pixels[0]).toBe(0);
1056
- expect(pixels[1]).toBe(255);
1057
- expect(pixels[2]).toBe(0);
1058
- expect(pixels[3]).toBe(255);
1059
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, fb);
1060
- gl2.framebufferTexture2D(gl2.FRAMEBUFFER, gl2.COLOR_ATTACHMENT0, gl2.TEXTURE_2D, null, 0);
1061
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, null);
1062
- gl2.deleteTexture(tex);
1063
- gl2.deleteFramebuffer(fb);
1064
- });
1065
- });
1066
- await describe("WebGL2 RGBA16F FBO draw + FLOAT readPixels", async () => {
1067
- beforeEach(async () => {
1068
- glArea.make_current();
1069
- });
1070
- await it("renders red triangle to RGBA16F FBO, reads back as FLOAT", async () => {
1071
- const gl = gl2;
1072
- const vs = `#version 300 es\nin vec2 position;\nvoid main(){gl_Position=vec4(position,0.,1.);}`;
1073
- const fs = `#version 300 es\nprecision mediump float;\nout vec4 c;\nvoid main(){c=vec4(1.,0.,0.,1.);}`;
1074
- const prog = makeProgram(gl, vs, fs);
1075
- expect(gl2.getProgramParameter(prog, gl2.LINK_STATUS)).toBeTruthy();
1076
- const fbo = makeTestFBOFloat(gl2);
1077
- gl2.useProgram(prog);
1078
- drawTriangle(gl);
1079
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
1080
- const pixels = new Float32Array(4);
1081
- gl2.readPixels(0, 0, 1, 1, gl2.RGBA, gl2.FLOAT, pixels);
1082
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
1083
- expect(Math.abs(pixels[0] - 1) < .01).toBeTruthy();
1084
- expect(Math.abs(pixels[1] - 0) < .01).toBeTruthy();
1085
- expect(Math.abs(pixels[2] - 0) < .01).toBeTruthy();
1086
- expect(Math.abs(pixels[3] - 1) < .01).toBeTruthy();
1087
- gl2.useProgram(null);
1088
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, fbo.fb);
1089
- gl2.framebufferTexture2D(gl2.FRAMEBUFFER, gl2.COLOR_ATTACHMENT0, gl2.TEXTURE_2D, null, 0);
1090
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, null);
1091
- gl2.deleteTexture(fbo.colorTex);
1092
- gl2.deleteFramebuffer(fbo.fb);
1093
- gl2.deleteProgram(prog);
1094
- });
1095
- });
1096
- await describe("WebGL2 MRT — writes to COLOR_ATTACHMENT0 and COLOR_ATTACHMENT1", async () => {
1097
- beforeEach(async () => {
1098
- glArea.make_current();
1099
- });
1100
- await it("MRT shader writes different colors to CA0 and CA1", async () => {
1101
- const gl = gl2;
1102
- const w = 4;
1103
- const h = 4;
1104
- const vs = `#version 300 es\nin vec2 position;\nvoid main(){gl_Position=vec4(position,0.,1.);}`;
1105
- const fs = `#version 300 es\nprecision mediump float;\nlayout(location=0) out vec4 c0;\nlayout(location=1) out vec4 c1;\nvoid main(){c0=vec4(1.,0.,0.,1.);c1=vec4(0.,0.,1.,1.);}`;
1106
- const prog = makeProgram(gl, vs, fs);
1107
- expect(gl2.getProgramParameter(prog, gl2.LINK_STATUS)).toBeTruthy();
1108
- const fb = gl2.createFramebuffer();
1109
- const tex0 = gl2.createTexture();
1110
- const tex1 = gl2.createTexture();
1111
- for (const [tex, attachment] of [[tex0, gl2.COLOR_ATTACHMENT0], [tex1, gl2.COLOR_ATTACHMENT0 + 1]]) {
1112
- gl2.bindTexture(gl2.TEXTURE_2D, tex);
1113
- gl2.texImage2D(gl2.TEXTURE_2D, 0, gl2.RGBA, w, h, 0, gl2.RGBA, gl2.UNSIGNED_BYTE, null);
1114
- gl2.texParameteri(gl2.TEXTURE_2D, gl2.TEXTURE_MIN_FILTER, gl2.NEAREST);
1115
- gl2.texParameteri(gl2.TEXTURE_2D, gl2.TEXTURE_MAG_FILTER, gl2.NEAREST);
1116
- gl2.bindTexture(gl2.TEXTURE_2D, null);
1117
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, fb);
1118
- gl2.framebufferTexture2D(gl2.FRAMEBUFFER, attachment, gl2.TEXTURE_2D, tex, 0);
1119
- }
1120
- expect(gl2.checkFramebufferStatus(gl2.FRAMEBUFFER)).toBe(gl2.FRAMEBUFFER_COMPLETE);
1121
- gl2.viewport(0, 0, w, h);
1122
- gl2.drawBuffers([gl2.COLOR_ATTACHMENT0, gl2.COLOR_ATTACHMENT0 + 1]);
1123
- gl2.useProgram(prog);
1124
- drawTriangle(gl);
1125
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
1126
- const fbRead0 = gl2.createFramebuffer();
1127
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, fbRead0);
1128
- gl2.framebufferTexture2D(gl2.FRAMEBUFFER, gl2.COLOR_ATTACHMENT0, gl2.TEXTURE_2D, tex0, 0);
1129
- const p0 = new Uint8Array(4);
1130
- gl2.readPixels(0, 0, 1, 1, gl2.RGBA, gl2.UNSIGNED_BYTE, p0);
1131
- expect(p0[0]).toBe(255);
1132
- expect(p0[1]).toBe(0);
1133
- expect(p0[2]).toBe(0);
1134
- const fbRead1 = gl2.createFramebuffer();
1135
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, fbRead1);
1136
- gl2.framebufferTexture2D(gl2.FRAMEBUFFER, gl2.COLOR_ATTACHMENT0, gl2.TEXTURE_2D, tex1, 0);
1137
- const p1 = new Uint8Array(4);
1138
- gl2.readPixels(0, 0, 1, 1, gl2.RGBA, gl2.UNSIGNED_BYTE, p1);
1139
- expect(p1[0]).toBe(0);
1140
- expect(p1[1]).toBe(0);
1141
- expect(p1[2]).toBe(255);
1142
- gl2.useProgram(null);
1143
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, null);
1144
- gl2.deleteTexture(tex0);
1145
- gl2.deleteTexture(tex1);
1146
- gl2.deleteFramebuffer(fb);
1147
- gl2.deleteFramebuffer(fbRead0);
1148
- gl2.deleteFramebuffer(fbRead1);
1149
- gl2.deleteProgram(prog);
1150
- });
1151
- });
1152
- await describe("WebGL2 Excalibur-style VAO draw + READ/DRAW blitToScreen", async () => {
1153
- beforeEach(async () => {
1154
- glArea.make_current();
1155
- });
1156
- await it("VAO draw to source FBO survives clearBufferfv+blit pipeline", async () => {
1157
- const W = 4;
1158
- const H = 4;
1159
- const gl = gl2;
1160
- const srcFbo = gl2.createFramebuffer();
1161
- const srcTex = gl2.createTexture();
1162
- gl2.bindTexture(gl2.TEXTURE_2D, srcTex);
1163
- gl2.texImage2D(gl2.TEXTURE_2D, 0, gl2.RGBA, W, H, 0, gl2.RGBA, gl2.UNSIGNED_BYTE, null);
1164
- gl2.texParameteri(gl2.TEXTURE_2D, gl2.TEXTURE_MIN_FILTER, gl2.NEAREST);
1165
- gl2.texParameteri(gl2.TEXTURE_2D, gl2.TEXTURE_MAG_FILTER, gl2.NEAREST);
1166
- gl2.texParameteri(gl2.TEXTURE_2D, gl2.TEXTURE_WRAP_S, gl2.CLAMP_TO_EDGE);
1167
- gl2.texParameteri(gl2.TEXTURE_2D, gl2.TEXTURE_WRAP_T, gl2.CLAMP_TO_EDGE);
1168
- gl2.bindTexture(gl2.TEXTURE_2D, null);
1169
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, srcFbo);
1170
- gl2.framebufferTexture2D(gl2.FRAMEBUFFER, gl2.COLOR_ATTACHMENT0, gl2.TEXTURE_2D, srcTex, 0);
1171
- expect(gl2.checkFramebufferStatus(gl2.FRAMEBUFFER)).toBe(gl2.FRAMEBUFFER_COMPLETE);
1172
- const dstFbo = gl2.createFramebuffer();
1173
- const dstTex = gl2.createTexture();
1174
- gl2.bindTexture(gl2.TEXTURE_2D, dstTex);
1175
- gl2.texImage2D(gl2.TEXTURE_2D, 0, gl2.RGBA, W, H, 0, gl2.RGBA, gl2.UNSIGNED_BYTE, null);
1176
- gl2.texParameteri(gl2.TEXTURE_2D, gl2.TEXTURE_MIN_FILTER, gl2.NEAREST);
1177
- gl2.texParameteri(gl2.TEXTURE_2D, gl2.TEXTURE_MAG_FILTER, gl2.NEAREST);
1178
- gl2.bindTexture(gl2.TEXTURE_2D, null);
1179
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, dstFbo);
1180
- gl2.framebufferTexture2D(gl2.FRAMEBUFFER, gl2.COLOR_ATTACHMENT0, gl2.TEXTURE_2D, dstTex, 0);
1181
- expect(gl2.checkFramebufferStatus(gl2.FRAMEBUFFER)).toBe(gl2.FRAMEBUFFER_COMPLETE);
1182
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, null);
1183
- const vs300 = `#version 300 es\nin vec2 a_pos;\nvoid main(){gl_Position=vec4(a_pos,0.,1.);}`;
1184
- const fs300 = `#version 300 es\nprecision mediump float;\nout vec4 c;\nvoid main(){c=vec4(0.,1.,0.,1.);}`;
1185
- const prog = makeProgram(gl, vs300, fs300);
1186
- expect(gl2.getProgramParameter(prog, gl2.LINK_STATUS)).toBeTruthy();
1187
- const vao = gl2.createVertexArray();
1188
- gl2.bindVertexArray(vao);
1189
- const vbo = gl2.createBuffer();
1190
- gl2.bindBuffer(gl2.ARRAY_BUFFER, vbo);
1191
- gl2.bufferData(gl2.ARRAY_BUFFER, new Float32Array([
1192
- -1,
1193
- -1,
1194
- 3,
1195
- -1,
1196
- -1,
1197
- 3
1198
- ]), gl2.STATIC_DRAW);
1199
- const aPos = gl2.getAttribLocation(prog, "a_pos");
1200
- gl2.enableVertexAttribArray(aPos);
1201
- gl2.vertexAttribPointer(aPos, 2, gl2.FLOAT, false, 0, 0);
1202
- gl2.bindVertexArray(null);
1203
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, srcFbo);
1204
- gl2.viewport(0, 0, W, H);
1205
- gl2.clearColor(0, 0, 1, 1);
1206
- gl2.clear(gl2.COLOR_BUFFER_BIT);
1207
- gl2.useProgram(prog);
1208
- gl2.bindVertexArray(vao);
1209
- gl2.drawArrays(gl2.TRIANGLES, 0, 3);
1210
- gl2.bindVertexArray(null);
1211
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
1212
- const READ_FRAMEBUFFER = 36008;
1213
- const DRAW_FRAMEBUFFER = 36009;
1214
- gl2.bindFramebuffer(READ_FRAMEBUFFER, srcFbo);
1215
- gl2.bindFramebuffer(DRAW_FRAMEBUFFER, dstFbo);
1216
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
1217
- gl2.clearBufferfv(gl2.COLOR, 0, [
1218
- 0,
1219
- 0,
1220
- 1,
1221
- 1
1222
- ]);
1223
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
1224
- gl2.blitFramebuffer(0, 0, W, H, 0, 0, W, H, gl2.COLOR_BUFFER_BIT, gl2.LINEAR);
1225
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
1226
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, dstFbo);
1227
- const pixels = new Uint8Array(4);
1228
- gl2.readPixels(0, 0, 1, 1, gl2.RGBA, gl2.UNSIGNED_BYTE, pixels);
1229
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
1230
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, srcFbo);
1231
- const srcPixels = new Uint8Array(4);
1232
- gl2.readPixels(0, 0, 1, 1, gl2.RGBA, gl2.UNSIGNED_BYTE, srcPixels);
1233
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
1234
- gl2.bindVertexArray(null);
1235
- gl2.deleteVertexArray(vao);
1236
- gl2.deleteBuffer(vbo);
1237
- gl2.deleteProgram(prog);
1238
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, srcFbo);
1239
- gl2.framebufferTexture2D(gl2.FRAMEBUFFER, gl2.COLOR_ATTACHMENT0, gl2.TEXTURE_2D, null, 0);
1240
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, dstFbo);
1241
- gl2.framebufferTexture2D(gl2.FRAMEBUFFER, gl2.COLOR_ATTACHMENT0, gl2.TEXTURE_2D, null, 0);
1242
- gl2.bindFramebuffer(gl2.FRAMEBUFFER, null);
1243
- gl2.deleteTexture(srcTex);
1244
- gl2.deleteTexture(dstTex);
1245
- gl2.deleteFramebuffer(srcFbo);
1246
- gl2.deleteFramebuffer(dstFbo);
1247
- expect(pixels[0]).toBe(0);
1248
- expect(pixels[1]).toBe(255);
1249
- expect(pixels[2]).toBe(0);
1250
- expect(pixels[3]).toBe(255);
1251
- expect(srcPixels[0]).toBe(0);
1252
- expect(srcPixels[1]).toBe(255);
1253
- expect(srcPixels[2]).toBe(0);
1254
- expect(srcPixels[3]).toBe(255);
1255
- });
1256
- });
1257
- await describe("WebGL2 bufferSubData with UNIFORM_BUFFER", async () => {
1258
- beforeEach(async () => {
1259
- glArea.make_current();
1260
- });
1261
- await it("bufferSubData with UNIFORM_BUFFER target returns NO_ERROR", async () => {
1262
- const UNIFORM_BUFFER = 35345;
1263
- const buf = gl2.createBuffer();
1264
- gl2.bindBuffer(UNIFORM_BUFFER, buf);
1265
- gl2.bufferData(UNIFORM_BUFFER, new Float32Array([
1266
- 1,
1267
- 0,
1268
- 0,
1269
- 1
1270
- ]), gl2.STATIC_DRAW);
1271
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
1272
- gl2.bufferSubData(UNIFORM_BUFFER, 0, new Float32Array([
1273
- 0,
1274
- 1,
1275
- 0,
1276
- 1
1277
- ]));
1278
- expect(gl2.getError()).toBe(gl2.NO_ERROR);
1279
- gl2.bindBuffer(UNIFORM_BUFFER, null);
1280
- gl2.deleteBuffer(buf);
1281
- });
1282
- });
1283
- win.destroy();
1284
- });
1285
- };
1286
-
1287
- //#endregion
1288
- export { webgl2_spec_default as default };
1
+ import{TEXTURE_FS_300 as e,TEXTURE_VS_300 as t,destroyTestFBO as n,drawTriangle as r,makeProgram as i,makeTestFBO as a,makeTestFBOFloat as o,makeTestFBOWithDepthTexture as s,readPixel as c}from"./test-utils.js";import{beforeEach as l,describe as u,expect as d,it as f,on as p}from"@gjsify/unit";import{WebGL2RenderingContext as m,WebGLBridge as h}from"@gjsify/webgl";import g from"@girs/glib-2.0";import _ from"@girs/gtk-4.0";var v=async()=>{await p(`Display`,async()=>{_.init();let p,v,y=new g.MainLoop(null,!1),b=new _.Window({});b.set_default_size(200,200),p=new h,p.onReady((e,t)=>{v=e.getContext(`webgl2`),y.quit()}),b.set_child(p),b.present();let x=g.timeout_add(g.PRIORITY_DEFAULT,1e4,()=>(y.quit(),g.SOURCE_REMOVE));if(y.run(),g.source_remove(x),!v){console.warn(`WebGL2 context not available — skipping WebGL2 tests`),b.destroy();return}p.make_current(),await u(`WebGL2 context`,async()=>{l(async()=>{p.make_current()}),await f(`getContext("webgl2") returns a WebGL2RenderingContext`,async()=>{d(v).toBeInstanceOf(m)}),await f(`VERSION is "WebGL 2.0"`,async()=>{d(v.getParameter(v.VERSION)).toBe(`WebGL 2.0`)}),await f(`SHADING_LANGUAGE_VERSION is "WebGL GLSL ES 3.00"`,async()=>{d(v.getParameter(v.SHADING_LANGUAGE_VERSION)).toBe(`WebGL GLSL ES 3.00`)}),await f(`drawingBufferWidth and drawingBufferHeight are positive`,async()=>{d(v.drawingBufferWidth>0).toBeTruthy(),d(v.drawingBufferHeight>0).toBeTruthy()}),await f(`getError() returns NO_ERROR initially`,async()=>{d(v.getError()).toBe(v.NO_ERROR)})}),await u(`GLSL ES 3.00 compilation`,async()=>{l(async()=>{p.make_current()});let e=[`#version 300 es`,`in vec2 position;`,`void main() { gl_Position = vec4(position, 0.0, 1.0); }`].join(`
2
+ `),t=[`#version 300 es`,`precision mediump float;`,`out vec4 fragColor;`,`void main() { fragColor = vec4(0.0, 1.0, 0.0, 1.0); }`].join(`
3
+ `);await f(`#version 300 es vertex shader compiles`,async()=>{let t=v.createShader(v.VERTEX_SHADER);v.shaderSource(t,e),v.compileShader(t),v.getShaderParameter(t,v.COMPILE_STATUS)||console.error(`VS300 log:`,v.getShaderInfoLog(t)),d(v.getShaderParameter(t,v.COMPILE_STATUS)).toBeTruthy(),v.deleteShader(t)}),await f(`#version 300 es fragment shader compiles`,async()=>{let e=v.createShader(v.FRAGMENT_SHADER);v.shaderSource(e,t),v.compileShader(e),v.getShaderParameter(e,v.COMPILE_STATUS)||console.error(`FS300 log:`,v.getShaderInfoLog(e)),d(v.getShaderParameter(e,v.COMPILE_STATUS)).toBeTruthy(),v.deleteShader(e)}),await f(`#version 300 es program links and renders green`,async()=>{let o=i(v,e,t);v.getProgramParameter(o,v.LINK_STATUS)||console.error(`Program300 log:`,v.getProgramInfoLog(o)),d(v.getProgramParameter(o,v.LINK_STATUS)).toBeTruthy();let s=a(v,4,4);v.clearColor(0,0,0,1),v.clear(v.COLOR_BUFFER_BIT),v.useProgram(o),r(v),d(v.getError()).toBe(v.NO_ERROR);let l=c(v,0,0);n(v,s),d(l[0]).toBe(0),d(l[1]).toBe(255),d(l[2]).toBe(0),d(l[3]).toBe(255),v.deleteProgram(o)})}),await u(`VAO`,async()=>{l(async()=>{p.make_current()}),await f(`createVertexArray returns a non-null object`,async()=>{let e=v.createVertexArray();d(e).not.toBeNull(),d(e).toBeDefined(),v.deleteVertexArray(e)}),await f(`isVertexArray is true after creation, false after deletion`,async()=>{let e=v.createVertexArray();v.bindVertexArray(e),d(v.isVertexArray(e)).toBeTruthy(),v.bindVertexArray(null),v.deleteVertexArray(e),d(v.isVertexArray(e)).toBeFalsy()}),await f(`VAO preserves vertex attribute state`,async()=>{let e=[`#version 300 es`,`in vec2 position;`,`void main() { gl_Position = vec4(position,0.0,1.0); }`].join(`
4
+ `),t=[`#version 300 es`,`precision mediump float;`,`out vec4 c;`,`void main() { c = vec4(0.0,1.0,0.0,1.0); }`].join(`
5
+ `),r=i(v,e,t);v.useProgram(r);let o=a(v,4,4),s=v.createVertexArray();v.bindVertexArray(s);let l=v.createBuffer();v.bindBuffer(v.ARRAY_BUFFER,l),v.bufferData(v.ARRAY_BUFFER,new Float32Array([-2,-2,-2,4,4,-2]),v.STREAM_DRAW),v.enableVertexAttribArray(0),v.vertexAttribPointer(0,2,v.FLOAT,!1,0,0),v.bindVertexArray(null),v.bindVertexArray(s),v.clearColor(0,0,0,1),v.clear(v.COLOR_BUFFER_BIT),v.drawArrays(v.TRIANGLES,0,3),d(v.getError()).toBe(v.NO_ERROR);let u=c(v,0,0);n(v,o),d(u[1]).toBe(255),v.bindVertexArray(null),v.deleteVertexArray(s),v.deleteBuffer(l),v.deleteProgram(r)})}),await u(`getBufferSubData`,async()=>{l(async()=>{p.make_current()}),await f(`reads back float data written with bufferData`,async()=>{let e=new Float32Array([1,2,3,4]),t=v.createBuffer();v.bindBuffer(v.ARRAY_BUFFER,t),v.bufferData(v.ARRAY_BUFFER,e,v.STATIC_READ);let n=new Float32Array(4);v.getBufferSubData(v.ARRAY_BUFFER,0,n),d(v.getError()).toBe(v.NO_ERROR),d(n[0]).toBe(1),d(n[1]).toBe(2),d(n[2]).toBe(3),d(n[3]).toBe(4),v.deleteBuffer(t)}),await f(`reads a sub-range correctly`,async()=>{let e=new Float32Array([10,20,30,40]),t=v.createBuffer();v.bindBuffer(v.ARRAY_BUFFER,t),v.bufferData(v.ARRAY_BUFFER,e,v.STATIC_READ);let n=new Float32Array(2);v.getBufferSubData(v.ARRAY_BUFFER,8,n),d(n[0]).toBe(30),d(n[1]).toBe(40),v.deleteBuffer(t)})}),await u(`sync objects`,async()=>{l(async()=>{p.make_current()}),await f(`fenceSync returns a non-null sync`,async()=>{let e=v.fenceSync(v.SYNC_GPU_COMMANDS_COMPLETE,0);d(e).not.toBeNull(),v.deleteSync(e),d(v.getError()).toBe(v.NO_ERROR)}),await f(`isSync is true before deletion, false after`,async()=>{let e=v.fenceSync(v.SYNC_GPU_COMMANDS_COMPLETE,0);d(v.isSync(e)).toBeTruthy(),v.deleteSync(e),d(v.isSync(e)).toBeFalsy()}),await f(`clientWaitSync returns TIMEOUT_EXPIRED or CONDITION_SATISFIED`,async()=>{let e=v.fenceSync(v.SYNC_GPU_COMMANDS_COMPLETE,0);v.flush();let t=v.clientWaitSync(e,0,0);d(t===v.TIMEOUT_EXPIRED||t===v.CONDITION_SATISFIED||t===v.ALREADY_SIGNALED).toBeTruthy(),v.deleteSync(e)})}),await u(`uint uniforms`,async()=>{l(async()=>{p.make_current()});let e=[`#version 300 es`,`in vec2 position;`,`void main() { gl_Position = vec4(position,0.0,1.0); }`].join(`
6
+ `),t=[`#version 300 es`,`precision mediump float;`,`uniform uint uR; uniform uint uG; uniform uint uB;`,`out vec4 c;`,`void main() { c = vec4(float(uR)/255.0, float(uG)/255.0, float(uB)/255.0, 1.0); }`].join(`
7
+ `);await f(`uniform1ui sets an unsigned int uniform`,async()=>{let n=i(v,e,t);d(v.getProgramParameter(n,v.LINK_STATUS)).toBeTruthy(),v.useProgram(n);let r=v.getUniformLocation(n,`uR`),a=v.getUniformLocation(n,`uG`),o=v.getUniformLocation(n,`uB`);v.uniform1ui(r,0),v.uniform1ui(a,255),v.uniform1ui(o,0),d(v.getError()).toBe(v.NO_ERROR),d(v.getUniform(n,r)).toBe(0),d(v.getUniform(n,a)).toBe(255),v.deleteProgram(n)}),await f(`uniform2ui, uniform3ui, uniform4ui set without error`,async()=>{let t=[`#version 300 es`,`precision mediump float;`,`uniform uvec2 u2; uniform uvec3 u3; uniform uvec4 u4;`,`out vec4 c;`,`void main() { c = vec4(float(u2.x), float(u3.x), float(u4.x), 1.0)/255.0; }`].join(`
8
+ `),n=i(v,e,t);v.useProgram(n),v.uniform2ui(v.getUniformLocation(n,`u2`),1,2),v.uniform3ui(v.getUniformLocation(n,`u3`),1,2,3),v.uniform4ui(v.getUniformLocation(n,`u4`),1,2,3,4),d(v.getError()).toBe(v.NO_ERROR),v.deleteProgram(n)})}),await u(`non-square matrix uniforms`,async()=>{l(async()=>{p.make_current()});let e=[`#version 300 es`,`in vec2 position;`,`void main() { gl_Position = vec4(position,0.0,1.0); }`].join(`
9
+ `);await f(`uniformMatrix2x3fv sets a mat2x3 without error`,async()=>{let t=[`#version 300 es`,`precision mediump float;`,`uniform mat2x3 m; out vec4 c;`,`void main() { c = vec4(m[0][0], m[0][1], m[0][2], 1.0); }`].join(`
10
+ `),n=i(v,e,t);v.useProgram(n),v.uniformMatrix2x3fv(v.getUniformLocation(n,`m`),!1,new Float32Array([1,0,0,0,1,0])),d(v.getError()).toBe(v.NO_ERROR),v.deleteProgram(n)}),await f(`uniformMatrix3x2fv sets a mat3x2 without error`,async()=>{let t=[`#version 300 es`,`precision mediump float;`,`uniform mat3x2 m; out vec4 c;`,`void main() { c = vec4(m[0][0], m[0][1], 0.0, 1.0); }`].join(`
11
+ `),n=i(v,e,t);v.useProgram(n),v.uniformMatrix3x2fv(v.getUniformLocation(n,`m`),!1,new Float32Array([1,0,0,1,0,0])),d(v.getError()).toBe(v.NO_ERROR),v.deleteProgram(n)})}),await u(`Uniform Buffer Objects`,async()=>{l(async()=>{p.make_current()}),await f(`getUniformBlockIndex + uniformBlockBinding works`,async()=>{let e=[`#version 300 es`,`layout(std140) uniform Params { vec4 color; };`,`in vec2 position;`,`out vec4 vColor;`,`void main() { vColor = color; gl_Position = vec4(position,0.0,1.0); }`].join(`
12
+ `),t=[`#version 300 es`,`precision mediump float;`,`in vec4 vColor; out vec4 c;`,`void main() { c = vColor; }`].join(`
13
+ `),o=i(v,e,t);d(v.getProgramParameter(o,v.LINK_STATUS)).toBeTruthy();let s=v.getUniformBlockIndex(o,`Params`);d(s).not.toBe(v.INVALID_INDEX),v.uniformBlockBinding(o,s,0),d(v.getError()).toBe(v.NO_ERROR);let l=v.createBuffer();v.bindBuffer(v.UNIFORM_BUFFER,l),v.bufferData(v.UNIFORM_BUFFER,new Float32Array([0,1,0,1]),v.STATIC_DRAW),v.bindBufferBase(v.UNIFORM_BUFFER,0,l);let u=a(v,4,4);v.useProgram(o),v.clearColor(0,0,0,1),v.clear(v.COLOR_BUFFER_BIT),r(v),d(v.getError()).toBe(v.NO_ERROR);let f=c(v,0,0);n(v,u),d(f[1]).toBe(255),v.deleteBuffer(l),v.deleteProgram(o)})}),await u(`instanced drawing`,async()=>{l(async()=>{p.make_current()}),await f(`drawArraysInstanced renders without error`,async()=>{let e=[`#version 300 es`,`in vec2 position;`,`void main() { gl_Position = vec4(position,0.0,1.0); }`].join(`
14
+ `),t=[`#version 300 es`,`precision mediump float;`,`out vec4 c;`,`void main() { c = vec4(0.0,1.0,0.0,1.0); }`].join(`
15
+ `),r=i(v,e,t);v.useProgram(r);let o=a(v,4,4),s=v.createBuffer();v.bindBuffer(v.ARRAY_BUFFER,s),v.bufferData(v.ARRAY_BUFFER,new Float32Array([-2,-2,-2,4,4,-2]),v.STREAM_DRAW),v.enableVertexAttribArray(0),v.vertexAttribPointer(0,2,v.FLOAT,!1,0,0),v.vertexAttribDivisor(0,0),v.clearColor(0,0,0,1),v.clear(v.COLOR_BUFFER_BIT),v.drawArraysInstanced(v.TRIANGLES,0,3,1),d(v.getError()).toBe(v.NO_ERROR);let l=c(v,0,0);n(v,o),d(l[1]).toBe(255),v.disableVertexAttribArray(0),v.deleteBuffer(s),v.deleteProgram(r)})}),await u(`drawBuffers (MRT)`,async()=>{l(async()=>{p.make_current()}),await f(`drawBuffers with 2 COLOR_ATTACHMENTs renders to both`,async()=>{let e=[`#version 300 es`,`in vec2 position;`,`void main() { gl_Position = vec4(position,0.0,1.0); }`].join(`
16
+ `),t=[`#version 300 es`,`precision mediump float;`,`layout(location=0) out vec4 c0;`,`layout(location=1) out vec4 c1;`,`void main() { c0 = vec4(1.0,0.0,0.0,1.0); c1 = vec4(0.0,0.0,1.0,1.0); }`].join(`
17
+ `),n=i(v,e,t);d(v.getProgramParameter(n,v.LINK_STATUS)).toBeTruthy();let a=v.createFramebuffer();v.bindFramebuffer(v.FRAMEBUFFER,a);let o=v.createTexture();v.bindTexture(v.TEXTURE_2D,o),v.texImage2D(v.TEXTURE_2D,0,v.RGBA,4,4,0,v.RGBA,v.UNSIGNED_BYTE,null),v.framebufferTexture2D(v.FRAMEBUFFER,v.COLOR_ATTACHMENT0,v.TEXTURE_2D,o,0);let s=v.createTexture();v.bindTexture(v.TEXTURE_2D,s),v.texImage2D(v.TEXTURE_2D,0,v.RGBA,4,4,0,v.RGBA,v.UNSIGNED_BYTE,null),v.framebufferTexture2D(v.FRAMEBUFFER,v.COLOR_ATTACHMENT1,v.TEXTURE_2D,s,0);let l=v.checkFramebufferStatus(v.FRAMEBUFFER);if(l!==v.FRAMEBUFFER_COMPLETE){console.warn(`MRT framebuffer not complete:`,l),v.bindFramebuffer(v.FRAMEBUFFER,null),v.deleteFramebuffer(a),v.deleteTexture(o),v.deleteTexture(s),v.deleteProgram(n);return}v.drawBuffers([v.COLOR_ATTACHMENT0,v.COLOR_ATTACHMENT1]),v.viewport(0,0,4,4),v.clearColor(0,0,0,1),v.clear(v.COLOR_BUFFER_BIT),v.useProgram(n),r(v),d(v.getError()).toBe(v.NO_ERROR),v.readBuffer(v.COLOR_ATTACHMENT0);let u=c(v,0,0);d(u[0]).toBe(255),d(u[1]).toBe(0),v.readBuffer(v.COLOR_ATTACHMENT1);let f=c(v,0,0);d(f[0]).toBe(0),d(f[2]).toBe(255),v.bindFramebuffer(v.FRAMEBUFFER,null),v.deleteFramebuffer(a),v.deleteTexture(o),v.deleteTexture(s),v.deleteProgram(n)})}),await u(`3D texture`,async()=>{l(async()=>{p.make_current()}),await f(`createTexture + TEXTURE_3D binding works`,async()=>{let e=v.createTexture();d(e).not.toBeNull(),v.bindTexture(v.TEXTURE_3D,e),d(v.getError()).toBe(v.NO_ERROR),v.deleteTexture(e)}),await f(`texImage3D uploads a 2×2×2 RGBA texture without error`,async()=>{let e=v.createTexture();v.bindTexture(v.TEXTURE_3D,e),v.texParameteri(v.TEXTURE_3D,v.TEXTURE_MIN_FILTER,v.NEAREST),v.texParameteri(v.TEXTURE_3D,v.TEXTURE_MAG_FILTER,v.NEAREST),v.texParameteri(v.TEXTURE_3D,v.TEXTURE_WRAP_S,v.CLAMP_TO_EDGE),v.texParameteri(v.TEXTURE_3D,v.TEXTURE_WRAP_T,v.CLAMP_TO_EDGE),v.texParameteri(v.TEXTURE_3D,v.TEXTURE_WRAP_R,v.CLAMP_TO_EDGE);let t=new Uint8Array(32).fill(128);v.texImage3D(v.TEXTURE_3D,0,v.RGBA,2,2,2,0,v.RGBA,v.UNSIGNED_BYTE,t),d(v.getError()).toBe(v.NO_ERROR),v.deleteTexture(e)}),await f(`texStorage3D allocates storage without error`,async()=>{let e=v.createTexture();v.bindTexture(v.TEXTURE_3D,e),v.texStorage3D(v.TEXTURE_3D,1,v.RGBA8,4,4,4),d(v.getError()).toBe(v.NO_ERROR),v.deleteTexture(e)})}),await u(`transform feedback`,async()=>{l(async()=>{p.make_current()}),await f(`captures vertex shader output via transform feedback`,async()=>{let e=[`#version 300 es`,`in float inVal;`,`out float outVal;`,`void main() { outVal = inVal * 2.0; gl_Position = vec4(0.0); }`].join(`
18
+ `),t=[`#version 300 es`,`precision mediump float;`,`out vec4 c;`,`void main() { c = vec4(0.0); }`].join(`
19
+ `),r=v.createShader(v.VERTEX_SHADER);v.shaderSource(r,e),v.compileShader(r),v.getShaderParameter(r,v.COMPILE_STATUS)||console.error(`TF VS:`,v.getShaderInfoLog(r));let i=v.createShader(v.FRAGMENT_SHADER);v.shaderSource(i,t),v.compileShader(i);let o=v.createProgram();v.attachShader(o,r),v.attachShader(o,i),v.transformFeedbackVaryings(o,[`outVal`],v.SEPARATE_ATTRIBS),v.linkProgram(o),v.getProgramParameter(o,v.LINK_STATUS)||console.error(`TF prog:`,v.getProgramInfoLog(o)),d(v.getProgramParameter(o,v.LINK_STATUS)).toBeTruthy();let s=v.createBuffer();v.bindBuffer(v.ARRAY_BUFFER,s),v.bufferData(v.ARRAY_BUFFER,new Float32Array([1,2,3,4]),v.STATIC_DRAW);let c=v.getAttribLocation(o,`inVal`);v.enableVertexAttribArray(c),v.vertexAttribPointer(c,1,v.FLOAT,!1,0,0);let l=v.createBuffer();v.bindBuffer(v.TRANSFORM_FEEDBACK_BUFFER,l),v.bufferData(v.TRANSFORM_FEEDBACK_BUFFER,16,v.STATIC_READ);let u=v.createTransformFeedback();v.bindTransformFeedback(v.TRANSFORM_FEEDBACK,u),v.bindBufferBase(v.TRANSFORM_FEEDBACK_BUFFER,0,l);let f=a(v,1,1);v.useProgram(o),v.enable(v.RASTERIZER_DISCARD),v.beginTransformFeedback(v.POINTS),v.drawArrays(v.POINTS,0,4),v.endTransformFeedback(),v.disable(v.RASTERIZER_DISCARD),d(v.getError()).toBe(v.NO_ERROR),v.bindTransformFeedback(v.TRANSFORM_FEEDBACK,null),n(v,f);let p=new Float32Array(4);v.bindBuffer(v.TRANSFORM_FEEDBACK_BUFFER,l),v.getBufferSubData(v.TRANSFORM_FEEDBACK_BUFFER,0,p),d(p[0]).toBe(2),d(p[1]).toBe(4),d(p[2]).toBe(6),d(p[3]).toBe(8),v.disableVertexAttribArray(c),v.deleteBuffer(s),v.deleteBuffer(l),v.deleteTransformFeedback(u),v.deleteProgram(o)})}),await u(`getStringi`,async()=>{l(async()=>{p.make_current()}),await f(`getStringi(EXTENSIONS, 0) returns a string`,async()=>{if(v.getParameter(v.NUM_EXTENSIONS)>0){let e=v.getStringi(v.EXTENSIONS,0);d(typeof e).toBe(`string`),d(e.length>0).toBeTruthy()}})}),await u(`WebGL2 extensions`,async()=>{l(async()=>{p.make_current()}),await f(`supports EXT_color_buffer_float`,async()=>{d(v.getExtension(`EXT_color_buffer_float`)).toBeTruthy()}),await f(`supports EXT_color_buffer_half_float`,async()=>{d(v.getExtension(`EXT_color_buffer_half_float`)).toBeTruthy()}),await f(`supports OES_texture_half_float`,async()=>{d(v.getExtension(`OES_texture_half_float`)).toBeTruthy()}),await f(`OES_texture_half_float exposes HALF_FLOAT_OES constant`,async()=>{let e=v.getExtension(`OES_texture_half_float`);d(e).toBeTruthy(),d(e.HALF_FLOAT_OES).toBe(36193)})}),await u(`WebGL2 renderbufferStorage formats`,async()=>{l(async()=>{p.make_current()});async function e(e){let t=v.createRenderbuffer();v.bindRenderbuffer(v.RENDERBUFFER,t),v.renderbufferStorage(v.RENDERBUFFER,e,64,64);let n=v.getError();return v.deleteRenderbuffer(t),n}await f(`accepts RGBA8 (0x8058)`,async()=>{d(await e(32856)).toBe(v.NO_ERROR)}),await f(`accepts DEPTH_COMPONENT24 (0x81A6)`,async()=>{d(await e(33190)).toBe(v.NO_ERROR)}),await f(`accepts DEPTH24_STENCIL8 (0x88F0)`,async()=>{d(await e(35056)).toBe(v.NO_ERROR)})}),await u(`WebGL2 float render target`,async()=>{l(async()=>{p.make_current()}),await f(`can create RGBA16F texture and attach to FBO`,async()=>{let e=v.createTexture();v.bindTexture(v.TEXTURE_2D,e),v.texImage2D(v.TEXTURE_2D,0,34842,64,64,0,v.RGBA,5131,null),d(v.getError()).toBe(v.NO_ERROR);let t=v.createFramebuffer();v.bindFramebuffer(v.FRAMEBUFFER,t),v.framebufferTexture2D(v.FRAMEBUFFER,v.COLOR_ATTACHMENT0,v.TEXTURE_2D,e,0),d(v.checkFramebufferStatus(v.FRAMEBUFFER)).toBe(v.FRAMEBUFFER_COMPLETE),v.clearColor(1,0,0,1),v.clear(v.COLOR_BUFFER_BIT),d(v.getError()).toBe(v.NO_ERROR),v.bindFramebuffer(v.FRAMEBUFFER,null),v.deleteFramebuffer(t),v.deleteTexture(e)}),await f(`can create RGBA8 renderbuffer FBO`,async()=>{let e=v.createRenderbuffer();v.bindRenderbuffer(v.RENDERBUFFER,e),v.renderbufferStorage(v.RENDERBUFFER,32856,64,64),d(v.getError()).toBe(v.NO_ERROR);let t=v.createFramebuffer();v.bindFramebuffer(v.FRAMEBUFFER,t),v.framebufferRenderbuffer(v.FRAMEBUFFER,v.COLOR_ATTACHMENT0,v.RENDERBUFFER,e),d(v.checkFramebufferStatus(v.FRAMEBUFFER)).toBe(v.FRAMEBUFFER_COMPLETE),v.clearColor(0,1,0,1),v.clear(v.COLOR_BUFFER_BIT),d(v.getError()).toBe(v.NO_ERROR),v.bindFramebuffer(v.FRAMEBUFFER,null),v.deleteFramebuffer(t),v.deleteRenderbuffer(e)})}),await u(`WebGL2 GLSL 1.0 compatibility`,async()=>{l(async()=>{p.make_current()}),await f(`compiles versionless shaders with attribute/varying`,async()=>{let e=i(v,`attribute vec2 position;
20
+ varying vec2 vUv;
21
+ void main() { vUv = position; gl_Position = vec4(position, 0.0, 1.0); }`,`precision mediump float;
22
+ varying vec2 vUv;
23
+ void main() { gl_FragColor = vec4(vUv, 0.0, 1.0); }`);d(e).toBeTruthy(),e&&(d(v.getProgramParameter(e,v.LINK_STATUS)).toBeTruthy(),v.deleteProgram(e))})}),await u(`WebGL2 textured rendering`,async()=>{l(async()=>{p.make_current()}),await f(`samples a 2x2 RGBA texture to produce red output`,async()=>{let o=a(v,4,4),s=v.createTexture();v.bindTexture(v.TEXTURE_2D,s);let l=new Uint8Array([255,0,0,255,255,0,0,255,255,0,0,255,255,0,0,255]);v.texImage2D(v.TEXTURE_2D,0,v.RGBA,2,2,0,v.RGBA,v.UNSIGNED_BYTE,l),v.texParameteri(v.TEXTURE_2D,v.TEXTURE_MIN_FILTER,v.NEAREST),v.texParameteri(v.TEXTURE_2D,v.TEXTURE_MAG_FILTER,v.NEAREST);let u=i(v,t,e);d(v.getProgramParameter(u,v.LINK_STATUS)).toBeTruthy(),v.useProgram(u);let f=v.getUniformLocation(u,`uTexture`);v.uniform1i(f,0),v.activeTexture(v.TEXTURE0),v.bindTexture(v.TEXTURE_2D,s),r(v),d(v.getError()).toBe(v.NO_ERROR);let p=c(v,0,0);d(p[0]).toBe(255),d(p[1]).toBe(0),d(p[2]).toBe(0),d(p[3]).toBe(255),v.deleteTexture(s),v.deleteProgram(u),n(v,o)})}),await u(`WebGL2 FBO chain (post-processing)`,async()=>{l(async()=>{p.make_current()}),await f(`renders green to FBO1, samples FBO1 texture into FBO2`,async()=>{let o=[`#version 300 es`,`in vec2 position;`,`void main() { gl_Position = vec4(position,0.0,1.0); }`].join(`
24
+ `),s=[`#version 300 es`,`precision mediump float;`,`out vec4 c;`,`void main() { c = vec4(0.0,1.0,0.0,1.0); }`].join(`
25
+ `),l=a(v,4,4);d(v.checkFramebufferStatus(v.FRAMEBUFFER)).toBe(v.FRAMEBUFFER_COMPLETE);let u=i(v,o,s);v.useProgram(u),v.clearColor(0,0,0,1),v.clear(v.COLOR_BUFFER_BIT),r(v),d(v.getError()).toBe(v.NO_ERROR),d(c(v,0,0)[1]).toBe(255);let f=a(v,4,4);d(v.checkFramebufferStatus(v.FRAMEBUFFER)).toBe(v.FRAMEBUFFER_COMPLETE);let p=i(v,t,e);v.useProgram(p);let m=v.getUniformLocation(p,`uTexture`);v.uniform1i(m,0),v.activeTexture(v.TEXTURE0),v.bindTexture(v.TEXTURE_2D,l.colorTex),v.clearColor(0,0,0,1),v.clear(v.COLOR_BUFFER_BIT),r(v),d(v.getError()).toBe(v.NO_ERROR),d(c(v,0,0)[1]).toBe(255),v.deleteProgram(u),v.deleteProgram(p),n(v,f),n(v,l)})}),await u(`WebGL2 half-float FBO chain`,async()=>{l(async()=>{p.make_current()}),await f(`renders green to RGBA16F FBO, samples into RGBA8 FBO`,async()=>{let s=[`#version 300 es`,`in vec2 position;`,`void main() { gl_Position = vec4(position,0.0,1.0); }`].join(`
26
+ `),l=[`#version 300 es`,`precision mediump float;`,`out vec4 c;`,`void main() { c = vec4(0.0,1.0,0.0,1.0); }`].join(`
27
+ `),u=o(v,4,4);d(v.checkFramebufferStatus(v.FRAMEBUFFER)).toBe(v.FRAMEBUFFER_COMPLETE);let f=i(v,s,l);v.useProgram(f),v.clearColor(0,0,0,1),v.clear(v.COLOR_BUFFER_BIT),r(v),d(v.getError()).toBe(v.NO_ERROR);let p=a(v,4,4),m=i(v,t,e);v.useProgram(m);let h=v.getUniformLocation(m,`uTexture`);v.uniform1i(h,0),v.activeTexture(v.TEXTURE0),v.bindTexture(v.TEXTURE_2D,u.colorTex),v.clearColor(0,0,0,1),v.clear(v.COLOR_BUFFER_BIT),r(v),d(v.getError()).toBe(v.NO_ERROR),d(c(v,0,0)[1]).toBe(255),v.deleteProgram(f),v.deleteProgram(m),n(v,p),v.bindFramebuffer(v.FRAMEBUFFER,u.fb),v.framebufferTexture2D(v.FRAMEBUFFER,v.COLOR_ATTACHMENT0,v.TEXTURE_2D,null,0),v.bindFramebuffer(v.FRAMEBUFFER,null),v.deleteTexture(u.colorTex),v.deleteFramebuffer(u.fb)})}),await u(`WebGL2 depth texture FBO`,async()=>{l(async()=>{p.make_current()}),await f(`creates FBO with depth texture and renders with depth test`,async()=>{let e=s(v,4,4);d(v.checkFramebufferStatus(v.FRAMEBUFFER)).toBe(v.FRAMEBUFFER_COMPLETE);let t=[`#version 300 es`,`in vec2 position;`,`uniform float uDepth;`,`void main() { gl_Position = vec4(position, uDepth, 1.0); }`].join(`
28
+ `),n=[`#version 300 es`,`precision mediump float;`,`uniform vec4 uColor;`,`out vec4 c;`,`void main() { c = uColor; }`].join(`
29
+ `),a=i(v,t,n);v.useProgram(a),v.enable(v.DEPTH_TEST),v.depthFunc(v.LESS),v.clearColor(0,0,0,1),v.clearDepth(1),v.clear(v.COLOR_BUFFER_BIT|v.DEPTH_BUFFER_BIT);let o=v.getUniformLocation(a,`uColor`),l=v.getUniformLocation(a,`uDepth`);v.uniform4f(o,1,0,0,1),v.uniform1f(l,.5),r(v),v.uniform4f(o,0,1,0,1),v.uniform1f(l,0),r(v),d(v.getError()).toBe(v.NO_ERROR),d(c(v,0,0)[1]).toBe(255),v.disable(v.DEPTH_TEST),v.bindFramebuffer(v.FRAMEBUFFER,e.fb),v.framebufferTexture2D(v.FRAMEBUFFER,v.COLOR_ATTACHMENT0,v.TEXTURE_2D,null,0),v.framebufferTexture2D(v.FRAMEBUFFER,v.DEPTH_ATTACHMENT,v.TEXTURE_2D,null,0),v.bindFramebuffer(v.FRAMEBUFFER,null),v.deleteTexture(e.colorTex),v.deleteTexture(e.depthTex),v.deleteFramebuffer(e.fb),v.deleteProgram(a)})}),await u(`WebGL2 generateMipmap`,async()=>{l(async()=>{p.make_current()}),await f(`generates mipmaps for a 4x4 texture without error`,async()=>{let o=v.createTexture();v.bindTexture(v.TEXTURE_2D,o);let s=new Uint8Array(64);for(let e=0;e<s.length;e+=4)s[e]=255,s[e+3]=255;v.texImage2D(v.TEXTURE_2D,0,v.RGBA,4,4,0,v.RGBA,v.UNSIGNED_BYTE,s),v.generateMipmap(v.TEXTURE_2D),d(v.getError()).toBe(v.NO_ERROR),v.texParameteri(v.TEXTURE_2D,v.TEXTURE_MIN_FILTER,v.LINEAR_MIPMAP_LINEAR),d(v.getError()).toBe(v.NO_ERROR);let l=a(v,4,4),u=i(v,t,e);v.useProgram(u),v.bindTexture(v.TEXTURE_2D,o),v.uniform1i(v.getUniformLocation(u,`uTexture`),0),r(v),d(v.getError()).toBe(v.NO_ERROR),d(c(v,0,0)[0]).toBeGreaterThan(200),v.deleteTexture(o),v.deleteProgram(u),n(v,l)})}),await u(`WebGL2 cubemap texture`,async()=>{l(async()=>{p.make_current()}),await f(`creates cubemap and verifies FBO attachment`,async()=>{let e=v.createTexture();v.bindTexture(v.TEXTURE_CUBE_MAP,e);let t=[v.TEXTURE_CUBE_MAP_POSITIVE_X,v.TEXTURE_CUBE_MAP_NEGATIVE_X,v.TEXTURE_CUBE_MAP_POSITIVE_Y,v.TEXTURE_CUBE_MAP_NEGATIVE_Y,v.TEXTURE_CUBE_MAP_POSITIVE_Z,v.TEXTURE_CUBE_MAP_NEGATIVE_Z],n=[[255,0,0,255],[0,255,0,255],[0,0,255,255],[255,255,0,255],[255,0,255,255],[0,255,255,255]];for(let e=0;e<6;e++)v.texImage2D(t[e],0,v.RGBA,1,1,0,v.RGBA,v.UNSIGNED_BYTE,new Uint8Array(n[e]));d(v.getError()).toBe(v.NO_ERROR),v.texParameteri(v.TEXTURE_CUBE_MAP,v.TEXTURE_MIN_FILTER,v.NEAREST),v.texParameteri(v.TEXTURE_CUBE_MAP,v.TEXTURE_MAG_FILTER,v.NEAREST);let r=v.createFramebuffer();v.bindFramebuffer(v.FRAMEBUFFER,r),v.framebufferTexture2D(v.FRAMEBUFFER,v.COLOR_ATTACHMENT0,v.TEXTURE_CUBE_MAP_POSITIVE_X,e,0),d(v.checkFramebufferStatus(v.FRAMEBUFFER)).toBe(v.FRAMEBUFFER_COMPLETE),v.viewport(0,0,1,1),v.clearColor(0,1,1,1),v.clear(v.COLOR_BUFFER_BIT),d(v.getError()).toBe(v.NO_ERROR);let i=c(v,0,0);d(i[0]).toBe(0),d(i[1]).toBe(255),d(i[2]).toBe(255),v.bindFramebuffer(v.FRAMEBUFFER,null),v.deleteFramebuffer(r),v.deleteTexture(e)})}),await u(`WebGL2 READ_FRAMEBUFFER / DRAW_FRAMEBUFFER`,async()=>{l(async()=>{p.make_current()}),await f(`bindFramebuffer accepts READ_FRAMEBUFFER and DRAW_FRAMEBUFFER`,async()=>{let e=v.createFramebuffer(),t=v.createFramebuffer();v.bindFramebuffer(36008,e),v.bindFramebuffer(36009,t),d(v.getError()).toBe(v.NO_ERROR),d(v.getParameter(36010)).toBe(e),d(v.getParameter(36006)).toBe(t),v.bindFramebuffer(v.FRAMEBUFFER,null),d(v.getError()).toBe(v.NO_ERROR),v.deleteFramebuffer(e),v.deleteFramebuffer(t)})}),await u(`WebGL2 RGBA8 FBO (sized internal format)`,async()=>{l(async()=>{p.make_current()}),await f(`renders solid color to RGBA8 FBO, readPixels confirms result`,async()=>{let e=v.createFramebuffer(),t=v.createTexture();v.bindTexture(v.TEXTURE_2D,t),v.texImage2D(v.TEXTURE_2D,0,32856,4,4,0,v.RGBA,v.UNSIGNED_BYTE,null),v.texParameteri(v.TEXTURE_2D,v.TEXTURE_MIN_FILTER,v.NEAREST),v.texParameteri(v.TEXTURE_2D,v.TEXTURE_MAG_FILTER,v.NEAREST),v.bindTexture(v.TEXTURE_2D,null),v.bindFramebuffer(v.FRAMEBUFFER,e),v.framebufferTexture2D(v.FRAMEBUFFER,v.COLOR_ATTACHMENT0,v.TEXTURE_2D,t,0),d(v.checkFramebufferStatus(v.FRAMEBUFFER)).toBe(v.FRAMEBUFFER_COMPLETE),v.viewport(0,0,4,4),v.clearColor(0,1,0,1),v.clear(v.COLOR_BUFFER_BIT),d(v.getError()).toBe(v.NO_ERROR);let n=new Uint8Array(4);v.readPixels(0,0,1,1,v.RGBA,v.UNSIGNED_BYTE,n),d(v.getError()).toBe(v.NO_ERROR),d(n[0]).toBe(0),d(n[1]).toBe(255),d(n[2]).toBe(0),d(n[3]).toBe(255),v.bindFramebuffer(v.FRAMEBUFFER,e),v.framebufferTexture2D(v.FRAMEBUFFER,v.COLOR_ATTACHMENT0,v.TEXTURE_2D,null,0),v.bindFramebuffer(v.FRAMEBUFFER,null),v.deleteTexture(t),v.deleteFramebuffer(e)})}),await u(`WebGL2 RGBA16F FBO draw + FLOAT readPixels`,async()=>{l(async()=>{p.make_current()}),await f(`renders red triangle to RGBA16F FBO, reads back as FLOAT`,async()=>{let e=v,t=i(e,`#version 300 es
30
+ in vec2 position;
31
+ void main(){gl_Position=vec4(position,0.,1.);}`,`#version 300 es
32
+ precision mediump float;
33
+ out vec4 c;
34
+ void main(){c=vec4(1.,0.,0.,1.);}`);d(v.getProgramParameter(t,v.LINK_STATUS)).toBeTruthy();let n=o(v);v.useProgram(t),r(e),d(v.getError()).toBe(v.NO_ERROR);let a=new Float32Array(4);v.readPixels(0,0,1,1,v.RGBA,v.FLOAT,a),d(v.getError()).toBe(v.NO_ERROR),d(Math.abs(a[0]-1)<.01).toBeTruthy(),d(Math.abs(a[1]-0)<.01).toBeTruthy(),d(Math.abs(a[2]-0)<.01).toBeTruthy(),d(Math.abs(a[3]-1)<.01).toBeTruthy(),v.useProgram(null),v.bindFramebuffer(v.FRAMEBUFFER,n.fb),v.framebufferTexture2D(v.FRAMEBUFFER,v.COLOR_ATTACHMENT0,v.TEXTURE_2D,null,0),v.bindFramebuffer(v.FRAMEBUFFER,null),v.deleteTexture(n.colorTex),v.deleteFramebuffer(n.fb),v.deleteProgram(t)})}),await u(`WebGL2 MRT — writes to COLOR_ATTACHMENT0 and COLOR_ATTACHMENT1`,async()=>{l(async()=>{p.make_current()}),await f(`MRT shader writes different colors to CA0 and CA1`,async()=>{let e=v,t=i(e,`#version 300 es
35
+ in vec2 position;
36
+ void main(){gl_Position=vec4(position,0.,1.);}`,`#version 300 es
37
+ precision mediump float;
38
+ layout(location=0) out vec4 c0;
39
+ layout(location=1) out vec4 c1;
40
+ void main(){c0=vec4(1.,0.,0.,1.);c1=vec4(0.,0.,1.,1.);}`);d(v.getProgramParameter(t,v.LINK_STATUS)).toBeTruthy();let n=v.createFramebuffer(),a=v.createTexture(),o=v.createTexture();for(let[e,t]of[[a,v.COLOR_ATTACHMENT0],[o,v.COLOR_ATTACHMENT0+1]])v.bindTexture(v.TEXTURE_2D,e),v.texImage2D(v.TEXTURE_2D,0,v.RGBA,4,4,0,v.RGBA,v.UNSIGNED_BYTE,null),v.texParameteri(v.TEXTURE_2D,v.TEXTURE_MIN_FILTER,v.NEAREST),v.texParameteri(v.TEXTURE_2D,v.TEXTURE_MAG_FILTER,v.NEAREST),v.bindTexture(v.TEXTURE_2D,null),v.bindFramebuffer(v.FRAMEBUFFER,n),v.framebufferTexture2D(v.FRAMEBUFFER,t,v.TEXTURE_2D,e,0);d(v.checkFramebufferStatus(v.FRAMEBUFFER)).toBe(v.FRAMEBUFFER_COMPLETE),v.viewport(0,0,4,4),v.drawBuffers([v.COLOR_ATTACHMENT0,v.COLOR_ATTACHMENT0+1]),v.useProgram(t),r(e),d(v.getError()).toBe(v.NO_ERROR);let s=v.createFramebuffer();v.bindFramebuffer(v.FRAMEBUFFER,s),v.framebufferTexture2D(v.FRAMEBUFFER,v.COLOR_ATTACHMENT0,v.TEXTURE_2D,a,0);let c=new Uint8Array(4);v.readPixels(0,0,1,1,v.RGBA,v.UNSIGNED_BYTE,c),d(c[0]).toBe(255),d(c[1]).toBe(0),d(c[2]).toBe(0);let l=v.createFramebuffer();v.bindFramebuffer(v.FRAMEBUFFER,l),v.framebufferTexture2D(v.FRAMEBUFFER,v.COLOR_ATTACHMENT0,v.TEXTURE_2D,o,0);let u=new Uint8Array(4);v.readPixels(0,0,1,1,v.RGBA,v.UNSIGNED_BYTE,u),d(u[0]).toBe(0),d(u[1]).toBe(0),d(u[2]).toBe(255),v.useProgram(null),v.bindFramebuffer(v.FRAMEBUFFER,null),v.deleteTexture(a),v.deleteTexture(o),v.deleteFramebuffer(n),v.deleteFramebuffer(s),v.deleteFramebuffer(l),v.deleteProgram(t)})}),await u(`WebGL2 Excalibur-style VAO draw + READ/DRAW blitToScreen`,async()=>{l(async()=>{p.make_current()}),await f(`VAO draw to source FBO survives clearBufferfv+blit pipeline`,async()=>{let e=v,t=v.createFramebuffer(),n=v.createTexture();v.bindTexture(v.TEXTURE_2D,n),v.texImage2D(v.TEXTURE_2D,0,v.RGBA,4,4,0,v.RGBA,v.UNSIGNED_BYTE,null),v.texParameteri(v.TEXTURE_2D,v.TEXTURE_MIN_FILTER,v.NEAREST),v.texParameteri(v.TEXTURE_2D,v.TEXTURE_MAG_FILTER,v.NEAREST),v.texParameteri(v.TEXTURE_2D,v.TEXTURE_WRAP_S,v.CLAMP_TO_EDGE),v.texParameteri(v.TEXTURE_2D,v.TEXTURE_WRAP_T,v.CLAMP_TO_EDGE),v.bindTexture(v.TEXTURE_2D,null),v.bindFramebuffer(v.FRAMEBUFFER,t),v.framebufferTexture2D(v.FRAMEBUFFER,v.COLOR_ATTACHMENT0,v.TEXTURE_2D,n,0),d(v.checkFramebufferStatus(v.FRAMEBUFFER)).toBe(v.FRAMEBUFFER_COMPLETE);let r=v.createFramebuffer(),a=v.createTexture();v.bindTexture(v.TEXTURE_2D,a),v.texImage2D(v.TEXTURE_2D,0,v.RGBA,4,4,0,v.RGBA,v.UNSIGNED_BYTE,null),v.texParameteri(v.TEXTURE_2D,v.TEXTURE_MIN_FILTER,v.NEAREST),v.texParameteri(v.TEXTURE_2D,v.TEXTURE_MAG_FILTER,v.NEAREST),v.bindTexture(v.TEXTURE_2D,null),v.bindFramebuffer(v.FRAMEBUFFER,r),v.framebufferTexture2D(v.FRAMEBUFFER,v.COLOR_ATTACHMENT0,v.TEXTURE_2D,a,0),d(v.checkFramebufferStatus(v.FRAMEBUFFER)).toBe(v.FRAMEBUFFER_COMPLETE),v.bindFramebuffer(v.FRAMEBUFFER,null);let o=i(e,`#version 300 es
41
+ in vec2 a_pos;
42
+ void main(){gl_Position=vec4(a_pos,0.,1.);}`,`#version 300 es
43
+ precision mediump float;
44
+ out vec4 c;
45
+ void main(){c=vec4(0.,1.,0.,1.);}`);d(v.getProgramParameter(o,v.LINK_STATUS)).toBeTruthy();let s=v.createVertexArray();v.bindVertexArray(s);let c=v.createBuffer();v.bindBuffer(v.ARRAY_BUFFER,c),v.bufferData(v.ARRAY_BUFFER,new Float32Array([-1,-1,3,-1,-1,3]),v.STATIC_DRAW);let l=v.getAttribLocation(o,`a_pos`);v.enableVertexAttribArray(l),v.vertexAttribPointer(l,2,v.FLOAT,!1,0,0),v.bindVertexArray(null),v.bindFramebuffer(v.FRAMEBUFFER,t),v.viewport(0,0,4,4),v.clearColor(0,0,1,1),v.clear(v.COLOR_BUFFER_BIT),v.useProgram(o),v.bindVertexArray(s),v.drawArrays(v.TRIANGLES,0,3),v.bindVertexArray(null),d(v.getError()).toBe(v.NO_ERROR),v.bindFramebuffer(36008,t),v.bindFramebuffer(36009,r),d(v.getError()).toBe(v.NO_ERROR),v.clearBufferfv(v.COLOR,0,[0,0,1,1]),d(v.getError()).toBe(v.NO_ERROR),v.blitFramebuffer(0,0,4,4,0,0,4,4,v.COLOR_BUFFER_BIT,v.LINEAR),d(v.getError()).toBe(v.NO_ERROR),v.bindFramebuffer(v.FRAMEBUFFER,r);let u=new Uint8Array(4);v.readPixels(0,0,1,1,v.RGBA,v.UNSIGNED_BYTE,u),d(v.getError()).toBe(v.NO_ERROR),v.bindFramebuffer(v.FRAMEBUFFER,t);let f=new Uint8Array(4);v.readPixels(0,0,1,1,v.RGBA,v.UNSIGNED_BYTE,f),d(v.getError()).toBe(v.NO_ERROR),v.bindVertexArray(null),v.deleteVertexArray(s),v.deleteBuffer(c),v.deleteProgram(o),v.bindFramebuffer(v.FRAMEBUFFER,t),v.framebufferTexture2D(v.FRAMEBUFFER,v.COLOR_ATTACHMENT0,v.TEXTURE_2D,null,0),v.bindFramebuffer(v.FRAMEBUFFER,r),v.framebufferTexture2D(v.FRAMEBUFFER,v.COLOR_ATTACHMENT0,v.TEXTURE_2D,null,0),v.bindFramebuffer(v.FRAMEBUFFER,null),v.deleteTexture(n),v.deleteTexture(a),v.deleteFramebuffer(t),v.deleteFramebuffer(r),d(u[0]).toBe(0),d(u[1]).toBe(255),d(u[2]).toBe(0),d(u[3]).toBe(255),d(f[0]).toBe(0),d(f[1]).toBe(255),d(f[2]).toBe(0),d(f[3]).toBe(255)})}),await u(`WebGL2 bufferSubData with UNIFORM_BUFFER`,async()=>{l(async()=>{p.make_current()}),await f(`bufferSubData with UNIFORM_BUFFER target returns NO_ERROR`,async()=>{let e=35345,t=v.createBuffer();v.bindBuffer(e,t),v.bufferData(e,new Float32Array([1,0,0,1]),v.STATIC_DRAW),d(v.getError()).toBe(v.NO_ERROR),v.bufferSubData(e,0,new Float32Array([0,1,0,1])),d(v.getError()).toBe(v.NO_ERROR),v.bindBuffer(e,null),v.deleteBuffer(t)})}),b.destroy()})};export{v as default};