summaryrefslogtreecommitdiffstats
path: root/dom/canvas/test/webgl-conf/checkout/conformance2/buffers
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/canvas/test/webgl-conf/checkout/conformance2/buffers
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.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')
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/buffers/00_test_list.txt13
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/buffers/bound-buffer-size-change-test.html119
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/buffers/buffer-copying-contents.html176
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/buffers/buffer-copying-restrictions.html102
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/buffers/buffer-data-and-buffer-sub-data-sub-source.html183
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/buffers/buffer-overflow-test.html51
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/buffers/buffer-type-restrictions.html110
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/buffers/delete-buffer.html80
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/buffers/get-buffer-sub-data-validity.html250
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/buffers/get-buffer-sub-data.html223
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/buffers/one-large-uniform-buffer.html195
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/buffers/uniform-buffers-second-compile.html104
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/buffers/uniform-buffers-state-restoration.html101
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/buffers/uniform-buffers.html576
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>