summaryrefslogtreecommitdiffstats
path: root/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback
diff options
context:
space:
mode:
Diffstat (limited to 'dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback')
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/00_test_list.txt9
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/default_transform_feedback.html117
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/non-existent-varying.html70
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/same-buffer-two-binding-points.html176
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/simultaneous_binding.html330
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/switching-objects.html231
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/too-small-buffers.html242
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/transform_feedback.html645
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/two-unreferenced-varyings.html136
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/unwritten-output-defaults-to-zero.html133
10 files changed, 2089 insertions, 0 deletions
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/00_test_list.txt b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/00_test_list.txt
new file mode 100644
index 0000000000..35caba6ccd
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/00_test_list.txt
@@ -0,0 +1,9 @@
+--min-version 2.0.1 non-existent-varying.html
+--min-version 2.0.1 default_transform_feedback.html
+transform_feedback.html
+two-unreferenced-varyings.html
+--min-version 2.0.1 too-small-buffers.html
+unwritten-output-defaults-to-zero.html
+--min-version 2.0.1 same-buffer-two-binding-points.html
+--min-version 2.0.1 simultaneous_binding.html
+--min-version 2.0.1 switching-objects.html
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/default_transform_feedback.html b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/default_transform_feedback.html
new file mode 100644
index 0000000000..1d5e72811a
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/default_transform_feedback.html
@@ -0,0 +1,117 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL Transform Feedback Conformance Test - Default Transform Feedback</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas>
+<div id="console"></div>
+<script id="vshader" type="x-shader/x-vertex">#version 300 es
+in float in_value;
+out float out_value;
+
+void main() {
+ out_value = in_value * 2.;
+}
+</script>
+<script id="fshader" type="x-shader/x-fragment">#version 300 es
+precision mediump float;
+out vec4 dummy;
+void main() {
+ dummy = vec4(1);
+}
+</script>
+<script>
+"use strict";
+description("This test verifies using the default transform feedback object");
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var canvas = document.getElementById("canvas");
+var gl = wtu.create3DContext(canvas, null, 2);
+
+if (!gl) {
+ testFailed("WebGL context does not exist");
+} else {
+ testPassed("WebGL context exists");
+
+ runDefaultTransformFeedbackTest();
+ finishTest();
+}
+
+function runDefaultTransformFeedbackTest() {
+ const prog = wtu.setupTransformFeedbackProgram(gl, ["vshader", "fshader"],
+ ["out_value"], gl.SEPARATE_ATTRIBS,
+ ["in_value"]);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "linking transform feedback shader should not set an error");
+
+ const inLoc = 0;
+ const outLoc = 0;
+
+ const srcBuffer1 = createBuffer(gl, new Float32Array([1, 2, 3]));
+ const srcVAO1 = createVAO(gl, srcBuffer1, inLoc);
+
+ const dstBuffer = createBuffer(gl, Float32Array.BYTES_PER_ELEMENT * 3);
+
+ const tf = null; // use the default transform feedback gl.createTransformFeedback();
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+ gl.useProgram(prog);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, outLoc, dstBuffer);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors binding a buffer to the default transform feedback");
+
+ runFeedback(gl, prog, srcVAO1, tf);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors having drawn with the default transform feedback");
+
+ const expected = [2, 4, 6];
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, dstBuffer);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, expected);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors after readback");
+}
+
+function runFeedback(gl, prog, srcVAO, tf, dstBufferInfo) {
+ gl.enable(gl.RASTERIZER_DISCARD);
+
+ gl.useProgram(prog);
+ gl.bindVertexArray(srcVAO);
+
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+ gl.beginTransformFeedback(gl.TRIANGLES);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors setting up to draw with the default transform feedback");
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
+ gl.endTransformFeedback();
+
+ gl.disable(gl.RASTERIZER_DISCARD);
+}
+
+function createBuffer(gl, dataOrSize) {
+ const buf = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
+ gl.bufferData(gl.ARRAY_BUFFER, dataOrSize, gl.STATIC_DRAW);
+ gl.bindBuffer(gl.ARRAY_BUFFER, null); // Clear this or we'll have tfo/non-tfo simultaneous usage.
+ return buf;
+}
+
+function createVAO(gl, buf, inLoc) {
+ const vao = gl.createVertexArray();
+ gl.bindVertexArray(vao);
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
+ gl.enableVertexAttribArray(inLoc);
+ gl.vertexAttribPointer(inLoc, 1, gl.FLOAT, false, 0, 0);
+ gl.bindVertexArray(null);
+ return vao;
+}
+</script>
+</body>
+</html>
+
+<!--
+Copyright (c) 2019 The Khronos Group Inc.
+Use of this source code is governed by an MIT-style license that can be
+found in the LICENSE.txt file.
+-->
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/non-existent-varying.html b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/non-existent-varying.html
new file mode 100644
index 0000000000..80800c2dc5
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/non-existent-varying.html
@@ -0,0 +1,70 @@
+<!--
+Copyright (c) 2019 The Khronos Group Inc.
+Use of this source code is governed by an MIT-style license that can be
+found in the LICENSE.txt file.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL Transform Feedback Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script id="vshader" type="x-shader/x-vertex">#version 300 es
+in vec3 position;
+void main()
+{
+ gl_Position = vec4(position, 1);
+}
+</script>
+<script id="fshader" type="x-shader/x-fragment">#version 300 es
+precision mediump float;
+out vec4 color;
+void main()
+{
+ color = vec4(0);
+}
+</script>
+<script>
+"use strict";
+description("Test that specifying non-existent varyings for transform feedback causes the program to fail to link. This test covers an ANGLE bug.");
+
+// Spec: GLES 3.0.5 section 2.12.8:
+// "A program will fail to link if:"
+// "any variable name specified in the varyings array is not declared as an output in the vertex shader;"
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var gl = wtu.create3DContext(undefined, undefined, 2);
+
+if (!gl) {
+ testFailed("WebGL context does not exist");
+} else {
+ runTest("bogus");
+ runTest("gl_Bogus");
+}
+
+function runTest(nonExistentVaryingName) {
+ var program = wtu.setupTransformFeedbackProgram(gl, ["vshader", "fshader"],
+ [nonExistentVaryingName], gl.INTERLEAVED_ATTRIBS,
+ ["position"]);
+ var msg = "Program should fail to link when a nonexistent varying '" + nonExistentVaryingName + "' is specified for transform feedback.";
+ if (program) {
+ testFailed(msg);
+ } else {
+ testPassed(msg);
+ }
+}
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/same-buffer-two-binding-points.html b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/same-buffer-two-binding-points.html
new file mode 100644
index 0000000000..86e7a5d402
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/same-buffer-two-binding-points.html
@@ -0,0 +1,176 @@
+<!--
+Copyright (c) 2019 The Khronos Group Inc.
+Use of this source code is governed by an MIT-style license that can be
+found in the LICENSE.txt file.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL Transform Feedback Conformance Tests - one buffer bound to two binding points</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script id="vshader" type="x-shader/x-vertex">#version 300 es
+
+in vec4 in_data;
+out vec4 out_add;
+out vec4 out_mul;
+void main(void) {
+ out_add = in_data + vec4(2.0, 3.0, 4.0, 5.0);
+ out_mul = in_data * vec4(2.0, 3.0, 4.0, 5.0);
+}
+</script>
+<script>
+"use strict";
+description();
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var gl = wtu.create3DContext(null, null, 2);
+var program = null;
+
+if (!gl) {
+ testFailed("WebGL context does not exist");
+} else {
+ testPassed("WebGL context exists");
+ runTwoOutFeedbackTest();
+}
+
+function doDrawWithTransformFeedback(expectedError, msg) {
+ gl.beginTransformFeedback(gl.POINTS);
+ wtu.glErrorShouldBe(gl, expectedError, msg);
+ gl.drawArrays(gl.POINTS, 0, 3);
+ gl.endTransformFeedback();
+ gl.getError();
+}
+
+function runTwoOutFeedbackTest() {
+ debug("");
+ debug("Test binding the same buffer to two transform feedback binding points. Buffer should be untouched and an error should be generated.")
+
+ // Build the input and output buffers
+ var in_data = [
+ 1.0, 2.0, 3.0, 4.0,
+ 2.0, 4.0, 8.0, 16.0,
+ 0.75, 0.5, 0.25, 0.0
+ ];
+
+ var in_buffer = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, in_buffer);
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(in_data), gl.STATIC_DRAW);
+
+ // Create the transform feedback shader
+ program = wtu.setupTransformFeedbackProgram(gl, ["vshader", wtu.simpleColorFragmentShaderESSL300],
+ ["out_add", "out_mul"], gl.SEPARATE_ATTRIBS,
+ ["in_data"]);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "linking transform feedback shader should not set an error");
+ shouldBeNonNull("program");
+
+ // Draw the the transform feedback buffers
+ var tf = gl.createTransformFeedback();
+
+ gl.enableVertexAttribArray(0);
+ gl.bindBuffer(gl.ARRAY_BUFFER, in_buffer);
+ gl.vertexAttribPointer(0, 4, gl.FLOAT, false, 16, 0);
+
+ var out_buffer = gl.createBuffer();
+ var out_buffer2 = gl.createBuffer();
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, out_buffer);
+
+ // Test binding the same buffer to two transform feedback binding points with bindBufferBase.
+ debug("");
+ gl.enable(gl.RASTERIZER_DISCARD);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, Float32Array.BYTES_PER_ELEMENT * in_data.length * 2, gl.STATIC_DRAW);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, out_buffer);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, out_buffer);
+
+ doDrawWithTransformFeedback(gl.INVALID_OPERATION, "same buffer bound to two transform feedback binding points with bindBufferBase");
+
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, null);
+ gl.disable(gl.RASTERIZER_DISCARD);
+
+ // Check buffer contents.
+ var expectedZeroes = [];
+ for (var i = 0; i < in_data.length * 2; ++i)
+ {
+ expectedZeroes.push(0.0);
+ }
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, out_buffer);
+ debug("buffer should be untouched - filled with zeroes");
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, expectedZeroes);
+
+ // Test binding the same buffer to two transform feedback binding points with bindBufferRange. The ranges overlap just slightly.
+ debug("");
+ gl.enable(gl.RASTERIZER_DISCARD);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, Float32Array.BYTES_PER_ELEMENT * in_data.length * 2, gl.STATIC_DRAW);
+ gl.bindBufferRange(gl.TRANSFORM_FEEDBACK_BUFFER, 0, out_buffer, 0, in_data.length * 4);
+ gl.bindBufferRange(gl.TRANSFORM_FEEDBACK_BUFFER, 1, out_buffer, (in_data.length - 1) * 4, in_data.length * 4);
+
+ doDrawWithTransformFeedback(gl.INVALID_OPERATION, "same buffer bound to two transform feedback binding points with bindBufferRange, overlapping ranges");
+
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, null);
+ gl.disable(gl.RASTERIZER_DISCARD);
+
+ // Check buffer contents.
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, out_buffer);
+ debug("buffer should be untouched - filled with zeroes");
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, expectedZeroes);
+
+ // Test non-overlapping ranges.
+ debug("");
+ gl.enable(gl.RASTERIZER_DISCARD);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, Float32Array.BYTES_PER_ELEMENT * in_data.length * 2, gl.STATIC_DRAW);
+ gl.bindBufferRange(gl.TRANSFORM_FEEDBACK_BUFFER, 0, out_buffer, 0, in_data.length * 4);
+ gl.bindBufferRange(gl.TRANSFORM_FEEDBACK_BUFFER, 1, out_buffer, in_data.length * 4, in_data.length * 4);
+
+ doDrawWithTransformFeedback(gl.INVALID_OPERATION, "same buffer bound to two transform feedback binding points with bindBufferRange, non-overlapping ranges");
+
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, null);
+ gl.disable(gl.RASTERIZER_DISCARD);
+
+ // Check buffer contents.
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, out_buffer);
+ debug("buffer should be untouched - filled with zeroes");
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, expectedZeroes);
+
+ // Test binding the same buffer to a binding point that doesn't have a corresponding output in the vertex shader.
+ debug("");
+ gl.enable(gl.RASTERIZER_DISCARD);
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, out_buffer2);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, Float32Array.BYTES_PER_ELEMENT * in_data.length * 2, gl.STATIC_DRAW);
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, out_buffer);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, Float32Array.BYTES_PER_ELEMENT * in_data.length * 2, gl.STATIC_DRAW);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, out_buffer);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, out_buffer2);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 2, out_buffer); // No corresponding output.
+
+ doDrawWithTransformFeedback(gl.INVALID_OPERATION, "same buffer bound to two transform feedback binding points with bindBufferBase, but one of the binding points doesn't have a corresponding shader output");
+
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, null);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 2, null);
+ gl.disable(gl.RASTERIZER_DISCARD);
+
+ // Check buffer contents.
+ debug("buffers should be untouched - filled with zeroes");
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, out_buffer);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, expectedZeroes);
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, out_buffer2);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, expectedZeroes);
+
+ finishTest();
+}
+</script>
+
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/simultaneous_binding.html b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/simultaneous_binding.html
new file mode 100644
index 0000000000..228b4ab5cf
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/simultaneous_binding.html
@@ -0,0 +1,330 @@
+<!--
+Copyright (c) 2019 The Khronos Group Inc.
+Use of this source code is governed by an MIT-style license that can be
+found in the LICENSE.txt file.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Simultaneous binding</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas>
+<div id="console"></div>
+<script id="vshader" type="x-shader/x-vertex">#version 300 es
+in float in_value;
+in float in_value2;
+out float out_value;
+
+void main() {
+ out_value = in_value * 2.;
+}
+</script>
+<script id="fshader" type="x-shader/x-fragment">#version 300 es
+precision mediump float;
+out vec4 dummy;
+uniform UniformBlock {
+ float fragment_value;
+};
+void main() {
+ dummy = vec4(fragment_value);
+}
+</script>
+<script>
+"use strict";
+description("This test verifies that access to a buffer simultaneously bound to a transform feedback object and a non-transform-feedback binding point is forbidden.");
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var canvas = document.getElementById("canvas");
+var gl = wtu.create3DContext(canvas, null, 2);
+
+if (!gl) {
+ testFailed("WebGL context does not exist");
+} else {
+ testPassed("WebGL context exists");
+}
+
+function drawWithFeedbackBound(gl, drawFunction, prog, vao, tf, enableFeedback) {
+ gl.useProgram(prog);
+ gl.bindVertexArray(vao);
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+ if (enableFeedback) gl.beginTransformFeedback(gl.POINTS);
+ let error = gl.getError();
+ if (error != gl.NO_ERROR) testFailed("Unexpected error before drawing: " + error)
+ drawFunction();
+ if (enableFeedback) gl.endTransformFeedback();
+ gl.bindVertexArray(null);
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
+}
+
+function createBuffer(gl, dataOrSize) {
+ const buf = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
+ gl.bufferData(gl.ARRAY_BUFFER, dataOrSize, gl.STATIC_DRAW);
+ gl.bindBuffer(gl.ARRAY_BUFFER, null);
+ return buf;
+}
+
+function createVAO(gl, vertexBuffer, vertexBuffer2, indexBuffer) {
+ const vao = gl.createVertexArray();
+ gl.bindVertexArray(vao);
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
+ gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
+ gl.enableVertexAttribArray(0);
+ gl.vertexAttribPointer(0, 1, gl.FLOAT, false, 0, 0);
+ gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer2);
+ gl.enableVertexAttribArray(1);
+ gl.vertexAttribPointer(1, 1, gl.FLOAT, false, 0, 0);
+ gl.vertexAttribDivisor(1, 2);
+ gl.bindBuffer(gl.ARRAY_BUFFER, null);
+ gl.bindVertexArray(null);
+ return vao;
+}
+
+const prog = wtu.setupTransformFeedbackProgram(gl, ["vshader", "fshader"],
+ ["out_value"], gl.SEPARATE_ATTRIBS,
+ ["in_value", "in_value2"]);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "linking transform feedback shader should not set an error");
+
+const vertexBuffer = createBuffer(gl, new Float32Array([1, 2, 3, 4]));
+const vertexBuffer2 = createBuffer(gl, new Float32Array([1, 2, 3, 4]));
+const vertexBuffer3 = createBuffer(gl, new Float32Array([1, 2, 3, 4]));
+
+const indexBuffer = gl.createBuffer();
+gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
+gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Int16Array([0, 1, 2, 3]), gl.STATIC_DRAW);
+
+const tfBuffer = createBuffer(gl, new Float32Array([0, 0, 0, 0]));
+
+const vao = createVAO(gl, vertexBuffer, vertexBuffer2, indexBuffer);
+// This tests that having a transform feedback buffer bound in an unbound VAO
+// does not affect anything.
+const unboundVao = createVAO(gl, tfBuffer, tfBuffer, indexBuffer);
+
+const tf = gl.createTransformFeedback();
+gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+gl.useProgram(prog);
+gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer);
+// this binds the default (id = 0) TRANSFORM_FEEBACK buffer
+gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
+
+const uniformBuffer = createBuffer(gl, new Float32Array([1, 0, 0, 0]));
+const ubi = gl.getUniformBlockIndex(prog, "UniformBlock");
+gl.uniformBlockBinding(prog, ubi, 0);
+gl.bindBufferBase(gl.UNIFORM_BUFFER, 0, uniformBuffer);
+
+const drawFunctions = [
+ [
+ ()=>gl.drawArrays(gl.POINTS, 0, 4),
+ ()=>gl.drawElements(gl.POINTS, 4, gl.UNSIGNED_SHORT, 0),
+ ],
+ [
+ ()=>gl.drawArraysInstanced(gl.POINTS, 0, 4, 1),
+ ()=>gl.drawElementsInstanced(gl.POINTS, 4, gl.UNSIGNED_SHORT, 0, 1),
+ ],
+ [
+ ()=>gl.drawArrays(gl.POINTS, 0, 4),
+ ()=>gl.drawRangeElements(gl.POINTS, 0, 3, 4, gl.UNSIGNED_SHORT, 0),
+ ],
+ ];
+
+for (let [drawArrays, drawElements] of drawFunctions) {
+ debug("<h3>With draw functions " + drawArrays + " and " + drawElements + "</h3>");
+ debug("<hr/>Test baseline");
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 16, gl.STATIC_DRAW);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "bufferData to TRANSFORM_FEEDBACK_BUFFER");
+ drawWithFeedbackBound(gl, drawElements, prog, vao, tf, false);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElements should be successful");
+ drawWithFeedbackBound(gl, drawArrays, prog, vao, tf, true);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "transform feedback should be successful");
+
+ const expected = [2, 4, 6, 8];
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, expected);
+
+ debug("<hr/>Test generic bind point set to null");
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, null);
+ drawWithFeedbackBound(gl, drawElements, prog, vao, tf, false);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElements should be successful");
+ drawWithFeedbackBound(gl, drawArrays, prog, vao, tf, true);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "transform feedback should be successful");
+
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, expected);
+
+ debug("<hr/>Test generic bind point set to vertex buffer");
+ // The TRANSFORM_FEEDBACK_BUFFER generic binding point is not part of the
+ // transform feedback object and not written to by transform feedback. Only
+ // the indexed binding points are written to. So it should be legal to draw
+ // from a buffer bound to the generic binding point.
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, vertexBuffer);
+ drawWithFeedbackBound(gl, drawElements, prog, vao, tf, false);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElements should be successful");
+ drawWithFeedbackBound(gl, drawArrays, prog, vao, tf, true);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "transform feedback should be successful");
+
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, expected);
+
+ debug("<hr/>Test ARRAY_BUFFER");
+ // this should fail because the transform feedback's buffer #0 and the
+ // badVao's buffer #0 are the same buffer
+ const badVao = createVAO(gl, tfBuffer, vertexBuffer2, indexBuffer);
+ drawWithFeedbackBound(gl, drawArrays, prog, badVao, tf, false);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawArrays: buffer used as vertex attrib and tf simultaneously");
+ drawWithFeedbackBound(gl, drawElements, prog, badVao, tf, false);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawElements: buffer used as vertex attrib and tf simultaneously");
+ drawWithFeedbackBound(gl, drawArrays, prog, badVao, tf, true);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "buffer used as vertex attrib and tf simultaneously");
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, expected, "should be the same as before as nothing has executed");
+
+ debug("<hr/>Test UNIFORM_BUFFER");
+ gl.bindBufferBase(gl.UNIFORM_BUFFER, 0, tfBuffer);
+ gl.bindBuffer(gl.UNIFORM_BUFFER, null); // tfBuffer is still bound at index 0
+ drawWithFeedbackBound(gl, drawArrays, prog, vao, tf, false);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawArrays: buffer used as uniform buffer and tf simultaneously");
+ drawWithFeedbackBound(gl, drawElements, prog, vao, tf, false);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawElements: buffer used as uniform buffer and tf simultaneously");
+ drawWithFeedbackBound(gl, drawArrays, prog, vao, tf, true);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "buffer used as uniform buffer and tf simultaneously");
+ gl.bindBufferBase(gl.UNIFORM_BUFFER, 0, uniformBuffer);
+ drawWithFeedbackBound(gl, drawArrays, prog, vao, tf, false);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawArrays: tf buffer not used as uniform buffer anymore");
+ drawWithFeedbackBound(gl, drawElements, prog, vao, tf, false);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElements: tf buffer not used as uniform buffer anymore");
+ drawWithFeedbackBound(gl, drawArrays, prog, vao, tf, true);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "tf buffer not used as uniform buffer anymore");
+
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+ const tfBuffer2 = createBuffer(gl, Float32Array.BYTES_PER_ELEMENT * 4);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer2);
+ drawWithFeedbackBound(gl, drawArrays, prog, badVao, tf);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "buffer is no longer bound for transform feedback");
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer);
+
+ debug("<hr/>Test TF buffer bound to target unused by draw");
+ // Even if the TF buffer is bound to a target that's not used by the draw, it's
+ // still an error.
+ gl.bindBuffer(gl.COPY_READ_BUFFER, tfBuffer);
+ drawWithFeedbackBound(gl, drawArrays, prog, vao, tf, true);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "tf enabled");
+ drawWithFeedbackBound(gl, drawArrays, prog, vao, tf, false);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawArrays: tf disabled");
+ drawWithFeedbackBound(gl, drawElements, prog, vao, tf, false);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElements: tf disabled");
+ gl.bindBuffer(gl.COPY_READ_BUFFER, null);
+
+ debug("<hr/>Test TF buffer bound to disabled vertex attrib");
+ // Having a TF buffer bound to a disabled vertex attrib should not be an error
+ // when TF is not enabled, because the buffer is not used.
+ gl.bindVertexArray(vao);
+ gl.bindBuffer(gl.ARRAY_BUFFER, tfBuffer);
+ gl.vertexAttribPointer(2, 1, gl.FLOAT, false, 0, 0);
+ gl.disableVertexAttribArray(2);
+ gl.bindVertexArray(null);
+ drawWithFeedbackBound(gl, drawArrays, prog, vao, tf, false);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "tf disabled, draw should succeed");
+ drawWithFeedbackBound(gl, drawElements, prog, vao, tf, false);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "tf disabled, draw should succeed");
+ // Remove the TF buffer binding from the VAO after the test.
+ gl.bindVertexArray(vao);
+ gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer3);
+ gl.vertexAttribPointer(2, 1, gl.FLOAT, false, 0, 0);
+ gl.disableVertexAttribArray(2);
+ gl.bindVertexArray(null);
+}
+
+debug("<h1>Non-drawing tests</h1>");
+
+debug("<hr/>Test bufferData");
+
+gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer);
+gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 16, gl.STATIC_DRAW);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "bufferData to TRANSFORM_FEEDBACK_BUFFER");
+gl.bindBuffer(gl.COPY_WRITE_BUFFER, tfBuffer);
+gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 16, gl.STATIC_DRAW);
+wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "bufferData with double bound buffer");
+gl.bindBuffer(gl.COPY_WRITE_BUFFER, null);
+
+// The value of the TRANSFORM_FEEDBACK_BUFFER generic bind point should not
+// affect the legality of any operation.
+let genericBindPointValues = [()=>null, ()=>tfBuffer, ()=>vertexBuffer];
+
+for (let genericBindPointValue of genericBindPointValues) {
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+ debug("<h3>With TRANSFORM_FEEDBACK_BUFFER generic bind point value " + genericBindPointValue + "</h3>");
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, genericBindPointValue());
+
+ debug("<hr/>Test PIXEL_UNPACK_BUFFER");
+ const tex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+ gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, vertexBuffer);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, 0);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "PIXEL_UNPACK_BUFFER is not bound for transform feedback");
+
+ gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, tfBuffer);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, 0);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "PIXEL_UNPACK_BUFFER is bound for transform feedback");
+ gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, null);
+
+ debug("<hr/>Test PIXEL_PACK_BUFFER");
+ gl.bindBuffer(gl.PIXEL_PACK_BUFFER, vertexBuffer);
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, 0);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "PIXEL_PACK_BUFFER is not bound for transform feedback");
+ gl.bindBuffer(gl.PIXEL_PACK_BUFFER, tfBuffer);
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, 0);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "PIXEL_PACK_BUFFER is bound for transform feedback");
+ gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null)
+
+ debug("<hr/>Test bufferData family with tf object bound");
+
+ gl.bindBuffer(gl.COPY_WRITE_BUFFER, tfBuffer);
+ gl.bufferData(gl.COPY_WRITE_BUFFER, 16, gl.STATIC_DRAW);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "bufferData with double bound buffer");
+ gl.bufferSubData(gl.COPY_WRITE_BUFFER, 0, new Uint8Array([0]));
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "bufferSubData with double bound buffer");
+ gl.getBufferSubData(gl.COPY_WRITE_BUFFER, 0, new Uint8Array([0]), 0, 1);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "getBufferSubData with double bound buffer");
+
+ gl.bindBuffer(gl.COPY_READ_BUFFER, vertexBuffer);
+ gl.copyBufferSubData(gl.COPY_WRITE_BUFFER, gl.COPY_READ_BUFFER, 0, 0, 1);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "copyBufferSubData with double bound buffer");
+ gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 1);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "copyBufferSubData with double bound buffer");
+
+ debug("<hr/>Test bufferData family with tf object unbound");
+
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
+ gl.bindBuffer(gl.COPY_WRITE_BUFFER, tfBuffer);
+ gl.bufferData(gl.COPY_WRITE_BUFFER, 16, gl.STATIC_DRAW);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "bufferData should succeed");
+ gl.bufferSubData(gl.COPY_WRITE_BUFFER, 0, new Uint8Array([0]));
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "bufferSubData should succeed");
+ gl.getBufferSubData(gl.COPY_WRITE_BUFFER, 0, new Uint8Array([0]), 0, 1);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "getBufferSubData should succeed");
+
+ gl.bindBuffer(gl.COPY_READ_BUFFER, vertexBuffer);
+ gl.copyBufferSubData(gl.COPY_WRITE_BUFFER, gl.COPY_READ_BUFFER, 0, 0, 1);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "copyBufferSubData should succeed");
+ gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 1);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "copyBufferSubData should succeed");
+}
+
+finishTest();
+
+</script>
+
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/switching-objects.html b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/switching-objects.html
new file mode 100644
index 0000000000..fce82dd21f
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/switching-objects.html
@@ -0,0 +1,231 @@
+<!--
+Copyright (c) 2019 The Khronos Group Inc.
+Use of this source code is governed by an MIT-style license that can be
+found in the LICENSE.txt file.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Switching transform feedback objects</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas>
+<div id="console"></div>
+<script id="vshader" type="x-shader/x-vertex">#version 300 es
+in float in_value;
+out float out_value1;
+out float out_value2;
+
+void main() {
+ out_value1 = in_value * 2.;
+ out_value2 = in_value * 4.;
+}
+</script>
+<script id="fshader" type="x-shader/x-fragment">#version 300 es
+precision mediump float;
+out vec4 dummy;
+void main() {
+ dummy = vec4(0.);
+}
+</script>
+<script>
+"use strict";
+description("Tests switching transform feedback objects.");
+
+debug("<h3>Setup</h3>")
+
+var wtu = WebGLTestUtils;
+var canvas = document.getElementById("canvas");
+var gl = wtu.create3DContext(canvas, null, 2);
+if (!gl) {
+ testFailed("WebGL context does not exist");
+}
+
+// Setup
+const prog_interleaved = wtu.setupTransformFeedbackProgram(gl, ["vshader", "fshader"],
+ ["out_value1", "out_value2"], gl.INTERLEAVED_ATTRIBS,
+ ["in_value"]);
+const prog_no_varyings = wtu.setupTransformFeedbackProgram(gl, ["vshader", "fshader"],
+ [], gl.INTERLEAVED_ATTRIBS,
+ ["in_value"]);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "shader compilation");
+const vertexBuffer = createBuffer(gl, new Float32Array([1, 2, 3, 4]));
+gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
+gl.enableVertexAttribArray(0);
+gl.vertexAttribPointer(0, 1, gl.FLOAT, false, 0, 0);
+gl.useProgram(prog_interleaved);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "vertex buffer and program setup");
+
+const tf1 = gl.createTransformFeedback();
+const tf2 = gl.createTransformFeedback();
+const tfBuffer1 = createBuffer(gl, new Float32Array([0, 0]));
+const tfBuffer2 = createBuffer(gl, new Float32Array([0, 0]));
+gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf1);
+gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer1);
+gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf2);
+gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer2);
+const expected_tf_output = [2, 4];
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "TF object setup");
+
+debug("<h3>Baseline transform feedback success case</h3>");
+
+gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf1);
+gl.beginTransformFeedback(gl.POINTS);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "begin TF");
+gl.drawArrays(gl.POINTS, 0, 1);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "draw");
+gl.endTransformFeedback();
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "end TF");
+
+gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer1);
+wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, expected_tf_output);
+
+debug("<h3>Generic binding is not changed when switching TF object</h3>");
+
+// According to the GL ES spec historically, TRANSFORM_FEEDBACK_BUFFER_BINDING is listed as part
+// of the transform feedback object state. However, many drivers treat it as global context state
+// and not part of the tranform feedback object, which means that it does not change when
+// bindTransformFeedback is called. Khronos has resolved to change the spec to specify the latter
+// behavior: https://gitlab.khronos.org/opengl/API/issues/66 (Khronos private link). This tests
+// for the new behavior.
+
+// Set each buffer to contain its buffer number. We use this to check which
+// buffer is *really* bound at the driver level by reading the buffer contents.
+gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
+gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer1);
+gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, new Float32Array([1]), gl.STREAM_READ);
+gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer2);
+gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, new Float32Array([2]), gl.STREAM_READ);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "bufferData");
+
+gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf1);
+checkParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, tfBuffer2);
+checkIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0, tfBuffer1);
+wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, [2]);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "readback");
+
+gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, null);
+gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf2);
+checkParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, null);
+checkIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0, tfBuffer2);
+
+debug("<h3>Error switching TF object while TF is enabled</h3>");
+
+gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf1);
+gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer1);
+gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, new Float32Array([0, 0]), gl.STREAM_READ);
+gl.beginTransformFeedback(gl.POINTS);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "begin");
+checkParameter(gl.TRANSFORM_FEEDBACK_BINDING, tf1);
+
+gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf2);
+wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "bind while unpaused");
+
+// Check that nothing actually changed and rendering still works
+checkParameter(gl.TRANSFORM_FEEDBACK_BINDING, tf1);
+gl.drawArrays(gl.POINTS, 0, 1);
+gl.endTransformFeedback();
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "transform feedback should complete successfully");
+wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, expected_tf_output);
+
+
+debug("<h3>Successfully switching TF object while TF is paused</h3>");
+
+gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer1);
+gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, new Float32Array([0, 0]), gl.STREAM_READ);
+gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer2);
+gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, new Float32Array([0, 0]), gl.STREAM_READ);
+
+gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf2);
+gl.beginTransformFeedback(gl.POINTS);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "begin on tf2");
+checkParameter(gl.TRANSFORM_FEEDBACK_BINDING, tf2);
+
+gl.pauseTransformFeedback();
+gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf1);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "bind while paused");
+gl.beginTransformFeedback(gl.POINTS);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "begin on tf1");
+checkParameter(gl.TRANSFORM_FEEDBACK_BINDING, tf1);
+gl.drawArrays(gl.POINTS, 0, 1);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "draw should succeed");
+gl.endTransformFeedback();
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "end on tf1");
+gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer1);
+wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, expected_tf_output);
+
+gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf2);
+gl.endTransformFeedback();
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "end on tf2");
+
+debug("<h3>Misc. invalid operations</h3>")
+
+gl.endTransformFeedback();
+wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "endTransformFeedback before begin");
+gl.pauseTransformFeedback();
+wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "pauseTransformFeedback when not active");
+gl.resumeTransformFeedback();
+wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "pauseTransformFeedback when not active");
+
+gl.beginTransformFeedback(gl.POINTS);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "transform feedback should begin successfully");
+gl.drawArrays(gl.TRIANGLE_STRIP, 0, 1);
+wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "wrong primitive mode");
+gl.useProgram(prog_no_varyings);
+wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "switch program while active");
+gl.resumeTransformFeedback();
+wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "resumeTransformFeedback when not paused");
+gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf2);
+wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "bindTransformFeedback when active");
+gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer2);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "bindBuffer(TRANSFORM_FEEDBACK_BUFFER) when active");
+gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer2);
+wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "bindBufferBase(TRANSFORM_FEEDBACK_BUFFER) when active");
+
+gl.pauseTransformFeedback();
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "pause");
+gl.pauseTransformFeedback();
+wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "already paused");
+gl.endTransformFeedback();
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "end while paused");
+
+finishTest();
+
+// Helper functions
+function createBuffer(gl, dataOrSize) {
+ const buf = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
+ gl.bufferData(gl.ARRAY_BUFFER, dataOrSize, gl.STATIC_DRAW);
+ gl.bindBuffer(gl.ARRAY_BUFFER, null);
+ return buf;
+}
+
+function checkParameter(param, expected) {
+ const value = gl.getParameter(param);
+ if (value != expected) {
+ testFailed(wtu.glEnumToString(gl, param) + " was " + value + ", but expected " + expected);
+ } else {
+ testPassed(wtu.glEnumToString(gl, param) + " was " + value + ", matching expected " + expected);
+ }
+}
+
+function checkIndexedParameter(param, index, expected) {
+ const value = gl.getIndexedParameter(param, index);
+ if (value != expected) {
+ testFailed(wtu.glEnumToString(gl, param) + "[" + index + "] was " + value + ", but expected " + expected);
+ } else {
+ testPassed(wtu.glEnumToString(gl, param) + "[" + index + "] was " + value + ", matching expected " + expected);
+ }
+}
+
+
+</script>
+
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/too-small-buffers.html b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/too-small-buffers.html
new file mode 100644
index 0000000000..64acfbad7b
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/too-small-buffers.html
@@ -0,0 +1,242 @@
+<!--
+Copyright (c) 2019 The Khronos Group Inc.
+Use of this source code is governed by an MIT-style license that can be
+found in the LICENSE.txt file.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>TF too small buffers</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas>
+<div id="console"></div>
+<script id="vshader" type="x-shader/x-vertex">#version 300 es
+in float in_value1;
+in float in_value2;
+out float out_value1;
+out float out_value2;
+void main() {
+ out_value1 = in_value1 * 2.;
+ out_value2 = in_value2 * 2.;
+}
+</script>
+<script id="fshader" type="x-shader/x-fragment">#version 300 es
+precision mediump float;
+out vec4 dummy;
+void main() {
+ dummy = vec4(0.);
+}
+</script>
+<script>
+"use strict";
+description("Transform feedback into buffers that are too small should produce errors.");
+
+var wtu = WebGLTestUtils;
+var canvas = document.getElementById("canvas");
+var gl = wtu.create3DContext(canvas, null, 2);
+
+if (!gl) {
+ testFailed("WebGL context does not exist");
+} else {
+ testPassed("WebGL context exists");
+}
+
+const progInterleaved = wtu.setupTransformFeedbackProgram(gl, ["vshader", "fshader"],
+ ["out_value1", "out_value2"], gl.INTERLEAVED_ATTRIBS,
+ ["in_value1", "in_value2"]);
+const progSeparate = wtu.setupTransformFeedbackProgram(gl, ["vshader", "fshader"],
+ ["out_value1", "out_value2"], gl.SEPARATE_ATTRIBS,
+ ["in_value1", "in_value2"]);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "program compilation");
+
+// Attrib 1 contains 4 vertices. Attrib 2 contains 4 instance indices.
+const vertexBuffer0 = gl.createBuffer();
+gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer0);
+gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 2, 3, 4]), gl.STATIC_DRAW);
+const vertexBuffer1 = gl.createBuffer();
+gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer1);
+gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 2, 3, 4]), gl.STATIC_DRAW);
+gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer0);
+gl.enableVertexAttribArray(0);
+gl.vertexAttribPointer(0, 1, gl.FLOAT, false, 0, 0);
+gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer1);
+gl.enableVertexAttribArray(1);
+gl.vertexAttribPointer(1, 1, gl.FLOAT, false, 0, 0);
+gl.vertexAttribDivisor(1, 1);
+
+let tfBuffer0 = gl.createBuffer();
+let tfBuffer1 = gl.createBuffer();
+
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "setup");
+
+const sizeOfFloat = 4;
+
+let cases = [
+ { name: "drawArrays",
+ drawFunction: ()=>gl.drawArrays(gl.POINTS, 0, 4),
+ result: [[2, 4, 6, 8], [2, 2, 2, 2]]},
+ { name: "drawArraysInstanced one instance",
+ drawFunction: ()=>gl.drawArraysInstanced(gl.POINTS, 0, 4, 1),
+ result: [[2, 4, 6, 8], [2, 2, 2, 2]]},
+ { name: "drawArraysInstanced four instances",
+ drawFunction: ()=>gl.drawArraysInstanced(gl.POINTS, 0, 1, 4),
+ result: [[2, 2, 2, 2], [2, 4, 6, 8]]},
+ ];
+
+for (let {name, drawFunction, result} of cases) {
+ debug("<h1>" + name + "</h1>")
+
+ let interleavedResult = [];
+ for (let i = 0; i < result[0].length; i++) {
+ interleavedResult.push(result[0][i], result[1][i]);
+ }
+
+ let doTransformFeedback = (drawFunction, error) => {
+ gl.beginTransformFeedback(gl.POINTS);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "before draw");
+ drawFunction();
+ wtu.glErrorShouldBe(gl, error, "draw");
+ gl.endTransformFeedback();
+ }
+
+ gl.useProgram(progInterleaved);
+
+ debug("<h3>interleaved - Baseline success case</h3>")
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 8*sizeOfFloat, gl.STREAM_READ);
+ doTransformFeedback(drawFunction, gl.NO_ERROR);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, interleavedResult);
+
+ debug("<h3>interleaved - Buffer too small</h3>")
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 8*sizeOfFloat-1, gl.STREAM_READ);
+ doTransformFeedback(drawFunction, gl.INVALID_OPERATION);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER,
+ [0, 0, 0, 0, 0, 0, 0]);
+
+ debug("<h3>interleaved - Multiple draws success case</h3>")
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 8*sizeOfFloat*2, gl.STREAM_READ);
+ doTransformFeedback(()=>{drawFunction(); drawFunction()}, gl.NO_ERROR);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, interleavedResult.concat(interleavedResult))
+
+ debug("<h3>interleaved - Too small for multiple draws</h3>")
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 8*sizeOfFloat*2-1, gl.STREAM_READ);
+ doTransformFeedback(()=>{drawFunction(); drawFunction()}, gl.INVALID_OPERATION);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, interleavedResult.concat([0, 0, 0, 0, 0, 0, 0]))
+
+ debug("<h3>interleaved - bindBufferRange too small</h3>")
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 8*sizeOfFloat, gl.STREAM_READ);
+ gl.bindBufferRange(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0, 0, 7*sizeOfFloat);
+ doTransformFeedback(drawFunction, gl.INVALID_OPERATION);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER,
+ [0, 0, 0, 0, 0, 0, 0, 0]);
+
+ debug("<h3>interleaved - bindBufferRange larger than buffer</h3>")
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 8*sizeOfFloat-1, gl.STREAM_READ);
+ gl.bindBufferRange(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0, 0, 8*sizeOfFloat);
+ doTransformFeedback(drawFunction, gl.INVALID_OPERATION);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER,
+ [0, 0, 0, 0, 0, 0, 0]);
+
+ gl.useProgram(progSeparate);
+
+ debug("<h3>separate - Baseline success case</h3>")
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat, gl.STREAM_READ);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, tfBuffer1);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat, gl.STREAM_READ);
+ doTransformFeedback(drawFunction, gl.NO_ERROR);
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer0);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, result[0]);
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer1);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, result[1]);
+
+ debug("<h3>separate - Buffer too small</h3>")
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat, gl.STREAM_READ);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, tfBuffer1);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat-1, gl.STREAM_READ);
+ doTransformFeedback(drawFunction, gl.INVALID_OPERATION);
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer0);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, [0, 0, 0, 0]);
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer1);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, [0, 0, 0]);
+
+ debug("<h3>separate - multiple draws success case</h3>")
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat*2, gl.STREAM_READ);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, tfBuffer1);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat*2, gl.STREAM_READ);
+ doTransformFeedback(()=>{drawFunction(); drawFunction();}, gl.NO_ERROR);
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer0);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, result[0].concat(result[0]));
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer1);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, result[1].concat(result[1]));
+
+ debug("<h3>separate - Too small for multiple draws</h3>")
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat*2, gl.STREAM_READ);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, tfBuffer1);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat*2-1, gl.STREAM_READ);
+ doTransformFeedback(()=>{drawFunction(); drawFunction();}, gl.INVALID_OPERATION);
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer0);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, result[0].concat([0, 0, 0, 0]));
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer1);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, result[1].concat([0, 0, 0]));
+
+ debug("<h3>separate - bindBufferRange too small</h3>")
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat, gl.STREAM_READ);
+ gl.bindBufferRange(gl.TRANSFORM_FEEDBACK_BUFFER, 1, tfBuffer1, 0, 3*sizeOfFloat);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat, gl.STREAM_READ);
+ doTransformFeedback(drawFunction, gl.INVALID_OPERATION);
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer0);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, [0, 0, 0, 0]);
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer1);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, [0, 0, 0]);
+
+ debug("<h3>separate - bindBufferRange larger than buffer</h3>")
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat, gl.STREAM_READ);
+ gl.bindBufferRange(gl.TRANSFORM_FEEDBACK_BUFFER, 1, tfBuffer1, 0, 4*sizeOfFloat);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat-1, gl.STREAM_READ);
+ doTransformFeedback(drawFunction, gl.INVALID_OPERATION);
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer0);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, [0, 0, 0, 0]);
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer1);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, [0, 0, 0]);
+}
+
+debug("<h1>integer overflow</h1>")
+
+gl.useProgram(progInterleaved);
+gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer0);
+gl.bufferData(gl.ARRAY_BUFFER, (1<<16)*sizeOfFloat, gl.STREAM_READ);
+gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer1);
+gl.bufferData(gl.ARRAY_BUFFER, (1<<16)*sizeOfFloat, gl.STREAM_READ);
+gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0);
+gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, (1<<16)*sizeOfFloat*2, gl.STREAM_READ);
+
+gl.beginTransformFeedback(gl.POINTS);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR, "before draw");
+// If count and primcount are stored in 32-bit signed integers and then
+// multiplied to calculate the number of transform feedback vertices, the
+// calculation will overflow to 0.
+gl.drawArraysInstanced(gl.POINTS, 0, 1<<16, 1<<16);
+wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "integer overflow and/or buffer too small");
+gl.endTransformFeedback();
+
+finishTest();
+
+</script>
+
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/transform_feedback.html b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/transform_feedback.html
new file mode 100644
index 0000000000..20256c6ace
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/transform_feedback.html
@@ -0,0 +1,645 @@
+<!--
+Copyright (c) 2019 The Khronos Group Inc.
+Use of this source code is governed by an MIT-style license that can be
+found in the LICENSE.txt file.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL Transform Feedback Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas>
+<div id="console"></div>
+<script id="vshader" type="x-shader/x-vertex">#version 300 es
+
+in vec4 in_data;
+out vec4 out_add;
+out vec4 out_mul;
+void main(void) {
+ out_add = in_data + vec4(2.0, 3.0, 4.0, 5.0);
+ out_mul = in_data * vec4(2.0, 3.0, 4.0, 5.0);
+}
+</script>
+<script id="fshader" type="x-shader/x-fragment">#version 300 es
+precision mediump float;
+out vec4 out_color;
+void main(void) {
+ out_color = vec4(1.0, 1.0, 1.0, 1.0);
+}
+</script>
+<script>
+"use strict";
+description("This test verifies the functionality of the Transform Feedback objects.");
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var canvas = document.getElementById("canvas");
+var gl = wtu.create3DContext(canvas, null, 2);
+var tf = null;
+var tf1 = null;
+var buf = null;
+let out_add_buffer = null;
+var program = null;
+var activeInfo = null;
+var query = null;
+var numberOfQueryCompletionAttempts = 0;
+
+if (!gl) {
+ testFailed("WebGL context does not exist");
+} else {
+ testPassed("WebGL context exists");
+
+ runBindingTest();
+ runTFBufferBindingTest();
+ runObjectTest();
+ runGetBufferSubDataTest();
+ runUnboundDeleteTest();
+ runBoundDeleteTest();
+ runOneOutputFeedbackTest();
+ // Must be the last test, since it's asynchronous and calls finishTest().
+ runTwoOutputFeedbackTest();
+}
+
+function runBindingTest() {
+ debug("");
+ debug("Testing binding enum");
+
+ shouldBe("gl.TRANSFORM_FEEDBACK_BINDING", "0x8E25");
+
+ gl.getParameter(gl.TRANSFORM_FEEDBACK_BINDING);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "TRANSFORM_FEEDBACK_BINDING query should succeed");
+
+ // Default value is null
+ shouldBe("gl.getParameter(gl.TRANSFORM_FEEDBACK_BINDING)", "null");
+
+ debug("Testing binding a Transform Feedback object");
+ tf = gl.createTransformFeedback();
+ tf1 = gl.createTransformFeedback();
+ shouldBeNull("gl.getParameter(gl.TRANSFORM_FEEDBACK_BINDING)");
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+ shouldBe("gl.getParameter(gl.TRANSFORM_FEEDBACK_BINDING)", "tf");
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf1);
+ shouldBe("gl.getParameter(gl.TRANSFORM_FEEDBACK_BINDING)", "tf1");
+ gl.deleteTransformFeedback(tf);
+ gl.deleteTransformFeedback(tf1);
+ shouldBeNull("gl.getParameter(gl.TRANSFORM_FEEDBACK_BINDING)");
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf1);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "binding a deleted Transform Feedback object");
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
+ shouldBeNull("gl.getParameter(gl.TRANSFORM_FEEDBACK_BINDING)");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors");
+}
+
+function runTFBufferBindingTest() {
+ debug("");
+ debug("Testing binding and unbinding transform feedback objects and buffers");
+
+ buf = gl.createBuffer();
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, buf);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 16, gl.STATIC_DRAW);
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, null);
+
+ tf = gl.createTransformFeedback();
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, buf);
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
+
+ // gl.TRANSFORM_FEEDBACK_BUFFER is part of Transform Feedback objects'
+ // state. See OpenGL ES 3.0.5 Section 6.24.
+ //
+ // Since the TRANSFORM_FEEDBACK was just unbound, there should be nothing
+ // bound.
+ shouldBeNull('gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)');
+
+ // Binding the buffer to the ARRAY_BUFFER binding point should succeed.
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "binding buffer to ARRAY_BUFFER");
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, null);
+ gl.deleteBuffer(buf);
+ buf = null;
+ gl.deleteTransformFeedback(tf);
+ tf = null;
+}
+
+function runObjectTest() {
+ debug("");
+ debug("Testing object creation");
+
+ tf = gl.createTransformFeedback();
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "createTransformFeedback should not set an error");
+ shouldBeNonNull("tf");
+
+ // Expect false if never bound
+ shouldBeFalse("gl.isTransformFeedback(tf)");
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+ shouldBeTrue("gl.isTransformFeedback(tf)");
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
+ shouldBeTrue("gl.isTransformFeedback(tf)");
+ gl.deleteTransformFeedback(tf);
+ shouldBeFalse("gl.isTransformFeedback(tf)");
+
+ shouldBeFalse("gl.isTransformFeedback(null)");
+
+ tf = null;
+}
+
+function runOneOutputFeedbackTest() {
+ debug("");
+ debug("Testing one-output transform feedback");
+
+ // Build the input and output buffers
+ var in_data = [
+ 1.0, 2.0, 3.0, 4.0,
+ 2.0, 4.0, 8.0, 16.0,
+ 0.75, 0.5, 0.25, 0.0
+ ];
+
+ var in_buffer = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, in_buffer);
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(in_data), gl.STATIC_DRAW);
+
+ out_add_buffer = gl.createBuffer();
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, out_add_buffer);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, Float32Array.BYTES_PER_ELEMENT * in_data.length, gl.STATIC_DRAW);
+
+ // Create the transform feedback shader
+ program = wtu.setupTransformFeedbackProgram(gl, ["vshader", "fshader"],
+ ["out_add"], gl.SEPARATE_ATTRIBS,
+ ["in_data"]);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "linking transform feedback shader should not set an error");
+ shouldBeNonNull("program");
+
+ // Draw the the transform feedback buffers
+ tf = gl.createTransformFeedback();
+
+ gl.enableVertexAttribArray(0);
+ gl.bindBuffer(gl.ARRAY_BUFFER, in_buffer);
+ gl.vertexAttribPointer(0, 4, gl.FLOAT, false, 16, 0);
+
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, out_add_buffer);
+
+ gl.enable(gl.RASTERIZER_DISCARD);
+ gl.beginTransformFeedback(gl.POINTS);
+
+ debug("Testing switching program while transform feedback is active");
+ gl.pauseTransformFeedback();
+ var program2 = wtu.setupSimpleColorProgram(gl);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Switching program while transform feedback is active and paused should succeed");
+ gl.useProgram(program);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Switching program while transform feedback is active and paused should succeed");
+ gl.resumeTransformFeedback();
+ gl.useProgram(program2);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Switching program while transform feedback is active should fail");
+ shouldBe("gl.getParameter(gl.CURRENT_PROGRAM)", "program");
+
+ debug("Testing deleting an active transform feedback object");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors before testing deletion");
+ gl.deleteTransformFeedback(tf);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Deleting the transform feedback while active should fail, and have no effect");
+ shouldBe("gl.isTransformFeedback(tf)", "true");
+ debug("Resuming testing of single-output transform feedback");
+
+ gl.drawArrays(gl.POINTS, 0, 3);
+
+ gl.endTransformFeedback();
+ gl.disable(gl.RASTERIZER_DISCARD);
+
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
+
+ // Verify the output buffer contents
+ var add_expected = [
+ 3.0, 5.0, 7.0, 9.0,
+ 4.0, 7.0, 12.0, 21.0,
+ 2.75, 3.5, 4.25, 5.0
+ ];
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, out_add_buffer);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, add_expected);
+
+ gl.deleteBuffer(in_buffer);
+ gl.deleteBuffer(out_add_buffer);
+ gl.deleteProgram(program);
+ gl.deleteTransformFeedback(tf);
+
+ tf = null;
+ program = null;
+}
+
+function runTwoOutputFeedbackTest() {
+ debug("");
+ debug("Testing two-output transform feedback");
+
+ // Build the input and output buffers
+ var in_data = [
+ 1.0, 2.0, 3.0, 4.0,
+ 2.0, 4.0, 8.0, 16.0,
+ 0.75, 0.5, 0.25, 0.0
+ ];
+
+ var in_buffer = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, in_buffer);
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(in_data), gl.STATIC_DRAW);
+
+ out_add_buffer = gl.createBuffer();
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, out_add_buffer);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, Float32Array.BYTES_PER_ELEMENT * in_data.length, gl.STATIC_DRAW);
+
+ var out_mul_buffer = gl.createBuffer();
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, out_mul_buffer);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, Float32Array.BYTES_PER_ELEMENT * in_data.length, gl.STATIC_DRAW);
+
+ // Create the transform feedback shader
+ program = wtu.setupTransformFeedbackProgram(gl, ["vshader", "fshader"],
+ ["out_add", "out_mul"], gl.SEPARATE_ATTRIBS,
+ ["in_data"]);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "linking transform feedback shader should not set an error");
+ shouldBeNonNull("program");
+
+ // Create a query object to check the number of primitives written
+ query = gl.createQuery();
+
+ // Draw the the transform feedback buffers
+ tf = gl.createTransformFeedback();
+
+ gl.enableVertexAttribArray(0);
+ gl.bindBuffer(gl.ARRAY_BUFFER, in_buffer);
+ gl.vertexAttribPointer(0, 4, gl.FLOAT, false, 16, 0);
+
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, out_add_buffer);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, out_mul_buffer);
+
+ gl.enable(gl.RASTERIZER_DISCARD);
+ gl.beginTransformFeedback(gl.POINTS);
+ gl.beginQuery(gl.TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, query);
+
+ gl.drawArrays(gl.POINTS, 0, 3);
+
+ gl.endQuery(gl.TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
+ gl.endTransformFeedback();
+ gl.disable(gl.RASTERIZER_DISCARD);
+
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, null);
+
+ // Verify the output buffer contents
+ var add_expected = [
+ 3.0, 5.0, 7.0, 9.0,
+ 4.0, 7.0, 12.0, 21.0,
+ 2.75, 3.5, 4.25, 5.0
+ ];
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, out_add_buffer);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, add_expected);
+
+ var mul_expected = [
+ 2.0, 6.0, 12.0, 20.0,
+ 4.0, 12.0, 32.0, 80.0,
+ 1.5, 1.5, 1.0, 0.0
+ ];
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, out_mul_buffer);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, mul_expected);
+
+ gl.deleteBuffer(in_buffer);
+ gl.deleteBuffer(out_add_buffer);
+ gl.deleteBuffer(out_mul_buffer);
+ gl.deleteProgram(program);
+ gl.deleteTransformFeedback(tf);
+
+ tf = null;
+ program = null;
+
+ // Check the result of the query. It should not be available yet.
+ // This constant was chosen arbitrarily to take around 1 second on
+ // one WebGL implementation on one desktop operating system. (Busy-
+ // loops based on calling Date.now() have been found unreliable.)
+ var numEarlyTests = 50000;
+ while (--numEarlyTests > 0) {
+ gl.finish();
+ if (gl.getQueryParameter(query, gl.QUERY_RESULT_AVAILABLE)) {
+ testFailed("Query's result became available too early");
+ finishTest();
+ return;
+ }
+ }
+ testPassed("TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN query's result didn't become available too early");
+
+ // Complete the rest of the test asynchronously.
+ requestAnimationFrame(completeTransformFeedbackQueryTest);
+}
+
+function runUnboundDeleteTest() {
+ debug("");
+ debug("Testing deleting buffers attached to an unbound transform feedback object");
+
+ // Theoretically it would be possible to verify the result of performing
+ // transform feedback into a deleted buffer object. The buffer would have to
+ // be latched into a VAO as well as into the transform feedback object. In
+ // order to get the results out of the output buffer, it would be necessary
+ // to run transform feedback again, reading from the buffer bound to the
+ // VAO, and writing into a (non-deleted) buffer object latched into the
+ // transform feedback object. It's not possible to arrange things to be able
+ // to copyBufferSubData from the deleted buffer object into a temporary one
+ // for readback.
+
+ // This would be a lot of code to test an unlikely corner case, so instead,
+ // this test verifies simpler behaviors.
+
+ out_add_buffer = gl.createBuffer();
+ const output_buffer_length = Float32Array.BYTES_PER_ELEMENT * 16;
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, out_add_buffer);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, output_buffer_length, gl.STATIC_DRAW);
+
+ // Set up the transform feedback object
+ tf = gl.createTransformFeedback();
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, out_add_buffer);
+
+ // Unbind transform feedback and delete out_add_buffer.
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
+ gl.deleteBuffer(out_add_buffer);
+ debug("isBuffer should report false after deletion");
+ shouldBe("gl.isBuffer(out_add_buffer)", "false");
+
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+ debug("Transform feedback object should keep output buffer alive");
+ shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "out_add_buffer");
+ // Deleting the buffer again while the transform feedback is bound shouldn't unbind it.
+ gl.deleteBuffer(out_add_buffer);
+ debug("Deleting output buffer again should be a no-op");
+ shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "out_add_buffer");
+
+ // Try unbinding and rebinding the transform feedback object just
+ // to make sure that has no effect on the attached output buffer.
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+ debug("Transform feedback object should still keep output buffer alive");
+ shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "out_add_buffer");
+
+ gl.deleteTransformFeedback(tf);
+
+ tf = null;
+ out_add_buffer = null;
+}
+
+function runBoundDeleteTest() {
+ debug("");
+ debug("Testing deleting buffers attached to a bound transform feedback object");
+
+ out_add_buffer = gl.createBuffer();
+ const output_buffer_length = Float32Array.BYTES_PER_ELEMENT * 16;
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, out_add_buffer);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, output_buffer_length, gl.STATIC_DRAW);
+
+ // Set up the transform feedback object
+ tf = gl.createTransformFeedback();
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, out_add_buffer);
+
+ // Delete the output buffer
+ gl.deleteBuffer(out_add_buffer);
+
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+ debug("Buffer should have been unbound from active transform feedback");
+ shouldBeNull("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)");
+
+ gl.deleteTransformFeedback(tf);
+
+ tf = null;
+ out_add_buffer = null;
+}
+
+var retArray;
+
+function verifyGetBufferSubData(expected) {
+ wtu.shouldGenerateGLError(gl, expected, "gl.getBufferSubData(gl.TRANSFORM_FEEDBACK_BUFFER, 0, retArray, 0, retArray.length)");
+}
+
+function runGetBufferSubDataTest() {
+ debug("");
+ debug("Test that getBufferSubData...");
+
+ // Build the input and output buffers
+ var in_data = [
+ 1.0, 2.0, 3.0, 4.0,
+ 2.0, 4.0, 8.0, 16.0,
+ 0.75, 0.5, 0.25, 0.0
+ ];
+
+ retArray = new Float32Array(in_data.length);
+
+ var in_buffer = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, in_buffer);
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(in_data), gl.STATIC_DRAW);
+
+ out_add_buffer = gl.createBuffer();
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, out_add_buffer);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, Float32Array.BYTES_PER_ELEMENT * in_data.length, gl.STATIC_DRAW);
+
+ // Create the transform feedback shader
+ program = wtu.setupTransformFeedbackProgram(gl, ["vshader", "fshader"],
+ ["out_add"], gl.SEPARATE_ATTRIBS,
+ ["in_data"]);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "linking transform feedback shader should not set an error");
+ shouldBeNonNull("program");
+
+ // Draw the the transform feedback buffers
+ tf = gl.createTransformFeedback();
+
+ gl.enableVertexAttribArray(0);
+ gl.bindBuffer(gl.ARRAY_BUFFER, in_buffer);
+ gl.vertexAttribPointer(0, 4, gl.FLOAT, false, 16, 0);
+
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
+
+ debug("... passes when a transform feedback object is not bound");
+ verifyGetBufferSubData(gl.NO_ERROR);
+
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, out_add_buffer);
+
+ debug("... passes when a transform feedback object is bound but not active");
+ verifyGetBufferSubData(gl.NO_ERROR);
+
+ gl.enable(gl.RASTERIZER_DISCARD);
+ gl.beginTransformFeedback(gl.POINTS);
+
+ debug("... fails when a transform feedback object is active");
+ verifyGetBufferSubData(gl.INVALID_OPERATION);
+
+ gl.drawArrays(gl.POINTS, 0, 3);
+
+ gl.endTransformFeedback();
+ gl.disable(gl.RASTERIZER_DISCARD);
+
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
+
+ // Verify the output buffer contents
+ var add_expected = [
+ 3.0, 5.0, 7.0, 9.0,
+ 4.0, 7.0, 12.0, 21.0,
+ 2.75, 3.5, 4.25, 5.0
+ ];
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, out_add_buffer);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, add_expected);
+
+ tf = null;
+ program = null;
+}
+
+function completeTransformFeedbackQueryTest() {
+ debug("");
+ debug("Testing TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN query");
+
+ ++numberOfQueryCompletionAttempts;
+ if (numberOfQueryCompletionAttempts > 500) {
+ testFailed("Query didn't become available in a reasonable time");
+ finishTest();
+ return;
+ }
+
+ if (!gl.getQueryParameter(query, gl.QUERY_RESULT_AVAILABLE)) {
+ requestAnimationFrame(completeTransformFeedbackQueryTest);
+ return;
+ }
+
+ var result = gl.getQueryParameter(query, gl.QUERY_RESULT);
+ if (result == 3) {
+ testPassed("TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN query returned a correct result (3)");
+ } else {
+ testFailed("TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN query returned an incorrect result " + result + " (expected 3)");
+ }
+
+ runVaryingsTest();
+}
+
+function verifyTransformFeedbackVarying(prog, index, valid, name) {
+ activeInfo = gl.getTransformFeedbackVarying(prog, index);
+ if (valid) {
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "Should be no errors from valid getTransformFeedbackVarying.");
+ shouldBeNonNull("activeInfo");
+ shouldBe("activeInfo.name", "'" + name + "'");
+ shouldBe("activeInfo.type", "gl.FLOAT_VEC4");
+ shouldBe("activeInfo.size", "1");
+ } else {
+ wtu.glErrorShouldBe(gl, gl.INVALID_VALUE,
+ "Should be INVALID_VALUE when calling getTransformFeedbackVarying with an invalid index.");
+ shouldBeNull("activeInfo");
+ }
+}
+
+function runVaryingsTest() {
+ debug("");
+ debug("Testing transform feedback varyings");
+
+ // Create the transform feedback shader. This is explicitly run after runTwoOutputFeedbackTest,
+ // as re-linking the shader here will test browser caching.
+ program = wtu.setupTransformFeedbackProgram(gl, ["vshader", "fshader"],
+ ["out_add", "out_mul"], gl.SEPARATE_ATTRIBS,
+ ["in_data"]);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "linking transform feedback shader should not set an error");
+ shouldBeNonNull("program");
+
+ // Check the varyings
+ shouldBe("gl.getProgramParameter(program, gl.TRANSFORM_FEEDBACK_VARYINGS)", "2");
+ verifyTransformFeedbackVarying(program, 0, true, "out_add");
+ verifyTransformFeedbackVarying(program, 1, true, "out_mul");
+ verifyTransformFeedbackVarying(program, 2, false);
+
+ // transformFeedbackVaryings() doesn't take effect until a successful link.
+ gl.transformFeedbackVaryings(program, ["out_mul"], gl.SEPARATE_ATTRIBS);
+ shouldBe("gl.getProgramParameter(program, gl.TRANSFORM_FEEDBACK_VARYINGS)", "2");
+ verifyTransformFeedbackVarying(program, 0, true, "out_add");
+ verifyTransformFeedbackVarying(program, 1, true, "out_mul");
+ verifyTransformFeedbackVarying(program, 2, false);
+
+ // Now relink.
+ gl.linkProgram(program);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "linking transform feedback shader should not set an error");
+ shouldBeTrue("gl.getProgramParameter(program, gl.LINK_STATUS)");
+ shouldBe("gl.getProgramParameter(program, gl.TRANSFORM_FEEDBACK_VARYINGS)", "1");
+ verifyTransformFeedbackVarying(program, 0, true, "out_mul");
+ verifyTransformFeedbackVarying(program, 1, false);
+ verifyTransformFeedbackVarying(program, 2, false);
+
+ // Test recompiling/relinking the program
+ // Regression test for http://crbug.com/716018
+ var skipCompileStatus = true;
+ program = wtu.setupTransformFeedbackProgram(gl, ["vshader", "fshader"],
+ ["out_add", "out_mul"], gl.SEPARATE_ATTRIBS,
+ ["in_data"], undefined, undefined, skipCompileStatus);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "linking transform feedback shader should not set an error");
+ shouldBeTrue("gl.getProgramParameter(program, gl.LINK_STATUS)");
+ shouldBe("gl.getProgramParameter(program, gl.TRANSFORM_FEEDBACK_VARYINGS)", "2");
+ verifyTransformFeedbackVarying(program, 0, true, "out_add");
+ verifyTransformFeedbackVarying(program, 1, true, "out_mul");
+ verifyTransformFeedbackVarying(program, 2, false);
+
+ runContextLostOneOutputFeedbackTest();
+}
+
+function runContextLostOneOutputFeedbackTest() {
+ var ext = gl.getExtension("WEBGL_lose_context");
+ if (!ext) {
+ debug("No WEBGL_lose_context support");
+ finishTest();
+ return;
+ }
+ debug("");
+ debug("Testing switching program after context lost while transform feedback is active");
+
+ var in_buffer = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, in_buffer);
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(12), gl.STATIC_DRAW);
+
+ out_add_buffer = gl.createBuffer();
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, out_add_buffer);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, Float32Array.BYTES_PER_ELEMENT * 12, gl.STATIC_DRAW);
+
+ // Create an extra program to try switching to
+ var program2 = wtu.setupSimpleColorProgram(gl);
+
+ // Create the transform feedback shader
+ program = wtu.setupTransformFeedbackProgram(gl, ["vshader", "fshader"],
+ ["out_add"], gl.SEPARATE_ATTRIBS,
+ ["in_data"]);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "linking transform feedback shader should not set an error");
+ shouldBeNonNull("program");
+
+ // Draw the the transform feedback buffers
+ tf = gl.createTransformFeedback();
+
+ gl.enableVertexAttribArray(0);
+ gl.bindBuffer(gl.ARRAY_BUFFER, in_buffer);
+ gl.vertexAttribPointer(0, 4, gl.FLOAT, false, 16, 0);
+
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, out_add_buffer);
+
+ gl.beginTransformFeedback(gl.POINTS);
+
+ debug("Calling loseContext()");
+ ext.loseContext();
+ shouldBeTrue("gl.isContextLost()");
+ shouldBe("gl.getError()", "gl.CONTEXT_LOST_WEBGL");
+ shouldBe("gl.getError()", "gl.NO_ERROR");
+ debug("Trying to switch program");
+ gl.useProgram(program2);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "No transform feedback error generated on lost context");
+ finishTest();
+}
+
+debug("");
+</script>
+
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/two-unreferenced-varyings.html b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/two-unreferenced-varyings.html
new file mode 100644
index 0000000000..2173fd35ae
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/two-unreferenced-varyings.html
@@ -0,0 +1,136 @@
+<!--
+Copyright (c) 2019 The Khronos Group Inc.
+Use of this source code is governed by an MIT-style license that can be
+found in the LICENSE.txt file.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL Transform Feedback Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas>
+<div id="console"></div>
+<script id="vshader" type="x-shader/x-vertex">#version 300 es
+in vec3 position;
+out vec3 outAttrib1;
+out vec3 outAttrib2;
+void main()
+{
+ outAttrib1 = position;
+ outAttrib2 = position;
+ gl_Position = vec4(position, 1);
+}
+</script>
+<script id="fshader" type="x-shader/x-fragment">#version 300 es
+precision mediump float;
+out vec4 color;
+in vec3 outAttrib1;
+in vec3 outAttrib2;
+void main()
+{
+ color = vec4(0);
+}
+</script>
+<script>
+"use strict";
+description("This test covers an ANGLE bug with two transform feedback varyings. When the two are declared, but not referenced in the fragment shader, ANGLE would fail capture.");
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var canvas = document.getElementById("canvas");
+var gl = wtu.create3DContext(canvas, null, 2);
+var quadVB;
+
+if (!gl) {
+ testFailed("WebGL context does not exist");
+} else {
+ testPassed("WebGL context exists");
+
+ debug("");
+ debug("Testing transform feedback with two unreferenced outputs");
+ runTest();
+}
+
+function getQuadVerts(depth) {
+ var quadVerts = new Float32Array(3 * 6);
+ quadVerts[0] = -1.0; quadVerts[1] = 1.0; quadVerts[2] = depth;
+ quadVerts[3] = -1.0; quadVerts[4] = -1.0; quadVerts[5] = depth;
+ quadVerts[6] = 1.0; quadVerts[7] = -1.0; quadVerts[8] = depth;
+ quadVerts[9] = -1.0; quadVerts[10] = 1.0; quadVerts[11] = depth;
+ quadVerts[12] = 1.0; quadVerts[13] = -1.0; quadVerts[14] = depth;
+ quadVerts[15] = 1.0; quadVerts[16] = 1.0; quadVerts[17] = depth;
+ return quadVerts;
+}
+
+function drawQuad(depth) {
+ if (!quadVB) {
+ quadVB = gl.createBuffer()
+ }
+
+ var quadVerts = getQuadVerts(depth);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, quadVB);
+ gl.bufferData(gl.ARRAY_BUFFER, quadVerts, gl.STATIC_DRAW);
+ gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 0, 0);
+ gl.enableVertexAttribArray(0);
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
+}
+
+function runTest() {
+
+ // Create the transform feedback program
+ var program = wtu.setupTransformFeedbackProgram(gl, ["vshader", "fshader"],
+ ["outAttrib1", "outAttrib2"], gl.INTERLEAVED_ATTRIBS,
+ ["position"]);
+ if (!program) {
+ testFailed("Fail to set up the program");
+ return;
+ }
+
+ // Init transform feedback buffers
+ var out_buffer = gl.createBuffer();
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, out_buffer);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, Float32Array.BYTES_PER_ELEMENT * 3 * 2 * 6,
+ gl.STREAM_DRAW);
+
+ var tf = gl.createTransformFeedback();
+
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, out_buffer);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Init transform feedback should succeed");
+
+ // Draw the quad
+ gl.useProgram(program)
+ gl.beginTransformFeedback(gl.TRIANGLES);
+ drawQuad(0.5);
+ gl.endTransformFeedback();
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Draw with transform feedback should succeed");
+
+ // Verify the output buffer contents
+ var quadVerts = getQuadVerts(0.5);
+ var expected_data = [];
+ for (var i = 0; i < quadVerts.length; i += 3) {
+ for (var count = 0; count < 2; count++) {
+ expected_data[expected_data.length] = quadVerts[i];
+ expected_data[expected_data.length] = quadVerts[i+1];
+ expected_data[expected_data.length] = quadVerts[i+2];
+ }
+ }
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, expected_data);
+}
+
+debug("");
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/unwritten-output-defaults-to-zero.html b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/unwritten-output-defaults-to-zero.html
new file mode 100644
index 0000000000..615ab9bab1
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/unwritten-output-defaults-to-zero.html
@@ -0,0 +1,133 @@
+<!--
+Copyright (c) 2019 The Khronos Group Inc.
+Use of this source code is governed by an MIT-style license that can be
+found in the LICENSE.txt file.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL Transform Feedback Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas>
+<div id="console"></div>
+<script id="vshader" type="x-shader/x-vertex">#version 300 es
+in vec4 in_data;
+uniform int flag;
+out vec4 out_data;
+void main(void) {
+ if (flag > 0) {
+ out_data = in_data + vec4(2.0, 3.0, 4.0, 5.0);
+ }
+}
+</script>
+<script id="fshader" type="x-shader/x-fragment">#version 300 es
+precision mediump float;
+out vec4 out_color;
+void main(void) {
+ out_color = vec4(1.0, 1.0, 1.0, 1.0);
+}
+</script>
+<script>
+"use strict";
+description("This test verifies if an output variable is specified to be streamed to a transform feedback buffer but not actually written, the value defaults to 0.");
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var canvas = document.getElementById("canvas");
+var gl = wtu.create3DContext(canvas, null, 2);
+
+if (!gl) {
+ testFailed("WebGL context does not exist");
+} else {
+ testPassed("WebGL context exists");
+
+ debug("");
+ debug("Testing transform feedback works fine");
+ runTest(1);
+
+ debug("");
+ debug("Testing unwritten output variables default to zero");
+ runTest(0);
+}
+
+function runTest(flag) {
+ var in_data = [
+ 1.0, 2.0, 3.0, 4.0,
+ 2.0, 4.0, 8.0, 16.0,
+ 0.75, 0.5, 0.25, 0.0
+ ];
+
+ var in_buffer = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, in_buffer);
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(in_data), gl.STATIC_DRAW);
+
+ var out_buffer = gl.createBuffer();
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, out_buffer);
+ gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, Float32Array.BYTES_PER_ELEMENT * in_data.length, gl.STATIC_DRAW);
+
+ // Create the transform feedback program
+ var program = wtu.setupTransformFeedbackProgram(gl, ["vshader", "fshader"],
+ ["out_data"], gl.SEPARATE_ATTRIBS,
+ ["in_data"]);
+ var loc = gl.getUniformLocation(program, "flag");
+ if (!program || !loc) {
+ testFailed("Fail to set up the program");
+ return;
+ }
+ gl.uniform1i(loc, flag);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Set up program should succeed");
+
+ // Draw the the transform feedback buffers
+ var tf = gl.createTransformFeedback();
+
+ gl.enableVertexAttribArray(0);
+ gl.bindBuffer(gl.ARRAY_BUFFER, in_buffer);
+ gl.vertexAttribPointer(0, 4, gl.FLOAT, false, 16, 0);
+
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, out_buffer);
+
+ gl.enable(gl.RASTERIZER_DISCARD);
+ gl.beginTransformFeedback(gl.POINTS);
+
+ gl.drawArrays(gl.POINTS, 0, 3);
+
+ gl.endTransformFeedback();
+ gl.disable(gl.RASTERIZER_DISCARD);
+
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
+
+ // Verify the output buffer contents
+ var expected_data;
+ if (flag > 0) {
+ expected_data = [
+ 3.0, 5.0, 7.0, 9.0,
+ 4.0, 7.0, 12.0, 21.0,
+ 2.75, 3.5, 4.25, 5.0
+ ];
+ } else {
+ expected_data = [
+ 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0
+ ];
+ }
+ gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, out_buffer);
+ wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, expected_data);
+}
+
+debug("");
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+
+</body>
+</html>