@gjsify/webgl 0.0.4 → 0.1.0

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 (149) hide show
  1. package/README.md +78 -6
  2. package/lib/esm/canvas-webgl-widget.js +116 -0
  3. package/lib/esm/html-canvas-element.js +31 -50
  4. package/lib/esm/index.js +7 -0
  5. package/lib/esm/index.spec.js +629 -120
  6. package/lib/esm/test-utils.js +87 -0
  7. package/lib/esm/test.js +5 -1
  8. package/lib/esm/utils.js +1 -1
  9. package/lib/esm/webgl-active-info.js +2 -2
  10. package/lib/esm/webgl-buffer.js +2 -3
  11. package/lib/esm/webgl-context-attributes.js +2 -3
  12. package/lib/esm/webgl-framebuffer.js +3 -4
  13. package/lib/esm/webgl-program.js +2 -3
  14. package/lib/esm/webgl-query.js +15 -0
  15. package/lib/esm/webgl-renderbuffer.js +2 -3
  16. package/lib/esm/webgl-rendering-context.js +105 -101
  17. package/lib/esm/webgl-sampler.js +15 -0
  18. package/lib/esm/webgl-shader-precision-format.js +2 -3
  19. package/lib/esm/webgl-shader.js +2 -3
  20. package/lib/esm/webgl-sync.js +15 -0
  21. package/lib/esm/webgl-texture.js +2 -3
  22. package/lib/esm/webgl-transform-feedback.js +15 -0
  23. package/lib/esm/webgl-uniform-location.js +2 -3
  24. package/lib/esm/webgl-vertex-array-object.js +21 -0
  25. package/lib/esm/webgl2-rendering-context.js +591 -0
  26. package/lib/esm/webgl2.spec.js +578 -0
  27. package/lib/types/canvas-webgl-widget.d.ts +509 -0
  28. package/lib/types/extensions/ext-blend-minmax.d.ts +2 -2
  29. package/lib/types/extensions/ext-texture-filter-anisotropic.d.ts +2 -2
  30. package/lib/types/extensions/oes-element-index-unit.d.ts +2 -2
  31. package/lib/types/extensions/oes-standard-derivatives.d.ts +2 -2
  32. package/lib/types/extensions/oes-texture-float-linear.d.ts +2 -2
  33. package/lib/types/extensions/oes-texture-float.d.ts +2 -2
  34. package/lib/types/extensions/stackgl-destroy-context.d.ts +3 -3
  35. package/lib/types/extensions/stackgl-resize-drawing-buffer.d.ts +3 -3
  36. package/lib/types/html-canvas-element.d.ts +19 -31
  37. package/lib/types/index.d.ts +7 -0
  38. package/lib/types/test-utils.d.ts +45 -0
  39. package/lib/types/types/extension.d.ts +2 -2
  40. package/lib/types/utils.d.ts +14 -15
  41. package/lib/types/webgl-active-info.d.ts +1 -2
  42. package/lib/types/webgl-buffer.d.ts +5 -6
  43. package/lib/types/webgl-context-attributes.d.ts +1 -2
  44. package/lib/types/webgl-framebuffer.d.ts +4 -5
  45. package/lib/types/webgl-program.d.ts +4 -5
  46. package/lib/types/webgl-query.d.ts +7 -0
  47. package/lib/types/webgl-renderbuffer.d.ts +4 -5
  48. package/lib/types/webgl-rendering-context.d.ts +15 -13
  49. package/lib/types/webgl-sampler.d.ts +7 -0
  50. package/lib/types/webgl-shader-precision-format.d.ts +1 -2
  51. package/lib/types/webgl-shader.d.ts +4 -5
  52. package/lib/types/webgl-sync.d.ts +7 -0
  53. package/lib/types/webgl-texture-unit.d.ts +3 -3
  54. package/lib/types/webgl-texture.d.ts +6 -7
  55. package/lib/types/webgl-transform-feedback.d.ts +7 -0
  56. package/lib/types/webgl-uniform-location.d.ts +3 -4
  57. package/lib/types/webgl-vertex-array-object.d.ts +9 -0
  58. package/lib/types/webgl-vertex-attribute.d.ts +5 -5
  59. package/lib/types/webgl2-rendering-context.d.ts +124 -0
  60. package/package.json +27 -26
  61. package/prebuilds/linux-aarch64/Gwebgl-0.1.typelib +0 -0
  62. package/prebuilds/linux-aarch64/libgwebgl.so +0 -0
  63. package/prebuilds/linux-x86_64/Gwebgl-0.1.typelib +0 -0
  64. package/prebuilds/linux-x86_64/libgwebgl.so +0 -0
  65. package/lib/cjs/@types/glsl-tokenizer/index.d.js +0 -0
  66. package/lib/cjs/extensions/ext-blend-minmax.js +0 -18
  67. package/lib/cjs/extensions/ext-texture-filter-anisotropic.js +0 -18
  68. package/lib/cjs/extensions/oes-element-index-unit.js +0 -14
  69. package/lib/cjs/extensions/oes-standard-derivatives.js +0 -17
  70. package/lib/cjs/extensions/oes-texture-float-linear.js +0 -14
  71. package/lib/cjs/extensions/oes-texture-float.js +0 -14
  72. package/lib/cjs/extensions/stackgl-destroy-context.js +0 -12
  73. package/lib/cjs/extensions/stackgl-resize-drawing-buffer.js +0 -12
  74. package/lib/cjs/html-canvas-element.js +0 -70
  75. package/lib/cjs/index.js +0 -18
  76. package/lib/cjs/index.spec.js +0 -146
  77. package/lib/cjs/linkable.js +0 -50
  78. package/lib/cjs/test.js +0 -3
  79. package/lib/cjs/types/constructor.js +0 -0
  80. package/lib/cjs/types/extension.js +0 -0
  81. package/lib/cjs/types/index.js +0 -5
  82. package/lib/cjs/types/typed-array.js +0 -0
  83. package/lib/cjs/types/webgl-constants.js +0 -0
  84. package/lib/cjs/types/webgl-context-attribute-options.js +0 -0
  85. package/lib/cjs/utils.js +0 -212
  86. package/lib/cjs/webgl-active-info.js +0 -10
  87. package/lib/cjs/webgl-buffer.js +0 -18
  88. package/lib/cjs/webgl-context-attributes.js +0 -24
  89. package/lib/cjs/webgl-drawing-buffer-wrapper.js +0 -10
  90. package/lib/cjs/webgl-framebuffer.js +0 -109
  91. package/lib/cjs/webgl-program.js +0 -26
  92. package/lib/cjs/webgl-renderbuffer.js +0 -24
  93. package/lib/cjs/webgl-rendering-context.js +0 -3343
  94. package/lib/cjs/webgl-shader-precision-format.js +0 -11
  95. package/lib/cjs/webgl-shader.js +0 -23
  96. package/lib/cjs/webgl-texture-unit.js +0 -12
  97. package/lib/cjs/webgl-texture.js +0 -22
  98. package/lib/cjs/webgl-uniform-location.js +0 -15
  99. package/lib/cjs/webgl-vertex-attribute.js +0 -147
  100. package/meson.build +0 -39
  101. package/src/test/app.vala +0 -60
  102. package/src/ts/@types/glsl-tokenizer/index.d.ts +0 -18
  103. package/src/ts/extensions/angle-instanced-arrays.ts.off +0 -232
  104. package/src/ts/extensions/ext-blend-minmax.ts +0 -18
  105. package/src/ts/extensions/ext-texture-filter-anisotropic.ts +0 -18
  106. package/src/ts/extensions/oes-element-index-unit.ts +0 -14
  107. package/src/ts/extensions/oes-standard-derivatives.ts +0 -17
  108. package/src/ts/extensions/oes-texture-float-linear.ts +0 -14
  109. package/src/ts/extensions/oes-texture-float.ts +0 -14
  110. package/src/ts/extensions/oes-vertex-array-object.ts.off +0 -128
  111. package/src/ts/extensions/stackgl-destroy-context.ts +0 -12
  112. package/src/ts/extensions/stackgl-resize-drawing-buffer.ts +0 -14
  113. package/src/ts/extensions/webgl-draw-buffers.ts.off +0 -107
  114. package/src/ts/html-canvas-element.ts +0 -98
  115. package/src/ts/index.spec.ts +0 -186
  116. package/src/ts/index.ts +0 -21
  117. package/src/ts/linkable.ts +0 -55
  118. package/src/ts/test.ts +0 -6
  119. package/src/ts/types/constructor.ts +0 -3
  120. package/src/ts/types/extension.ts +0 -3
  121. package/src/ts/types/index.ts +0 -26
  122. package/src/ts/types/typed-array.ts +0 -1
  123. package/src/ts/types/webgl-constants.ts +0 -300
  124. package/src/ts/types/webgl-context-attribute-options.ts +0 -12
  125. package/src/ts/utils.ts +0 -266
  126. package/src/ts/webgl-active-info.ts +0 -13
  127. package/src/ts/webgl-buffer.ts +0 -21
  128. package/src/ts/webgl-context-attributes.ts +0 -24
  129. package/src/ts/webgl-drawing-buffer-wrapper.ts +0 -10
  130. package/src/ts/webgl-framebuffer.ts +0 -133
  131. package/src/ts/webgl-program.ts +0 -30
  132. package/src/ts/webgl-renderbuffer.ts +0 -28
  133. package/src/ts/webgl-rendering-context.ts +0 -4050
  134. package/src/ts/webgl-shader-precision-format.ts +0 -12
  135. package/src/ts/webgl-shader.ts +0 -29
  136. package/src/ts/webgl-texture-unit.ts +0 -16
  137. package/src/ts/webgl-texture.ts +0 -27
  138. package/src/ts/webgl-uniform-location.ts +0 -18
  139. package/src/ts/webgl-vertex-attribute.ts +0 -169
  140. package/src/vala/handle-types.vala +0 -23
  141. package/src/vala/webgl-rendering-context-base.vala +0 -1265
  142. package/src/vala/webgl-rendering-context.vala +0 -265
  143. package/src/vapi/epoxy.vapi +0 -14558
  144. package/src/vapi/glesv2.vapi +0 -670
  145. package/test.gjs.js +0 -39937
  146. package/test.gjs.js.meta.json +0 -1
  147. package/tmp/.tsbuildinfo +0 -1
  148. package/tsconfig.json +0 -38
  149. package/tsconfig.types.json +0 -7
@@ -1,144 +1,653 @@
1
- import { describe, it, expect } from "@gjsify/unit";
2
- import { HTMLCanvasElement } from "@gjsify/webgl";
1
+ import { describe, it, expect, beforeEach, on } from "@gjsify/unit";
2
+ import { CanvasWebGLWidget } from "@gjsify/webgl";
3
+ import {
4
+ makeProgram,
5
+ drawTriangle,
6
+ readPixel,
7
+ pixelClose,
8
+ makeTestFBO,
9
+ destroyTestFBO,
10
+ makeTestFBOWithDepth,
11
+ destroyTestFBOWithDepth
12
+ } from "./test-utils.js";
3
13
  import GLib from "@girs/glib-2.0";
4
14
  import Gtk from "@girs/gtk-4.0";
5
- import Gio from "@girs/gio-2.0";
6
15
  const GL_CONSTANT_NAMES = ["ACTIVE_ATTRIBUTES", "ACTIVE_TEXTURE", "ACTIVE_UNIFORMS", "ALIASED_LINE_WIDTH_RANGE", "ALIASED_POINT_SIZE_RANGE", "ALPHA", "ALPHA_BITS", "ALWAYS", "ARRAY_BUFFER", "ARRAY_BUFFER_BINDING", "ATTACHED_SHADERS", "BACK", "BLEND", "BLEND_COLOR", "BLEND_DST_ALPHA", "BLEND_DST_RGB", "BLEND_EQUATION", "BLEND_EQUATION_ALPHA", "BLEND_EQUATION_RGB", "BLEND_SRC_ALPHA", "BLEND_SRC_RGB", "BLUE_BITS", "BOOL", "BOOL_VEC2", "BOOL_VEC3", "BOOL_VEC4", "BROWSER_DEFAULT_WEBGL", "BUFFER_SIZE", "BUFFER_USAGE", "BYTE", "CCW", "CLAMP_TO_EDGE", "COLOR_ATTACHMENT0", "COLOR_BUFFER_BIT", "COLOR_CLEAR_VALUE", "COLOR_WRITEMASK", "COMPILE_STATUS", "COMPRESSED_TEXTURE_FORMATS", "CONSTANT_ALPHA", "CONSTANT_COLOR", "CONTEXT_LOST_WEBGL", "CULL_FACE", "CULL_FACE_MODE", "CURRENT_PROGRAM", "CURRENT_VERTEX_ATTRIB", "CW", "DECR", "DECR_WRAP", "DELETE_STATUS", "DEPTH_ATTACHMENT", "DEPTH_BITS", "DEPTH_BUFFER_BIT", "DEPTH_CLEAR_VALUE", "DEPTH_COMPONENT", "DEPTH_COMPONENT16", "DEPTH_FUNC", "DEPTH_RANGE", "DEPTH_STENCIL", "DEPTH_STENCIL_ATTACHMENT", "DEPTH_TEST", "DEPTH_WRITEMASK", "DITHER", "DONT_CARE", "DST_ALPHA", "DST_COLOR", "DYNAMIC_DRAW", "ELEMENT_ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER_BINDING", "EQUAL", "FASTEST", "FLOAT", "FLOAT_MAT2", "FLOAT_MAT3", "FLOAT_MAT4", "FLOAT_VEC2", "FLOAT_VEC3", "FLOAT_VEC4", "FRAGMENT_SHADER", "FRAMEBUFFER", "FRAMEBUFFER_ATTACHMENT_OBJECT_NAME", "FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE", "FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE", "FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL", "FRAMEBUFFER_BINDING", "FRAMEBUFFER_COMPLETE", "FRAMEBUFFER_INCOMPLETE_ATTACHMENT", "FRAMEBUFFER_INCOMPLETE_DIMENSIONS", "FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT", "FRAMEBUFFER_UNSUPPORTED", "FRONT", "FRONT_AND_BACK", "FRONT_FACE", "FUNC_ADD", "FUNC_REVERSE_SUBTRACT", "FUNC_SUBTRACT", "GENERATE_MIPMAP_HINT", "GEQUAL", "GREATER", "GREEN_BITS", "HIGH_FLOAT", "HIGH_INT", "IMPLEMENTATION_COLOR_READ_FORMAT", "IMPLEMENTATION_COLOR_READ_TYPE", "INCR", "INCR_WRAP", "INT", "INT_VEC2", "INT_VEC3", "INT_VEC4", "INVALID_ENUM", "INVALID_FRAMEBUFFER_OPERATION", "INVALID_OPERATION", "INVALID_VALUE", "INVERT", "KEEP", "LEQUAL", "LESS", "LINEAR", "LINEAR_MIPMAP_LINEAR", "LINEAR_MIPMAP_NEAREST", "LINES", "LINE_LOOP", "LINE_STRIP", "LINE_WIDTH", "LINK_STATUS", "LOW_FLOAT", "LOW_INT", "LUMINANCE", "LUMINANCE_ALPHA", "MAX_COMBINED_TEXTURE_IMAGE_UNITS", "MAX_CUBE_MAP_TEXTURE_SIZE", "MAX_FRAGMENT_UNIFORM_VECTORS", "MAX_RENDERBUFFER_SIZE", "MAX_TEXTURE_IMAGE_UNITS", "MAX_TEXTURE_SIZE", "MAX_VARYING_VECTORS", "MAX_VERTEX_ATTRIBS", "MAX_VERTEX_TEXTURE_IMAGE_UNITS", "MAX_VERTEX_UNIFORM_VECTORS", "MAX_VIEWPORT_DIMS", "MEDIUM_FLOAT", "MEDIUM_INT", "MIRRORED_REPEAT", "NEAREST", "NEAREST_MIPMAP_LINEAR", "NEAREST_MIPMAP_NEAREST", "NEVER", "NICEST", "NONE", "NOTEQUAL", "NO_ERROR", "ONE", "ONE_MINUS_CONSTANT_ALPHA", "ONE_MINUS_CONSTANT_COLOR", "ONE_MINUS_DST_ALPHA", "ONE_MINUS_DST_COLOR", "ONE_MINUS_SRC_ALPHA", "ONE_MINUS_SRC_COLOR", "OUT_OF_MEMORY", "PACK_ALIGNMENT", "POINTS", "POLYGON_OFFSET_FACTOR", "POLYGON_OFFSET_FILL", "POLYGON_OFFSET_UNITS", "RED_BITS", "RENDERBUFFER", "RENDERBUFFER_ALPHA_SIZE", "RENDERBUFFER_BINDING", "RENDERBUFFER_BLUE_SIZE", "RENDERBUFFER_DEPTH_SIZE", "RENDERBUFFER_GREEN_SIZE", "RENDERBUFFER_HEIGHT", "RENDERBUFFER_INTERNAL_FORMAT", "RENDERBUFFER_RED_SIZE", "RENDERBUFFER_STENCIL_SIZE", "RENDERBUFFER_WIDTH", "RENDERER", "REPEAT", "REPLACE", "RGB", "RGB565", "RGB5_A1", "RGBA", "RGBA4", "SAMPLER_2D", "SAMPLER_CUBE", "SAMPLES", "SAMPLE_ALPHA_TO_COVERAGE", "SAMPLE_BUFFERS", "SAMPLE_COVERAGE", "SAMPLE_COVERAGE_INVERT", "SAMPLE_COVERAGE_VALUE", "SCISSOR_BOX", "SCISSOR_TEST", "SHADER_TYPE", "SHADING_LANGUAGE_VERSION", "SHORT", "SRC_ALPHA", "SRC_ALPHA_SATURATE", "SRC_COLOR", "STATIC_DRAW", "STENCIL_ATTACHMENT", "STENCIL_BACK_FAIL", "STENCIL_BACK_FUNC", "STENCIL_BACK_PASS_DEPTH_FAIL", "STENCIL_BACK_PASS_DEPTH_PASS", "STENCIL_BACK_REF", "STENCIL_BACK_VALUE_MASK", "STENCIL_BACK_WRITEMASK", "STENCIL_BITS", "STENCIL_BUFFER_BIT", "STENCIL_CLEAR_VALUE", "STENCIL_FAIL", "STENCIL_FUNC", "STENCIL_INDEX8", "STENCIL_PASS_DEPTH_FAIL", "STENCIL_PASS_DEPTH_PASS", "STENCIL_REF", "STENCIL_TEST", "STENCIL_VALUE_MASK", "STENCIL_WRITEMASK", "STREAM_DRAW", "SUBPIXEL_BITS", "TEXTURE", "TEXTURE0", "TEXTURE1", "TEXTURE10", "TEXTURE11", "TEXTURE12", "TEXTURE13", "TEXTURE14", "TEXTURE15", "TEXTURE16", "TEXTURE17", "TEXTURE18", "TEXTURE19", "TEXTURE2", "TEXTURE20", "TEXTURE21", "TEXTURE22", "TEXTURE23", "TEXTURE24", "TEXTURE25", "TEXTURE26", "TEXTURE27", "TEXTURE28", "TEXTURE29", "TEXTURE3", "TEXTURE30", "TEXTURE31", "TEXTURE4", "TEXTURE5", "TEXTURE6", "TEXTURE7", "TEXTURE8", "TEXTURE9", "TEXTURE_2D", "TEXTURE_BINDING_2D", "TEXTURE_BINDING_CUBE_MAP", "TEXTURE_CUBE_MAP", "TEXTURE_CUBE_MAP_NEGATIVE_X", "TEXTURE_CUBE_MAP_NEGATIVE_Y", "TEXTURE_CUBE_MAP_NEGATIVE_Z", "TEXTURE_CUBE_MAP_POSITIVE_X", "TEXTURE_CUBE_MAP_POSITIVE_Y", "TEXTURE_CUBE_MAP_POSITIVE_Z", "TEXTURE_MAG_FILTER", "TEXTURE_MIN_FILTER", "TEXTURE_WRAP_S", "TEXTURE_WRAP_T", "TRIANGLES", "TRIANGLE_FAN", "TRIANGLE_STRIP", "UNPACK_ALIGNMENT", "UNPACK_COLORSPACE_CONVERSION_WEBGL", "UNPACK_FLIP_Y_WEBGL", "UNPACK_PREMULTIPLY_ALPHA_WEBGL", "UNSIGNED_BYTE", "UNSIGNED_INT", "UNSIGNED_SHORT", "UNSIGNED_SHORT_4_4_4_4", "UNSIGNED_SHORT_5_5_5_1", "UNSIGNED_SHORT_5_6_5", "VALIDATE_STATUS", "VENDOR", "VERSION", "VERTEX_ATTRIB_ARRAY_BUFFER_BINDING", "VERTEX_ATTRIB_ARRAY_ENABLED", "VERTEX_ATTRIB_ARRAY_NORMALIZED", "VERTEX_ATTRIB_ARRAY_POINTER", "VERTEX_ATTRIB_ARRAY_SIZE", "VERTEX_ATTRIB_ARRAY_STRIDE", "VERTEX_ATTRIB_ARRAY_TYPE", "VERTEX_SHADER", "VIEWPORT", "ZERO"];
7
16
  var index_spec_default = async () => {
8
- let app;
9
- let win;
10
- let glArea;
11
- let canvas;
12
- let ctx;
13
- if (GLib.getenv("CI") || GLib.getenv("GITHUB_ACTIONS") || GLib.getenv("TRAVIS") || GLib.getenv("CIRCLECI") || GLib.getenv("GITLAB_CI")) {
14
- console.info("Skipping webgl tests on CI");
15
- return;
16
- }
17
- const initApp = async () => {
17
+ await on("Display", async () => {
18
18
  Gtk.init();
19
- app = new Gtk.Application({
20
- application_id: "gjsify.webgl.index.spec.ts",
21
- flags: Gio.ApplicationFlags.FLAGS_NONE
22
- });
23
- const p = new Promise((resolve) => {
24
- app.connect("activate", () => {
25
- resolve(app);
26
- });
27
- });
28
- win = new Gtk.ApplicationWindow(app);
29
- win.set_default_size(800, 600);
30
- app.run([]);
31
- return p;
32
- };
33
- const initGLArea = async () => {
34
- glArea = new Gtk.GLArea({});
35
- const p = new Promise((resolve, reject) => {
36
- glArea.connect("realize", () => {
19
+ let glArea;
20
+ let gl;
21
+ const readyLoop = new GLib.MainLoop(null, false);
22
+ const win = new Gtk.Window({});
23
+ win.set_default_size(200, 200);
24
+ glArea = new CanvasWebGLWidget();
25
+ glArea.onReady((_c, g) => {
26
+ gl = g;
27
+ readyLoop.quit();
28
+ });
29
+ win.set_child(glArea);
30
+ win.present();
31
+ const giveUpId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1e4, () => {
32
+ readyLoop.quit();
33
+ return GLib.SOURCE_REMOVE;
34
+ });
35
+ readyLoop.run();
36
+ GLib.source_remove(giveUpId);
37
+ if (!gl) {
38
+ console.warn("WebGL context not available after 10s \u2014 skipping tests");
39
+ win.destroy();
40
+ return;
41
+ }
42
+ glArea.make_current();
43
+ await describe("WebGLRenderingContext constants", async () => {
44
+ beforeEach(async () => {
37
45
  glArea.make_current();
38
- const error = glArea.get_error();
39
- if (error) {
40
- reject(error);
46
+ });
47
+ await it("should have all standard WebGL constants as numbers", async () => {
48
+ for (const name of GL_CONSTANT_NAMES) {
49
+ const value = gl?.[name];
50
+ expect(typeof value).toBe("number");
51
+ if (name === "NONE" || name === "ZERO" || name === "NO_ERROR" || name === "POINTS") {
52
+ expect(value === 0).toBeTruthy();
53
+ } else {
54
+ expect(value > 0).toBeTruthy();
55
+ }
41
56
  }
42
- resolve(glArea);
43
57
  });
44
58
  });
45
- glArea.set_use_es(true);
46
- glArea.set_has_depth_buffer(true);
47
- glArea.set_has_stencil_buffer(true);
48
- glArea.set_required_version(3, 2);
49
- win.set_child(glArea);
50
- win.present();
51
- return p;
52
- };
53
- await describe("Gtk.ApplicationWindow", async () => {
54
- await it("should be able to create a new instance", async () => {
55
- app = await initApp();
56
- expect(app).toBeDefined();
59
+ await describe("Buffers", async () => {
60
+ beforeEach(async () => {
61
+ glArea.make_current();
62
+ });
63
+ await it("createBuffer returns a WebGLBuffer", async () => {
64
+ const buf = gl.createBuffer();
65
+ expect(buf).toBeDefined();
66
+ expect(buf).not.toBeNull();
67
+ });
68
+ await it("bindBuffer + bufferData with Float32Array", async () => {
69
+ const buf = gl.createBuffer();
70
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
71
+ const data = new Float32Array([1, 2, 3, 4]);
72
+ expect(() => gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)).not.toThrow();
73
+ });
74
+ await it("getBufferParameter returns buffer size", async () => {
75
+ const buf = gl.createBuffer();
76
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
77
+ const data = new Float32Array([1, 2, 3, 4]);
78
+ gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
79
+ const size = gl.getBufferParameter(gl.ARRAY_BUFFER, gl.BUFFER_SIZE);
80
+ expect(size).toBe(data.byteLength);
81
+ });
82
+ await it("bufferSubData updates buffer data", async () => {
83
+ const buf = gl.createBuffer();
84
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
85
+ const data = new Float32Array([1, 2, 3, 4]);
86
+ gl.bufferData(gl.ARRAY_BUFFER, data, gl.DYNAMIC_DRAW);
87
+ expect(() => gl.bufferSubData(gl.ARRAY_BUFFER, 0, new Float32Array([9, 9]))).not.toThrow();
88
+ });
89
+ await it("deleteBuffer cleans up", async () => {
90
+ const buf = gl.createBuffer();
91
+ expect(() => gl.deleteBuffer(buf)).not.toThrow();
92
+ });
57
93
  });
58
- });
59
- await describe("Gtk.GLArea", async () => {
60
- await it("should be able to create a new instance", async () => {
61
- glArea = await initGLArea();
62
- expect(glArea).toBeDefined();
94
+ await describe("Shaders", async () => {
95
+ beforeEach(async () => {
96
+ glArea.make_current();
97
+ });
98
+ const vertSrc = "attribute vec2 position; void main() { gl_Position = vec4(position, 0.0, 1.0); }";
99
+ const fragSrc = "void main() { gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); }";
100
+ await it("createShader returns a WebGLShader", async () => {
101
+ const shader = gl.createShader(gl.VERTEX_SHADER);
102
+ expect(shader).toBeDefined();
103
+ expect(shader).not.toBeNull();
104
+ });
105
+ await it("vertex shader compiles successfully", async () => {
106
+ const shader = gl.createShader(gl.VERTEX_SHADER);
107
+ gl.shaderSource(shader, vertSrc);
108
+ gl.compileShader(shader);
109
+ const status = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
110
+ if (!status) {
111
+ console.error("Vertex shader log:", gl.getShaderInfoLog(shader));
112
+ }
113
+ expect(status).toBeTruthy();
114
+ });
115
+ await it("fragment shader compiles successfully", async () => {
116
+ const shader = gl.createShader(gl.FRAGMENT_SHADER);
117
+ gl.shaderSource(shader, fragSrc);
118
+ gl.compileShader(shader);
119
+ const status = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
120
+ if (!status) {
121
+ console.error("Fragment shader log:", gl.getShaderInfoLog(shader));
122
+ }
123
+ expect(status).toBeTruthy();
124
+ });
125
+ await it("program links successfully", async () => {
126
+ const vert = gl.createShader(gl.VERTEX_SHADER);
127
+ gl.shaderSource(vert, vertSrc);
128
+ gl.compileShader(vert);
129
+ const frag = gl.createShader(gl.FRAGMENT_SHADER);
130
+ gl.shaderSource(frag, fragSrc);
131
+ gl.compileShader(frag);
132
+ const prog = gl.createProgram();
133
+ gl.attachShader(prog, vert);
134
+ gl.attachShader(prog, frag);
135
+ gl.linkProgram(prog);
136
+ const status = gl.getProgramParameter(prog, gl.LINK_STATUS);
137
+ if (!status) {
138
+ console.error("Program log:", gl.getProgramInfoLog(prog));
139
+ }
140
+ expect(status).toBeTruthy();
141
+ });
63
142
  });
64
- });
65
- await describe("HTMLCanvasElement", async () => {
66
- await it("should be defined", async () => {
67
- expect(HTMLCanvasElement).toBeDefined();
68
- });
69
- await it("should be able to create a new instance", async () => {
70
- expect(() => {
71
- canvas = new HTMLCanvasElement(glArea);
72
- }).not.toThrow();
73
- expect(canvas).toBeDefined();
74
- });
75
- await it("should be able to get a webgl context", async () => {
76
- expect(() => {
77
- ctx = canvas.getContext("webgl");
78
- }).not.toThrow();
79
- expect(ctx).toBeDefined();
143
+ await describe("Textures", async () => {
144
+ beforeEach(async () => {
145
+ glArea.make_current();
146
+ });
147
+ await it("createTexture returns a WebGLTexture", async () => {
148
+ const tex = gl.createTexture();
149
+ expect(tex).toBeDefined();
150
+ expect(tex).not.toBeNull();
151
+ });
152
+ await it("bindTexture + texImage2D with Uint8Array", async () => {
153
+ const tex = gl.createTexture();
154
+ gl.bindTexture(gl.TEXTURE_2D, tex);
155
+ const pixels = new Uint8Array([255, 0, 0, 255]);
156
+ expect(() => gl.texImage2D(
157
+ gl.TEXTURE_2D,
158
+ 0,
159
+ gl.RGBA,
160
+ 1,
161
+ 1,
162
+ 0,
163
+ gl.RGBA,
164
+ gl.UNSIGNED_BYTE,
165
+ pixels
166
+ )).not.toThrow();
167
+ });
168
+ await it("texParameteri sets filter params without error", async () => {
169
+ const tex = gl.createTexture();
170
+ gl.bindTexture(gl.TEXTURE_2D, tex);
171
+ expect(() => {
172
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
173
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
174
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
175
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
176
+ }).not.toThrow();
177
+ });
178
+ await it("deleteTexture cleans up", async () => {
179
+ const tex = gl.createTexture();
180
+ expect(() => gl.deleteTexture(tex)).not.toThrow();
181
+ });
80
182
  });
81
- });
82
- await describe("WebGLRenderingContext", async () => {
83
- await it("should have defined WebGL constants", async () => {
84
- for (const GL_CONSTANT_NAME of GL_CONSTANT_NAMES) {
85
- const constant = ctx?.[GL_CONSTANT_NAME];
86
- console.log(GL_CONSTANT_NAME, constant);
87
- expect(typeof constant).toBe("number");
88
- if (GL_CONSTANT_NAME === "NONE" || GL_CONSTANT_NAME === "ZERO" || GL_CONSTANT_NAME === "NO_ERROR" || GL_CONSTANT_NAME === "POINTS") {
89
- expect(constant === 0).toBeTruthy();
90
- } else {
91
- expect(constant > 0).toBeTruthy();
183
+ await describe("Draw", async () => {
184
+ beforeEach(async () => {
185
+ glArea.make_current();
186
+ });
187
+ await it("clearColor + clear does not throw", async () => {
188
+ expect(() => {
189
+ gl.clearColor(0, 0, 0, 1);
190
+ gl.clear(gl.COLOR_BUFFER_BIT);
191
+ }).not.toThrow();
192
+ expect(gl.getError()).toBe(gl.NO_ERROR);
193
+ });
194
+ await it("viewport does not throw", async () => {
195
+ expect(() => gl.viewport(0, 0, 200, 200)).not.toThrow();
196
+ });
197
+ await it("drawArrays (triangle) produces no GL error", async () => {
198
+ const vertSrc = "attribute vec2 position; void main() { gl_Position = vec4(position, 0.0, 1.0); }";
199
+ const fragSrc = "void main() { gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); }";
200
+ const vert = gl.createShader(gl.VERTEX_SHADER);
201
+ gl.shaderSource(vert, vertSrc);
202
+ gl.compileShader(vert);
203
+ const frag = gl.createShader(gl.FRAGMENT_SHADER);
204
+ gl.shaderSource(frag, fragSrc);
205
+ gl.compileShader(frag);
206
+ const prog = gl.createProgram();
207
+ gl.attachShader(prog, vert);
208
+ gl.attachShader(prog, frag);
209
+ gl.linkProgram(prog);
210
+ gl.useProgram(prog);
211
+ const buf = gl.createBuffer();
212
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
213
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, 0, 0, -1, 1, 1]), gl.STATIC_DRAW);
214
+ const loc = gl.getAttribLocation(prog, "position");
215
+ gl.enableVertexAttribArray(loc);
216
+ gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);
217
+ gl.clearColor(0, 0, 0, 1);
218
+ gl.clear(gl.COLOR_BUFFER_BIT);
219
+ expect(() => gl.drawArrays(gl.TRIANGLES, 0, 3)).not.toThrow();
220
+ expect(gl.getError()).toBe(gl.NO_ERROR);
221
+ });
222
+ });
223
+ await describe("Extensions", async () => {
224
+ beforeEach(async () => {
225
+ glArea.make_current();
226
+ });
227
+ await it("OES_texture_float is supported", async () => {
228
+ const ext = gl.getExtension("OES_texture_float");
229
+ expect(ext).not.toBeNull();
230
+ });
231
+ await it("OES_standard_derivatives is supported", async () => {
232
+ const ext = gl.getExtension("OES_standard_derivatives");
233
+ expect(ext).not.toBeNull();
234
+ });
235
+ await it("OES_element_index_uint is supported", async () => {
236
+ const ext = gl.getExtension("OES_element_index_uint");
237
+ expect(ext).not.toBeNull();
238
+ });
239
+ await it("getSupportedExtensions returns an array", async () => {
240
+ const exts = gl.getSupportedExtensions();
241
+ expect(Array.isArray(exts)).toBeTruthy();
242
+ expect((exts?.length ?? 0) > 0).toBeTruthy();
243
+ });
244
+ });
245
+ await describe("Gwebgl.WebGLRenderingContext native bindings", async () => {
246
+ beforeEach(async () => {
247
+ glArea.make_current();
248
+ });
249
+ await it("getParameterb returns a boolean", async () => {
250
+ const res = gl?._native.getParameterb(gl?.BLEND);
251
+ expect(typeof res).toBe("boolean");
252
+ });
253
+ await it("getParameterbv returns an array of booleans", async () => {
254
+ const results = gl?._native.getParameterbv(gl?.COLOR_WRITEMASK, 16);
255
+ expect(Array.isArray(results)).toBeTruthy();
256
+ if (Array.isArray(results)) {
257
+ for (const r of results) expect(typeof r).toBe("boolean");
92
258
  }
93
- }
259
+ });
260
+ await it("getParameterf returns a float", async () => {
261
+ const result = gl?._native.getParameterf(gl?.SAMPLE_COVERAGE_VALUE);
262
+ expect(typeof result).toBe("number");
263
+ });
264
+ await it("getParameterfv returns an array of floats", async () => {
265
+ const results = gl?._native.getParameterfv(gl?.DEPTH_RANGE, 8);
266
+ expect(Array.isArray(results)).toBeTruthy();
267
+ if (Array.isArray(results)) {
268
+ for (const r of results) expect(typeof r).toBe("number");
269
+ }
270
+ });
271
+ await it("getParameteri returns an integer", async () => {
272
+ const result = gl?._native.getParameteri(gl?.ARRAY_BUFFER_BINDING);
273
+ expect(typeof result).toBe("number");
274
+ });
275
+ await it("getParameteriv returns an array of integers", async () => {
276
+ const results = gl?._native.getParameteriv(gl?.MAX_VIEWPORT_DIMS, 8);
277
+ expect(Array.isArray(results)).toBeTruthy();
278
+ if (Array.isArray(results)) {
279
+ for (const r of results) expect(typeof r).toBe("number");
280
+ }
281
+ });
94
282
  });
95
- });
96
- await describe("Gwebgl.WebGLRenderingContext", async () => {
97
- await it("getParameterb should return a boolean", async () => {
98
- const res = ctx?._native.getParameterb(ctx?.BLEND);
99
- console.log("res", res);
100
- expect(typeof res).toBe("boolean");
101
- });
102
- await it("getParameterbv should return an array of boolean", async () => {
103
- const results = ctx?._native.getParameterbv(ctx?.COLOR_WRITEMASK, 16);
104
- console.log("results", results);
105
- expect(Array.isArray(results)).toBeTruthy();
106
- if (Array.isArray(results)) {
107
- for (const res of results) {
108
- expect(typeof res).toBe("boolean");
283
+ await describe("simple-shader rendering", async () => {
284
+ beforeEach(async () => {
285
+ glArea.make_current();
286
+ });
287
+ const VS = "attribute vec2 position; void main() { gl_Position = vec4(position, 0.0, 1.0); }";
288
+ const FS = "void main() { gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); }";
289
+ await it("renders a green triangle \u2014 all pixels (0,255,0,255)", async () => {
290
+ const fbo = makeTestFBO(gl, 8, 8);
291
+ gl.clearColor(0, 0, 0, 1);
292
+ gl.clear(gl.COLOR_BUFFER_BIT);
293
+ const prog = makeProgram(gl, VS, FS);
294
+ expect(gl.getProgramParameter(prog, gl.LINK_STATUS)).toBeTruthy();
295
+ gl.useProgram(prog);
296
+ drawTriangle(gl);
297
+ expect(gl.getError()).toBe(gl.NO_ERROR);
298
+ const pixels = new Uint8Array(fbo.width * fbo.height * 4);
299
+ gl.readPixels(0, 0, fbo.width, fbo.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
300
+ let allGreen = true;
301
+ for (let i = 0; i < pixels.length; i += 4) {
302
+ if (pixels[i] !== 0 || pixels[i + 1] !== 255 || pixels[i + 2] !== 0 || pixels[i + 3] !== 255) {
303
+ allGreen = false;
304
+ break;
305
+ }
109
306
  }
110
- }
307
+ expect(allGreen).toBeTruthy();
308
+ gl.deleteProgram(prog);
309
+ destroyTestFBO(gl, fbo);
310
+ });
111
311
  });
112
- await it("getParameterf should return a float number", async () => {
113
- const result = ctx?._native.getParameterf(ctx?.SAMPLE_COVERAGE_VALUE);
114
- console.log("result", result);
115
- expect(typeof result).toBe("number");
116
- });
117
- await it("getParameterfv should return an array of float numbers", async () => {
118
- const results = ctx?._native.getParameterfv(ctx?.DEPTH_RANGE, 8);
119
- console.log("results", results);
120
- expect(Array.isArray(results)).toBeTruthy();
121
- if (Array.isArray(results)) {
122
- for (const res of results) {
123
- expect(typeof res).toBe("number");
312
+ await describe("clearColor", async () => {
313
+ beforeEach(async () => {
314
+ glArea.make_current();
315
+ });
316
+ await it("clears to black (0,0,0,0)", async () => {
317
+ const fbo = makeTestFBO(gl);
318
+ gl.clearColor(0, 0, 0, 0);
319
+ gl.clear(gl.COLOR_BUFFER_BIT);
320
+ const p = readPixel(gl, 0, 0);
321
+ destroyTestFBO(gl, fbo);
322
+ expect(p[0]).toBe(0);
323
+ expect(p[1]).toBe(0);
324
+ expect(p[2]).toBe(0);
325
+ expect(p[3]).toBe(0);
326
+ });
327
+ await it("clears to white (255,255,255,255)", async () => {
328
+ const fbo = makeTestFBO(gl);
329
+ gl.clearColor(1, 1, 1, 1);
330
+ gl.clear(gl.COLOR_BUFFER_BIT);
331
+ const p = readPixel(gl, 0, 0);
332
+ destroyTestFBO(gl, fbo);
333
+ expect(p[0]).toBe(255);
334
+ expect(p[1]).toBe(255);
335
+ expect(p[2]).toBe(255);
336
+ expect(p[3]).toBe(255);
337
+ });
338
+ await it("clears to green (0,255,0,255)", async () => {
339
+ const fbo = makeTestFBO(gl);
340
+ gl.clearColor(0, 1, 0, 1);
341
+ gl.clear(gl.COLOR_BUFFER_BIT);
342
+ const p = readPixel(gl, 0, 0);
343
+ destroyTestFBO(gl, fbo);
344
+ expect(p[0]).toBe(0);
345
+ expect(p[1]).toBe(255);
346
+ expect(p[2]).toBe(0);
347
+ expect(p[3]).toBe(255);
348
+ });
349
+ await it("clears to magenta (255,0,255,255)", async () => {
350
+ const fbo = makeTestFBO(gl);
351
+ gl.clearColor(1, 0, 1, 1);
352
+ gl.clear(gl.COLOR_BUFFER_BIT);
353
+ const p = readPixel(gl, 0, 0);
354
+ destroyTestFBO(gl, fbo);
355
+ expect(p[0]).toBe(255);
356
+ expect(p[1]).toBe(0);
357
+ expect(p[2]).toBe(255);
358
+ expect(p[3]).toBe(255);
359
+ });
360
+ });
361
+ await describe("blending", async () => {
362
+ beforeEach(async () => {
363
+ glArea.make_current();
364
+ });
365
+ const VS_BLEND = [
366
+ "precision mediump float;",
367
+ "attribute vec2 position;",
368
+ "void main() { gl_Position = vec4(position, 0.0, 1.0); }"
369
+ ].join("\n");
370
+ const blendTests = [
371
+ {
372
+ name: "ADD ONE ONE",
373
+ equn: gl.FUNC_ADD,
374
+ func1: gl.ONE,
375
+ func2: gl.ONE,
376
+ dstColor: [0.5, 0.5, 0.5, 1],
377
+ srcColor: [0.5, 0.5, 0.5, 1],
378
+ expected: [255, 255, 255, 255]
379
+ },
380
+ {
381
+ name: "ADD ONE ZERO",
382
+ equn: gl.FUNC_ADD,
383
+ func1: gl.ONE,
384
+ func2: gl.ZERO,
385
+ dstColor: [0.5, 0.5, 0.5, 0.5],
386
+ srcColor: [0.2, 0.2, 0.2, 1],
387
+ expected: [51, 51, 51, 255]
388
+ },
389
+ {
390
+ name: "ADD ZERO SRC_COLOR",
391
+ equn: gl.FUNC_ADD,
392
+ func1: gl.ZERO,
393
+ func2: gl.SRC_COLOR,
394
+ dstColor: [0.8, 0.8, 0.8, 1],
395
+ srcColor: [0.5, 0.5, 0.5, 0.5],
396
+ expected: [102, 102, 102, 128]
397
+ },
398
+ {
399
+ name: "ADD DST_COLOR ZERO",
400
+ equn: gl.FUNC_ADD,
401
+ func1: gl.DST_COLOR,
402
+ func2: gl.ZERO,
403
+ dstColor: [0.8, 0.8, 0.8, 1],
404
+ srcColor: [0.5, 0.5, 0.5, 0.5],
405
+ expected: [102, 102, 102, 128]
406
+ },
407
+ {
408
+ name: "ADD SRC_ALPHA ONE_MINUS_SRC_ALPHA",
409
+ equn: gl.FUNC_ADD,
410
+ func1: gl.SRC_ALPHA,
411
+ func2: gl.ONE_MINUS_SRC_ALPHA,
412
+ dstColor: [0.5, 0, 0.5, 1],
413
+ srcColor: [0.5, 1, 0, 0.5],
414
+ expected: [127, 127, 64, 191]
124
415
  }
416
+ ];
417
+ for (const tc of blendTests) {
418
+ const { name, equn, func1, func2, dstColor, srcColor, expected } = tc;
419
+ const FS_BLEND = [
420
+ "precision mediump float;",
421
+ `void main() { gl_FragColor = vec4(${srcColor[0]},${srcColor[1]},${srcColor[2]},${srcColor[3]}); }`
422
+ ].join("\n");
423
+ await it(name, async () => {
424
+ const fbo = makeTestFBO(gl);
425
+ gl.clearColor(dstColor[0], dstColor[1], dstColor[2], dstColor[3]);
426
+ gl.clear(gl.COLOR_BUFFER_BIT);
427
+ const prog = makeProgram(gl, VS_BLEND, FS_BLEND);
428
+ gl.useProgram(prog);
429
+ gl.enable(gl.BLEND);
430
+ gl.blendEquation(equn);
431
+ gl.blendFunc(func1, func2);
432
+ drawTriangle(gl);
433
+ expect(gl.getError()).toBe(gl.NO_ERROR);
434
+ gl.disable(gl.BLEND);
435
+ const p = readPixel(gl, 0, 0);
436
+ gl.deleteProgram(prog);
437
+ destroyTestFBO(gl, fbo);
438
+ expect(pixelClose(p, expected)).toBeTruthy();
439
+ });
125
440
  }
126
441
  });
127
- await it("getParameteri should return a integer number", async () => {
128
- const result = ctx?._native.getParameteri(ctx?.ARRAY_BUFFER_BINDING);
129
- console.log("result", result);
130
- expect(typeof result).toBe("number");
131
- });
132
- await it("getParameteriv should return an array of integer numbers", async () => {
133
- const results = ctx?._native.getParameteriv(ctx?.MAX_VIEWPORT_DIMS, 8);
134
- console.log("results", results, typeof results);
135
- expect(Array.isArray(results)).toBeTruthy();
136
- if (Array.isArray(results)) {
137
- for (const res of results) {
138
- expect(typeof res).toBe("number");
442
+ await describe("drawElements (indexed drawing)", async () => {
443
+ beforeEach(async () => {
444
+ glArea.make_current();
445
+ });
446
+ await it("draws a quad via index buffer \u2014 all pixels green", async () => {
447
+ const fbo = makeTestFBO(gl, 8, 8);
448
+ const VS = "attribute vec2 position; void main() { gl_Position = vec4(position, 0.0, 1.0); }";
449
+ const FS = "void main() { gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); }";
450
+ gl.clearColor(1, 0, 0, 1);
451
+ gl.clear(gl.COLOR_BUFFER_BIT);
452
+ const prog = makeProgram(gl, VS, FS);
453
+ gl.useProgram(prog);
454
+ const vbuf = gl.createBuffer();
455
+ gl.bindBuffer(gl.ARRAY_BUFFER, vbuf);
456
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);
457
+ gl.enableVertexAttribArray(0);
458
+ gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
459
+ const ebuf = gl.createBuffer();
460
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebuf);
461
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 2, 1, 3]), gl.STATIC_DRAW);
462
+ gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
463
+ expect(gl.getError()).toBe(gl.NO_ERROR);
464
+ const pixels = new Uint8Array(fbo.width * fbo.height * 4);
465
+ gl.readPixels(0, 0, fbo.width, fbo.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
466
+ let allGreen = true;
467
+ for (let i = 0; i < pixels.length; i += 4) {
468
+ if (pixels[i] !== 0 || pixels[i + 1] !== 255 || pixels[i + 2] !== 0 || pixels[i + 3] !== 255) {
469
+ allGreen = false;
470
+ break;
471
+ }
139
472
  }
140
- }
473
+ expect(allGreen).toBeTruthy();
474
+ gl.disableVertexAttribArray(0);
475
+ gl.deleteBuffer(vbuf);
476
+ gl.deleteBuffer(ebuf);
477
+ gl.deleteProgram(prog);
478
+ destroyTestFBO(gl, fbo);
479
+ });
480
+ });
481
+ await describe("readPixels", async () => {
482
+ beforeEach(async () => {
483
+ glArea.make_current();
484
+ });
485
+ await it("RGBA + UNSIGNED_BYTE returns correct data size", async () => {
486
+ const fbo = makeTestFBO(gl, 4, 4);
487
+ gl.clearColor(1, 0, 0, 1);
488
+ gl.clear(gl.COLOR_BUFFER_BIT);
489
+ const pixels = new Uint8Array(fbo.width * fbo.height * 4);
490
+ gl.readPixels(0, 0, fbo.width, fbo.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
491
+ expect(gl.getError()).toBe(gl.NO_ERROR);
492
+ expect(pixels.length).toBe(fbo.width * fbo.height * 4);
493
+ destroyTestFBO(gl, fbo);
494
+ });
495
+ await it("pixel values match the clear color", async () => {
496
+ const fbo = makeTestFBO(gl);
497
+ gl.clearColor(0, 0, 1, 1);
498
+ gl.clear(gl.COLOR_BUFFER_BIT);
499
+ const p = readPixel(gl, 0, 0);
500
+ destroyTestFBO(gl, fbo);
501
+ expect(p[0]).toBe(0);
502
+ expect(p[1]).toBe(0);
503
+ expect(p[2]).toBe(255);
504
+ expect(p[3]).toBe(255);
505
+ });
506
+ await it("readPixels reads a single red pixel", async () => {
507
+ const fbo = makeTestFBO(gl);
508
+ gl.clearColor(1, 0, 0, 1);
509
+ gl.clear(gl.COLOR_BUFFER_BIT);
510
+ const buf = new Uint8Array(4);
511
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, buf);
512
+ destroyTestFBO(gl, fbo);
513
+ expect(buf[0]).toBe(255);
514
+ expect(buf[1]).toBe(0);
515
+ expect(buf[2]).toBe(0);
516
+ expect(buf[3]).toBe(255);
517
+ });
518
+ });
519
+ await describe("depth buffer", async () => {
520
+ beforeEach(async () => {
521
+ glArea.make_current();
522
+ });
523
+ await it("depth test (LESS) \u2014 nearer triangle occludes farther one", async () => {
524
+ const VS = [
525
+ "attribute vec2 position;",
526
+ "uniform float depth;",
527
+ "void main() { gl_Position = vec4(position, depth, 1.0); }"
528
+ ].join("\n");
529
+ const FS = [
530
+ "precision mediump float;",
531
+ "uniform vec4 color;",
532
+ "void main() { gl_FragColor = color; }"
533
+ ].join("\n");
534
+ const fbo = makeTestFBOWithDepth(gl, 4, 4);
535
+ gl.clearColor(0, 0, 0, 1);
536
+ gl.clearDepth(1);
537
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
538
+ gl.enable(gl.DEPTH_TEST);
539
+ gl.depthFunc(gl.LESS);
540
+ const prog = makeProgram(gl, VS, FS);
541
+ gl.useProgram(prog);
542
+ gl.uniform1f(gl.getUniformLocation(prog, "depth"), 0);
543
+ gl.uniform4f(gl.getUniformLocation(prog, "color"), 1, 0, 0, 1);
544
+ drawTriangle(gl);
545
+ gl.uniform1f(gl.getUniformLocation(prog, "depth"), 0.5);
546
+ gl.uniform4f(gl.getUniformLocation(prog, "color"), 0, 0, 1, 1);
547
+ drawTriangle(gl);
548
+ expect(gl.getError()).toBe(gl.NO_ERROR);
549
+ gl.disable(gl.DEPTH_TEST);
550
+ const p = readPixel(gl, 0, 0);
551
+ gl.deleteProgram(prog);
552
+ destroyTestFBOWithDepth(gl, fbo);
553
+ expect(p[0]).toBe(255);
554
+ expect(p[1]).toBe(0);
555
+ expect(p[2]).toBe(0);
556
+ });
557
+ });
558
+ await describe("uniform1fv regression", async () => {
559
+ beforeEach(async () => {
560
+ glArea.make_current();
561
+ });
562
+ const VS_U1FV = [
563
+ "attribute vec2 position;",
564
+ "void main() { gl_Position = vec4(position, 0.0, 1.0); }"
565
+ ].join("\n");
566
+ await it("uniform1fv sets a float uniform and affects rendering", async () => {
567
+ const FS = [
568
+ "precision mediump float;",
569
+ "uniform float uRed;",
570
+ "void main() { gl_FragColor = vec4(uRed, 0.0, 0.0, 1.0); }"
571
+ ].join("\n");
572
+ const prog = makeProgram(gl, VS_U1FV, FS);
573
+ expect(gl.getProgramParameter(prog, gl.LINK_STATUS)).toBeTruthy();
574
+ gl.useProgram(prog);
575
+ const loc = gl.getUniformLocation(prog, "uRed");
576
+ expect(loc).not.toBeNull();
577
+ gl.uniform1fv(loc, new Float32Array([1]));
578
+ expect(gl.getError()).toBe(gl.NO_ERROR);
579
+ const fbo = makeTestFBO(gl, 4, 4);
580
+ gl.clearColor(0, 0, 0, 1);
581
+ gl.clear(gl.COLOR_BUFFER_BIT);
582
+ drawTriangle(gl);
583
+ const p = readPixel(gl, 0, 0);
584
+ destroyTestFBO(gl, fbo);
585
+ expect(p[0]).toBe(255);
586
+ expect(p[1]).toBe(0);
587
+ expect(p[2]).toBe(0);
588
+ gl.deleteProgram(prog);
589
+ });
590
+ await it("uniform1fv sets each array element individually", async () => {
591
+ const FS_ARR = [
592
+ "precision mediump float;",
593
+ "uniform float uWeights[3];",
594
+ "void main() { gl_FragColor = vec4(uWeights[0], uWeights[1], uWeights[2], 1.0); }"
595
+ ].join("\n");
596
+ const prog = makeProgram(gl, VS_U1FV, FS_ARR);
597
+ expect(gl.getProgramParameter(prog, gl.LINK_STATUS)).toBeTruthy();
598
+ gl.useProgram(prog);
599
+ const loc0 = gl.getUniformLocation(prog, "uWeights[0]");
600
+ const loc1 = gl.getUniformLocation(prog, "uWeights[1]");
601
+ const loc2 = gl.getUniformLocation(prog, "uWeights[2]");
602
+ expect(loc0).not.toBeNull();
603
+ expect(loc1).not.toBeNull();
604
+ expect(loc2).not.toBeNull();
605
+ gl.uniform1fv(loc0, new Float32Array([0]));
606
+ gl.uniform1fv(loc1, new Float32Array([1]));
607
+ gl.uniform1fv(loc2, new Float32Array([0]));
608
+ expect(gl.getError()).toBe(gl.NO_ERROR);
609
+ const fbo = makeTestFBO(gl, 4, 4);
610
+ gl.clearColor(0, 0, 0, 1);
611
+ gl.clear(gl.COLOR_BUFFER_BIT);
612
+ drawTriangle(gl);
613
+ const p = readPixel(gl, 0, 0);
614
+ destroyTestFBO(gl, fbo);
615
+ expect(p[0]).toBe(0);
616
+ expect(p[1]).toBe(255);
617
+ expect(p[2]).toBe(0);
618
+ gl.deleteProgram(prog);
619
+ });
620
+ });
621
+ await describe("linkProgram with inactive attributes", async () => {
622
+ beforeEach(async () => {
623
+ glArea.make_current();
624
+ });
625
+ await it("links a program whose vertex shader declares but does not use an attribute", async () => {
626
+ const VS = [
627
+ "attribute vec2 position;",
628
+ "attribute vec3 unused;",
629
+ "void main() { gl_Position = vec4(position, 0.0, 1.0); }"
630
+ ].join("\n");
631
+ const FS = [
632
+ "precision mediump float;",
633
+ "void main() { gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); }"
634
+ ].join("\n");
635
+ const prog = makeProgram(gl, VS, FS);
636
+ expect(gl.getProgramParameter(prog, gl.LINK_STATUS)).toBeTruthy();
637
+ expect(gl.getError()).toBe(gl.NO_ERROR);
638
+ gl.useProgram(prog);
639
+ const fbo = makeTestFBO(gl, 4, 4);
640
+ gl.clearColor(0, 0, 0, 1);
641
+ gl.clear(gl.COLOR_BUFFER_BIT);
642
+ drawTriangle(gl);
643
+ expect(gl.getError()).toBe(gl.NO_ERROR);
644
+ const p = readPixel(gl, 0, 0);
645
+ destroyTestFBO(gl, fbo);
646
+ expect(p[1]).toBe(255);
647
+ gl.deleteProgram(prog);
648
+ });
141
649
  });
650
+ win.destroy();
142
651
  });
143
652
  };
144
653
  export {