diff options
Diffstat (limited to 'dom/canvas/test/webgl-conf/checkout/extra/getbuffersubdata-nonblocking-benchmark.html')
-rw-r--r-- | dom/canvas/test/webgl-conf/checkout/extra/getbuffersubdata-nonblocking-benchmark.html | 223 |
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> |