diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/canvas/test/webgl-conf/checkout/conformance2/buffers | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/canvas/test/webgl-conf/checkout/conformance2/buffers')
14 files changed, 2283 insertions, 0 deletions
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/00_test_list.txt b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/00_test_list.txt new file mode 100644 index 0000000000..21e4bb2bc4 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/00_test_list.txt @@ -0,0 +1,13 @@ +bound-buffer-size-change-test.html +buffer-copying-contents.html +buffer-copying-restrictions.html +buffer-data-and-buffer-sub-data-sub-source.html +buffer-type-restrictions.html +buffer-overflow-test.html +--min-version 2.0.1 delete-buffer.html +get-buffer-sub-data.html +--min-version 2.0.1 get-buffer-sub-data-validity.html +one-large-uniform-buffer.html +uniform-buffers.html +--min-version 2.0.1 uniform-buffers-second-compile.html +--min-version 2.0.1 uniform-buffers-state-restoration.html diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/bound-buffer-size-change-test.html b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/bound-buffer-size-change-test.html new file mode 100644 index 0000000000..a61d154eb1 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/bound-buffer-size-change-test.html @@ -0,0 +1,119 @@ +<!-- +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 buffer size change test for bindBufferBase/bindBufferRange</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> +"use strict"; +description("WebGL buffer size change for bindings through bindBufferBase/bindBufferRange"); + +// This test verifies the ES3 behavior, that the bound buffer range (offset, size) is not +// limited by the actual buffer size, and the driver is responsible that no out-of-range +// access may happen. + +var wtu = WebGLTestUtils; + +var gl = wtu.create3DContext(undefined, undefined, 2); + +debug(""); +debug("bindBufferBase with TRANSFORM_FEEDBACK_BUFFER target"); +var buffer1 = gl.createBuffer(); +gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, buffer1); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, + "Calling bindBufferBase on a buffer where no storage is allocated should succeed."); +shouldBe("gl.getParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING)", "buffer1"); +shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "buffer1"); +shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)", "0"); +shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)", "0"); + +gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4, gl.STATIC_DRAW); +shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "buffer1"); +shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)", "0"); +shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)", "0"); + +wtu.glErrorShouldBe(gl, gl.NO_ERROR); + +debug(""); +debug("bindBufferBase with UNIFORM_BUFFER target"); +var buffer2 = gl.createBuffer(); +gl.bindBufferBase(gl.UNIFORM_BUFFER, 1, buffer2); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, + "Calling bindBufferBase on a buffer where no storage is allocated should succeed."); +shouldBe("gl.getParameter(gl.UNIFORM_BUFFER_BINDING)", "buffer2"); +shouldBe("gl.getIndexedParameter(gl.UNIFORM_BUFFER_BINDING, 1)", "buffer2"); +shouldBe("gl.getIndexedParameter(gl.UNIFORM_BUFFER_SIZE, 1)", "0"); +shouldBe("gl.getIndexedParameter(gl.UNIFORM_BUFFER_START, 1)", "0"); + +gl.bufferData(gl.UNIFORM_BUFFER, 8, gl.STATIC_DRAW); +shouldBe("gl.getIndexedParameter(gl.UNIFORM_BUFFER_BINDING, 1)", "buffer2"); +shouldBe("gl.getIndexedParameter(gl.UNIFORM_BUFFER_SIZE, 1)", "0"); +shouldBe("gl.getIndexedParameter(gl.UNIFORM_BUFFER_START, 1)", "0"); + +wtu.glErrorShouldBe(gl, gl.NO_ERROR); + +debug(""); +debug("bindBufferRange with TRANSFORM_FEEDBACK_BUFFER target"); +var buffer3 = gl.createBuffer(); +gl.bindBufferRange(gl.TRANSFORM_FEEDBACK_BUFFER, 0, buffer3, 4, 8); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, + "Calling bindBufferRange on a buffer where no storage is allocated should succeed."); +shouldBe("gl.getParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING)", "buffer3"); +shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "buffer3"); +shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)", "8"); +shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)", "4"); + +gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4, gl.STATIC_DRAW); +shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "buffer3"); +shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)", "8"); +shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)", "4"); + +gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 12, gl.STATIC_DRAW); +shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "buffer3"); +shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)", "8"); +shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)", "4"); + +wtu.glErrorShouldBe(gl, gl.NO_ERROR); + +debug(""); +debug("bindBufferRange with UNIFORM_BUFFER target"); +var buffer4 = gl.createBuffer(); +var offset = gl.getParameter(gl.UNIFORM_BUFFER_OFFSET_ALIGNMENT); +gl.bindBufferRange(gl.UNIFORM_BUFFER, 1, buffer4, offset, 12); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, + "Calling bindBufferRange on a buffer where no storage is allocated should succeed."); +shouldBe("gl.getParameter(gl.UNIFORM_BUFFER_BINDING)", "buffer4"); +shouldBe("gl.getIndexedParameter(gl.UNIFORM_BUFFER_BINDING, 1)", "buffer4"); +shouldBe("gl.getIndexedParameter(gl.UNIFORM_BUFFER_SIZE, 1)", "12"); +shouldBe("gl.getIndexedParameter(gl.UNIFORM_BUFFER_START, 1)", "offset"); + +gl.bufferData(gl.UNIFORM_BUFFER, offset + 8, gl.STATIC_DRAW); +shouldBe("gl.getIndexedParameter(gl.UNIFORM_BUFFER_BINDING, 1)", "buffer4"); +shouldBe("gl.getIndexedParameter(gl.UNIFORM_BUFFER_SIZE, 1)", "12"); +shouldBe("gl.getIndexedParameter(gl.UNIFORM_BUFFER_START, 1)", "offset"); + +gl.bufferData(gl.UNIFORM_BUFFER, offset + 12, gl.STATIC_DRAW); +shouldBe("gl.getIndexedParameter(gl.UNIFORM_BUFFER_BINDING, 1)", "buffer4"); +shouldBe("gl.getIndexedParameter(gl.UNIFORM_BUFFER_SIZE, 1)", "12"); +shouldBe("gl.getIndexedParameter(gl.UNIFORM_BUFFER_START, 1)", "offset"); + +wtu.glErrorShouldBe(gl, gl.NO_ERROR); + +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/buffers/buffer-copying-contents.html b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/buffer-copying-contents.html new file mode 100644 index 0000000000..47d11093b2 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/buffer-copying-contents.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 buffer copying contents test.</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> +"use strict"; +description("Test copying a buffer's contents to another buffer governed by the WebGL 2 spec."); + +var wtu = WebGLTestUtils; + +var gl = wtu.create3DContext(undefined, undefined, 2); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup."); + +var vertices = [ + 1.1, 1.0, 1.3, + -1.0, -1.0, -5.0, + 5.3, -1.0, 1.0 +]; + +debug(""); +debug("Test copying between buffers returns correct data"); + +function testCopyBuffers(srcTarget, dstTarget, copyRead, copyWrite) { + var msg = "Copying from " + targetToString(gl, srcTarget) + + " to " + targetToString(gl, dstTarget) + if (copyRead && copyWrite) + msg += " via COPY_READ_BUFFER and COPY_WRITE_BUFFER" + else if (copyRead) + msg += " via COPY_READ_BUFFER" + else if (copyWrite) + msg += " via COPY_WRITE_BUFFER" + else + msg += " directly" + debug("") + debug(msg) + + var srcBuffer = gl.createBuffer(), dstBuffer = gl.createBuffer(); + var originalData = new Float32Array(vertices); + var length = vertices.length * 4; + + gl.bindBuffer(srcTarget, srcBuffer); + gl.bufferData(srcTarget, originalData, gl.STATIC_DRAW); + if (copyRead) + gl.bindBuffer(gl.COPY_READ_BUFFER, srcBuffer); + + gl.bindBuffer(dstTarget, dstBuffer); + gl.bufferData(dstTarget, new Float32Array(length), gl.STATIC_DRAW); + if (copyWrite) + gl.bindBuffer(gl.COPY_WRITE_BUFFER, dstBuffer); + + var expectedGLError = gl.NO_ERROR; + + if (srcTarget == dstTarget) { + if (!copyRead && copyWrite) { + // srcBuffer isn't bound to any targets because of setting up dstBuffer. + gl.bindBuffer(srcTarget, srcBuffer); + } + if (!copyRead && !copyWrite) { + // Same buffer object, overlapping range. + expectedGLError = gl.INVALID_VALUE; + } + } + if ((srcTarget == gl.ELEMENT_ARRAY_BUFFER && + dstTarget != gl.ELEMENT_ARRAY_BUFFER) || + (srcTarget != gl.ELEMENT_ARRAY_BUFFER && + dstTarget == gl.ELEMENT_ARRAY_BUFFER)) { + expectedGLError = gl.INVALID_OPERATION; + } + + gl.copyBufferSubData(copyRead ? gl.COPY_READ_BUFFER : srcTarget, + copyWrite ? gl.COPY_WRITE_BUFFER : dstTarget, + 0, 0, length); + if (expectedGLError == gl.NO_ERROR) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Copying should work"); + + var retBuffer = new Uint8Array(length); + gl.getBufferSubData(dstTarget, 0, retBuffer); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, + "gl.getBufferSubData(" + targetToString(gl, dstTarget) + + ", 0, retBuffer) should work"); + + var failed = false; + var retArray = new Float32Array(retBuffer.buffer); + for (var i = 0; i < vertices.length; i++) { + if (originalData[i] != retArray[i]) { + failed = true; + break; + } + } + if (failed) + testFailed("The returned array buffer fails to match original data"); + else + testPassed("The returned array buffer matches original data"); + } else { + wtu.glErrorShouldBe(gl, expectedGLError, "Copying should fail"); + } + + gl.deleteBuffer(srcBuffer); + gl.deleteBuffer(dstBuffer); + shouldBeNull("gl.getParameter(gl.ARRAY_BUFFER_BINDING)"); + shouldBeNull("gl.getParameter(gl.COPY_READ_BUFFER_BINDING)"); + shouldBeNull("gl.getParameter(gl.COPY_WRITE_BUFFER_BINDING)"); + shouldBeNull("gl.getParameter(gl.ELEMENT_ARRAY_BUFFER_BINDING)"); + shouldBeNull("gl.getParameter(gl.PIXEL_PACK_BUFFER_BINDING)"); + shouldBeNull("gl.getParameter(gl.PIXEL_UNPACK_BUFFER_BINDING)"); + shouldBeNull("gl.getParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING)"); + shouldBeNull("gl.getParameter(gl.UNIFORM_BUFFER_BINDING)"); +} + +function targetToString(gl, target) { + switch (target) { + case gl.ARRAY_BUFFER: + return "ARRAY_BUFFER"; + case gl.COPY_READ_BUFFER: + return "COPY_READ_BUFFER"; + case gl.COPY_WRITE_BUFFER: + return "COPY_WRITE_BUFFER"; + case gl.ELEMENT_ARRAY_BUFFER: + return "ELEMENT_ARRAY_BUFFER"; + case gl.PIXEL_PACK_BUFFER: + return "PIXEL_PACK_BUFFER"; + case gl.PIXEL_UNPACK_BUFFER: + return "PIXEL_UNPACK_BUFFER"; + case gl.TRANSFORM_FEEDBACK_BUFFER: + return "TRANSFORM_FEEDBACK_BUFFER"; + case gl.UNIFORM_BUFFER: + return "UNIFORM_BUFFER"; + default: + return "UNKNOWN BUFFER"; + } +} + +var targets = [ + gl.ARRAY_BUFFER, + gl.ELEMENT_ARRAY_BUFFER, + gl.PIXEL_PACK_BUFFER, + gl.PIXEL_UNPACK_BUFFER, + gl.TRANSFORM_FEEDBACK_BUFFER, + gl.UNIFORM_BUFFER, +] + +for (var srcIndex in targets) { + for (var dstIndex in targets) { + if (targets[srcIndex] != gl.TRANSFORM_FEEDBACK_BUFFER && + targets[dstIndex] != gl.TRANSFORM_FEEDBACK_BUFFER) { + testCopyBuffers(targets[srcIndex], targets[dstIndex], true, true); + } + if (targets[srcIndex] != gl.TRANSFORM_FEEDBACK_BUFFER) { + testCopyBuffers(targets[srcIndex], targets[dstIndex], true, false); + } + if (targets[dstIndex] != gl.TRANSFORM_FEEDBACK_BUFFER) { + testCopyBuffers(targets[srcIndex], targets[dstIndex], false, true); + } + testCopyBuffers(targets[srcIndex], targets[dstIndex], false, false); + } +} + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/buffer-copying-restrictions.html b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/buffer-copying-restrictions.html new file mode 100644 index 0000000000..3bfe0e3e80 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/buffer-copying-restrictions.html @@ -0,0 +1,102 @@ +<!-- +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 buffer copying restrictions test.</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> +"use strict"; +description("Test buffer copying restrictions governed by the WebGL 2 spec. The test makes sure that copyBufferSubData acts as expected."); + +var wtu = WebGLTestUtils; + +var gl = wtu.create3DContext(undefined, undefined, 2); + +var validTargets = [gl.ARRAY_BUFFER, gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, + gl.ELEMENT_ARRAY_BUFFER, gl.PIXEL_PACK_BUFFER, + gl.PIXEL_UNPACK_BUFFER, gl.TRANSFORM_FEEDBACK_BUFFER, + gl.UNIFORM_BUFFER]; + +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup."); + +var testCopyBuffer = function(srcTarget, dstTarget) { + var srcTargetStr = wtu.glEnumToString(gl, srcTarget), + dstTargetStr = wtu.glEnumToString(gl, dstTarget); + var srcBuffer = gl.createBuffer(), + dstBuffer = gl.createBuffer(); + var testCopyStr = "copying from a gl." + wtu.glEnumToString(gl, srcTarget) + " buffer to a gl." + + wtu.glEnumToString(gl, dstTarget) + " buffer" + + gl.bindBuffer(srcTarget, srcBuffer); + gl.bufferData(srcTarget, new Float32Array(32), gl.STATIC_DRAW); + gl.bindBuffer(dstTarget, dstBuffer); + gl.bufferData(dstTarget, new Float32Array(32), gl.STATIC_DRAW); + gl.copyBufferSubData(srcTarget, dstTarget, 8, 0, 4); + if (srcTarget == dstTarget) + wtu.glErrorShouldBe(gl, gl.NO_ERROR, testCopyStr + " should work."); + else if (srcTarget == gl.ELEMENT_ARRAY_BUFFER || dstTarget == gl.ELEMENT_ARRAY_BUFFER ) + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, testCopyStr + " should fail."); + else + wtu.glErrorShouldBe(gl, gl.NO_ERROR, testCopyStr + " should work."); + + // Special case: COPY_READ_BUFFER and COPY_WRITE_BUFFER are compatible with ELEMENT_ARRAY_BUFFER + // only if the buffer had been initially bound to an ELEMENT_ARRAY_BUFFER + if (srcTarget == gl.ELEMENT_ARRAY_BUFFER && + (dstTarget == gl.COPY_READ_BUFFER || dstTarget == gl.COPY_WRITE_BUFFER)) { + dstBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, dstBuffer); + } else if (dstTarget == gl.ELEMENT_ARRAY_BUFFER && + (srcTarget == gl.COPY_READ_BUFFER || srcTarget == gl.COPY_WRITE_BUFFER)) { + srcBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, srcBuffer); + } else { + return; + } + + gl.bindBuffer(srcTarget, srcBuffer); + gl.bufferData(srcTarget, new Float32Array(32), gl.STATIC_DRAW); + gl.bindBuffer(dstTarget, dstBuffer); + gl.bufferData(dstTarget, new Float32Array(32), gl.STATIC_DRAW); + gl.copyBufferSubData(srcTarget, dstTarget, 8, 0, 4); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, testCopyStr + " should work if all buffers were initially bound to ELEMENT_ARRAY_BUFFER."); +}; + +for (var i = 0; i < validTargets.length; i++) { + debug(""); + debug("Copying data from a " + wtu.glEnumToString(gl, validTargets[i]) + + " buffer to another target"); + for (var j = 0; j < validTargets.length; j++) + testCopyBuffer(validTargets[i], validTargets[j]); +} + +debug(""); +debug("Test copying a buffer of other data (gl.ARRAY_BUFFER) bound to gl.COPY_READ_BUFFER to a " + + "buffer bound to gl.ELEMENT_ARRAY_BUFFER"); +var srcBuffer = gl.createBuffer(), dstBuffer = gl.createBuffer(); +gl.bindBuffer(gl.ARRAY_BUFFER, srcBuffer); +gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, dstBuffer); +gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Float32Array(32), gl.STATIC_DRAW); +gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(32), gl.STATIC_DRAW); +gl.bindBuffer(gl.COPY_READ_BUFFER, srcBuffer); +gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.ELEMENT_ARRAY_BUFFER, 0, 0, 4); +wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Copying gl.ARRAY_BUFFER bound to " + + "gl.COPY_READ_BUFFER to a buffer bound to gl.ELEMENT_ARRAY_BUFFER should fail."); + +finishTest(); + +var successfullyParsed = true; +</script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/buffer-data-and-buffer-sub-data-sub-source.html b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/buffer-data-and-buffer-sub-data-sub-source.html new file mode 100644 index 0000000000..ed9a131f77 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/buffer-data-and-buffer-sub-data-sub-source.html @@ -0,0 +1,183 @@ +<!-- +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"> +<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> +"use strict"; +description("Test bufferData/bufferSubData with ArrayBufferView sub source input"); + +var wtu = WebGLTestUtils; +var gl = wtu.create3DContext(undefined, undefined, 2); + +function verifyBufferData(testCase, sourceByteOffset, size, data) { + var readbackBuffer = new ArrayBuffer(testCase.size * size); + var readbackView = new window[testCase.type](readbackBuffer); + gl.getBufferSubData(gl.ARRAY_BUFFER, sourceByteOffset, readbackView); + var pass = true; + var offset = sourceByteOffset / testCase.size; + for (var ii = 0; ii < size; ++ii) { + if (readbackView[ii] != data[ii]) { + testFailed("expected data at " + ii + ": " + data[ii] + ", got " + readbackView[ii]); + pass = false; + } + } + if (pass) { + testPassed("buffer data uploaded correctly"); + } +} + +function bufferDataTest(testCases) { + for (var idx = 0; idx < testCases.length; ++idx) { + var test = testCases[idx]; + debug(""); + debug("Test bufferData with " + test.type); + + var buf = gl.createBuffer(); + shouldBeNonNull(buf); + + gl.bindBuffer(gl.ARRAY_BUFFER, buf); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + var count = 4; + var arrayBuffer = new ArrayBuffer(test.size * count); + var view = new window[test.type](arrayBuffer); + for (var ii = 0; ii < count; ++ii) { + view[ii] = ii; + } + + gl.bufferData(gl.ARRAY_BUFFER, view, gl.STATIC_DRAW, 0, count + 1); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, + "calling bufferData when srcOffset + length is larger than source size"); + + gl.bufferData(gl.ARRAY_BUFFER, view, gl.STATIC_DRAW, count + 1); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, + "calling bufferData when srcOffset + length is larger than source size"); + + gl.bufferData(gl.ARRAY_BUFFER, view, gl.STATIC_DRAW, count - 1, 2); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, + "calling bufferData when srcOffset + length is larger than source size"); + + var offset = 2; + var size = count - offset; + gl.bufferData(gl.ARRAY_BUFFER, view, gl.STATIC_DRAW, offset); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, + "calling bufferData with valid sub source should succeed"); + verifyBufferData(test, 0, size, view.slice(offset, offset + size)); + + offset = 1; + size = 1; + gl.bufferData(gl.ARRAY_BUFFER, view, gl.STATIC_DRAW, offset, size); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, + "calling bufferData with valid sub source should succeed"); + verifyBufferData(test, 0, size, view.slice(offset, offset + size)); + + gl.bufferData(gl.ARRAY_BUFFER, view, gl.STATIC_DRAW, count); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, + "calling bufferData with valid sub source of size 0 should succeed"); + + gl.deleteBuffer(buf); + gl.bindBuffer(gl.ARRAY_BUFFER, null); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no GL error"); + } +} + +function bufferSubDataTest(testCases) { + for (var idx = 0; idx < testCases.length; ++idx) { + var test = testCases[idx]; + debug(""); + debug("Test bufferSubData with " + test.type); + + var count = 4; + var totalBufferBytes = test.size * count * 2; + var buf = gl.createBuffer(); + shouldBeNonNull(buf); + gl.bindBuffer(gl.ARRAY_BUFFER, buf); + gl.bufferData(gl.ARRAY_BUFFER, totalBufferBytes, gl.STATIC_DRAW); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + var arrayBuffer = new ArrayBuffer(test.size * count); + var view = new window[test.type](arrayBuffer); + for (var ii = 0; ii < count; ++ii) { + view[ii] = ii; + } + + var sourceByteOffset = test.size * 2; + + gl.bufferSubData(gl.ARRAY_BUFFER, sourceByteOffset, view, 0, count + 1); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, + "calling bufferSubData when srcOffset + length is larger than source size"); + + gl.bufferSubData(gl.ARRAY_BUFFER, sourceByteOffset, view, count + 1); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, + "calling bufferSubData when srcOffset + length is larger than source size"); + + gl.bufferSubData(gl.ARRAY_BUFFER, sourceByteOffset, view, count - 1, 2); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, + "calling bufferSubData when srcOffset + length is larger than source size"); + + var offset = 2; + var size = count - offset; + gl.bufferSubData(gl.ARRAY_BUFFER, sourceByteOffset, view, offset); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, + "calling bufferSubData with valid sub source should succeed"); + verifyBufferData(test, sourceByteOffset, size, view.slice(offset, offset + size)); + + offset = 1; + size = 1; + gl.bufferSubData(gl.ARRAY_BUFFER, sourceByteOffset, view, offset, size); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, + "calling bufferSubData with valid sub source should succeed"); + verifyBufferData(test, sourceByteOffset, size, view.slice(offset, offset + size)); + + gl.bufferSubData(gl.ARRAY_BUFFER, sourceByteOffset, view, count); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, + "calling bufferSubData with valid sub source of size 0 should succeed"); + + gl.deleteBuffer(buf); + gl.bindBuffer(gl.ARRAY_BUFFER, null); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no GL error"); + } +} + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + var testCases = [ + {type: "Uint8Array", size: 1}, + {type: "Int8Array", size: 1}, + {type: "Int16Array", size: 2}, + {type: "Uint16Array", size: 2}, + {type: "Uint32Array", size: 4}, + {type: "Int32Array", size: 4}, + {type: "Float32Array", size: 4}, + {type: "Float64Array", size: 8} + ]; + + bufferDataTest(testCases); + bufferSubDataTest(testCases); +} + +var successfullyParsed = true; +</script> + +<script src="../../js/js-test-post.js"></script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/buffer-overflow-test.html b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/buffer-overflow-test.html new file mode 100644 index 0000000000..9334ba5cb6 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/buffer-overflow-test.html @@ -0,0 +1,51 @@ +<!-- +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 buffer overflow test for bindBufferRange</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> +"use strict"; +description("WebGL buffer overflow test: buffer overflow will not lead to failure in bindBufferRange"); + +var wtu = WebGLTestUtils; + +var gl = wtu.create3DContext(undefined, undefined, 2); + +debug(""); +var buffer1 = gl.createBuffer(); +gl.bindBufferRange(gl.TRANSFORM_FEEDBACK_BUFFER, 0, buffer1, 0, 4); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, + "Calling bindBufferRange on a buffer where no storage is allocated should succeed."); +shouldBe("buffer1", "gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)"); +shouldBe("4", "gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)"); +shouldBe("0", "gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)"); + +var buffer2 = gl.createBuffer(); +gl.bindBuffer(gl.UNIFORM_BUFFER, buffer2); +gl.bufferData(gl.UNIFORM_BUFFER, 4, gl.STATIC_DRAW); +gl.bindBufferRange(gl.UNIFORM_BUFFER, 1, buffer2, 0, 8); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, + "Calling bindBufferRange on a buffer where the storage is not big enough should succeed."); +shouldBe("buffer2", "gl.getIndexedParameter(gl.UNIFORM_BUFFER_BINDING, 1)"); +shouldBe("8", "gl.getIndexedParameter(gl.UNIFORM_BUFFER_SIZE, 1)"); +shouldBe("0", "gl.getIndexedParameter(gl.UNIFORM_BUFFER_START, 1)"); + +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/buffers/buffer-type-restrictions.html b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/buffer-type-restrictions.html new file mode 100644 index 0000000000..438b66df02 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/buffer-type-restrictions.html @@ -0,0 +1,110 @@ +<!-- +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 buffer binding restrictions test.</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> +"use strict"; +description("Test binding restrictions governed by the WebGL 2 spec. The test makes sure that bindBuffer," + + "bindBufferRange, and bindBufferBase acts as expected with every target combination."); + +var wtu = WebGLTestUtils; + +var gl = wtu.create3DContext(undefined, undefined, 2); + +var validTargets = [gl.ARRAY_BUFFER, gl.ELEMENT_ARRAY_BUFFER, gl.COPY_READ_BUFFER, + gl.COPY_WRITE_BUFFER, gl.PIXEL_PACK_BUFFER, gl.PIXEL_UNPACK_BUFFER, + gl.TRANSFORM_FEEDBACK_BUFFER, gl.UNIFORM_BUFFER]; + +var bindFunctions = ["bindBuffer", "bindBufferRange", "bindBufferBase"]; + +var isCopyTarget = function(target) { + return target == gl.COPY_READ_BUFFER || target == gl.COPY_WRITE_BUFFER; +} + +var noElementArrayVsOtherDataConflicts = function(first, second) { + return isCopyTarget(second) || ((first == gl.ELEMENT_ARRAY_BUFFER) == (second == gl.ELEMENT_ARRAY_BUFFER)); +}; + + +var bind = function(bindFn, target, buffer) { + if (bindFn == "bindBuffer") + gl.bindBuffer(target, buffer); + else if (bindFn == "bindBufferRange") + gl.bindBufferRange(target, 0, buffer, 0, 4); + else if (bindFn == "bindBufferBase") + gl.bindBufferBase(target, 0, buffer); + else + throw new Error("Cannot bind unknown function: " + bindFn); +} + +var testBindingFn = function(firstBindFn, secondBindFn, firstTarget, secondTarget) { + var firstTargetStr = wtu.glEnumToString(gl, firstTarget), + secondTargetStr = wtu.glEnumToString(gl, secondTarget); + var buffer = gl.createBuffer(); + + bind(firstBindFn, firstTarget, buffer); + bind(firstBindFn, firstTarget, null); + bind(secondBindFn, secondTarget, buffer); + bind(secondBindFn, secondTarget, null); + + var messagePrefix = "Binding buffer first with " + firstBindFn + " to gl." + firstTargetStr + + " and then binding buffer with " + secondBindFn + " to gl." + secondTargetStr + " should "; + if (firstTarget == secondTarget || noElementArrayVsOtherDataConflicts(firstTarget, secondTarget)) + wtu.glErrorShouldBe(gl, gl.NO_ERROR, messagePrefix + "WORK"); + else + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, messagePrefix + "FAIL"); +} + +var testBinding = function(firstTarget, secondTarget) { + for (var i = 0; i < bindFunctions.length; i++) + if (i == 0 || firstTarget == gl.UNIFORM_BUFFER || firstTarget == gl.TRANSFORM_FEEDBACK_BUFFER) + for (var j = 0; j < bindFunctions.length; j++) + if (j == 0 || secondTarget == gl.UNIFORM_BUFFER || secondTarget == gl.TRANSFORM_FEEDBACK_BUFFER) + testBindingFn(bindFunctions[i], bindFunctions[j], firstTarget, secondTarget); +}; + +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup."); + +debug(""); +debug("Testing each binding function"); + +var buffer1 = gl.createBuffer(); +bind("bindBuffer", gl.ARRAY_BUFFER, buffer1); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "bindBuffer(gl.ARRAY_BUFFER, buffer1) should WORK"); + +var buffer2 = gl.createBuffer(); +bind("bindBufferRange", gl.UNIFORM_BUFFER, buffer2); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "bindBufferRange(gl.UNIFORM_BUFFER, 0, buffer2, 0, 4) should WORK"); + +var buffer3 = gl.createBuffer(); +bind("bindBufferBase", gl.UNIFORM_BUFFER, buffer3); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "bindBufferBase(gl.UNIFORM_BUFFER, 0, buffer3) should WORK"); + +for (var i = 0; i < validTargets.length; i++) { + debug(""); + debug("Testing binding a buffer first to " + wtu.glEnumToString(gl, validTargets[i]) + + " and then to another target"); + for (var j = 0; j < validTargets.length; j++) + testBinding(validTargets[i], validTargets[j]); +} + +finishTest(); + +var successfullyParsed = true; +</script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/delete-buffer.html b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/delete-buffer.html new file mode 100644 index 0000000000..d438d65521 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/delete-buffer.html @@ -0,0 +1,80 @@ +<!-- +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 buffer deletion behavior test.</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> +"use strict"; +description("Test buffer deletion behavior."); +// This is a regression test for https://crbug.com/822976 + +var wtu = WebGLTestUtils; + +var gl = wtu.create3DContext(undefined, undefined, 2); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup."); + +function runTexImageTest(gl) { + debug(""); + var buffer = gl.createBuffer(); + gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, buffer); + gl.bufferData(gl.PIXEL_UNPACK_BUFFER, 4, gl.DYNAMIC_DRAW); + gl.bindBufferBase(gl.UNIFORM_BUFFER, 0, buffer); + gl.deleteBuffer(buffer); + // Indexed uniform buffer bindings should not prevent a buffer from being + // deleted. Therefore, PIXEL_UNPACK_BUFFER binding should also be 0. + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no error"); + + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + var data = new Uint8Array(1024); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, 16, 16, 0, + gl.RGBA, gl.UNSIGNED_BYTE, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texImage2D should succeed"); + + // Clean up bindings just in case an implementation gets it wrong. + gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, null); + gl.bindBufferBase(gl.UNIFORM_BUFFER, 0, null); +} + +function runReadPixelsTest(gl) { + debug(""); + var buffer = gl.createBuffer(); + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, buffer); + gl.bufferData(gl.PIXEL_PACK_BUFFER, 4, gl.DYNAMIC_DRAW); + gl.bindBufferBase(gl.UNIFORM_BUFFER, 0, buffer); + gl.deleteBuffer(buffer); + // Indexed transform feedback buffer bindings should not prevent a buffer + // from being deleted. Therefore, PIXEL_PACK_BUFFER binding should also be 0. + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no error"); + + var buffer = new Uint8Array(1024); + gl.readPixels(0, 0, 16, 16, gl.RGBA, gl.UNSIGNED_BYTE, buffer); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "readPixels should succeed"); + + // Clean up bindings just in case an implementation gets it wrong. + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); + gl.bindBufferBase(gl.UNIFORM_BUFFER, 0, null); +} + +runTexImageTest(gl); +runReadPixelsTest(gl); + +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/buffers/get-buffer-sub-data-validity.html b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/get-buffer-sub-data-validity.html new file mode 100644 index 0000000000..06ce2ecb5a --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/get-buffer-sub-data-validity.html @@ -0,0 +1,250 @@ +<!-- +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>WebGL2 getBufferSubData validity 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 uint in_data; +flat out uint out_data; +void main() { + out_data = in_data; +} +</script> +<script id="fshader" type="x-shader/x-fragment">#version 300 es +void main() {} +</script> + +<script> +"use strict"; +description("Test that getBufferSubData returns valid data in edge cases"); + +var wtu = WebGLTestUtils; + +var gl = wtu.create3DContext(undefined, undefined, 2); + +const srcData = new Uint8Array([ 1, 2, 3, 4, 5, 6, 7, 8 ]); +const noData = new Uint8Array(8); + +const srcBuffer = gl.createBuffer(); +gl.bindBuffer(gl.COPY_READ_BUFFER, srcBuffer); +gl.bufferData(gl.COPY_READ_BUFFER, srcData, gl.STATIC_DRAW); + +const badBuffer = gl.createBuffer(); +gl.bindBuffer(gl.ARRAY_BUFFER, badBuffer); +gl.bufferData(gl.ARRAY_BUFFER, 8, gl.STATIC_DRAW); + +let readbackBuffer; +function deleteReadbackBuffer() { + gl.deleteBuffer(readbackBuffer); +} +function recreateReadbackBuffer() { + readbackBuffer = gl.createBuffer(); + gl.bindBuffer(gl.COPY_WRITE_BUFFER, readbackBuffer); + gl.bufferData(gl.COPY_WRITE_BUFFER, 8, gl.STREAM_READ); +} +recreateReadbackBuffer(); + +const dest = new Uint8Array(8); + +// Makes a new "resolvable" Promise +function resolvable() { + let resolve; + const promise = new Promise(res => { resolve = res; }); + promise.resolve = resolve; + return promise; +} + +function wait() { + return new Promise(res => { + setTimeout(res, 0); + }); +} + +async function fence() { + const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0); + gl.flush(); + + let status; + do { + await wait(); + status = gl.clientWaitSync(sync, 0, 0); + } while (status != gl.ALREADY_SIGNALED && status != gl.CONDITION_SATISFIED); + gl.deleteSync(sync); +} + +function checkGetBufferSubData(err, data) { + dest.fill(0); + wtu.shouldGenerateGLError(gl, err, "gl.getBufferSubData(gl.COPY_WRITE_BUFFER, 0, dest)"); + if (!err) { + shouldBeTrue(`areArraysEqual(dest, ${data})`); + } +} + +const tfProgram = wtu.setupTransformFeedbackProgram(gl, ["vshader", "fshader"], + ["out_data"], gl.SEPARATE_ATTRIBS, + ["in_data"]); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "linking transform feedback shader should not set an error"); +shouldBeNonNull("tfProgram"); +const tf = gl.createTransformFeedback(); + +function copyBufferUsingTransformFeedback(src, dst) { + gl.enableVertexAttribArray(0); + gl.bindBuffer(gl.ARRAY_BUFFER, src); + gl.vertexAttribIPointer(0, 1, gl.UNSIGNED_INT, 0, 0); + + gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf); + gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, dst); + + gl.drawBuffers([gl.NONE]); + + gl.enable(gl.RASTERIZER_DISCARD); + gl.beginTransformFeedback(gl.POINTS); + // treats the input and output data as two uint32s + gl.drawArrays(gl.POINTS, 0, 2); + gl.endTransformFeedback(); + gl.disable(gl.RASTERIZER_DISCARD); + + gl.bindBuffer(gl.ARRAY_BUFFER, badBuffer); + gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null); + gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null); +} + +(async () => { + debug(""); + debug("write-read"); + gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8); + checkGetBufferSubData(gl.NO_ERROR, "srcData"); + + debug(""); + debug("fence-wait-write-read"); + await fence(); + gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8); + checkGetBufferSubData(gl.NO_ERROR, "srcData"); + + debug(""); + debug("write-read-fence-wait"); + gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8); + checkGetBufferSubData(gl.NO_ERROR, "srcData"); + await fence(); + + debug(""); + debug("write-fence-fence-wait-read"); + gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8); + fence(); // no await + await fence(); + checkGetBufferSubData(gl.NO_ERROR, "srcData"); + + debug(""); + debug("write-fence-wait-read"); + gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8); + await fence(); + checkGetBufferSubData(gl.NO_ERROR, "srcData"); + + debug(""); + debug("write-fence-wait-write-read"); + gl.copyBufferSubData(gl.ARRAY_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8); + await fence(); + gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8); + checkGetBufferSubData(gl.NO_ERROR, "srcData"); + + debug(""); + debug("write-fence-write-wait-read"); + gl.copyBufferSubData(gl.ARRAY_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8); + { + const p = fence(); + gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8); + await p; + } + checkGetBufferSubData(gl.NO_ERROR, "srcData"); + + debug(""); + debug("write-fence-transformfeedback-wait-read"); + gl.copyBufferSubData(gl.ARRAY_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8); + { + const p = fence(); + gl.bindBuffer(gl.COPY_WRITE_BUFFER, null); + copyBufferUsingTransformFeedback(srcBuffer, readbackBuffer); + gl.bindBuffer(gl.COPY_WRITE_BUFFER, readbackBuffer); + await p; + } + checkGetBufferSubData(gl.NO_ERROR, "srcData"); + + debug(""); + debug("write-unbind-fence-wait-bind-read"); + gl.bindBuffer(gl.COPY_WRITE_BUFFER, null); + gl.bindBuffer(gl.ARRAY_BUFFER, readbackBuffer); + gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.ARRAY_BUFFER, 0, 0, 8); + gl.bindBuffer(gl.ARRAY_BUFFER, badBuffer); + await fence(); + gl.bindBuffer(gl.COPY_WRITE_BUFFER, readbackBuffer); + checkGetBufferSubData(gl.NO_ERROR, "srcData"); + + debug(""); + debug("write-fence-wait-delete-read"); + gl.copyBufferSubData(gl.ARRAY_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8); + await fence(); + deleteReadbackBuffer(); + checkGetBufferSubData(gl.INVALID_OPERATION, "noData"); + recreateReadbackBuffer(); + + debug(""); + debug("write-fence-delete-wait-read"); + gl.copyBufferSubData(gl.ARRAY_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8); + { + const p = fence(); + deleteReadbackBuffer(); + await p; + } + checkGetBufferSubData(gl.INVALID_OPERATION, "noData"); + recreateReadbackBuffer(); + + debug(""); + debug("write-fence-delete-wait-read"); + gl.copyBufferSubData(gl.ARRAY_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8); + deleteReadbackBuffer(); + await fence(); + checkGetBufferSubData(gl.INVALID_OPERATION, "noData"); + recreateReadbackBuffer(); + + // crbug.com/941930 + { + debug(""); + debug("write-delete-recreate-fence-wait-read"); + gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8); + deleteReadbackBuffer(); + recreateReadbackBuffer(); + await fence(); + checkGetBufferSubData(gl.NO_ERROR, "noData"); + + debug(""); + debug("write-delete-fence-wait-read"); + gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8); + { + const p = fence(); + deleteReadbackBuffer(); + await p; + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + } + + finishTest(); +})(); + +var successfullyParsed = true; +</script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/get-buffer-sub-data.html b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/get-buffer-sub-data.html new file mode 100644 index 0000000000..5919a86996 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/get-buffer-sub-data.html @@ -0,0 +1,223 @@ +<!-- +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 getBufferSubData test.</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> +"use strict"; +description("This test makes sure that getBufferSubData acts as expected governed by WebGL 2."); + +var wtu = WebGLTestUtils; + +var gl = wtu.create3DContext(undefined, undefined, 2); + +var vertices = [ + 1.1, 1.0, 1.3, + -1.0, -1.0, -5.0, + 5.3, -1.0, 1.0 +]; +var floatArray = new Float32Array(vertices); + +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup."); + +var buffer = gl.createBuffer(); +gl.bindBuffer(gl.ARRAY_BUFFER, buffer); +gl.bufferData(gl.ARRAY_BUFFER, floatArray, gl.STATIC_DRAW); + +var uninitializedBuffer = gl.createBuffer(); +gl.bindBuffer(gl.ARRAY_BUFFER, uninitializedBuffer); +gl.bufferData(gl.ARRAY_BUFFER, 36, gl.STATIC_DRAW); +gl.bindBuffer(gl.ARRAY_BUFFER, null); + +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from buffer setup."); + +gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + +debug(""); +debug("Test that getBufferSubData successfully works reading buffer data from gl.ARRAY_BUFFER"); +var retArray = new Float32Array(vertices.length); +wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.getBufferSubData(gl.ARRAY_BUFFER, 0, retArray)"); + +debug("Argument must be ArrayBufferView, not ArrayBuffer") +shouldThrow("gl.getBufferSubData(gl.ARRAY_BUFFER, 0, new ArrayBuffer(4))"); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should not generate GL error"); +debug("Argument must be ArrayBufferView, not null") +shouldThrow("gl.getBufferSubData(gl.ARRAY_BUFFER, 0, null)"); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should not generate GL error"); + +debug("Check array data to match original data set by the buffer"); +shouldBeTrue("areArraysEqual(retArray, floatArray)"); + +debug("Test that getBufferSubData successfully works with dstOffset"); +retArray = new Float32Array(vertices.length); +wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.getBufferSubData(gl.ARRAY_BUFFER, 0, retArray, 2)"); +shouldBeTrue("areArraysEqual(retArray.slice(0, 2), [0, 0])"); +shouldBeTrue("areArraysEqual(retArray.slice(2), floatArray.slice(0, floatArray.length - 2))"); + +retArray = new Float32Array(vertices.length); +wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.getBufferSubData(gl.ARRAY_BUFFER, 0, retArray, retArray.length)"); +shouldBeTrue("areArraysEqual(retArray, [0, 0, 0, 0, 0, 0, 0, 0, 0])"); + +debug("Test that getBufferSubData fails when given a dstOffset beyond the end of retArray"); +wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, "gl.getBufferSubData(gl.ARRAY_BUFFER, 0, retArray, retArray.length + 1)"); + +debug("Test that getBufferSubData successfully works with dstOffset and length"); +retArray = new Float32Array(vertices.length); +wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.getBufferSubData(gl.ARRAY_BUFFER, 0, retArray, 2, 2)"); +shouldBeTrue("areArraysEqual(retArray.slice(0, 2), [0, 0])"); +shouldBeTrue("areArraysEqual(retArray.slice(2, 4), floatArray.slice(0, 2))"); +shouldBeTrue("areArraysEqual(retArray.slice(4), [0, 0, 0, 0, 0])"); + +retArray = new Float32Array(vertices.length); +wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.getBufferSubData(gl.ARRAY_BUFFER, 0, retArray, retArray.length - 1, 1)"); +shouldBeTrue("areArraysEqual(retArray.slice(0, 8), [0, 0, 0, 0, 0, 0, 0, 0])"); +shouldBeTrue("areArraysEqual(retArray.slice(8), floatArray.slice(0, 1))"); + +debug("Test that getBufferSubData fails when given a dstOffset+length beyond the end of retArray"); +wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, "gl.getBufferSubData(gl.ARRAY_BUFFER, 0, retArray, retArray.length - 1, 2)"); + +debug("Test that getBufferSubData successfully works with srcByteOffset"); +retArray = new Float32Array(vertices.length - 2); +const float32Size = Float32Array.BYTES_PER_ELEMENT; +wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.getBufferSubData(gl.ARRAY_BUFFER, 2*float32Size, retArray)"); +shouldBeTrue("areArraysEqual(retArray, floatArray.slice(2))"); + +retArray = new Float32Array(vertices.length); +wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.getBufferSubData(gl.ARRAY_BUFFER, 2*float32Size, retArray, 2)"); +shouldBeTrue("areArraysEqual(retArray.slice(0, 2), [0, 0])"); +shouldBeTrue("areArraysEqual(retArray.slice(2), floatArray.slice(2))"); + +retArray = new Float32Array(vertices.length); +wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.getBufferSubData(gl.ARRAY_BUFFER, 4*float32Size, retArray, 3, 3)"); +shouldBeTrue("areArraysEqual(retArray.slice(0, 3), [0, 0, 0])"); +shouldBeTrue("areArraysEqual(retArray.slice(3, 6), floatArray.slice(4, 7))"); +shouldBeTrue("areArraysEqual(retArray.slice(6), [0, 0, 0])"); + +debug("Test that getBufferSubData fails when given a buffer with its size larger than the original data"); +var extraLargeBuffer = new Float32Array(vertices.length + 1); +wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, + "gl.getBufferSubData(gl.ARRAY_BUFFER, 0, extraLargeBuffer)", + "Extra length should generate INVALID_VALUE."); +extraLargeBuffer = new Float32Array(vertices.length - 1); +wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, + "gl.getBufferSubData(gl.ARRAY_BUFFER, 2*float32Size, extraLargeBuffer)", + "Extra length should generate INVALID_VALUE."); + +debug("Test that getBufferSubData fails when offset summed with buffer length is larger than the size of the original data size"); +wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, + "gl.getBufferSubData(gl.ARRAY_BUFFER, retArray.byteLength + 1, retArray)"); +wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, "gl.getBufferSubData(gl.ARRAY_BUFFER, 1, retArray)"); + +debug("Test that getBufferSubData successfully works with offset view into ArrayBuffer"); +const verticesLengthInBytes = vertices.length * float32Size; +const retStorage = new ArrayBuffer(4* verticesLengthInBytes); // Over allocate 4x +retArray = new Float32Array(retStorage, verticesLengthInBytes, vertices.length); +wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.getBufferSubData(gl.ARRAY_BUFFER, 0, retArray)"); +shouldBeTrue("areArraysEqual(retArray, floatArray)"); + +debug("Test that getBufferSubData successfully works with offset view into ArrayBuffer and dstOffset"); +retArray = new Float32Array(retStorage, verticesLengthInBytes, vertices.length); +retArray.fill(0); +wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.getBufferSubData(gl.ARRAY_BUFFER, 0, retArray, 2)"); +shouldBeTrue("areArraysEqual(retArray.slice(0, 2), [0, 0])"); +shouldBeTrue("areArraysEqual(retArray.slice(2), floatArray.slice(0, floatArray.length - 2))"); + +retArray = new Float32Array(retStorage, verticesLengthInBytes, vertices.length); +retArray.fill(0); +wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.getBufferSubData(gl.ARRAY_BUFFER, 0, retArray, retArray.length)"); +shouldBeTrue("areArraysEqual(retArray, [0, 0, 0, 0, 0, 0, 0, 0, 0])"); + +debug("Test that getBufferSubData fails when given a dstOffset beyond the end of offset view into ArrayBuffer"); +wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, "gl.getBufferSubData(gl.ARRAY_BUFFER, 0, retArray, retArray.length + 1)"); + +debug("Test that getBufferSubData successfully works with offset view into ArrayBuffer and dstOffset and length"); +retArray = new Float32Array(retStorage, verticesLengthInBytes, vertices.length); +retArray.fill(0); +wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.getBufferSubData(gl.ARRAY_BUFFER, 0, retArray, 2, 2)"); +shouldBeTrue("areArraysEqual(retArray.slice(0, 2), [0, 0])"); +shouldBeTrue("areArraysEqual(retArray.slice(2, 4), floatArray.slice(0, 2))"); +shouldBeTrue("areArraysEqual(retArray.slice(4), [0, 0, 0, 0, 0])"); + +retArray = new Float32Array(retStorage, verticesLengthInBytes, vertices.length); +retArray.fill(0); +wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.getBufferSubData(gl.ARRAY_BUFFER, 0, retArray, retArray.length - 1, 1)"); +shouldBeTrue("areArraysEqual(retArray.slice(0, 8), [0, 0, 0, 0, 0, 0, 0, 0])"); +shouldBeTrue("areArraysEqual(retArray.slice(8), floatArray.slice(0, 1))"); + +debug("Test that getBufferSubData fails when given a dstOffset+length beyond the end of offset view into ArrayBuffer"); +wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, "gl.getBufferSubData(gl.ARRAY_BUFFER, 0, retArray, retArray.length - 1, 2)"); + +debug("Test that getBufferSubData successfully works with offset view into ArrayBuffer and srcByteOffset"); +retArray = new Float32Array(retStorage, verticesLengthInBytes, vertices.length - 2); +retArray.fill(0); +wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.getBufferSubData(gl.ARRAY_BUFFER, 2*float32Size, retArray)"); +shouldBeTrue("areArraysEqual(retArray, floatArray.slice(2))"); + +retArray = new Float32Array(retStorage, verticesLengthInBytes, vertices.length); +retArray.fill(0); +wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.getBufferSubData(gl.ARRAY_BUFFER, 2*float32Size, retArray, 2)"); +shouldBeTrue("areArraysEqual(retArray.slice(0, 2), [0, 0])"); +shouldBeTrue("areArraysEqual(retArray.slice(2), floatArray.slice(2))"); + +retArray = new Float32Array(retStorage, verticesLengthInBytes, vertices.length); +retArray.fill(0); +wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.getBufferSubData(gl.ARRAY_BUFFER, 4*float32Size, retArray, 3, 3)"); +shouldBeTrue("areArraysEqual(retArray.slice(0, 3), [0, 0, 0])"); +shouldBeTrue("areArraysEqual(retArray.slice(3, 6), floatArray.slice(4, 7))"); +shouldBeTrue("areArraysEqual(retArray.slice(6), [0, 0, 0])"); + +debug("Test that getBufferSubData fails when 0 is bound to the target"); +gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); +wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, + "gl.getBufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, retArray)"); + +debug("Test that getBufferSubData fails when offset is less than 0"); +wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, "gl.getBufferSubData(gl.ARRAY_BUFFER, -1, retArray)"); + +debug(""); +debug("Test that getBufferSubData successfully works with uninitialized buffers"); +retArray = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9]); +gl.bindBuffer(gl.ARRAY_BUFFER, uninitializedBuffer); +wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + "gl.getBufferSubData(gl.ARRAY_BUFFER, 0, retArray)"); +shouldBeTrue("areArraysEqual(retArray, [0, 0, 0, 0, 0, 0, 0, 0, 0])"); + +debug(""); +debug("Test that getBufferSubData works when a buffer is immediately resized to be too small"); + +retArray = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9]); +gl.bindBuffer(gl.ARRAY_BUFFER, buffer); +wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + "gl.getBufferSubData(gl.ARRAY_BUFFER, 0, retArray)"); +gl.bufferData(gl.ARRAY_BUFFER, 4, gl.STATIC_DRAW); +shouldBeTrue("areArraysEqual(retArray, floatArray)"); + +debug(""); +debug("Test that getBufferSubData works when a buffer is immediately deleted"); +retArray = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9]); +gl.bindBuffer(gl.ARRAY_BUFFER, buffer); +gl.bufferData(gl.ARRAY_BUFFER, floatArray, gl.STATIC_DRAW); +wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + "gl.getBufferSubData(gl.ARRAY_BUFFER, 0, retArray)"); +gl.deleteBuffer(buffer); +shouldBeTrue("areArraysEqual(retArray, floatArray)"); + +finishTest(); + +var successfullyParsed = true; +</script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/one-large-uniform-buffer.html b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/one-large-uniform-buffer.html new file mode 100644 index 0000000000..a2bddce04c --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/one-large-uniform-buffer.html @@ -0,0 +1,195 @@ +<!-- +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 Uniform Buffers 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 position; +void main() +{ + gl_Position = position; +} +</script> +<script id="fshader" type="x-shader/x-fragment">#version 300 es +precision mediump float; +uniform uni { + vec4 color; +}; + +out vec4 fragColor; + +void main() +{ + fragColor = color; +} +</script> +<script> +"use strict"; +description("This test covers ANGLE bugs when using a large uniform blocks. ANGLE would confuse an internal clipped uniform buffer size and produce an assert or error. Also there were issues with readback of large UBOs. See http://crbug.com/660670."); + +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 uniform block with large data store"); + runTest(); + + debug(""); + debug("Testing readback on uniform block with large data store"); + runReadbackTest(); +} + +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 program + var program = wtu.setupProgram(gl, ["vshader", "fshader"], ["position"]); + if (!program) { + testFailed("Failed to set up the program"); + return; + } + + // Init uniform buffer. To trigger the bug, it's necessary to use the + // DYNAMIC_DRAW usage. This makes ANGLE attempt to map the buffer internally + // with an incorrect copy size. + var ubo = gl.createBuffer(); + var big_size = 4096 * 64; + var data = new Float32Array([0.5, 0.75, 0.25, 1.0]); + gl.bindBuffer(gl.UNIFORM_BUFFER, ubo); + gl.bufferData(gl.UNIFORM_BUFFER, big_size, gl.DYNAMIC_DRAW); + gl.bufferSubData(gl.UNIFORM_BUFFER, 0, data); + + gl.bindBufferBase(gl.UNIFORM_BUFFER, 0, ubo); + var buffer_index = gl.getUniformBlockIndex(program, "uni"); + if (buffer_index == -1) { + testFailed("Failed to get uniform block index"); + return; + } + gl.uniformBlockBinding(program, buffer_index, 0); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setting up uniform block should succeed"); + + // Draw the quad + gl.useProgram(program); + drawQuad(0.5); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Draw with uniform block should succeed"); + + // Verify the output color + var color = [127, 191, 64, 255]; + wtu.checkCanvas(gl, color, "canvas should be same as input uniform", 1); +} + +function runReadbackTest() { + + // Create the program + var program = wtu.setupProgram(gl, ["vshader", "fshader"], ["position"]); + if (!program) { + testFailed("Failed to set up the program"); + return; + } + + // Init uniform buffer. To trigger the bug, it's necessary to use the + // DYNAMIC_DRAW usage. This makes ANGLE attempt to map the buffer internally + // with an incorrect copy size. + var ubo = gl.createBuffer(); + var num_floats = 4096 * 16; + var expected_data = new Float32Array(num_floats); + for (var index = 0; index < num_floats; ++index) { + expected_data[index] = index; + } + + expected_data[0] = 0.5; + expected_data[1] = 0.75; + expected_data[2] = 0.25; + expected_data[3] = 1.0; + + gl.bindBuffer(gl.UNIFORM_BUFFER, ubo); + gl.bufferData(gl.UNIFORM_BUFFER, expected_data, gl.DYNAMIC_DRAW); + gl.bufferSubData(gl.UNIFORM_BUFFER, 0, expected_data); + + gl.bindBufferBase(gl.UNIFORM_BUFFER, 0, ubo); + var buffer_index = gl.getUniformBlockIndex(program, "uni"); + if (buffer_index == -1) { + testFailed("Failed to get uniform block index"); + return; + } + gl.uniformBlockBinding(program, buffer_index, 0); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setting up uniform block should succeed"); + + // Draw the quad + gl.useProgram(program); + drawQuad(0.5); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Draw with uniform block should succeed"); + + // Verify the output color + var color = [127, 191, 64, 255]; + wtu.checkCanvas(gl, color, "canvas should be same as input uniform", 1); + + // Verify readback + var actual_data = new Float32Array(num_floats); + gl.getBufferSubData(gl.UNIFORM_BUFFER, 0, actual_data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Readback from uniform block should succeed"); + + for (var index = 0; index < num_floats; ++index) { + if (actual_data[index] != expected_data[index]) { + testFailed("Expected and actual buffer data do not match"); + return; + } + } +} + +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/buffers/uniform-buffers-second-compile.html b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/uniform-buffers-second-compile.html new file mode 100644 index 0000000000..936126856e --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/uniform-buffers-second-compile.html @@ -0,0 +1,104 @@ +<!-- +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 Uniform Buffers should work on second compile</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> +<script id='vshader' type='x-shader/x-vertex'> +#version 300 es + +layout(location=0) in vec4 position; +layout(std140, column_major) uniform Uniforms1 { + mat4 transform; +}; +out vec3 vColor; + +void main() { + gl_Position = transform * position; +} +</script> +<script id='fshader' type='x-shader/x-fragment'> +#version 300 es +precision highp float; + +layout(std140) uniform Uniforms2 { + vec4 color; +}; +out vec4 fragColor; + +void main(){ + fragColor = color; +} +</script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas> +<div id="console"></div> +<script> +"use strict"; +description("This test verifies that getUniformBlockIndex isn't broken on second compile."); + +debug("This is a regression test for <a href='http://crbug.com/716018'>http://crbug.com/716018</a>"); +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, null, 2); + +// Initialize +gl.clearColor(0, 0, 0, 1); +var vsSource = document.getElementById("vshader").text.trim(); +var fsSource = document.getElementById("fshader").text.trim(); + +function runTest() { + // Run twice to make sure that the second build does not cause errors + // (i.e. due to caching). + for (var i = 0; i < 2; ++i) { + debug("Compile/test iteration " + i); + + var vertexShader = gl.createShader(gl.VERTEX_SHADER); + gl.shaderSource(vertexShader, vsSource); + gl.compileShader(vertexShader); + // Note: if COMPILE_STATUS is retrieved here, it hides the bug. + + var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); + gl.shaderSource(fragmentShader, fsSource); + gl.compileShader(fragmentShader); + // Note: if COMPILE_STATUS is retrieved here, it hides the bug. + + var program = gl.createProgram(); + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + gl.linkProgram(program); + + gl.useProgram(program); + + var uniforms1Location = gl.getUniformBlockIndex(program, "Uniforms1"); + gl.uniformBlockBinding(program, uniforms1Location, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uniforms1Location was " + uniforms1Location); + + var uniforms2Location = gl.getUniformBlockIndex(program, "Uniforms2"); + gl.uniformBlockBinding(program, uniforms2Location, 1); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uniforms2Location was " + uniforms2Location); + + debug(""); + } +} + +runTest(); + +var successfullyParsed = true; +</script> + +<script src="../../js/js-test-post.js"></script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/uniform-buffers-state-restoration.html b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/uniform-buffers-state-restoration.html new file mode 100644 index 0000000000..51edd6ec07 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/uniform-buffers-state-restoration.html @@ -0,0 +1,101 @@ +<!-- +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 Uniform Buffers State Restoration 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> +<script id='vshader' type='x-shader/x-vertex'>#version 300 es +layout(location=0) in vec3 p; +void main() +{ + gl_Position = vec4(p.xyz, 1.0); +} +</script> +<script id='fshader' type='x-shader/x-fragment'>#version 300 es +precision mediump float; +layout(location=0) out vec4 oColor; + +uniform UBOData { + vec4 uboColor; +}; + +void main() +{ + oColor = uboColor; +} +</script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas> +<div id="console"></div> +<script> +"use strict"; +description("This is a regression test verifying that uniform buffer bindings persist correctly across frames: <a href='http://crbug.com/722060'>crbug.com/722060</a>"); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, null, 2); +var greenBuf = null; +var throwawayBuf = null; +var red = new Float32Array([1, 0, 0, 1]); +var green = new Float32Array([0, 1, 0, 1]); +var frame = 0; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + initTest(); + wtu.waitForComposite(runTest); +} + +function initTest() { + wtu.setupUnitQuad(gl); + + var program = wtu.setupProgram(gl, ['vshader', 'fshader']); + if (!program) { + testFailed("Could not compile shader with uniform blocks without error"); + return; + } + var uboLocation = gl.getUniformBlockIndex(program, "UBOData"); + gl.uniformBlockBinding(program, uboLocation, 0); + + greenBuf = gl.createBuffer(); + throwawayBuf = gl.createBuffer(); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "createBuffer should not set an error"); + + // Bind uniform buffer (both index 0 AND generic binding points) to greenBuf + gl.bindBufferBase(gl.UNIFORM_BUFFER, 0, greenBuf); + gl.bufferData(gl.UNIFORM_BUFFER, green, gl.STATIC_DRAW); + // Bind throwaray uniform buffer (from only the generic binding point) + gl.bindBuffer(gl.UNIFORM_BUFFER, throwawayBuf); +} + +function runTest() { + // ONLY the binding point at index 0 (not the generic binding point) should be greenBuf. + // (The generic binding point should point at throwawayBuf.) + // So this bufferData should go into throwawayBuf. + gl.bufferData(gl.UNIFORM_BUFFER, red, gl.STATIC_DRAW); + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvas(gl, [0, 255, 0, 255], "draw call should set canvas to green", 2); + finishTest(); +} + +debug(""); +var successfullyParsed = true; +</script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/uniform-buffers.html b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/uniform-buffers.html new file mode 100644 index 0000000000..a6bd1b4bc9 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/uniform-buffers.html @@ -0,0 +1,576 @@ +<!-- +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 Uniform Buffers 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> +<script id='vshader' type='x-shader/x-vertex'>#version 300 es +layout(location=0) in vec3 p; +void main() +{ + gl_Position = vec4(p.xyz, 1.0); +} +</script> +<script id='fbadshader' type='x-shader/x-fragment'>#version 300 es +precision mediump float; +layout(location=0) out vec4 oColor; + +uniform UBOData { + float UBORed; + float UBOGreen; + float UBOBlue; +}; + +uniform Color { + float Red; + float UBOGreen; + float Blue; +}; + +void main() +{ + oColor = vec4(UBORed * Red, UBOGreen * UBOGreen, UBOBlue * Blue, 1.0); +} +</script> +<script id='fshader' type='x-shader/x-fragment'>#version 300 es +precision mediump float; +layout(location=0) out vec4 oColor; + +uniform UBOData { + float UBORed; + float UBOGreen; + float UBOBlue; +}; + +uniform UBOD { + float UBOR; + float UBOG; + float UBOB; +}; + +void main() +{ + oColor = vec4(UBORed * UBOR, UBOGreen * UBOG, UBOBlue * UBOB, 1.0); +} +</script> +<script id='fshadernamed' type='x-shader/x-fragment'>#version 300 es +precision mediump float; +layout(location=0) out vec4 oColor; + +uniform UBOData { + float Red; + float Green; + float Blue; +} UBOA; + +void main() +{ + oColor = vec4(UBOA.Red, UBOA.Green, UBOA.Blue, 1.0); +} +</script> +<script id='fshadernamedarray' type='x-shader/x-fragment'>#version 300 es +precision mediump float; +layout(location=0) out vec4 oColor; + +uniform UBOData { + float Red; + float Green; + float Blue; +} UBOA[2]; + +void main() +{ + oColor = vec4((UBOA[0].Red + UBOA[1].Red) / 2.0, + (UBOA[0].Green + UBOA[1].Green) / 2.0, + (UBOA[0].Blue + UBOA[1].Blue) / 2.0, 1.0); +} +</script> +<script id='fshadernestedstruct' type='x-shader/x-fragment'>#version 300 es +precision mediump float; +layout(location=0) out vec4 oColor; + +struct color_t { + float red; + float green; + float blue; +}; + +struct wrapper_t { + color_t color; +}; + +uniform UBOData { + wrapper_t UBOStruct; +}; + +// This is intended to reproduce a specific ANGLE bug that triggers when the wrapper struct is passed to a function. +// https://bugs.chromium.org/p/angleproject/issues/detail?id=2084 +void processColor(wrapper_t wrapper) { + oColor = vec4(wrapper.color.red, wrapper.color.green, wrapper.color.blue, 1.0); +} + +void main() +{ + processColor(UBOStruct); +} +</script> +<script id='fshaderarrayofstructs' type='x-shader/x-fragment'>#version 300 es +precision mediump float; +layout(location=0) out vec4 oColor; + +struct color_t { + float red; + float green; + float blue; +}; + +uniform UBOData { + color_t UBOColors[2]; +}; + +// This is intended to reproduce a specific ANGLE bug that triggers when a struct from an array of structs in an interface block is passed to a function. +// https://bugs.chromium.org/p/angleproject/issues/detail?id=2084 +vec3 processColor(color_t color) { + return vec3(color.red, color.green, color.blue); +} + +void main() +{ + oColor = vec4(processColor(UBOColors[0]) + processColor(UBOColors[1]), 1.0); +} +</script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas> +<div id="console"></div> +<script> +"use strict"; +description("This test verifies the functionality of the Uniform Buffer objects"); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, null, 2); +var b1 = null; +var b2 = null; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + wtu.setupUnitQuad(gl); + + runBindingTest(); + runBadShaderTest(); + runUniformBufferOffsetAlignmentTest(); + runDrawTest(); + runNamedDrawTest(); + runNamedArrayDrawTest(); + runNestedStructsDrawTest(); + runArrayOfStructsDrawTest(); +} + +function runBindingTest() { + debug(""); + debug("Testing uniform buffer binding behavior"); + shouldBeNull("gl.getParameter(gl.UNIFORM_BUFFER_BINDING)"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "UNIFORM_BUFFER_BINDING query should succeed"); + + debug("Testing basic uniform buffer binding and unbinding"); + b1 = gl.createBuffer(); + b2 = gl.createBuffer(); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "createBuffer should not set an error"); + shouldBeNonNull("b1"); + shouldBeNonNull("b2"); + gl.bindBuffer(gl.UNIFORM_BUFFER, b1); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to bind uniform buffer"); + shouldBe("gl.getParameter(gl.UNIFORM_BUFFER_BINDING)", "b1"); + gl.bindBuffer(gl.UNIFORM_BUFFER, b2); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to update uniform buffer binding"); + shouldBe("gl.getParameter(gl.UNIFORM_BUFFER_BINDING)", "b2"); + gl.bindBuffer(gl.UNIFORM_BUFFER, null); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to unbind uniform buffer"); + + debug("Testing deleting uniform buffers"); + gl.deleteBuffer(b1); + gl.deleteBuffer(b2); + shouldBeNull("gl.getParameter(gl.UNIFORM_BUFFER_BINDING)"); + + // Shouldn't be able to bind a deleted buffer. + gl.bindBuffer(gl.UNIFORM_BUFFER, b2); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "binding a deleted buffer should generate INVALID_OPERATION"); + shouldBeNull("gl.getParameter(gl.UNIFORM_BUFFER_BINDING)"); +} + +function runBadShaderTest() { + debug(""); + var testProgram = wtu.setupProgram(gl, ['vshader', 'fbadshader']); + if (testProgram) { + testFailed("To define the same uniform in two uniform blocks should fail"); + } else { + testPassed("To define the same uniform in two uniform blocks should fail"); + } +} + +function runUniformBufferOffsetAlignmentTest() { + debug(""); + var offsetAlignment = gl.getParameter(gl.UNIFORM_BUFFER_OFFSET_ALIGNMENT); + + if (offsetAlignment % 4 != 0) { + testFailed("Unexpected UNIFORM_BUFFER_OFFSET_ALIGNMENT - should be aligned on a 4-byte boundary"); + } else { + testPassed("UNIFORM_BUFFER_OFFSET_ALIGNMENT is divisible by four"); + } +} + +function setRGBValuesToFloat32Array(floatView, red, green, blue) { + floatView[0] = red; + floatView[1] = green; + floatView[2] = blue; +} + +function checkFloat32UniformOffsetsInStd140Layout(uniformOffsets, expectedInitialOffset) { + if (expectedInitialOffset === undefined) + { + expectedInitialOffset = 0; + } + // Verify that the uniform offsets are set according to the std140 layout, which WebGL enforces. + // This function checks this for 32-bit float values, which are expected to be tightly packed. + for (var i = 0; i < uniformOffsets.length; ++i) + { + if (uniformOffsets[i] != expectedInitialOffset + i * Float32Array.BYTES_PER_ELEMENT) + { + testFailed("Uniform offsets are not according to std140 layout"); + return false; + } + } + return true; +} + +function runDrawTest() { + debug(""); + debug("Testing drawing with uniform buffers"); + + var program = wtu.setupProgram(gl, ['vshader', 'fshader']); + if (!program) { + testFailed("Could not compile shader with uniform blocks without error"); + return; + } + + var blockIndex_1 = gl.getUniformBlockIndex(program, "UBOData"); + var blockSize_1 = gl.getActiveUniformBlockParameter(program, blockIndex_1, gl.UNIFORM_BLOCK_DATA_SIZE); + var uniformIndices_1 = gl.getUniformIndices(program, ["UBORed", "UBOGreen", "UBOBlue"]); + var uniformOffsets_1 = gl.getActiveUniforms(program, uniformIndices_1, gl.UNIFORM_OFFSET); + var blockIndex_2 = gl.getUniformBlockIndex(program, "UBOD"); + var blockSize_2 = gl.getActiveUniformBlockParameter(program, blockIndex_2, gl.UNIFORM_BLOCK_DATA_SIZE); + var uniformIndices_2 = gl.getUniformIndices(program, ["UBOR", "UBOG", "UBOB"]); + var uniformOffsets_2 = gl.getActiveUniforms(program, uniformIndices_2, gl.UNIFORM_OFFSET); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to query uniform block information without error"); + + if (uniformOffsets_1.length < 3 || uniformOffsets_2.length < 3) { + testFailed("Could not query uniform offsets"); + return; + } + + if (!checkFloat32UniformOffsetsInStd140Layout(uniformOffsets_1) || !checkFloat32UniformOffsetsInStd140Layout(uniformOffsets_2)) + { + return; + } + + var uboArray_1 = new ArrayBuffer(blockSize_1); + var uboFloatView_1 = new Float32Array(uboArray_1); + setRGBValuesToFloat32Array(uboFloatView_1, 1.0, 0.0, 0.0); // UBORed, UBOGreen, UBOBlue + var uboArray_2 = new ArrayBuffer(blockSize_2); + var uboFloatView_2 = new Float32Array(uboArray_2); + setRGBValuesToFloat32Array(uboFloatView_2, 1.0, 1.0, 1.0); // UBOR, UBOG, UBOB + + var b_1 = gl.createBuffer(); + gl.bindBuffer(gl.UNIFORM_BUFFER, b_1); + gl.bufferData(gl.UNIFORM_BUFFER, uboFloatView_1, gl.DYNAMIC_DRAW); + var b_2 = gl.createBuffer(); + gl.bindBuffer(gl.UNIFORM_BUFFER, b_2); + gl.bufferData(gl.UNIFORM_BUFFER, uboFloatView_2, gl.DYNAMIC_DRAW); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to set UBO data with no errors"); + + var bindings = [1, 2]; + gl.uniformBlockBinding(program, blockIndex_1, bindings[0]); + gl.bindBufferBase(gl.UNIFORM_BUFFER, bindings[0], b_1); + gl.uniformBlockBinding(program, blockIndex_2, bindings[1]); + gl.bindBufferBase(gl.UNIFORM_BUFFER, bindings[1], b_2); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call bindBufferBase without errors"); + + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvas(gl, [255, 0, 0, 255], "draw call should set canvas to red", 2); + + debug("Changing the data in the uniform buffer should automatically update the uniforms exposed to the draw call"); + setRGBValuesToFloat32Array(uboFloatView_1, 0.0, 0.0, 1.0); // UBORed, UBOGreen, UBOBlue + gl.bindBuffer(gl.UNIFORM_BUFFER, b_1); + gl.bufferData(gl.UNIFORM_BUFFER, uboFloatView_1, gl.DYNAMIC_DRAW); + + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvas(gl, [0, 0, 255, 255], "draw call should set canvas to blue", 2); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +function runNamedDrawTest() { + debug(""); + debug("Testing drawing with named uniform buffers"); + + var program = wtu.setupProgram(gl, ['vshader', 'fshadernamed']); + if (!program) { + testFailed("Could not compile shader with named uniform blocks without error"); + return; + } + + var blockIndex = gl.getUniformBlockIndex(program, "UBOData"); + var blockSize = gl.getActiveUniformBlockParameter(program, blockIndex, gl.UNIFORM_BLOCK_DATA_SIZE); + var uniformIndices = gl.getUniformIndices(program, ["UBOData.Red", "UBOData.Green", "UBOData.Blue"]); + var uniformOffsets = gl.getActiveUniforms(program, uniformIndices, gl.UNIFORM_OFFSET); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to query uniform block information without error"); + + if (uniformOffsets.length < 3) { + testFailed("Could not query uniform offsets"); + return; + } + + if (!checkFloat32UniformOffsetsInStd140Layout(uniformOffsets)) + { + return; + } + + var uboArray = new ArrayBuffer(blockSize); + var uboFloatView = new Float32Array(uboArray); + setRGBValuesToFloat32Array(uboFloatView, 1.0, 0.0, 0.0); + + b1 = gl.createBuffer(); + gl.bindBuffer(gl.UNIFORM_BUFFER, b1); + gl.bufferData(gl.UNIFORM_BUFFER, uboArray, gl.DYNAMIC_DRAW); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to set UBO data with no errors"); + + var binding = 3; + gl.uniformBlockBinding(program, blockIndex, binding); + gl.bindBufferBase(gl.UNIFORM_BUFFER, binding, b1); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call bindBufferBase without errors"); + + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvas(gl, [255, 0, 0, 255], "draw call should set canvas to red", 2); + + debug("Changing the data in the uniform buffer should automatically update the uniforms exposed to the draw call"); + setRGBValuesToFloat32Array(uboFloatView, 0.0, 0.0, 1.0); + gl.bufferData(gl.UNIFORM_BUFFER, uboArray, gl.DYNAMIC_DRAW); + + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvas(gl, [0, 0, 255, 255], "draw call should set canvas to blue", 2); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +function runNamedArrayDrawTest() { + debug(""); + debug("Testing drawing with named uniform buffer arrays"); + + var program = wtu.setupProgram(gl, ['vshader', 'fshadernamedarray']); + if (!program) { + testFailed("could not compile shader with named uniform block arrays without error"); + return; + } + + var blockIndex = [gl.getUniformBlockIndex(program, "UBOData[0]"), + gl.getUniformBlockIndex(program, "UBOData[1]")]; + if (blockIndex[0] == gl.INVALID_INDEX || + blockIndex[1] == gl.INVALID_INDEX) { + testFailed("Could not query uniform block index"); + return; + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to query uniform block indices without error"); + var blockSize = [gl.getActiveUniformBlockParameter(program, blockIndex[0], gl.UNIFORM_BLOCK_DATA_SIZE), + gl.getActiveUniformBlockParameter(program, blockIndex[1], gl.UNIFORM_BLOCK_DATA_SIZE)]; + if (blockSize[0] != blockSize[1]) { + testFailed("uniform block instance array with different block sizes"); + } + var uniformIndices = gl.getUniformIndices(program, ["UBOData.Red", "UBOData.Green", "UBOData.Blue"]); + if (uniformIndices < 3 || + uniformIndices[0] == gl.INVALID_INDEX || + uniformIndices[1] == gl.INVALID_INDEX || + uniformIndices[2] == gl.INVALID_INDEX) { + testFailed("Could not query uniform indices"); + return; + } + var uniformOffsets = gl.getActiveUniforms(program, uniformIndices, gl.UNIFORM_OFFSET); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to query uniform block information without error"); + if (uniformOffsets.length < 3) { + testFailed("Could not query uniform offsets"); + return; + } + + if (!checkFloat32UniformOffsetsInStd140Layout(uniformOffsets)) + { + return; + } + + var offsetAlignment = gl.getParameter(gl.UNIFORM_BUFFER_OFFSET_ALIGNMENT); + var offset = Math.ceil(blockSize[0] / offsetAlignment) * offsetAlignment; + + var bufferSize = offset + blockSize[1]; + var uboArray = new ArrayBuffer(bufferSize); + var uboFloatView = new Float32Array(uboArray); + setRGBValuesToFloat32Array(uboFloatView, 1.0, 0.0, 0.0); + var uboFloatView2 = new Float32Array(uboArray, offset); + setRGBValuesToFloat32Array(uboFloatView2, 0.0, 0.0, 1.0); + + b1 = gl.createBuffer(); + gl.bindBuffer(gl.UNIFORM_BUFFER, b1); + gl.bufferData(gl.UNIFORM_BUFFER, uboArray, gl.DYNAMIC_DRAW); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to set UBO data with no errors"); + + var bindings = [4, 5]; + gl.uniformBlockBinding(program, blockIndex[0], bindings[0]); + gl.bindBufferRange(gl.UNIFORM_BUFFER, bindings[0], b1, 0, blockSize[0]); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call bindBufferRange without errors"); + gl.uniformBlockBinding(program, blockIndex[1], bindings[1]); + gl.bindBufferRange(gl.UNIFORM_BUFFER, bindings[1], b1, offset, blockSize[1]); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call bindBufferRange without errors"); + + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvas(gl, [127, 0, 127, 255], "draw call should set canvas to (0.5, 0, 0.5)", 2); + + debug("Changing the data in the uniform buffer should automatically update the uniforms exposed to the draw call"); + setRGBValuesToFloat32Array(uboFloatView, 0.0, 1.0, 1.0); + setRGBValuesToFloat32Array(uboFloatView2, 0.0, 0.0, 1.0); + gl.bufferData(gl.UNIFORM_BUFFER, uboArray, gl.DYNAMIC_DRAW); + + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvas(gl, [0, 127, 255, 255], "draw call should set canvas to (0, 0.5, 1)", 2); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +function runNestedStructsDrawTest() { + debug(""); + debug("Testing drawing with nested struct inside uniform block. The wrapper struct is passed to a function."); + + var program = wtu.setupProgram(gl, ['vshader', 'fshadernestedstruct'], undefined, undefined, true); + if (!program) { + testFailed("Could not compile shader with nested structs without error"); + return; + } + + var blockIndex = gl.getUniformBlockIndex(program, "UBOData"); + var blockSize = gl.getActiveUniformBlockParameter(program, blockIndex, gl.UNIFORM_BLOCK_DATA_SIZE); + var uniformIndices = gl.getUniformIndices(program, ["UBOStruct.color.red", "UBOStruct.color.green", "UBOStruct.color.blue"]); + var uniformOffsets = gl.getActiveUniforms(program, uniformIndices, gl.UNIFORM_OFFSET); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to query uniform block information without error"); + + if (uniformOffsets.length < 3) { + testFailed("Could not query uniform offsets"); + return; + } + + if (!checkFloat32UniformOffsetsInStd140Layout(uniformOffsets)) + { + return; + } + + var uboArray = new ArrayBuffer(blockSize); + var uboFloatView = new Float32Array(uboArray); + setRGBValuesToFloat32Array(uboFloatView, 0.0, 1.0, 0.0); + + b1 = gl.createBuffer(); + gl.bindBuffer(gl.UNIFORM_BUFFER, b1); + gl.bufferData(gl.UNIFORM_BUFFER, uboArray, gl.DYNAMIC_DRAW); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to set UBO data with no errors"); + + var binding = 3; + gl.uniformBlockBinding(program, blockIndex, binding); + gl.bindBufferBase(gl.UNIFORM_BUFFER, binding, b1); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call bindBufferBase without errors"); + + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvas(gl, [0, 255, 0, 255], "draw call should set canvas to green", 2); + + debug("Changing the data in the uniform buffer should automatically update the uniforms exposed to the draw call"); + setRGBValuesToFloat32Array(uboFloatView, 0.0, 0.0, 1.0); + gl.bufferData(gl.UNIFORM_BUFFER, uboArray, gl.DYNAMIC_DRAW); + + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvas(gl, [0, 0, 255, 255], "draw call should set canvas to blue", 2); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +function runArrayOfStructsDrawTest() { + debug(""); + debug("Testing drawing with array of structs inside uniform block. A struct in the block is passed to a function."); + + var program = wtu.setupProgram(gl, ['vshader', 'fshaderarrayofstructs'], undefined, undefined, true); + if (!program) { + testFailed("Could not compile shader with an array of structs in an interface block without error"); + return; + } + + var blockIndex = gl.getUniformBlockIndex(program, "UBOData"); + var blockSize = gl.getActiveUniformBlockParameter(program, blockIndex, gl.UNIFORM_BLOCK_DATA_SIZE); + var uniformIndices = gl.getUniformIndices(program, ["UBOColors[0].red", "UBOColors[0].green", "UBOColors[0].blue"]); + var uniformOffsets = gl.getActiveUniforms(program, uniformIndices, gl.UNIFORM_OFFSET); + var uniformIndices_2 = gl.getUniformIndices(program, ["UBOColors[1].red", "UBOColors[1].green", "UBOColors[1].blue"]); + var uniformOffsets_2 = gl.getActiveUniforms(program, uniformIndices_2, gl.UNIFORM_OFFSET); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to query uniform block information without error"); + + if (uniformOffsets.length < 3) { + testFailed("Could not query uniform offsets"); + return; + } + + if (!checkFloat32UniformOffsetsInStd140Layout(uniformOffsets)) + { + return; + } + if (!checkFloat32UniformOffsetsInStd140Layout(uniformOffsets_2, uniformOffsets_2[0])) + { + return; + } + + var uboArray = new ArrayBuffer(blockSize); + var uboFloatView = new Float32Array(uboArray); + setRGBValuesToFloat32Array(uboFloatView, 0.0, 0.5, 0.0); + var uboFloatView2 = new Float32Array(uboArray, uniformOffsets_2[0]); + setRGBValuesToFloat32Array(uboFloatView2, 0.0, 0.5, 0.0); + + b1 = gl.createBuffer(); + gl.bindBuffer(gl.UNIFORM_BUFFER, b1); + gl.bufferData(gl.UNIFORM_BUFFER, uboArray, gl.DYNAMIC_DRAW); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to set UBO data with no errors"); + + var binding = 3; + gl.uniformBlockBinding(program, blockIndex, binding); + gl.bindBufferBase(gl.UNIFORM_BUFFER, binding, b1); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call bindBufferBase without errors"); + + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvas(gl, [0, 255, 0, 255], "draw call should set canvas to green", 2); + + debug("Changing the data in the uniform buffer should automatically update the uniforms exposed to the draw call"); + setRGBValuesToFloat32Array(uboFloatView, 1.0, 0.0, 0.0); + setRGBValuesToFloat32Array(uboFloatView2, 0.0, 0.0, 1.0); + gl.bufferData(gl.UNIFORM_BUFFER, uboArray, gl.DYNAMIC_DRAW); + + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvas(gl, [255, 0, 255, 255], "draw call should set canvas to purple", 2); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +debug(""); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> |