From 5dadf4eabc325e85e8362c91c3a23b9ee36fa67f Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Thu, 29 Sep 2016 18:07:13 -0700 Subject: [PATCH 01/21] WebGL 2: test getBufferSubDataAsync --- .../buffers/getBufferSubDataAsync.html | 143 ++++++++++++++++++ sdk/tests/conformance2/context/methods-2.html | 1 + 2 files changed, 144 insertions(+) create mode 100644 sdk/tests/conformance2/buffers/getBufferSubDataAsync.html diff --git a/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html b/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html new file mode 100644 index 0000000000..27c27b9058 --- /dev/null +++ b/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html @@ -0,0 +1,143 @@ + + + + + + +WebGL getBufferSubDataAsync test. + + + + + +
+
+ + + diff --git a/sdk/tests/conformance2/context/methods-2.html b/sdk/tests/conformance2/context/methods-2.html index 4dbf368b13..aaeceb66c6 100644 --- a/sdk/tests/conformance2/context/methods-2.html +++ b/sdk/tests/conformance2/context/methods-2.html @@ -181,6 +181,7 @@ // WebGL2 methods "getBufferSubData", + "getBufferSubDataAsync", "copyBufferSubData", "blitFramebuffer", "framebufferTextureLayer", From cd7d8bd0e34eaa17e7be39b82c492dd7f899afc4 Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Wed, 5 Oct 2016 10:39:11 -0700 Subject: [PATCH 02/21] stress test getBufferSubDataAsync --- .../conformance2/buffers/00_test_list.txt | 2 + .../buffers/getBufferSubDataAsyncStress.html | 95 +++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 sdk/tests/conformance2/buffers/getBufferSubDataAsyncStress.html diff --git a/sdk/tests/conformance2/buffers/00_test_list.txt b/sdk/tests/conformance2/buffers/00_test_list.txt index 5f6bca3676..7c30b30eaf 100644 --- a/sdk/tests/conformance2/buffers/00_test_list.txt +++ b/sdk/tests/conformance2/buffers/00_test_list.txt @@ -5,4 +5,6 @@ buffer-data-and-buffer-sub-data-sub-source.html buffer-type-restrictions.html buffer-overflow-test.html getBufferSubData.html +getBufferSubDataAsync.html +getBufferSubDataAsyncStress.html uniform-buffers.html diff --git a/sdk/tests/conformance2/buffers/getBufferSubDataAsyncStress.html b/sdk/tests/conformance2/buffers/getBufferSubDataAsyncStress.html new file mode 100644 index 0000000000..9ed5757409 --- /dev/null +++ b/sdk/tests/conformance2/buffers/getBufferSubDataAsyncStress.html @@ -0,0 +1,95 @@ + + + + + + +WebGL getBufferSubDataAsync stress test. + + + + + +
+ +
+ + + From a21d8cae9448d16904742442f214b2eb69bae627 Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Wed, 5 Oct 2016 13:11:13 -0700 Subject: [PATCH 03/21] getBufferSubDataAsync-lose-context.html --- .../conformance2/buffers/00_test_list.txt | 3 +- .../getBufferSubDataAsync-lose-context.html | 105 ++++++++++++++++++ ...html => getBufferSubDataAsync-stress.html} | 7 +- 3 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 sdk/tests/conformance2/buffers/getBufferSubDataAsync-lose-context.html rename sdk/tests/conformance2/buffers/{getBufferSubDataAsyncStress.html => getBufferSubDataAsync-stress.html} (96%) diff --git a/sdk/tests/conformance2/buffers/00_test_list.txt b/sdk/tests/conformance2/buffers/00_test_list.txt index 7c30b30eaf..407ec8738a 100644 --- a/sdk/tests/conformance2/buffers/00_test_list.txt +++ b/sdk/tests/conformance2/buffers/00_test_list.txt @@ -6,5 +6,6 @@ buffer-type-restrictions.html buffer-overflow-test.html getBufferSubData.html getBufferSubDataAsync.html -getBufferSubDataAsyncStress.html +getBufferSubDataAsync-lose-context.html +getBufferSubDataAsync-stress.html uniform-buffers.html diff --git a/sdk/tests/conformance2/buffers/getBufferSubDataAsync-lose-context.html b/sdk/tests/conformance2/buffers/getBufferSubDataAsync-lose-context.html new file mode 100644 index 0000000000..3100e28f1a --- /dev/null +++ b/sdk/tests/conformance2/buffers/getBufferSubDataAsync-lose-context.html @@ -0,0 +1,105 @@ + + + + + + +WebGL getBufferSubDataAsync context loss regression test. + + + + + +
+ +
+ + + diff --git a/sdk/tests/conformance2/buffers/getBufferSubDataAsyncStress.html b/sdk/tests/conformance2/buffers/getBufferSubDataAsync-stress.html similarity index 96% rename from sdk/tests/conformance2/buffers/getBufferSubDataAsyncStress.html rename to sdk/tests/conformance2/buffers/getBufferSubDataAsync-stress.html index 9ed5757409..f11490eb86 100644 --- a/sdk/tests/conformance2/buffers/getBufferSubDataAsyncStress.html +++ b/sdk/tests/conformance2/buffers/getBufferSubDataAsync-stress.html @@ -69,7 +69,7 @@ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, 0); var promise = gl.getBufferSubDataAsync(gl.PIXEL_PACK_BUFFER, 0, readbackBuffer); gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); - promises.push(promise.then(function(buf) { + promise = promise.then(function(buf) { if (buf[0] == color[0] && buf[1] == color[1] && buf[2] == color[2] && @@ -83,7 +83,10 @@ testFailed("getBufferSubDataAsync calls #" + last_counter + " and #" + counter + " out of order"); } last_counter = counter; - })); + }, function(e) { + testFailed(e); + }); + promises.push(promise); })(); } From 05f5648929677c7c8f37471cbabef59829e68485 Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Wed, 5 Oct 2016 13:43:40 -0700 Subject: [PATCH 04/21] add getBufferSubDataAsync to WebGL 2 spec --- specs/latest/2.0/index.html | 62 ++++++++++++++++++++++++++++++++++++- specs/latest/2.0/webgl2.idl | 4 +++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/specs/latest/2.0/index.html b/specs/latest/2.0/index.html index 781294534a..1b223d6a9c 100644 --- a/specs/latest/2.0/index.html +++ b/specs/latest/2.0/index.html @@ -712,6 +712,9 @@

The WebGL context

// replaces it for the purpose of fetching data back from the GPU. void getBufferSubData(GLenum target, GLintptr srcByteOffset, ArrayBufferView dstData, optional GLuint dstOffset = 0, optional GLuint length = 0); + // Asynchronous version of getBufferSubData which fulfills the returned promise when the data becomes available. + Promise<ArrayBuffer> getBufferSubDataAsync(GLenum target, GLintptr srcByteOffset, ArrayBufferView dstData, + optional GLuint dstOffset = 0, optional GLuint length = 0); /* Framebuffer objects */ void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, @@ -1202,7 +1205,7 @@

Buffer objects

an INVALID_VALUE error is generated.
  • If srcByteOffset is less than zero, an INVALID_VALUE error is generated. -
  • If zero is bound to target, an INVALID_OPERATION +
  • If no WebGLBuffer is bound to target, an INVALID_OPERATION error is generated.
  • If target is TRANSFORM_FEEDBACK_BUFFER, and any transform feedback object is currently active, an INVALID_OPERATION @@ -1216,6 +1219,63 @@

    Buffer objects

    it is the responsibility of the WebGL API to ensure that data are accessed consistently. This applies even if the buffer is currently bound to a transform feedback binding point. +
    +

    + Promise<ArrayBuffer> getBufferSubDataAsync(GLenum target, GLintptr srcByteOffset, ArrayBufferView dstData, + optional GLuint dstOffset = 0, optional GLuint length = 0) +

    +
    +
    + If length is 0, it defaults to dstBuffer.length - dstOffset. + For the WebGLBuffer bound to the passed target, read length + elements into dstBuffer, reading starting at srcByteOffset + and writing starting at dstOffset into dstBuffer. +
      +
    • If dstOffset + length is greater than dstBuffer.length + an INVALID_VALUE error is generated. +
    • If srcByteOffset is less than zero, an INVALID_VALUE + error is generated. +
    • If no WebGLBuffer is bound to target, an INVALID_OPERATION + error is generated. +
    • If target is TRANSFORM_FEEDBACK_BUFFER, + and any transform feedback object is currently active, an INVALID_OPERATION + error is generated. +
    • If srcByteOffset + length*ElementSize(dstBuffer) is larger + than the length of the GL buffer, generate INVALID_OPERATION. +
    + These errors are generated synchronously and are visible immediately after + getBufferSubDataAsync returns. + + This command executes serially with respect to others in the command stream, but the data is + only written to dstData when the promise is fulfilled; it is available in + the Promise's success callback, but not earlier. + +

    + If a failure occurs that prevents the promise from being fulfilled, then the returned + Promise is rejected with an "InvalidStateError" DOMException, and no data is written to + dstData. If any of the above errors occur, the Promise is already rejected upon + return. If the context is lost before the Promise is fulfilled, or if dstData + is currently neutered when the Promise is fulfilled, then the Promise will be rejected + later. In this case, no WebGL error is generated. + +

    + Otherwise, the Promise is fulfilled when the data becomes available, + and dstData, originally passed to getBufferSubDataAsync, is + passed to the success callback. + +

    + If the buffer is written and read sequentially by other operations + and getBufferSubDataAsync, it is the responsibility of the WebGL API to ensure + that data are accessed consistently. This applies even if the buffer is currently bound to a + transform feedback binding point. + +
    + Compared to the synchronous version of getBufferSubData, this version may + impose less overhead on applications. Intended use cases include reading pixels into a + pixel buffer object and examining that data on the CPU. It does not force the graphics + pipeline to be stalled as getBufferSubData does. +
    +
    diff --git a/specs/latest/2.0/webgl2.idl b/specs/latest/2.0/webgl2.idl index ce962fbf49..a10b6df12d 100644 --- a/specs/latest/2.0/webgl2.idl +++ b/specs/latest/2.0/webgl2.idl @@ -328,6 +328,10 @@ interface WebGL2RenderingContextBase // replaces it for the purpose of fetching data back from the GPU. void getBufferSubData(GLenum target, GLintptr srcByteOffset, ArrayBufferView dstData, optional GLuint dstOffset = 0, optional GLuint length = 0); + // Asynchronous version of getBufferSubData which fulfills the returned promise when the data becomes available. + Promise getBufferSubDataAsync(GLenum target, GLintptr srcByteOffset, + ArrayBufferView dstData, optional GLuint dstOffset = 0, + optional GLuint length = 0); /* Framebuffer objects */ void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, From 183846a2d55b8cd8ccdecf6165effc89f1591314 Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Wed, 5 Oct 2016 16:59:33 -0700 Subject: [PATCH 05/21] more tests for getBufferSubDataAsync --- .../buffers/getBufferSubDataAsync.html | 144 +++++++++++++++--- specs/latest/2.0/index.html | 5 +- 2 files changed, 122 insertions(+), 27 deletions(-) diff --git a/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html b/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html index 27c27b9058..2385984fd6 100644 --- a/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html +++ b/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html @@ -51,11 +51,15 @@ 5.3, -1.0, 1.0 ]; var floatArray = new Float32Array(vertices); -var retArray = new Float32Array(vertices.length); +var dstData = new Float32Array(vertices.length); var extraLargeBuffer = new Float32Array(vertices.length + 1); var resolvedArray; var promise; +function clearDstData() { + dstData = new Float32Array(vertices.length); +} + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup."); function expectRejected(promise, message) { @@ -66,73 +70,163 @@ }); } +var types = [ + 'ARRAY_BUFFER', + 'ELEMENT_ARRAY_BUFFER', + 'COPY_READ_BUFFER', + 'COPY_WRITE_BUFFER', + 'PIXEL_PACK_BUFFER', + 'PIXEL_UNPACK_BUFFER', + 'TRANSFORM_FEEDBACK_BUFFER', + 'UNIFORM_BUFFER' +]; +var buffers = []; + +var buffer; +for (var i = 0; i < types.length; i++) { + buffer = gl.createBuffer(); + gl.bindBuffer(gl[types[i]], buffer); + gl.bufferData(gl[types[i]], floatArray, gl.STATIC_DRAW); + buffers.push(buffer); +} + // This long promise chain is to serialize the tests - so that the errors // associated with a given test always appear underneath that test. // For each test in this chain, if it performs test checks in a promise, // it MUST return that promise so that it completes before the next test. -Promise.resolve().then(function() { +var allPromises = Promise.resolve(); + +allPromises = allPromises.then(function() { debug("Testing promise rejection"); +}); -}).then(function() { +allPromises = allPromises.then(function() { var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, new ArrayBuffer(4))"; shouldBeType(code, "Promise"); return expectRejected(promise, code); +}); -}).then(function() { +allPromises = allPromises.then(function() { var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, null)"; shouldBeType(code, "Promise"); return expectRejected(promise, code); +}); + +allPromises = allPromises.then(function() { + debug(""); +}); + +for (var i = 0; i < types.length; i++) { + (function() { + var type = types[i]; + allPromises = allPromises.then(function() { + debug("Test that getBufferSubDataAsync successfully works reading buffer data from gl." + type); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + "promise = gl.getBufferSubDataAsync(gl." + type + ", 0, dstData)"); + gl.bindBuffer(gl.ARRAY_BUFFER, null); + return promise.then(function(arr) { + resolvedArray = arr; + shouldBe("resolvedArray", "dstData"); + shouldBeTrue("areArraysEqual(resolvedArray, floatArray)"); + }); + }); + })(); +} -}).then(function() { +allPromises = allPromises.then(function() { debug(""); - debug("Test that getBufferSubDataAsync successfully works reading buffer data from gl.ARRAY_BUFFER"); + debug("Test that getBufferSubDataAsync successfully works with dstOffset"); + + gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); + + clearDstData(); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, 2)"); + return promise.then(function(arr) { + resolvedArray = arr; + shouldBe("resolvedArray", "dstData"); + shouldBeTrue("areArraysEqual(resolvedArray.slice(0, 2), [0, 0])"); + shouldBeTrue("areArraysEqual(resolvedArray.slice(2), floatArray.slice(0, floatArray.length - 2))"); + }); +}); + +allPromises = allPromises.then(function() { + debug("Test that getBufferSubDataAsync fails when given a dstOffset beyond the end of dstData"); + + var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length)"; + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); + return expectRejected(promise, code); +}); - var buffer = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, buffer); - gl.bufferData(gl.ARRAY_BUFFER, floatArray, gl.STATIC_DRAW); +allPromises = allPromises.then(function() { + debug(""); + debug("Test that getBufferSubDataAsync successfully works with dstOffset and length"); - debug("Check array data to match original data set by the buffer"); - wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, retArray)"); + clearDstData(); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, 2, 2)"); return promise.then(function(arr) { resolvedArray = arr; - shouldBe("resolvedArray", "retArray"); - shouldBeTrue("areArraysEqual(resolvedArray, floatArray)"); + shouldBe("resolvedArray", "dstData"); + shouldBeTrue("areArraysEqual(resolvedArray.slice(0, 2), [0, 0])"); + shouldBeTrue("areArraysEqual(resolvedArray.slice(2, 4), floatArray.slice(0, 2))"); + shouldBeTrue("areArraysEqual(resolvedArray.slice(4), [0, 0, 0, 0, 0])"); }); +}); + +allPromises = allPromises.then(function() { + debug("Test that getBufferSubDataAsync fails when given a dstOffset+length beyond the end of dstData"); + + var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length - 1, 2)"; + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); + return expectRejected(promise, code); +}); + +allPromises = allPromises.then(function() { + var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length, 0)"; + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); + return expectRejected(promise, code); +}); -}).then(function() { +allPromises = allPromises.then(function() { debug(""); debug("Test that getBufferSubDataAsync fails when given a buffer with its size larger than the original data"); var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, extraLargeBuffer)"; - wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code, "Extra length should generate INVALID_VALUE."); + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); return expectRejected(promise, code); +}); -}).then(function() { +allPromises = allPromises.then(function() { debug("Test that getBufferSubDataAsync fails when offset summed with buffer length is larger than the size of the original data size"); - var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, retArray.byteLength + 1, retArray)"; + var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, dstData.byteLength + 1, dstData)"; wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); return expectRejected(promise, code); +}); -}).then(function() { - var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 1, retArray)"; +allPromises = allPromises.then(function() { + var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 1, dstData)"; wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); return expectRejected(promise, code); +}); -}).then(function() { +allPromises = allPromises.then(function() { debug("Test that getBufferSubDataAsync fails when 0 is bound to the target"); - var code = "promise = gl.getBufferSubDataAsync(gl.ELEMENT_ARRAY_BUFFER, 0, retArray)"; + var code = "promise = gl.getBufferSubDataAsync(gl.ELEMENT_ARRAY_BUFFER, 0, dstData)"; gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, code); return expectRejected(promise, code); +}); -}).then(function() { +allPromises = allPromises.then(function() { debug("Test that getBufferSubDataAsync fails when offset is less than 0"); - var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, -1, retArray)"; + var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, -1, dstData)"; wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); return expectRejected(promise, code); +}); -}).then(function() { +allPromises.then(function() { + debug(""); finishTest(); - }).catch(function(e) { testFailed(e.toString()); }); diff --git a/specs/latest/2.0/index.html b/specs/latest/2.0/index.html index 1b223d6a9c..b9a7be6161 100644 --- a/specs/latest/2.0/index.html +++ b/specs/latest/2.0/index.html @@ -1255,8 +1255,9 @@

    Buffer objects

    Promise is rejected with an "InvalidStateError" DOMException, and no data is written to dstData. If any of the above errors occur, the Promise is already rejected upon return. If the context is lost before the Promise is fulfilled, or if dstData - is currently neutered when the Promise is fulfilled, then the Promise will be rejected - later. In this case, no WebGL error is generated. + is neutered at the time the Promise is fulfilled, then the Promise will be rejected later. + In this case, no WebGL error is generated. If the promise rejects, no data is written to + dstBuffer.

    Otherwise, the Promise is fulfilled when the data becomes available, From 4be07995550e9e6e33b17e089e1f6d43de18a2dc Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Wed, 5 Oct 2016 18:17:23 -0700 Subject: [PATCH 06/21] add corner case --- .../buffers/getBufferSubDataAsync.html | 30 +++++++++++++------ specs/latest/2.0/index.html | 2 ++ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html b/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html index 2385984fd6..cd9bf14eb6 100644 --- a/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html +++ b/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html @@ -144,16 +144,25 @@ "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, 2)"); return promise.then(function(arr) { resolvedArray = arr; - shouldBe("resolvedArray", "dstData"); shouldBeTrue("areArraysEqual(resolvedArray.slice(0, 2), [0, 0])"); shouldBeTrue("areArraysEqual(resolvedArray.slice(2), floatArray.slice(0, floatArray.length - 2))"); }); }); +allPromises = allPromises.then(function() { + clearDstData(); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length)"); + return promise.then(function(arr) { + resolvedArray = arr; + shouldBeTrue("areArraysEqual(resolvedArray, [0, 0, 0, 0, 0, 0, 0, 0, 0])"); + }); +}); + allPromises = allPromises.then(function() { debug("Test that getBufferSubDataAsync fails when given a dstOffset beyond the end of dstData"); - var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length)"; + var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length + 1)"; wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); return expectRejected(promise, code); }); @@ -167,7 +176,6 @@ "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, 2, 2)"); return promise.then(function(arr) { resolvedArray = arr; - shouldBe("resolvedArray", "dstData"); shouldBeTrue("areArraysEqual(resolvedArray.slice(0, 2), [0, 0])"); shouldBeTrue("areArraysEqual(resolvedArray.slice(2, 4), floatArray.slice(0, 2))"); shouldBeTrue("areArraysEqual(resolvedArray.slice(4), [0, 0, 0, 0, 0])"); @@ -175,15 +183,19 @@ }); allPromises = allPromises.then(function() { - debug("Test that getBufferSubDataAsync fails when given a dstOffset+length beyond the end of dstData"); - - var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length - 1, 2)"; - wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); - return expectRejected(promise, code); + clearDstData(); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length, 0)"); + return promise.then(function(arr) { + resolvedArray = arr; + shouldBeTrue("areArraysEqual(resolvedArray, [0, 0, 0, 0, 0, 0, 0, 0, 0])"); + }); }); allPromises = allPromises.then(function() { - var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length, 0)"; + debug("Test that getBufferSubDataAsync fails when given a dstOffset+length beyond the end of dstData"); + + var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length - 1, 2)"; wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); return expectRejected(promise, code); }); diff --git a/specs/latest/2.0/index.html b/specs/latest/2.0/index.html index b9a7be6161..8f7ef8b4e6 100644 --- a/specs/latest/2.0/index.html +++ b/specs/latest/2.0/index.html @@ -1242,6 +1242,8 @@

    Buffer objects

    error is generated.
  • If srcByteOffset + length*ElementSize(dstBuffer) is larger than the length of the GL buffer, generate INVALID_OPERATION. +
  • If dstBuffer.length equals dstOffset, no data is written to + dstBuffer. These errors are generated synchronously and are visible immediately after getBufferSubDataAsync returns. From 4e129ffa90f88f243e6193d96d6a1d0c2e67ee85 Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Thu, 6 Oct 2016 18:34:24 -0700 Subject: [PATCH 07/21] update spec language to match new getBufferSubData --- specs/latest/2.0/index.html | 51 ++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/specs/latest/2.0/index.html b/specs/latest/2.0/index.html index 8f7ef8b4e6..648f26d9c8 100644 --- a/specs/latest/2.0/index.html +++ b/specs/latest/2.0/index.html @@ -1226,28 +1226,39 @@

    Buffer objects

    - If length is 0, it defaults to dstBuffer.length - dstOffset. - For the WebGLBuffer bound to the passed target, read length - elements into dstBuffer, reading starting at srcByteOffset - and writing starting at dstOffset into dstBuffer. + Reads back data asynchronously from the bound WebGLBuffer into dstBuffer. +

    + Let buf be the buffer bound to target. + If length is 0, let copyLength be + dstBuffer.length - dstOffset; otherwise, let + copyLength be length. +

    + Copy copyLength typed elements (each of size dstBuffer.BYTES_PER_ELEMENT) + from buf into dstBuffer, + reading buf starting at byte index srcByteOffset and + writing into dstBuffer starting at element index dstOffset. + If copyLength is 0, no data is written to dstBuffer.
      -
    • If dstOffset + length is greater than dstBuffer.length - an INVALID_VALUE error is generated. -
    • If srcByteOffset is less than zero, an INVALID_VALUE - error is generated. -
    • If no WebGLBuffer is bound to target, an INVALID_OPERATION - error is generated. +
    • If no WebGLBuffer is bound to target, + an INVALID_OPERATION error is generated.
    • If target is TRANSFORM_FEEDBACK_BUFFER, - and any transform feedback object is currently active, an INVALID_OPERATION - error is generated. -
    • If srcByteOffset + length*ElementSize(dstBuffer) is larger - than the length of the GL buffer, generate INVALID_OPERATION. -
    • If dstBuffer.length equals dstOffset, no data is written to - dstBuffer. + and any transform feedback object is currently active, + an INVALID_OPERATION error is generated. +
    • If dstOffset is greater than dstBuffer.length, + an INVALID_VALUE error is generated. +
    • If dstOffset + copyLength is greater than dstBuffer.length, + an INVALID_VALUE error is generated. +
    • If srcByteOffset is less than zero, + an INVALID_VALUE error is generated. +
    • If srcByteOffset + copyLength*dstBuffer.BYTES_PER_ELEMENT + is larger than the length of buf, + an INVALID_OPERATION is generated.
    + If any error is generated, no data is written to dstBuffer. These errors are generated synchronously and are visible immediately after getBufferSubDataAsync returns. +

    This command executes serially with respect to others in the command stream, but the data is only written to dstData when the promise is fulfilled; it is available in the Promise's success callback, but not earlier. @@ -1267,10 +1278,10 @@

    Buffer objects

    passed to the success callback.

    - If the buffer is written and read sequentially by other operations - and getBufferSubDataAsync, it is the responsibility of the WebGL API to ensure - that data are accessed consistently. This applies even if the buffer is currently bound to a - transform feedback binding point. + If the buffer is written and read sequentially by other operations and getBufferSubData, + it is the responsibility of the WebGL API to ensure that data are accessed consistently. This applies + even if the buffer is currently bound to a transform feedback binding point. +

    Compared to the synchronous version of getBufferSubData, this version may From b00c4126dac74986be3f8d85b6f940b7571988f1 Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Fri, 7 Oct 2016 10:48:44 -0700 Subject: [PATCH 08/21] Correct stress test and add more tests --- .../buffers/getBufferSubDataAsync-stress.html | 2 +- .../buffers/getBufferSubDataAsync.html | 87 +++++++++++++++++-- 2 files changed, 82 insertions(+), 7 deletions(-) diff --git a/sdk/tests/conformance2/buffers/getBufferSubDataAsync-stress.html b/sdk/tests/conformance2/buffers/getBufferSubDataAsync-stress.html index f11490eb86..af39d4261a 100644 --- a/sdk/tests/conformance2/buffers/getBufferSubDataAsync-stress.html +++ b/sdk/tests/conformance2/buffers/getBufferSubDataAsync-stress.html @@ -62,7 +62,7 @@ for (var i = 0; i < TEST_COUNT; i++) { (function() { var counter = i; - var color = [counter & 0xff, (counter << 8) & 0xff, (counter << 16) & 0xff, 255]; + var color = [counter & 0xff, (counter >> 8) & 0xff, (counter >> 16) & 0xff, 255]; gl.clearColor(color[0] / 255, color[1] / 255, color[2] / 255, color[3] / 255); gl.clear(gl.COLOR_BUFFER_BIT); gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo); diff --git a/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html b/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html index cd9bf14eb6..341d9e4df7 100644 --- a/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html +++ b/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html @@ -87,9 +87,16 @@ buffer = gl.createBuffer(); gl.bindBuffer(gl[types[i]], buffer); gl.bufferData(gl[types[i]], floatArray, gl.STATIC_DRAW); - buffers.push(buffer); + buffers[i] = buffer; } +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."); + // This long promise chain is to serialize the tests - so that the errors // associated with a given test always appear underneath that test. // For each test in this chain, if it performs test checks in a promise, @@ -101,14 +108,22 @@ }); allPromises = allPromises.then(function() { + debug("Argument must be ArrayBufferView, not ArrayBuffer") + gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, new ArrayBuffer(4))"; - shouldBeType(code, "Promise"); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, code); + shouldBeType("promise", "Promise"); + gl.bindBuffer(gl.ARRAY_BUFFER, null); return expectRejected(promise, code); }); allPromises = allPromises.then(function() { + debug("Argument must be ArrayBufferView, not null") + gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, null)"; - shouldBeType(code, "Promise"); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, code); + shouldBeType("promise", "Promise"); + gl.bindBuffer(gl.ARRAY_BUFFER, null); return expectRejected(promise, code); }); @@ -118,12 +133,15 @@ for (var i = 0; i < types.length; i++) { (function() { - var type = types[i]; + var index = i; allPromises = allPromises.then(function() { debug("Test that getBufferSubDataAsync successfully works reading buffer data from gl." + type); + var type = types[index]; + var buf = buffers[index]; + gl.bindBuffer(gl[type], buf); wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "promise = gl.getBufferSubDataAsync(gl." + type + ", 0, dstData)"); - gl.bindBuffer(gl.ARRAY_BUFFER, null); + gl.bindBuffer(gl[type], null); return promise.then(function(arr) { resolvedArray = arr; shouldBe("resolvedArray", "dstData"); @@ -192,9 +210,23 @@ }); }); +allPromises = allPromises.then(function() { + debug(""); + debug("Test that getBufferSubDataAsync successfully works with uninitialized buffers"); + dstData = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9]); + gl.bindBuffer(gl.ARRAY_BUFFER, uninitializedBuffer); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData)"); + return promise.then(function(arr) { + resolvedArray = arr; + shouldBeTrue("areArraysEqual(resolvedArray, [0, 0, 0, 0, 0, 0, 0, 0, 0])"); + }); +}); + allPromises = allPromises.then(function() { debug("Test that getBufferSubDataAsync fails when given a dstOffset+length beyond the end of dstData"); + gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length - 1, 2)"; wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); return expectRejected(promise, code); @@ -203,6 +235,7 @@ allPromises = allPromises.then(function() { debug(""); debug("Test that getBufferSubDataAsync fails when given a buffer with its size larger than the original data"); + gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, extraLargeBuffer)"; wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); return expectRejected(promise, code); @@ -210,19 +243,22 @@ allPromises = allPromises.then(function() { debug("Test that getBufferSubDataAsync fails when offset summed with buffer length is larger than the size of the original data size"); + gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, dstData.byteLength + 1, dstData)"; wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); return expectRejected(promise, code); }); allPromises = allPromises.then(function() { + gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 1, dstData)"; wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); return expectRejected(promise, code); }); allPromises = allPromises.then(function() { - debug("Test that getBufferSubDataAsync fails when 0 is bound to the target"); + debug("Test that getBufferSubDataAsync fails when no buffer is bound to the target"); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); var code = "promise = gl.getBufferSubDataAsync(gl.ELEMENT_ARRAY_BUFFER, 0, dstData)"; gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, code); @@ -231,16 +267,55 @@ allPromises = allPromises.then(function() { debug("Test that getBufferSubDataAsync fails when offset is less than 0"); + gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, -1, dstData)"; wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); return expectRejected(promise, code); }); +allPromises = allPromises.then(function() { + debug(""); + debug("Test that getBufferSubDataAsync works when a buffer is immediately resized to be too small"); + + buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.bufferData(gl.ARRAY_BUFFER, floatArray, gl.STATIC_DRAW); + buffers[i] = buffer; + + dstData = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9]); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData)"); + gl.bufferData(gl.ARRAY_BUFFER, 4, gl.STATIC_DRAW); + return promise.then(function(arr) { + resolvedArray = arr; + shouldBeTrue("areArraysEqual(resolvedArray, floatArray)"); + }); +}); + +allPromises = allPromises.then(function() { + debug(""); + debug("Test that getBufferSubDataAsync works when a buffer is immediately deleted"); + + dstData = 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, + "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData)"); + gl.deleteBuffer(buffer); + return promise.then(function(arr) { + resolvedArray = arr; + shouldBeTrue("areArraysEqual(resolvedArray, floatArray)"); + }); +}); + allPromises.then(function() { debug(""); finishTest(); }).catch(function(e) { testFailed(e.toString()); + debug(""); + finishTest(); }); var successfullyParsed = true; From 27b36240e1b47117b74c109aa019cfd82c376004 Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Fri, 7 Oct 2016 14:47:37 -0700 Subject: [PATCH 09/21] remove ordering requirement, and fix/clean up some catches --- .../buffers/getBufferSubDataAsync-stress.html | 13 +++++-------- .../conformance2/buffers/getBufferSubDataAsync.html | 9 ++------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/sdk/tests/conformance2/buffers/getBufferSubDataAsync-stress.html b/sdk/tests/conformance2/buffers/getBufferSubDataAsync-stress.html index af39d4261a..1e9355461a 100644 --- a/sdk/tests/conformance2/buffers/getBufferSubDataAsync-stress.html +++ b/sdk/tests/conformance2/buffers/getBufferSubDataAsync-stress.html @@ -55,7 +55,6 @@ var readbackBuffer = new Uint8Array(4); var TEST_COUNT = 300; -var last_counter = -1; var promises = []; promises.length = TEST_COUNT; debug("kicking off " + TEST_COUNT + " getBufferSubDataAsync calls"); @@ -76,21 +75,19 @@ buf[3] == color[3]) { testPassed("color readback #" + counter + " was correct"); } else { - testFailed("color readback #" + counter + "was incorrect: was " + + testFailed("color readback #" + counter + " was incorrect: was " + buf + " but expected " + color); } - if (counter <= last_counter) { - testFailed("getBufferSubDataAsync calls #" + last_counter + " and #" + counter + " out of order"); - } - last_counter = counter; }, function(e) { - testFailed(e); + testFailed(e.toString()); }); promises.push(promise); })(); } -Promise.all(promises).then(finishTest); +Promise.all(promises).catch(function(e) { + testFailed(e.toString()); +}).then(finishTest); var successfullyParsed = true; diff --git a/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html b/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html index 341d9e4df7..12500c5be2 100644 --- a/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html +++ b/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html @@ -309,14 +309,9 @@ }); }); -allPromises.then(function() { - debug(""); - finishTest(); -}).catch(function(e) { +allPromises.catch(function(e) { testFailed(e.toString()); - debug(""); - finishTest(); -}); +}).then(finishTest); var successfullyParsed = true; From 8b7df39ad9d999cbbb29061745cd01012be800fb Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Fri, 7 Oct 2016 14:53:53 -0700 Subject: [PATCH 10/21] update spec language to match getBufferSubData --- specs/latest/2.0/index.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/specs/latest/2.0/index.html b/specs/latest/2.0/index.html index bece5fed9f..c1c64c1390 100644 --- a/specs/latest/2.0/index.html +++ b/specs/latest/2.0/index.html @@ -1246,11 +1246,13 @@

    Buffer objects

    dstBuffer.length - dstOffset; otherwise, let copyLength be length.

    - Copy copyLength typed elements (each of size dstBuffer.BYTES_PER_ELEMENT) + If copyLength is greater than zero, + copy copyLength typed elements (each of size dstBuffer.BYTES_PER_ELEMENT) from buf into dstBuffer, reading buf starting at byte index srcByteOffset and writing into dstBuffer starting at element index dstOffset. - If copyLength is 0, no data is written to dstBuffer. + If copyLength is 0, no data is written to dstBuffer, but + this does not cause a GL error to be generated.
    • If no WebGLBuffer is bound to target, an INVALID_OPERATION error is generated. From 81121c75361f914e148b3737bce087adae69b905 Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Mon, 10 Oct 2016 15:18:20 -0700 Subject: [PATCH 11/21] hopefully improve robustness of getBufferSubDataAsync-lose-context.html --- .../getBufferSubDataAsync-lose-context.html | 74 +++++++++---------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/sdk/tests/conformance2/buffers/getBufferSubDataAsync-lose-context.html b/sdk/tests/conformance2/buffers/getBufferSubDataAsync-lose-context.html index 3100e28f1a..2be074e9cb 100644 --- a/sdk/tests/conformance2/buffers/getBufferSubDataAsync-lose-context.html +++ b/sdk/tests/conformance2/buffers/getBufferSubDataAsync-lose-context.html @@ -48,12 +48,6 @@ var gl = wtu.create3DContext(canvas, undefined, 2); wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup."); -var pbo = gl.createBuffer(); -gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo); -gl.bufferData(gl.PIXEL_PACK_BUFFER, 4, gl.DYNAMIC_READ); -gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); -var readbackBuffer = new Uint8Array(4); - var extensionName = wtu.getSupportedExtensionWithKnownPrefixes(gl, "WEBGL_lose_context"); if (!extensionName) { debug("Could not find WEBGL_lose_context extension"); @@ -61,42 +55,48 @@ } var extension = gl.getExtension(extensionName); -gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo); -gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, 0); -var promise = gl.getBufferSubDataAsync(gl.PIXEL_PACK_BUFFER, 0, readbackBuffer); -gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); -var completed = false; -promise.then(function(buf) { - testFailed("should not resolve"); -}, function(e) { - testPassed("correctly rejected with " + e); - wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no new errors from late rejection"); -}).then(function() { - completed = true; +var iter = 0; +var ITERS = 20; +canvas.addEventListener("webglcontextrestored", test); +canvas.addEventListener("webglcontextlost", function(e) { + e.preventDefault(); + webglHarnessCollectGarbage(); + setTimeout(function() { + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "extension.restoreContext()"); + }, 100); }); -extension.loseContext(); -wtu.glErrorShouldBe(gl, gl.CONTEXT_LOST_WEBGL, "context was lost"); - -var t0 = performance.now(); -requestAnimationFrame(animate); -finish(); - -var animating = true; -function animate() { - webglHarnessCollectGarbage(); - animating = performance.now() - t0 < 2000; - if (animating) { - requestAnimationFrame(animate); - } -} +test(); -function finish() { - if (completed && !animating) { +function test() { + shouldBeFalse("gl.isContextLost()"); + if (iter >= ITERS) { finishTest(); - } else { - setTimeout(finish, 100); + return; } + iter++; + + var pbo = gl.createBuffer(); + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo); + gl.bufferData(gl.PIXEL_PACK_BUFFER, 4, gl.DYNAMIC_READ); + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); + var readbackBuffer = new Uint8Array(4); + + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo); + gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, 0); + var promise = gl.getBufferSubDataAsync(gl.PIXEL_PACK_BUFFER, 0, readbackBuffer); + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); + var completed = false; + promise.then(function(buf) { + testFailed("should not resolve"); + }, function(e) { + testPassed("correctly rejected with " + e); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no new errors from late rejection"); + }).then(function() { + completed = true; + }); + + wtu.shouldGenerateGLError(gl, gl.CONTEXT_LOST_WEBGL, "extension.loseContext()"); } var successfullyParsed = true; From a695837049e1e9732a28745b0f46fed3c2a90edf Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Wed, 12 Oct 2016 14:02:25 -0700 Subject: [PATCH 12/21] add test for getBufferSubDataAsync when transform feedback is active --- .../buffers/getBufferSubDataAsync.html | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html b/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html index 12500c5be2..22665266aa 100644 --- a/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html +++ b/sdk/tests/conformance2/buffers/getBufferSubDataAsync.html @@ -309,6 +309,23 @@ }); }); +allPromises = allPromises.then(function() { + debug(""); + debug("Test that getBufferSubDataAsync..."); + + var transformFeedbackBuffer = gl.createBuffer(); + gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, transformFeedbackBuffer); + gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, floatArray, gl.STATIC_DRAW); + var transformFeedback = gl.createTransformFeedback(); + debug("... passes when a transform feedback object is not bound"); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.getBufferSubDataAsync(gl.TRANSFORM_FEEDBACK_BUFFER, 0, dstData)"); + gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, transformFeedback); + debug("... fails when a transform feedback object is bound"); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.getBufferSubDataAsync(gl.TRANSFORM_FEEDBACK_BUFFER, 0, dstData)"); + gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null); + gl.deleteTransformFeedback(transformFeedback); +}); + allPromises.catch(function(e) { testFailed(e.toString()); }).then(finishTest); From b060aac9d08ad58e353286cf4e394075675ccc66 Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Wed, 12 Oct 2016 15:08:23 -0700 Subject: [PATCH 13/21] Revert "add test for getBufferSubDataAsync when transform feedback is active" This reverts commit a695837049e1e9732a28745b0f46fed3c2a90edf. --- .../buffers/get-buffer-sub-data-async.html | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/sdk/tests/conformance2/buffers/get-buffer-sub-data-async.html b/sdk/tests/conformance2/buffers/get-buffer-sub-data-async.html index 22665266aa..12500c5be2 100644 --- a/sdk/tests/conformance2/buffers/get-buffer-sub-data-async.html +++ b/sdk/tests/conformance2/buffers/get-buffer-sub-data-async.html @@ -309,23 +309,6 @@ }); }); -allPromises = allPromises.then(function() { - debug(""); - debug("Test that getBufferSubDataAsync..."); - - var transformFeedbackBuffer = gl.createBuffer(); - gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, transformFeedbackBuffer); - gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, floatArray, gl.STATIC_DRAW); - var transformFeedback = gl.createTransformFeedback(); - debug("... passes when a transform feedback object is not bound"); - wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.getBufferSubDataAsync(gl.TRANSFORM_FEEDBACK_BUFFER, 0, dstData)"); - gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, transformFeedback); - debug("... fails when a transform feedback object is bound"); - wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.getBufferSubDataAsync(gl.TRANSFORM_FEEDBACK_BUFFER, 0, dstData)"); - gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null); - gl.deleteTransformFeedback(transformFeedback); -}); - allPromises.catch(function(e) { testFailed(e.toString()); }).then(finishTest); From 4af531ec5edd879361ac23996e7d3d8ebec1c1a5 Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Wed, 12 Oct 2016 16:12:56 -0700 Subject: [PATCH 14/21] small fix --- .../conformance2/buffers/get-buffer-sub-data-async-stress.html | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/tests/conformance2/buffers/get-buffer-sub-data-async-stress.html b/sdk/tests/conformance2/buffers/get-buffer-sub-data-async-stress.html index 1e9355461a..7b1c66b6d2 100644 --- a/sdk/tests/conformance2/buffers/get-buffer-sub-data-async-stress.html +++ b/sdk/tests/conformance2/buffers/get-buffer-sub-data-async-stress.html @@ -56,7 +56,6 @@ var TEST_COUNT = 300; var promises = []; -promises.length = TEST_COUNT; debug("kicking off " + TEST_COUNT + " getBufferSubDataAsync calls"); for (var i = 0; i < TEST_COUNT; i++) { (function() { From a8a2d58f0eed5ce623f601f307d0f3d7ea89c974 Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Wed, 12 Oct 2016 16:15:07 -0700 Subject: [PATCH 15/21] test transform feedback error case --- .../conformance2/transform_feedback/transform_feedback.html | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/tests/conformance2/transform_feedback/transform_feedback.html b/sdk/tests/conformance2/transform_feedback/transform_feedback.html index 9c4a0aef17..254404c2f8 100644 --- a/sdk/tests/conformance2/transform_feedback/transform_feedback.html +++ b/sdk/tests/conformance2/transform_feedback/transform_feedback.html @@ -294,6 +294,7 @@ function verifyGetBufferSubData(expected) { wtu.shouldGenerateGLError(gl, expected, "gl.getBufferSubData(gl.TRANSFORM_FEEDBACK_BUFFER, 0, retArray, retArray.length)"); + wtu.shouldGenerateGLError(gl, expected, "gl.getBufferSubDataAsync(gl.TRANSFORM_FEEDBACK_BUFFER, 0, retArray, retArray.length)"); } function runGetBufferSubDataTest() { From 80df4a0cfe89f003eb9670e05c1dae074bea52b2 Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Fri, 21 Oct 2016 17:53:31 -0700 Subject: [PATCH 16/21] formalize getBufferSubDataAsync spec text + modify stress test --- .../get-buffer-sub-data-async-stress.html | 7 ++ specs/latest/2.0/index.html | 72 ++++++++++--------- 2 files changed, 45 insertions(+), 34 deletions(-) diff --git a/sdk/tests/conformance2/buffers/get-buffer-sub-data-async-stress.html b/sdk/tests/conformance2/buffers/get-buffer-sub-data-async-stress.html index 7b1c66b6d2..5ea9f01e99 100644 --- a/sdk/tests/conformance2/buffers/get-buffer-sub-data-async-stress.html +++ b/sdk/tests/conformance2/buffers/get-buffer-sub-data-async-stress.html @@ -55,6 +55,7 @@ var readbackBuffer = new Uint8Array(4); var TEST_COUNT = 300; +var lastCounter = -1; var promises = []; debug("kicking off " + TEST_COUNT + " getBufferSubDataAsync calls"); for (var i = 0; i < TEST_COUNT; i++) { @@ -77,6 +78,12 @@ testFailed("color readback #" + counter + " was incorrect: was " + buf + " but expected " + color); } + if (counter == lastCounter + 1) { + testPassed(" and its Promise resolved in order after " + lastCounter); + } else { + testFailed(" and its Promise resolved out of order order after " + lastCounter); + } + lastCounter = counter; }, function(e) { testFailed(e.toString()); }); diff --git a/specs/latest/2.0/index.html b/specs/latest/2.0/index.html index 6767ac638e..80eaafc1f3 100644 --- a/specs/latest/2.0/index.html +++ b/specs/latest/2.0/index.html @@ -700,11 +700,11 @@

      The WebGL context

      // MapBufferRange, in particular its read-only and write-only modes, // can not be exposed safely to JavaScript. GetBufferSubData // replaces it for the purpose of fetching data back from the GPU. - void getBufferSubData(GLenum target, GLintptr srcByteOffset, ArrayBufferView dstData, + void getBufferSubData(GLenum target, GLintptr srcByteOffset, ArrayBufferView dstBuffer, optional GLuint dstOffset = 0, optional GLuint length = 0); // Asynchronous version of getBufferSubData which fulfills the returned promise when the data becomes available. - Promise<ArrayBuffer> getBufferSubDataAsync(GLenum target, GLintptr srcByteOffset, ArrayBufferView dstData, - optional GLuint dstOffset = 0, optional GLuint length = 0); + Promise<ArrayBuffer> getBufferSubDataAsync(GLenum target, GLintptr srcByteOffset, ArrayBufferView dstBuffer, + optional GLuint dstOffset = 0, optional GLuint length = 0); // May throw DOMException /* Framebuffer objects */ void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, @@ -1224,14 +1224,15 @@

      Buffer objects

    - Promise<ArrayBuffer> getBufferSubDataAsync(GLenum target, GLintptr srcByteOffset, ArrayBufferView dstData, - optional GLuint dstOffset = 0, optional GLuint length = 0) + Promise<ArrayBuffer> getBufferSubDataAsync(GLenum target, GLintptr srcByteOffset, ArrayBufferView dstBuffer, + optional GLuint dstOffset = 0, optional GLuint length = 0) // May throw DOMException

    Reads back data asynchronously from the bound WebGLBuffer into dstBuffer.

    - Let buf be the buffer bound to target. + Let buf be the buffer bound to target at the time + getBufferSubDataAsync is called. If length is 0, let copyLength be dstBuffer.length - dstOffset; otherwise, let copyLength be length. @@ -1259,36 +1260,38 @@

    Buffer objects

    is larger than the length of buf, an INVALID_OPERATION is generated. - If any error is generated, no data is written to dstBuffer. - These errors are generated synchronously and are visible immediately after - getBufferSubDataAsync returns. - -

    - This command executes serially with respect to others in the command stream, but the data is - only written to dstData when the promise is fulfilled; it is available in - the Promise's success callback, but not earlier. - -

    - If a failure occurs that prevents the promise from being fulfilled, then the returned - Promise is rejected with an "InvalidStateError" DOMException, and no data is written to - dstData. If any of the above errors occur, the Promise is already rejected upon - return. If the context is lost before the Promise is fulfilled, or if dstData - is neutered at the time the Promise is fulfilled, then the Promise will be rejected later. - In this case, no WebGL error is generated. If the promise rejects, no data is written to - dstBuffer. - -

    - Otherwise, the Promise is fulfilled when the data becomes available, - and dstData, originally passed to getBufferSubDataAsync, is - passed to the success callback. - -

    - If the buffer is written and read sequentially by other operations and getBufferSubData, - it is the responsibility of the WebGL API to ensure that data are accessed consistently. This applies - even if the buffer is currently bound to a transform feedback binding point. -

    + When invoked, getBufferSubDataAsync must run these steps: +
      +
    • Let promise be a Promise to be returned. +
    • Check for the errors defined above. If there are any errors, generate the GL error + synchronously and + reject + promise with an InvalidStateError. +
    • Insert a readback of buf into the GL command stream, using the range + defined above. +
    • Return promise, but continue running these steps in parallel. +
    • Upon completion of the readback, queue a task performing the following steps: +
        +
      • If the context has been lost, or if dstBuffer has been neutered, + reject + promise with an InvalidStateError. In this case, no GL + error is generated. +
      • Write the readback result into dstBuffer, using the range defined + above. +
      • Resolve + promise with dstBuffer. +
      + The task source for this task is the WebGL task source. +
    + If the returned Promise is rejected, no data is written to dstBuffer.
    + Even if getBufferSubDataAsync is called multiple times in a row with the same + dstBuffer, then callbacks added synchronously will never see + results of subsequent getBufferSubDataAsync calls. +
    + +
    Compared to the synchronous version of getBufferSubData, this version may impose less overhead on applications. Intended use cases include reading pixels into a pixel buffer object and examining that data on the CPU. It does not force the graphics @@ -2300,6 +2303,7 @@

    Reading back pixels

    generates an INVALID_OPERATION error.

    If dstData doesn't have enough space for the read operation starting at + dstOffset, generate INVALID_OPERATION.

    From fa9821c266d4235390f56ae628465effbe70bb62 Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Tue, 13 Dec 2016 15:55:01 -0800 Subject: [PATCH 17/21] move to extension --- .../extension.xml | 118 +++++++++++++++++ .../conformance2/buffers/00_test_list.txt | 3 - sdk/tests/conformance2/context/methods-2.html | 1 - .../conformance2/extensions/00_test_list.txt | 3 + ...t-buffer-sub-data-async-lose-context.html} | 0 ...bgl-get-buffer-sub-data-async-stress.html} | 0 .../webgl-get-buffer-sub-data-async.html} | 0 .../transform_feedback.html | 5 +- ...ferSubDataAsync-dom-suspension-manual.html | 125 ++++++++++++++++++ .../getBufferSubDataAsync-out-of-memory.html | 73 ++++++++++ .../getBufferSubDataAsync-out-of-order-2.html | 79 +++++++++++ .../getBufferSubDataAsync-out-of-order.html | 83 ++++++++++++ specs/latest/2.0/index.html | 82 +----------- specs/latest/2.0/webgl2.idl | 4 - 14 files changed, 486 insertions(+), 90 deletions(-) create mode 100644 extensions/WEBGL_get_buffer_sub_data_async/extension.xml rename sdk/tests/conformance2/{buffers/get-buffer-sub-data-async-lose-context.html => extensions/webgl-get-buffer-sub-data-async-lose-context.html} (100%) rename sdk/tests/conformance2/{buffers/get-buffer-sub-data-async-stress.html => extensions/webgl-get-buffer-sub-data-async-stress.html} (100%) rename sdk/tests/conformance2/{buffers/get-buffer-sub-data-async.html => extensions/webgl-get-buffer-sub-data-async.html} (100%) create mode 100644 sdk/tests/extra/getBufferSubDataAsync-dom-suspension-manual.html create mode 100644 sdk/tests/extra/getBufferSubDataAsync-out-of-memory.html create mode 100644 sdk/tests/extra/getBufferSubDataAsync-out-of-order-2.html create mode 100644 sdk/tests/extra/getBufferSubDataAsync-out-of-order.html diff --git a/extensions/WEBGL_get_buffer_sub_data_async/extension.xml b/extensions/WEBGL_get_buffer_sub_data_async/extension.xml new file mode 100644 index 0000000000..ee37940fc8 --- /dev/null +++ b/extensions/WEBGL_get_buffer_sub_data_async/extension.xml @@ -0,0 +1,118 @@ + + + + WEBGL_get_buffer_sub_data_async + + WebGL working group (public_webgl 'at' khronos.org) + + + Members of the WebGL working group + + ?? + + + + +

    +

    + + + + + +
    +

    + Promise<ArrayBuffer> getBufferSubDataAsync(GLenum target, GLintptr srcByteOffset, ArrayBufferView dstBuffer, + optional GLuint dstOffset = 0, optional GLuint length = 0) // May throw DOMException +

    +
    +
    + Reads back data asynchronously from the bound WebGLBuffer into dstBuffer. +

    + Let buf be the buffer bound to target at the time + getBufferSubDataAsync is called. + If length is 0, let copyLength be + dstBuffer.length - dstOffset; otherwise, let + copyLength be length. +

    + If copyLength is greater than zero, + copy copyLength typed elements (each of size dstBuffer.BYTES_PER_ELEMENT) + from buf into dstBuffer, + reading buf starting at byte index srcByteOffset and + writing into dstBuffer starting at element index dstOffset. + If copyLength is 0, no data is written to dstBuffer, but + this does not cause a GL error to be generated. +
      +
    • If no WebGLBuffer is bound to target, + an INVALID_OPERATION error is generated. +
    • If target is TRANSFORM_FEEDBACK_BUFFER, + and any transform feedback object is currently active, + an INVALID_OPERATION error is generated. +
    • If dstOffset is greater than dstBuffer.length, + an INVALID_VALUE error is generated. +
    • If dstOffset + copyLength is greater than dstBuffer.length, + an INVALID_VALUE error is generated. +
    • If srcByteOffset is less than zero, + an INVALID_VALUE error is generated. +
    • If srcByteOffset + copyLength*dstBuffer.BYTES_PER_ELEMENT + is larger than the length of buf, + an INVALID_OPERATION is generated. +
    + When invoked, getBufferSubDataAsync must run these steps: +
      +
    • Let promise be a Promise to be returned. +
    • Check for the errors defined above. If there are any errors, generate the GL error + synchronously and + reject + promise with an InvalidStateError. +
    • Insert a readback of buf into the GL command stream, using the range + defined above. +
    • Return promise, but continue running these steps in parallel. +
    • Upon completion of the readback, queue a task performing the following steps: +
        +
      • If the context has been lost, or if dstBuffer has been neutered, + reject + promise with an InvalidStateError. In this case, no GL + error is generated. +
      • Write the readback result into dstBuffer, using the range defined + above. +
      • Resolve + promise with dstBuffer. +
      + The task source for this task is the WebGL task source. +
    + If the returned Promise is rejected, no data is written to dstBuffer. + +
    + Even if getBufferSubDataAsync is called multiple times in a row with the same + dstBuffer, then callbacks added synchronously will never see + results of subsequent getBufferSubDataAsync calls. +
    + +
    + Compared to the synchronous version of getBufferSubData, this version may + impose less overhead on applications. Intended use cases include reading pixels into a + pixel buffer object and examining that data on the CPU. It does not force the graphics + pipeline to be stalled as getBufferSubData does. +
    +
    + + + +
    +
    +
    + +[NoInterfaceObject] +interface WEBGL_get_buffer_sub_data_async { + // Asynchronous version of getBufferSubData which fulfills the returned promise when the data becomes available. + Promise<ArrayBuffer> getBufferSubDataAsync(GLenum target, GLintptr srcByteOffset, ArrayBufferView dstBuffer, + optional GLuint dstOffset = 0, optional GLuint length = 0); // May throw DOMException +}; + + + + Initial revision. + + +
    diff --git a/sdk/tests/conformance2/buffers/00_test_list.txt b/sdk/tests/conformance2/buffers/00_test_list.txt index f8f8607e75..96921fbea9 100644 --- a/sdk/tests/conformance2/buffers/00_test_list.txt +++ b/sdk/tests/conformance2/buffers/00_test_list.txt @@ -5,8 +5,5 @@ buffer-data-and-buffer-sub-data-sub-source.html buffer-type-restrictions.html buffer-overflow-test.html get-buffer-sub-data.html -get-buffer-sub-data-async.html -get-buffer-sub-data-async-lose-context.html -get-buffer-sub-data-async-stress.html one-large-uniform-buffer.html uniform-buffers.html diff --git a/sdk/tests/conformance2/context/methods-2.html b/sdk/tests/conformance2/context/methods-2.html index aaeceb66c6..4dbf368b13 100644 --- a/sdk/tests/conformance2/context/methods-2.html +++ b/sdk/tests/conformance2/context/methods-2.html @@ -181,7 +181,6 @@ // WebGL2 methods "getBufferSubData", - "getBufferSubDataAsync", "copyBufferSubData", "blitFramebuffer", "framebufferTextureLayer", diff --git a/sdk/tests/conformance2/extensions/00_test_list.txt b/sdk/tests/conformance2/extensions/00_test_list.txt index 7ce7c8b57f..ea43e75c6c 100644 --- a/sdk/tests/conformance2/extensions/00_test_list.txt +++ b/sdk/tests/conformance2/extensions/00_test_list.txt @@ -2,3 +2,6 @@ ext-color-buffer-float.html ext-disjoint-timer-query-webgl2.html promoted-extensions.html promoted-extensions-in-shaders.html +webgl-get-buffer-sub-data-async.html +webgl-get-buffer-sub-data-async-lose-context.html +webgl-get-buffer-sub-data-async-stress.html diff --git a/sdk/tests/conformance2/buffers/get-buffer-sub-data-async-lose-context.html b/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async-lose-context.html similarity index 100% rename from sdk/tests/conformance2/buffers/get-buffer-sub-data-async-lose-context.html rename to sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async-lose-context.html diff --git a/sdk/tests/conformance2/buffers/get-buffer-sub-data-async-stress.html b/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async-stress.html similarity index 100% rename from sdk/tests/conformance2/buffers/get-buffer-sub-data-async-stress.html rename to sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async-stress.html diff --git a/sdk/tests/conformance2/buffers/get-buffer-sub-data-async.html b/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async.html similarity index 100% rename from sdk/tests/conformance2/buffers/get-buffer-sub-data-async.html rename to sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async.html diff --git a/sdk/tests/conformance2/transform_feedback/transform_feedback.html b/sdk/tests/conformance2/transform_feedback/transform_feedback.html index f0b7a998f0..865af8d0b6 100644 --- a/sdk/tests/conformance2/transform_feedback/transform_feedback.html +++ b/sdk/tests/conformance2/transform_feedback/transform_feedback.html @@ -64,6 +64,7 @@ var wtu = WebGLTestUtils; var canvas = document.getElementById("canvas"); var gl = wtu.create3DContext(canvas, null, 2); +var WEBGL_get_buffer_sub_data_async = gl.getExtension("WEBGL_get_buffer_sub_data_async"); var tf = null; var tf1 = null; var program = null; @@ -294,7 +295,9 @@ function verifyGetBufferSubData(expected) { wtu.shouldGenerateGLError(gl, expected, "gl.getBufferSubData(gl.TRANSFORM_FEEDBACK_BUFFER, 0, retArray, 0, retArray.length)"); - wtu.shouldGenerateGLError(gl, expected, "gl.getBufferSubDataAsync(gl.TRANSFORM_FEEDBACK_BUFFER, 0, retArray, 0, retArray.length)"); + if (WEBGL_get_buffer_sub_data_async) { + wtu.shouldGenerateGLError(gl, expected, "WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.TRANSFORM_FEEDBACK_BUFFER, 0, retArray, 0, retArray.length)"); + } } function runGetBufferSubDataTest() { diff --git a/sdk/tests/extra/getBufferSubDataAsync-dom-suspension-manual.html b/sdk/tests/extra/getBufferSubDataAsync-dom-suspension-manual.html new file mode 100644 index 0000000000..f388e06c25 --- /dev/null +++ b/sdk/tests/extra/getBufferSubDataAsync-dom-suspension-manual.html @@ -0,0 +1,125 @@ + + + + + + +WebGL getBufferSubDataAsync stress test. + + + + + +
    + +
    + + + diff --git a/sdk/tests/extra/getBufferSubDataAsync-out-of-memory.html b/sdk/tests/extra/getBufferSubDataAsync-out-of-memory.html new file mode 100644 index 0000000000..2e1f1ec2f7 --- /dev/null +++ b/sdk/tests/extra/getBufferSubDataAsync-out-of-memory.html @@ -0,0 +1,73 @@ + + + + + + +WebGL getBufferSubDataAsync out of memory test. + + + + + +
    +
    + + + diff --git a/sdk/tests/extra/getBufferSubDataAsync-out-of-order-2.html b/sdk/tests/extra/getBufferSubDataAsync-out-of-order-2.html new file mode 100644 index 0000000000..228f9471dd --- /dev/null +++ b/sdk/tests/extra/getBufferSubDataAsync-out-of-order-2.html @@ -0,0 +1,79 @@ + + + + + + +WebGL getBufferSubDataAsync stress test. + + + + + +
    + +
    + + + diff --git a/sdk/tests/extra/getBufferSubDataAsync-out-of-order.html b/sdk/tests/extra/getBufferSubDataAsync-out-of-order.html new file mode 100644 index 0000000000..42ff942549 --- /dev/null +++ b/sdk/tests/extra/getBufferSubDataAsync-out-of-order.html @@ -0,0 +1,83 @@ + + + + + + +WebGL getBufferSubDataAsync stress test. + + + + + +
    + +
    + + + diff --git a/specs/latest/2.0/index.html b/specs/latest/2.0/index.html index 12732bd90a..7635f41668 100644 --- a/specs/latest/2.0/index.html +++ b/specs/latest/2.0/index.html @@ -712,11 +712,8 @@

    The WebGL context

    // MapBufferRange, in particular its read-only and write-only modes, // can not be exposed safely to JavaScript. GetBufferSubData // replaces it for the purpose of fetching data back from the GPU. - void getBufferSubData(GLenum target, GLintptr srcByteOffset, ArrayBufferView dstBuffer, + void getBufferSubData(GLenum target, GLintptr srcByteOffset, ArrayBufferView dstData, optional GLuint dstOffset = 0, optional GLuint length = 0); - // Asynchronous version of getBufferSubData which fulfills the returned promise when the data becomes available. - Promise<ArrayBuffer> getBufferSubDataAsync(GLenum target, GLintptr srcByteOffset, ArrayBufferView dstBuffer, - optional GLuint dstOffset = 0, optional GLuint length = 0); // May throw DOMException /* Framebuffer objects */ void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, @@ -1321,82 +1318,6 @@

    Buffer objects

    it is the responsibility of the WebGL API to ensure that data are accessed consistently. This applies even if the buffer is currently bound to a transform feedback binding point. -
    -

    - Promise<ArrayBuffer> getBufferSubDataAsync(GLenum target, GLintptr srcByteOffset, ArrayBufferView dstBuffer, - optional GLuint dstOffset = 0, optional GLuint length = 0) // May throw DOMException -

    -
    -
    - Reads back data asynchronously from the bound WebGLBuffer into dstBuffer. -

    - Let buf be the buffer bound to target at the time - getBufferSubDataAsync is called. - If length is 0, let copyLength be - dstBuffer.length - dstOffset; otherwise, let - copyLength be length. -

    - If copyLength is greater than zero, - copy copyLength typed elements (each of size dstBuffer.BYTES_PER_ELEMENT) - from buf into dstBuffer, - reading buf starting at byte index srcByteOffset and - writing into dstBuffer starting at element index dstOffset. - If copyLength is 0, no data is written to dstBuffer, but - this does not cause a GL error to be generated. -
      -
    • If no WebGLBuffer is bound to target, - an INVALID_OPERATION error is generated. -
    • If target is TRANSFORM_FEEDBACK_BUFFER, - and any transform feedback object is currently active, - an INVALID_OPERATION error is generated. -
    • If dstOffset is greater than dstBuffer.length, - an INVALID_VALUE error is generated. -
    • If dstOffset + copyLength is greater than dstBuffer.length, - an INVALID_VALUE error is generated. -
    • If srcByteOffset is less than zero, - an INVALID_VALUE error is generated. -
    • If srcByteOffset + copyLength*dstBuffer.BYTES_PER_ELEMENT - is larger than the length of buf, - an INVALID_OPERATION is generated. -
    - When invoked, getBufferSubDataAsync must run these steps: -
      -
    • Let promise be a Promise to be returned. -
    • Check for the errors defined above. If there are any errors, generate the GL error - synchronously and - reject - promise with an InvalidStateError. -
    • Insert a readback of buf into the GL command stream, using the range - defined above. -
    • Return promise, but continue running these steps in parallel. -
    • Upon completion of the readback, queue a task performing the following steps: -
        -
      • If the context has been lost, or if dstBuffer has been neutered, - reject - promise with an InvalidStateError. In this case, no GL - error is generated. -
      • Write the readback result into dstBuffer, using the range defined - above. -
      • Resolve - promise with dstBuffer. -
      - The task source for this task is the WebGL task source. -
    - If the returned Promise is rejected, no data is written to dstBuffer. - -
    - Even if getBufferSubDataAsync is called multiple times in a row with the same - dstBuffer, then callbacks added synchronously will never see - results of subsequent getBufferSubDataAsync calls. -
    - -
    - Compared to the synchronous version of getBufferSubData, this version may - impose less overhead on applications. Intended use cases include reading pixels into a - pixel buffer object and examining that data on the CPU. It does not force the graphics - pipeline to be stalled as getBufferSubData does. -
    -
    @@ -2433,7 +2354,6 @@

    Reading back pixels

    generates an INVALID_OPERATION error.

    If dstData doesn't have enough space for the read operation starting at - dstOffset, generate INVALID_OPERATION. diff --git a/specs/latest/2.0/webgl2.idl b/specs/latest/2.0/webgl2.idl index 421b232d3b..e5dbdc4867 100644 --- a/specs/latest/2.0/webgl2.idl +++ b/specs/latest/2.0/webgl2.idl @@ -320,10 +320,6 @@ interface WebGL2RenderingContextBase // replaces it for the purpose of fetching data back from the GPU. void getBufferSubData(GLenum target, GLintptr srcByteOffset, ArrayBufferView dstData, optional GLuint dstOffset = 0, optional GLuint length = 0); - // Asynchronous version of getBufferSubData which fulfills the returned promise when the data becomes available. - Promise getBufferSubDataAsync(GLenum target, GLintptr srcByteOffset, - ArrayBufferView dstData, optional GLuint dstOffset = 0, - optional GLuint length = 0); /* Framebuffer objects */ void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, From 100232bf4e59e208957c0a2bc76cd9957ac4d37c Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Tue, 13 Dec 2016 16:49:31 -0800 Subject: [PATCH 18/21] update tests --- ...et-buffer-sub-data-async-lose-context.html | 96 ++-- ...ebgl-get-buffer-sub-data-async-stress.html | 100 ++-- .../webgl-get-buffer-sub-data-async.html | 505 +++++++++--------- 3 files changed, 367 insertions(+), 334 deletions(-) diff --git a/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async-lose-context.html b/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async-lose-context.html index 2be074e9cb..80e0896495 100644 --- a/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async-lose-context.html +++ b/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async-lose-context.html @@ -29,7 +29,7 @@ -WebGL getBufferSubDataAsync context loss regression test. +WEBGL_get_buffer_sub_data_async context loss regression test. @@ -46,57 +46,69 @@ var canvas = document.getElementById("canvas"); var gl = wtu.create3DContext(canvas, undefined, 2); +var WEBGL_get_buffer_sub_data_async = gl.getExtension("WEBGL_get_buffer_sub_data_async"); wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup."); - -var extensionName = wtu.getSupportedExtensionWithKnownPrefixes(gl, "WEBGL_lose_context"); -if (!extensionName) { - debug("Could not find WEBGL_lose_context extension"); +if (!WEBGL_get_buffer_sub_data_async) { + testPassed("No WEBGL_get_buffer_sub_data_async support -- this is legal"); finishTest(); +} else { + testPassed("Successfully enabled WEBGL_get_buffer_sub_data_async extension"); + runTests(); } -var extension = gl.getExtension(extensionName); - -var iter = 0; -var ITERS = 20; -canvas.addEventListener("webglcontextrestored", test); -canvas.addEventListener("webglcontextlost", function(e) { - e.preventDefault(); - webglHarnessCollectGarbage(); - setTimeout(function() { - wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "extension.restoreContext()"); - }, 100); -}); -test(); +var extension; -function test() { - shouldBeFalse("gl.isContextLost()"); - if (iter >= ITERS) { - finishTest(); +function runTests() { + var extensionName = wtu.getSupportedExtensionWithKnownPrefixes(gl, "WEBGL_lose_context"); + if (!extensionName) { + debug("Could not find WEBGL_lose_context extension"); return; } - iter++; + extension = gl.getExtension(extensionName); - var pbo = gl.createBuffer(); - gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo); - gl.bufferData(gl.PIXEL_PACK_BUFFER, 4, gl.DYNAMIC_READ); - gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); - var readbackBuffer = new Uint8Array(4); - - gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo); - gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, 0); - var promise = gl.getBufferSubDataAsync(gl.PIXEL_PACK_BUFFER, 0, readbackBuffer); - gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); - var completed = false; - promise.then(function(buf) { - testFailed("should not resolve"); - }, function(e) { - testPassed("correctly rejected with " + e); - wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no new errors from late rejection"); - }).then(function() { - completed = true; + var iter = 0; + var ITERS = 20; + canvas.addEventListener("webglcontextrestored", test); + canvas.addEventListener("webglcontextlost", function(e) { + e.preventDefault(); + webglHarnessCollectGarbage(); + setTimeout(function() { + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "extension.restoreContext()"); + }, 100); }); - wtu.shouldGenerateGLError(gl, gl.CONTEXT_LOST_WEBGL, "extension.loseContext()"); + test(); + + function test() { + shouldBeFalse("gl.isContextLost()"); + if (iter >= ITERS) { + finishTest(); + return; + } + iter++; + + var pbo = gl.createBuffer(); + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo); + gl.bufferData(gl.PIXEL_PACK_BUFFER, 4, gl.DYNAMIC_READ); + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); + var readbackBuffer = new Uint8Array(4); + + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo); + gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, 0); + var promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.PIXEL_PACK_BUFFER, 0, readbackBuffer); + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); + var completed = false; + promise.then(function(buf) { + testFailed("should not resolve"); + }, function(e) { + testPassed("correctly rejected with " + e); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no new errors from late rejection"); + }).then(function() { + completed = true; + }); + + wtu.shouldGenerateGLError(gl, gl.CONTEXT_LOST_WEBGL, "extension.loseContext()"); + } } var successfullyParsed = true; diff --git a/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async-stress.html b/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async-stress.html index 5ea9f01e99..d583e7defc 100644 --- a/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async-stress.html +++ b/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async-stress.html @@ -29,7 +29,7 @@ -WebGL getBufferSubDataAsync stress test. +WEBGL_get_buffer_sub_data_async stress test. @@ -46,54 +46,64 @@ var canvas = document.getElementById("canvas"); var gl = wtu.create3DContext(canvas, undefined, 2); +var WEBGL_get_buffer_sub_data_async = gl.getExtension("WEBGL_get_buffer_sub_data_async"); wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup."); +if (!WEBGL_get_buffer_sub_data_async) { + testPassed("No WEBGL_get_buffer_sub_data_async support -- this is legal"); + finishTest(); +} else { + testPassed("Successfully enabled WEBGL_get_buffer_sub_data_async extension"); + runTests(); +} -var pbo = gl.createBuffer(); -gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo); -gl.bufferData(gl.PIXEL_PACK_BUFFER, 4, gl.DYNAMIC_READ); -gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); -var readbackBuffer = new Uint8Array(4); +function runTests() { + var pbo = gl.createBuffer(); + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo); + gl.bufferData(gl.PIXEL_PACK_BUFFER, 4, gl.DYNAMIC_READ); + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); + var readbackBuffer = new Uint8Array(4); -var TEST_COUNT = 300; -var lastCounter = -1; -var promises = []; -debug("kicking off " + TEST_COUNT + " getBufferSubDataAsync calls"); -for (var i = 0; i < TEST_COUNT; i++) { - (function() { - var counter = i; - var color = [counter & 0xff, (counter >> 8) & 0xff, (counter >> 16) & 0xff, 255]; - gl.clearColor(color[0] / 255, color[1] / 255, color[2] / 255, color[3] / 255); - gl.clear(gl.COLOR_BUFFER_BIT); - gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo); - gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, 0); - var promise = gl.getBufferSubDataAsync(gl.PIXEL_PACK_BUFFER, 0, readbackBuffer); - gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); - promise = promise.then(function(buf) { - if (buf[0] == color[0] && - buf[1] == color[1] && - buf[2] == color[2] && - buf[3] == color[3]) { - testPassed("color readback #" + counter + " was correct"); - } else { - testFailed("color readback #" + counter + " was incorrect: was " + - buf + " but expected " + color); - } - if (counter == lastCounter + 1) { - testPassed(" and its Promise resolved in order after " + lastCounter); - } else { - testFailed(" and its Promise resolved out of order order after " + lastCounter); - } - lastCounter = counter; - }, function(e) { - testFailed(e.toString()); - }); - promises.push(promise); - })(); -} + var TEST_COUNT = 300; + var lastCounter = -1; + var promises = []; + debug("kicking off " + TEST_COUNT + " getBufferSubDataAsync calls"); + for (var i = 0; i < TEST_COUNT; i++) { + (function() { + var counter = i; + var color = [counter & 0xff, (counter >> 8) & 0xff, (counter >> 16) & 0xff, 255]; + gl.clearColor(color[0] / 255, color[1] / 255, color[2] / 255, color[3] / 255); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo); + gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, 0); + var promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.PIXEL_PACK_BUFFER, 0, readbackBuffer); + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); + promise = promise.then(function(buf) { + if (buf[0] == color[0] && + buf[1] == color[1] && + buf[2] == color[2] && + buf[3] == color[3]) { + testPassed("color readback #" + counter + " was correct"); + } else { + testFailed("color readback #" + counter + " was incorrect: was " + + buf + " but expected " + color); + } + if (counter == lastCounter + 1) { + testPassed(" and its Promise resolved in order after " + lastCounter); + } else { + testFailed(" and its Promise resolved out of order order after " + lastCounter); + } + lastCounter = counter; + }, function(e) { + testFailed(e.toString()); + }); + promises.push(promise); + })(); + } -Promise.all(promises).catch(function(e) { - testFailed(e.toString()); -}).then(finishTest); + Promise.all(promises).catch(function(e) { + testFailed(e.toString()); + }).then(finishTest); +} var successfullyParsed = true; diff --git a/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async.html b/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async.html index 12500c5be2..4db2122436 100644 --- a/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async.html +++ b/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async.html @@ -29,7 +29,7 @@ -WebGL getBufferSubDataAsync test. +WEBGL_get_buffer_sub_data_async test. @@ -44,274 +44,285 @@ var wtu = WebGLTestUtils; var gl = wtu.create3DContext(undefined, undefined, 2); +var WEBGL_get_buffer_sub_data_async = gl.getExtension("WEBGL_get_buffer_sub_data_async"); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup."); +if (!WEBGL_get_buffer_sub_data_async) { + testPassed("No WEBGL_get_buffer_sub_data_async support -- this is legal"); + finishTest(); +} else { + testPassed("Successfully enabled WEBGL_get_buffer_sub_data_async extension"); + runTests(); +} + +var floatArray, dstData, extraLargeBuffer, resolvedArray, promise; -var vertices = [ - 1.1, 1.0, 1.3, +function runTests() { + 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); -var dstData = new Float32Array(vertices.length); -var extraLargeBuffer = new Float32Array(vertices.length + 1); -var resolvedArray; -var promise; - -function clearDstData() { + 5.3, -1.0, 1.0 + ]; + floatArray = new Float32Array(vertices); + dstData = new Float32Array(vertices.length); + extraLargeBuffer = new Float32Array(vertices.length + 1); + + function clearDstData() { dstData = new Float32Array(vertices.length); -} + } -wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup."); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup."); + + function expectRejected(promise, message) { + return promise.then(function(v) { + testFailed("should be rejected but was " + v + ": " + message); + }, function(e) { + testPassed("correctly rejected with " + e + ": " + message); + }); + } + + var types = [ + 'ARRAY_BUFFER', + 'ELEMENT_ARRAY_BUFFER', + 'COPY_READ_BUFFER', + 'COPY_WRITE_BUFFER', + 'PIXEL_PACK_BUFFER', + 'PIXEL_UNPACK_BUFFER', + 'TRANSFORM_FEEDBACK_BUFFER', + 'UNIFORM_BUFFER' + ]; + var buffers = []; + + var buffer; + for (var i = 0; i < types.length; i++) { + buffer = gl.createBuffer(); + gl.bindBuffer(gl[types[i]], buffer); + gl.bufferData(gl[types[i]], floatArray, gl.STATIC_DRAW); + buffers[i] = buffer; + } + + 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."); + + // This long promise chain is to serialize the tests - so that the errors + // associated with a given test always appear underneath that test. + // For each test in this chain, if it performs test checks in a promise, + // it MUST return that promise so that it completes before the next test. + var allPromises = Promise.resolve(); -function expectRejected(promise, message) { - return promise.then(function(v) { - testFailed("should be rejected but was " + v + ": " + message); - }, function(e) { - testPassed("correctly rejected with " + e + ": " + message); + allPromises = allPromises.then(function() { + debug("Testing promise rejection"); }); -} -var types = [ - 'ARRAY_BUFFER', - 'ELEMENT_ARRAY_BUFFER', - 'COPY_READ_BUFFER', - 'COPY_WRITE_BUFFER', - 'PIXEL_PACK_BUFFER', - 'PIXEL_UNPACK_BUFFER', - 'TRANSFORM_FEEDBACK_BUFFER', - 'UNIFORM_BUFFER' -]; -var buffers = []; - -var buffer; -for (var i = 0; i < types.length; i++) { - buffer = gl.createBuffer(); - gl.bindBuffer(gl[types[i]], buffer); - gl.bufferData(gl[types[i]], floatArray, gl.STATIC_DRAW); - buffers[i] = buffer; -} + allPromises = allPromises.then(function() { + debug("Argument must be ArrayBufferView, not ArrayBuffer") + gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); + var code = "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, new ArrayBuffer(4))"; + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, code); + shouldBeType("promise", "Promise"); + gl.bindBuffer(gl.ARRAY_BUFFER, null); + return expectRejected(promise, code); + }); -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."); - -// This long promise chain is to serialize the tests - so that the errors -// associated with a given test always appear underneath that test. -// For each test in this chain, if it performs test checks in a promise, -// it MUST return that promise so that it completes before the next test. -var allPromises = Promise.resolve(); - -allPromises = allPromises.then(function() { - debug("Testing promise rejection"); -}); - -allPromises = allPromises.then(function() { - debug("Argument must be ArrayBufferView, not ArrayBuffer") - gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); - var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, new ArrayBuffer(4))"; - wtu.shouldGenerateGLError(gl, gl.NO_ERROR, code); - shouldBeType("promise", "Promise"); - gl.bindBuffer(gl.ARRAY_BUFFER, null); - return expectRejected(promise, code); -}); - -allPromises = allPromises.then(function() { - debug("Argument must be ArrayBufferView, not null") - gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); - var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, null)"; - wtu.shouldGenerateGLError(gl, gl.NO_ERROR, code); - shouldBeType("promise", "Promise"); - gl.bindBuffer(gl.ARRAY_BUFFER, null); - return expectRejected(promise, code); -}); - -allPromises = allPromises.then(function() { - debug(""); -}); - -for (var i = 0; i < types.length; i++) { - (function() { - var index = i; - allPromises = allPromises.then(function() { - debug("Test that getBufferSubDataAsync successfully works reading buffer data from gl." + type); - var type = types[index]; - var buf = buffers[index]; - gl.bindBuffer(gl[type], buf); - wtu.shouldGenerateGLError(gl, gl.NO_ERROR, - "promise = gl.getBufferSubDataAsync(gl." + type + ", 0, dstData)"); - gl.bindBuffer(gl[type], null); - return promise.then(function(arr) { - resolvedArray = arr; - shouldBe("resolvedArray", "dstData"); - shouldBeTrue("areArraysEqual(resolvedArray, floatArray)"); + allPromises = allPromises.then(function() { + debug("Argument must be ArrayBufferView, not null") + gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); + var code = "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, null)"; + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, code); + shouldBeType("promise", "Promise"); + gl.bindBuffer(gl.ARRAY_BUFFER, null); + return expectRejected(promise, code); + }); + + allPromises = allPromises.then(function() { + debug(""); + }); + + for (var i = 0; i < types.length; i++) { + (function() { + var index = i; + allPromises = allPromises.then(function() { + debug("Test that getBufferSubDataAsync successfully works reading buffer data from gl." + type); + var type = types[index]; + var buf = buffers[index]; + gl.bindBuffer(gl[type], buf); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl." + type + ", 0, dstData)"); + gl.bindBuffer(gl[type], null); + return promise.then(function(arr) { + resolvedArray = arr; + shouldBe("resolvedArray", "dstData"); + shouldBeTrue("areArraysEqual(resolvedArray, floatArray)"); + }); }); + })(); + } + + allPromises = allPromises.then(function() { + debug(""); + debug("Test that getBufferSubDataAsync successfully works with dstOffset"); + + gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); + + clearDstData(); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, 2)"); + return promise.then(function(arr) { + resolvedArray = arr; + shouldBeTrue("areArraysEqual(resolvedArray.slice(0, 2), [0, 0])"); + shouldBeTrue("areArraysEqual(resolvedArray.slice(2), floatArray.slice(0, floatArray.length - 2))"); }); - })(); -} + }); + + allPromises = allPromises.then(function() { + clearDstData(); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length)"); + return promise.then(function(arr) { + resolvedArray = arr; + shouldBeTrue("areArraysEqual(resolvedArray, [0, 0, 0, 0, 0, 0, 0, 0, 0])"); + }); + }); -allPromises = allPromises.then(function() { - debug(""); - debug("Test that getBufferSubDataAsync successfully works with dstOffset"); + allPromises = allPromises.then(function() { + debug("Test that getBufferSubDataAsync fails when given a dstOffset beyond the end of dstData"); - gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); + var code = "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length + 1)"; + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); + return expectRejected(promise, code); + }); - clearDstData(); - wtu.shouldGenerateGLError(gl, gl.NO_ERROR, - "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, 2)"); - return promise.then(function(arr) { - resolvedArray = arr; - shouldBeTrue("areArraysEqual(resolvedArray.slice(0, 2), [0, 0])"); - shouldBeTrue("areArraysEqual(resolvedArray.slice(2), floatArray.slice(0, floatArray.length - 2))"); + allPromises = allPromises.then(function() { + debug(""); + debug("Test that getBufferSubDataAsync successfully works with dstOffset and length"); + + clearDstData(); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, 2, 2)"); + return promise.then(function(arr) { + resolvedArray = arr; + shouldBeTrue("areArraysEqual(resolvedArray.slice(0, 2), [0, 0])"); + shouldBeTrue("areArraysEqual(resolvedArray.slice(2, 4), floatArray.slice(0, 2))"); + shouldBeTrue("areArraysEqual(resolvedArray.slice(4), [0, 0, 0, 0, 0])"); + }); }); -}); - -allPromises = allPromises.then(function() { - clearDstData(); - wtu.shouldGenerateGLError(gl, gl.NO_ERROR, - "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length)"); - return promise.then(function(arr) { - resolvedArray = arr; - shouldBeTrue("areArraysEqual(resolvedArray, [0, 0, 0, 0, 0, 0, 0, 0, 0])"); + + allPromises = allPromises.then(function() { + clearDstData(); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length, 0)"); + return promise.then(function(arr) { + resolvedArray = arr; + shouldBeTrue("areArraysEqual(resolvedArray, [0, 0, 0, 0, 0, 0, 0, 0, 0])"); + }); }); -}); - -allPromises = allPromises.then(function() { - debug("Test that getBufferSubDataAsync fails when given a dstOffset beyond the end of dstData"); - - var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length + 1)"; - wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); - return expectRejected(promise, code); -}); - -allPromises = allPromises.then(function() { - debug(""); - debug("Test that getBufferSubDataAsync successfully works with dstOffset and length"); - - clearDstData(); - wtu.shouldGenerateGLError(gl, gl.NO_ERROR, - "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, 2, 2)"); - return promise.then(function(arr) { - resolvedArray = arr; - shouldBeTrue("areArraysEqual(resolvedArray.slice(0, 2), [0, 0])"); - shouldBeTrue("areArraysEqual(resolvedArray.slice(2, 4), floatArray.slice(0, 2))"); - shouldBeTrue("areArraysEqual(resolvedArray.slice(4), [0, 0, 0, 0, 0])"); + + allPromises = allPromises.then(function() { + debug(""); + debug("Test that getBufferSubDataAsync successfully works with uninitialized buffers"); + dstData = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9]); + gl.bindBuffer(gl.ARRAY_BUFFER, uninitializedBuffer); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData)"); + return promise.then(function(arr) { + resolvedArray = arr; + shouldBeTrue("areArraysEqual(resolvedArray, [0, 0, 0, 0, 0, 0, 0, 0, 0])"); + }); }); -}); - -allPromises = allPromises.then(function() { - clearDstData(); - wtu.shouldGenerateGLError(gl, gl.NO_ERROR, - "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length, 0)"); - return promise.then(function(arr) { - resolvedArray = arr; - shouldBeTrue("areArraysEqual(resolvedArray, [0, 0, 0, 0, 0, 0, 0, 0, 0])"); + + allPromises = allPromises.then(function() { + debug("Test that getBufferSubDataAsync fails when given a dstOffset+length beyond the end of dstData"); + + gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); + var code = "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length - 1, 2)"; + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); + return expectRejected(promise, code); }); -}); -allPromises = allPromises.then(function() { - debug(""); - debug("Test that getBufferSubDataAsync successfully works with uninitialized buffers"); - dstData = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9]); - gl.bindBuffer(gl.ARRAY_BUFFER, uninitializedBuffer); - wtu.shouldGenerateGLError(gl, gl.NO_ERROR, - "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData)"); - return promise.then(function(arr) { - resolvedArray = arr; - shouldBeTrue("areArraysEqual(resolvedArray, [0, 0, 0, 0, 0, 0, 0, 0, 0])"); + allPromises = allPromises.then(function() { + debug(""); + debug("Test that getBufferSubDataAsync fails when given a buffer with its size larger than the original data"); + gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); + var code = "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, extraLargeBuffer)"; + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); + return expectRejected(promise, code); + }); + + allPromises = allPromises.then(function() { + debug("Test that getBufferSubDataAsync fails when offset summed with buffer length is larger than the size of the original data size"); + gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); + var code = "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, dstData.byteLength + 1, dstData)"; + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); + return expectRejected(promise, code); + }); + + allPromises = allPromises.then(function() { + gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); + var code = "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 1, dstData)"; + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); + return expectRejected(promise, code); + }); + + allPromises = allPromises.then(function() { + debug("Test that getBufferSubDataAsync fails when no buffer is bound to the target"); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); + var code = "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ELEMENT_ARRAY_BUFFER, 0, dstData)"; + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, code); + return expectRejected(promise, code); + }); + + allPromises = allPromises.then(function() { + debug("Test that getBufferSubDataAsync fails when offset is less than 0"); + gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); + var code = "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, -1, dstData)"; + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); + return expectRejected(promise, code); }); -}); - -allPromises = allPromises.then(function() { - debug("Test that getBufferSubDataAsync fails when given a dstOffset+length beyond the end of dstData"); - - gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); - var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData, dstData.length - 1, 2)"; - wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); - return expectRejected(promise, code); -}); - -allPromises = allPromises.then(function() { - debug(""); - debug("Test that getBufferSubDataAsync fails when given a buffer with its size larger than the original data"); - gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); - var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, extraLargeBuffer)"; - wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); - return expectRejected(promise, code); -}); - -allPromises = allPromises.then(function() { - debug("Test that getBufferSubDataAsync fails when offset summed with buffer length is larger than the size of the original data size"); - gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); - var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, dstData.byteLength + 1, dstData)"; - wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); - return expectRejected(promise, code); -}); - -allPromises = allPromises.then(function() { - gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); - var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 1, dstData)"; - wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); - return expectRejected(promise, code); -}); - -allPromises = allPromises.then(function() { - debug("Test that getBufferSubDataAsync fails when no buffer is bound to the target"); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); - var code = "promise = gl.getBufferSubDataAsync(gl.ELEMENT_ARRAY_BUFFER, 0, dstData)"; - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); - wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, code); - return expectRejected(promise, code); -}); - -allPromises = allPromises.then(function() { - debug("Test that getBufferSubDataAsync fails when offset is less than 0"); - gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); - var code = "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, -1, dstData)"; - wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, code); - return expectRejected(promise, code); -}); - -allPromises = allPromises.then(function() { - debug(""); - debug("Test that getBufferSubDataAsync works when a buffer is immediately resized to be too small"); - - buffer = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, buffer); - gl.bufferData(gl.ARRAY_BUFFER, floatArray, gl.STATIC_DRAW); - buffers[i] = buffer; - - dstData = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9]); - gl.bindBuffer(gl.ARRAY_BUFFER, buffer); - wtu.shouldGenerateGLError(gl, gl.NO_ERROR, - "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData)"); - gl.bufferData(gl.ARRAY_BUFFER, 4, gl.STATIC_DRAW); - return promise.then(function(arr) { - resolvedArray = arr; - shouldBeTrue("areArraysEqual(resolvedArray, floatArray)"); + + allPromises = allPromises.then(function() { + debug(""); + debug("Test that getBufferSubDataAsync works when a buffer is immediately resized to be too small"); + + buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.bufferData(gl.ARRAY_BUFFER, floatArray, gl.STATIC_DRAW); + buffers[i] = buffer; + + dstData = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9]); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData)"); + gl.bufferData(gl.ARRAY_BUFFER, 4, gl.STATIC_DRAW); + return promise.then(function(arr) { + resolvedArray = arr; + shouldBeTrue("areArraysEqual(resolvedArray, floatArray)"); + }); }); -}); - -allPromises = allPromises.then(function() { - debug(""); - debug("Test that getBufferSubDataAsync works when a buffer is immediately deleted"); - - dstData = 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, - "promise = gl.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData)"); - gl.deleteBuffer(buffer); - return promise.then(function(arr) { - resolvedArray = arr; - shouldBeTrue("areArraysEqual(resolvedArray, floatArray)"); + + allPromises = allPromises.then(function() { + debug(""); + debug("Test that getBufferSubDataAsync works when a buffer is immediately deleted"); + + dstData = 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, + "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.ARRAY_BUFFER, 0, dstData)"); + gl.deleteBuffer(buffer); + return promise.then(function(arr) { + resolvedArray = arr; + shouldBeTrue("areArraysEqual(resolvedArray, floatArray)"); + }); }); -}); -allPromises.catch(function(e) { - testFailed(e.toString()); -}).then(finishTest); + allPromises.catch(function(e) { + testFailed(e.toString()); + }).then(finishTest); +} var successfullyParsed = true; From 3c9a7c6dd59bc08fa8e1171b756eacec37b0a7d3 Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Tue, 13 Dec 2016 17:12:52 -0800 Subject: [PATCH 19/21] write extension draft --- .../extension.xml | 188 ++++++++++-------- 1 file changed, 103 insertions(+), 85 deletions(-) diff --git a/extensions/WEBGL_get_buffer_sub_data_async/extension.xml b/extensions/WEBGL_get_buffer_sub_data_async/extension.xml index ee37940fc8..c85f02376c 100644 --- a/extensions/WEBGL_get_buffer_sub_data_async/extension.xml +++ b/extensions/WEBGL_get_buffer_sub_data_async/extension.xml @@ -6,110 +6,128 @@ WebGL working group (public_webgl 'at' khronos.org) + Kai Ninomiya, Google Inc. Members of the WebGL working group - ?? + 34 - +

    + This extension allows asynchronous buffer readback in WebGL 2.0.

    - - - -
    -

    - Promise<ArrayBuffer> getBufferSubDataAsync(GLenum target, GLintptr srcByteOffset, ArrayBufferView dstBuffer, - optional GLuint dstOffset = 0, optional GLuint length = 0) // May throw DOMException -

    -
    -
    - Reads back data asynchronously from the bound WebGLBuffer into dstBuffer. -

    - Let buf be the buffer bound to target at the time - getBufferSubDataAsync is called. - If length is 0, let copyLength be - dstBuffer.length - dstOffset; otherwise, let - copyLength be length. -

    - If copyLength is greater than zero, - copy copyLength typed elements (each of size dstBuffer.BYTES_PER_ELEMENT) - from buf into dstBuffer, - reading buf starting at byte index srcByteOffset and - writing into dstBuffer starting at element index dstOffset. - If copyLength is 0, no data is written to dstBuffer, but - this does not cause a GL error to be generated. -
      -
    • If no WebGLBuffer is bound to target, - an INVALID_OPERATION error is generated. -
    • If target is TRANSFORM_FEEDBACK_BUFFER, - and any transform feedback object is currently active, - an INVALID_OPERATION error is generated. -
    • If dstOffset is greater than dstBuffer.length, - an INVALID_VALUE error is generated. -
    • If dstOffset + copyLength is greater than dstBuffer.length, - an INVALID_VALUE error is generated. -
    • If srcByteOffset is less than zero, - an INVALID_VALUE error is generated. -
    • If srcByteOffset + copyLength*dstBuffer.BYTES_PER_ELEMENT - is larger than the length of buf, - an INVALID_OPERATION is generated. -
    - When invoked, getBufferSubDataAsync must run these steps: -
      -
    • Let promise be a Promise to be returned. -
    • Check for the errors defined above. If there are any errors, generate the GL error - synchronously and - reject - promise with an InvalidStateError. -
    • Insert a readback of buf into the GL command stream, using the range - defined above. -
    • Return promise, but continue running these steps in parallel. -
    • Upon completion of the readback, queue a task performing the following steps: -
        -
      • If the context has been lost, or if dstBuffer has been neutered, - reject - promise with an InvalidStateError. In this case, no GL - error is generated. -
      • Write the readback result into dstBuffer, using the range defined - above. -
      • Resolve - promise with dstBuffer. -
      - The task source for this task is the WebGL task source. -
    - If the returned Promise is rejected, no data is written to dstBuffer. - -
    - Even if getBufferSubDataAsync is called multiple times in a row with the same - dstBuffer, then callbacks added synchronously will never see - results of subsequent getBufferSubDataAsync calls. -
    - -
    - Compared to the synchronous version of getBufferSubData, this version may - impose less overhead on applications. Intended use cases include reading pixels into a - pixel buffer object and examining that data on the CPU. It does not force the graphics - pipeline to be stalled as getBufferSubData does. -
    -
    - - - + This extension exposes an asynchronous buffer readback entry point for + non-blocking readbacks from WebGL buffers. It is equivalent to + getBufferSubData but returns a Promise + instead of an immediate readback result.
    + [NoInterfaceObject] interface WEBGL_get_buffer_sub_data_async { // Asynchronous version of getBufferSubData which fulfills the returned promise when the data becomes available. Promise<ArrayBuffer> getBufferSubDataAsync(GLenum target, GLintptr srcByteOffset, ArrayBufferView dstBuffer, - optional GLuint dstOffset = 0, optional GLuint length = 0); // May throw DOMException + optional GLuint dstOffset = 0, optional GLuint length = 0); // May throw DOMException }; + + + + + + + + + Reads back data asynchronously from the bound WebGLBuffer into dstBuffer. +

    + Let buf be the buffer bound to target at the time + getBufferSubDataAsync is called. + If length is 0, let copyLength be + dstBuffer.length - dstOffset; otherwise, let + copyLength be length. +

    + If copyLength is greater than zero, + copy copyLength typed elements (each of size dstBuffer.BYTES_PER_ELEMENT) + from buf into dstBuffer, + reading buf starting at byte index srcByteOffset and + writing into dstBuffer starting at element index dstOffset. + If copyLength is 0, no data is written to dstBuffer, but + this does not cause a GL error to be generated. +
      +
    • If no WebGLBuffer is bound to target, + an INVALID_OPERATION error is generated. +
    • +
    • If target is TRANSFORM_FEEDBACK_BUFFER, + and any transform feedback object is currently active, + an INVALID_OPERATION error is generated. +
    • +
    • If dstOffset is greater than dstBuffer.length, + an INVALID_VALUE error is generated. +
    • +
    • If dstOffset + copyLength is greater than dstBuffer.length, + an INVALID_VALUE error is generated. +
    • +
    • If srcByteOffset is less than zero, + an INVALID_VALUE error is generated. +
    • +
    • If srcByteOffset + copyLength*dstBuffer.BYTES_PER_ELEMENT + is larger than the length of buf, + an INVALID_OPERATION is generated. +
    • +
    + When invoked, getBufferSubDataAsync must run these steps: +
      +
    • Let promise be a Promise to be returned. +
    • +
    • Check for the errors defined above. If there are any errors, generate the GL error + synchronously and + reject + promise with an InvalidStateError. +
    • +
    • Insert a readback of buf into the GL command stream, using the range + defined above. +
    • +
    • Return promise, but continue running these steps in parallel. +
    • +
    • Upon completion of the readback, queue a task performing the following steps: +
        +
      • If the context has been lost, or if dstBuffer has been neutered, + reject + promise with an InvalidStateError. In this case, no GL + error is generated. +
      • +
      • Write the readback result into dstBuffer, using the range defined + above. +
      • +
      • Resolve + promise with dstBuffer. +
      • +
      + The task source for this task is the WebGL task source. +
    • +
    + If the returned Promise is rejected, no data is written to dstBuffer. + +
    + Even if getBufferSubDataAsync is called multiple times in a row with the same + dstBuffer, then callbacks added synchronously will never see + results of subsequent getBufferSubDataAsync calls. +
    + +
    + Compared to the synchronous version of getBufferSubData, this version may + impose less overhead on applications. Intended use cases include reading pixels into a + pixel buffer object and examining that data on the CPU. It does not force the graphics + pipeline to be stalled as getBufferSubData does. +
    +
    +
    + Initial revision. From 47489348c085e9573ff5ccc11cb2ac0a4388eb3e Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Thu, 15 Dec 2016 10:54:48 -0800 Subject: [PATCH 20/21] use 'let' in for-loops --- ...ebgl-get-buffer-sub-data-async-stress.html | 59 +++++++++---------- .../webgl-get-buffer-sub-data-async.html | 31 +++++----- 2 files changed, 42 insertions(+), 48 deletions(-) diff --git a/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async-stress.html b/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async-stress.html index d583e7defc..03cdcb5d3d 100644 --- a/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async-stress.html +++ b/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async-stress.html @@ -67,37 +67,34 @@ var lastCounter = -1; var promises = []; debug("kicking off " + TEST_COUNT + " getBufferSubDataAsync calls"); - for (var i = 0; i < TEST_COUNT; i++) { - (function() { - var counter = i; - var color = [counter & 0xff, (counter >> 8) & 0xff, (counter >> 16) & 0xff, 255]; - gl.clearColor(color[0] / 255, color[1] / 255, color[2] / 255, color[3] / 255); - gl.clear(gl.COLOR_BUFFER_BIT); - gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo); - gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, 0); - var promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.PIXEL_PACK_BUFFER, 0, readbackBuffer); - gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); - promise = promise.then(function(buf) { - if (buf[0] == color[0] && - buf[1] == color[1] && - buf[2] == color[2] && - buf[3] == color[3]) { - testPassed("color readback #" + counter + " was correct"); - } else { - testFailed("color readback #" + counter + " was incorrect: was " + - buf + " but expected " + color); - } - if (counter == lastCounter + 1) { - testPassed(" and its Promise resolved in order after " + lastCounter); - } else { - testFailed(" and its Promise resolved out of order order after " + lastCounter); - } - lastCounter = counter; - }, function(e) { - testFailed(e.toString()); - }); - promises.push(promise); - })(); + for (let counter = 0; counter < TEST_COUNT; counter++) { + let color = [counter & 0xff, (counter >> 8) & 0xff, (counter >> 16) & 0xff, 255]; + gl.clearColor(color[0] / 255, color[1] / 255, color[2] / 255, color[3] / 255); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo); + gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, 0); + let promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl.PIXEL_PACK_BUFFER, 0, readbackBuffer); + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); + promise = promise.then(function(buf) { + if (buf[0] == color[0] && + buf[1] == color[1] && + buf[2] == color[2] && + buf[3] == color[3]) { + testPassed("color readback #" + counter + " was correct"); + } else { + testFailed("color readback #" + counter + " was incorrect: was " + + buf + " but expected " + color); + } + if (counter == lastCounter + 1) { + testPassed(" and its Promise resolved in order after " + lastCounter); + } else { + testFailed(" and its Promise resolved out of order order after " + lastCounter); + } + lastCounter = counter; + }, function(e) { + testFailed(e.toString()); + }); + promises.push(promise); } Promise.all(promises).catch(function(e) { diff --git a/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async.html b/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async.html index 4db2122436..6b29a04c0c 100644 --- a/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async.html +++ b/sdk/tests/conformance2/extensions/webgl-get-buffer-sub-data-async.html @@ -141,24 +141,21 @@ debug(""); }); - for (var i = 0; i < types.length; i++) { - (function() { - var index = i; - allPromises = allPromises.then(function() { - debug("Test that getBufferSubDataAsync successfully works reading buffer data from gl." + type); - var type = types[index]; - var buf = buffers[index]; - gl.bindBuffer(gl[type], buf); - wtu.shouldGenerateGLError(gl, gl.NO_ERROR, - "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl." + type + ", 0, dstData)"); - gl.bindBuffer(gl[type], null); - return promise.then(function(arr) { - resolvedArray = arr; - shouldBe("resolvedArray", "dstData"); - shouldBeTrue("areArraysEqual(resolvedArray, floatArray)"); - }); + for (let i = 0; i < types.length; i++) { + allPromises = allPromises.then(function() { + debug("Test that getBufferSubDataAsync successfully works reading buffer data from gl." + type); + var type = types[i]; + var buf = buffers[i]; + gl.bindBuffer(gl[type], buf); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + "promise = WEBGL_get_buffer_sub_data_async.getBufferSubDataAsync(gl." + type + ", 0, dstData)"); + gl.bindBuffer(gl[type], null); + return promise.then(function(arr) { + resolvedArray = arr; + shouldBe("resolvedArray", "dstData"); + shouldBeTrue("areArraysEqual(resolvedArray, floatArray)"); }); - })(); + }); } allPromises = allPromises.then(function() { From 5cabc1a2d945e98a9720f309d99316a22200d958 Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Thu, 15 Dec 2016 11:36:11 -0800 Subject: [PATCH 21/21] remove manual tests which were not updated anyway --- ...ferSubDataAsync-dom-suspension-manual.html | 125 ------------------ .../getBufferSubDataAsync-out-of-memory.html | 73 ---------- .../getBufferSubDataAsync-out-of-order-2.html | 79 ----------- .../getBufferSubDataAsync-out-of-order.html | 83 ------------ 4 files changed, 360 deletions(-) delete mode 100644 sdk/tests/extra/getBufferSubDataAsync-dom-suspension-manual.html delete mode 100644 sdk/tests/extra/getBufferSubDataAsync-out-of-memory.html delete mode 100644 sdk/tests/extra/getBufferSubDataAsync-out-of-order-2.html delete mode 100644 sdk/tests/extra/getBufferSubDataAsync-out-of-order.html diff --git a/sdk/tests/extra/getBufferSubDataAsync-dom-suspension-manual.html b/sdk/tests/extra/getBufferSubDataAsync-dom-suspension-manual.html deleted file mode 100644 index f388e06c25..0000000000 --- a/sdk/tests/extra/getBufferSubDataAsync-dom-suspension-manual.html +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - -WebGL getBufferSubDataAsync stress test. - - - - - -
    - -
    - - - diff --git a/sdk/tests/extra/getBufferSubDataAsync-out-of-memory.html b/sdk/tests/extra/getBufferSubDataAsync-out-of-memory.html deleted file mode 100644 index 2e1f1ec2f7..0000000000 --- a/sdk/tests/extra/getBufferSubDataAsync-out-of-memory.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - -WebGL getBufferSubDataAsync out of memory test. - - - - - -
    -
    - - - diff --git a/sdk/tests/extra/getBufferSubDataAsync-out-of-order-2.html b/sdk/tests/extra/getBufferSubDataAsync-out-of-order-2.html deleted file mode 100644 index 228f9471dd..0000000000 --- a/sdk/tests/extra/getBufferSubDataAsync-out-of-order-2.html +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - -WebGL getBufferSubDataAsync stress test. - - - - - -
    - -
    - - - diff --git a/sdk/tests/extra/getBufferSubDataAsync-out-of-order.html b/sdk/tests/extra/getBufferSubDataAsync-out-of-order.html deleted file mode 100644 index 42ff942549..0000000000 --- a/sdk/tests/extra/getBufferSubDataAsync-out-of-order.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - -WebGL getBufferSubDataAsync stress test. - - - - - -
    - -
    - - -