summaryrefslogtreecommitdiffstats
path: root/dom/canvas/test/webgl-conf/checkout/extra/getbuffersubdata-nonblocking-benchmark.html
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/canvas/test/webgl-conf/checkout/extra/getbuffersubdata-nonblocking-benchmark.html223
1 files changed, 223 insertions, 0 deletions
diff --git a/dom/canvas/test/webgl-conf/checkout/extra/getbuffersubdata-nonblocking-benchmark.html b/dom/canvas/test/webgl-conf/checkout/extra/getbuffersubdata-nonblocking-benchmark.html
new file mode 100644
index 0000000000..e6233ea83f
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/extra/getbuffersubdata-nonblocking-benchmark.html
@@ -0,0 +1,223 @@
+<!--
+Copyright (c) 2019 The Khronos Group Inc.
+Use of this source code is governed by an MIT-style license that can be
+found in the LICENSE.txt file.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>getBufferSubData non-blocking test</title>
+<link rel="stylesheet" href="../resources/js-test-style.css"/>
+<script src="../js/js-test-pre.js"></script>
+<script src="../js/webgl-test-utils.js"> </script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("Test that getBufferSubData is non-blocking when used with fenceSync");
+
+var wtu = WebGLTestUtils;
+
+var gl = wtu.create3DContext(undefined, undefined, 2);
+
+const srcData = new Uint8Array([ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 ]);
+const zeroData = new Uint8Array(8);
+
+const srcBuffer = gl.createBuffer();
+gl.bindBuffer(gl.COPY_READ_BUFFER, srcBuffer);
+gl.bufferData(gl.COPY_READ_BUFFER, srcData, gl.STATIC_DRAW);
+
+const readbackBuffer = gl.createBuffer();
+gl.bindBuffer(gl.COPY_WRITE_BUFFER, readbackBuffer);
+gl.bufferData(gl.COPY_WRITE_BUFFER, 8, gl.STREAM_READ);
+
+// unrelated buffers for tests
+gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); // used as copy dst
+gl.bufferData(gl.ARRAY_BUFFER, 8, gl.STATIC_DRAW);
+gl.bindBuffer(gl.UNIFORM_BUFFER, gl.createBuffer()); // used as copy src
+gl.bufferData(gl.UNIFORM_BUFFER, 8, gl.STATIC_DRAW);
+
+const dest = new Uint8Array(8);
+
+// Makes a new "resolvable" Promise
+function resolvable() {
+ let resolve;
+ const promise = new Promise(res => { resolve = res; });
+ promise.resolve = resolve;
+ return promise;
+}
+
+function fence() {
+ const promise = resolvable();
+
+ const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
+ gl.flush();
+ function check() {
+ const status = gl.clientWaitSync(sync, 0, 0);
+ if (status == gl.ALREADY_SIGNALED || status == gl.CONDITION_SATISFIED) {
+ gl.deleteSync(sync);
+ promise.resolve();
+ } else {
+ setTimeout(check, 0);
+ }
+ }
+ setTimeout(check, 0);
+
+ return promise;
+}
+
+function writeToReadbackBuffer() {
+ gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8);
+}
+
+function timedGetBufferSubData() {
+ dest.fill(0);
+ const t0 = performance.now();
+ gl.getBufferSubData(gl.COPY_WRITE_BUFFER, 0, dest);
+ return (performance.now() - t0);
+}
+
+function timeBlockingReadback() {
+ const promise = resolvable();
+ setTimeout(() => {
+ writeToReadbackBuffer();
+ const tBlocking = timedGetBufferSubData();
+ const tLatency = tBlocking;
+ promise.resolve({latency: tLatency, blocking: tBlocking});
+ }, 0);
+ return promise;
+}
+
+function timeNonblockingReadback() {
+ writeToReadbackBuffer();
+ const tLatency0 = performance.now();
+ return fence().then(() => {
+ const tBlocking = timedGetBufferSubData();
+ const tLatency = performance.now() - tLatency0;
+ return {latency: tLatency, blocking: tBlocking};
+ });
+}
+
+function timeReadbackWithUnrelatedCopy() {
+ writeToReadbackBuffer();
+ const tLatency0 = performance.now();
+ const f = fence();
+ // copy to a buffer unrelated to the readback
+ gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.ARRAY_BUFFER, 0, 0, 8);
+ return f.then(() => {
+ const tBlocking = timedGetBufferSubData();
+ const tLatency = performance.now() - tLatency0;
+ return {latency: tLatency, blocking: tBlocking};
+ });
+}
+
+function timeReadbackInterrupted() {
+ writeToReadbackBuffer();
+ const tLatency0 = performance.now();
+ const f = fence();
+ // interrupt the readback by inserting another write
+ gl.copyBufferSubData(gl.UNIFORM_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8);
+ return f.then(() => {
+ const tBlocking = timedGetBufferSubData();
+ const tLatency = performance.now() - tLatency0;
+ return {latency: tLatency, blocking: tBlocking};
+ });
+}
+
+function computeMean(timings) {
+ let total = 0;
+ for (let i = 0; i < timings.length; ++i) {
+ total += timings[i];
+ }
+ return total / timings.length;
+}
+
+function measureMean(fn, iterations) {
+ const timingsLatency = Array(iterations);
+ const timingsBlocking = Array(iterations);
+
+ // Chain together `iterations` promises to call `fn` sequentially.
+ let promise = Promise.resolve();
+ for (let i = 0; i < iterations; ++i) {
+ promise = promise
+ .then(fn)
+ .then(t => {
+ timingsLatency[i] = t.latency;
+ timingsBlocking[i] = t.blocking;
+ });
+ }
+
+ return promise.then(() => {
+ const meanLatency = computeMean(timingsLatency);
+ const meanBlocking = computeMean(timingsBlocking);
+ return { latency: meanLatency, blocking: meanBlocking };
+ });
+}
+
+let t_blocking, t_nonblocking;
+let t_unrelated;
+let t_interrupted;
+Promise.resolve()
+ .then(() => {
+ let iterations = 500;
+ debug(`blocking readback: mean over ${iterations} iterations...`);
+ return measureMean(timeBlockingReadback, iterations);
+ })
+ .then(t => {
+ t_blocking = t;
+ debug(`... latency = ${t.latency}ms, blocking = ${t.blocking}ms`);
+ })
+ .then(() => shouldBeTrue("areArraysEqual(dest, srcData)"))
+
+ .then(() => debug(""))
+ .then(() => {
+ let iterations = 500;
+ debug(`nonblocking readback: mean over ${iterations} iterations...`);
+ return measureMean(timeNonblockingReadback, iterations);
+ })
+ .then(t => {
+ t_nonblocking = t;
+ debug(`... latency = ${t.latency}ms, blocking = ${t.blocking}ms`);
+ })
+ .then(() => shouldBeTrue("areArraysEqual(dest, srcData)"))
+
+ .then(() => debug(""))
+ .then(() => {
+ let iterations = 500;
+ debug(`readback interrupted by unrelated read from copy source: mean over ${iterations} iterations...`);
+ return measureMean(timeReadbackWithUnrelatedCopy, iterations);
+ })
+ .then(t => {
+ t_unrelated = t;
+ debug(`... latency = ${t.latency}ms, blocking = ${t.blocking}ms`);
+ })
+ .then(() => shouldBeTrue("areArraysEqual(dest, srcData)"))
+
+ .then(() => debug(""))
+ .then(() => {
+ let iterations = 500;
+ debug(`readback interrupted by write to readback source: mean over ${iterations} iterations...`);
+ return measureMean(timeReadbackInterrupted, iterations);
+ })
+ .then(t => {
+ t_interrupted = t;
+ debug(`... latency = ${t.latency}ms, blocking = ${t.blocking}ms`);
+ })
+ .then(() => shouldBeTrue("areArraysEqual(dest, zeroData)"))
+
+ .then(() => {
+ debug("");
+ shouldBeTrue("t_nonblocking.blocking < t_blocking.blocking");
+ shouldBeTrue("t_unrelated.blocking < t_blocking.blocking");
+ shouldBeTrue("t_nonblocking.blocking < t_interrupted.blocking");
+ })
+ .then(finishTest);
+
+var successfullyParsed = true;
+</script>
+</body>
+</html>