diff options
Diffstat (limited to 'dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback')
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> |