getbuffersubdata-nonblocking-benchmark.html (6899B)
1 <!-- 2 Copyright (c) 2019 The Khronos Group Inc. 3 Use of this source code is governed by an MIT-style license that can be 4 found in the LICENSE.txt file. 5 --> 6 7 <!DOCTYPE html> 8 <html> 9 <head> 10 <meta charset="utf-8"> 11 <title>getBufferSubData non-blocking test</title> 12 <link rel="stylesheet" href="../resources/js-test-style.css"/> 13 <script src="../js/js-test-pre.js"></script> 14 <script src="../js/webgl-test-utils.js"> </script> 15 </head> 16 <body> 17 <div id="description"></div> 18 <div id="console"></div> 19 <script> 20 "use strict"; 21 description("Test that getBufferSubData is non-blocking when used with fenceSync"); 22 23 var wtu = WebGLTestUtils; 24 25 var gl = wtu.create3DContext(undefined, undefined, 2); 26 27 const srcData = new Uint8Array([ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 ]); 28 const zeroData = new Uint8Array(8); 29 30 const srcBuffer = gl.createBuffer(); 31 gl.bindBuffer(gl.COPY_READ_BUFFER, srcBuffer); 32 gl.bufferData(gl.COPY_READ_BUFFER, srcData, gl.STATIC_DRAW); 33 34 const readbackBuffer = gl.createBuffer(); 35 gl.bindBuffer(gl.COPY_WRITE_BUFFER, readbackBuffer); 36 gl.bufferData(gl.COPY_WRITE_BUFFER, 8, gl.STREAM_READ); 37 38 // unrelated buffers for tests 39 gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); // used as copy dst 40 gl.bufferData(gl.ARRAY_BUFFER, 8, gl.STATIC_DRAW); 41 gl.bindBuffer(gl.UNIFORM_BUFFER, gl.createBuffer()); // used as copy src 42 gl.bufferData(gl.UNIFORM_BUFFER, 8, gl.STATIC_DRAW); 43 44 const dest = new Uint8Array(8); 45 46 // Makes a new "resolvable" Promise 47 function resolvable() { 48 let resolve; 49 const promise = new Promise(res => { resolve = res; }); 50 promise.resolve = resolve; 51 return promise; 52 } 53 54 function fence() { 55 const promise = resolvable(); 56 57 const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0); 58 gl.flush(); 59 function check() { 60 const status = gl.clientWaitSync(sync, 0, 0); 61 if (status == gl.ALREADY_SIGNALED || status == gl.CONDITION_SATISFIED) { 62 gl.deleteSync(sync); 63 promise.resolve(); 64 } else { 65 setTimeout(check, 0); 66 } 67 } 68 setTimeout(check, 0); 69 70 return promise; 71 } 72 73 function writeToReadbackBuffer() { 74 gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8); 75 } 76 77 function timedGetBufferSubData() { 78 dest.fill(0); 79 const t0 = performance.now(); 80 gl.getBufferSubData(gl.COPY_WRITE_BUFFER, 0, dest); 81 return (performance.now() - t0); 82 } 83 84 function timeBlockingReadback() { 85 const promise = resolvable(); 86 setTimeout(() => { 87 writeToReadbackBuffer(); 88 const tBlocking = timedGetBufferSubData(); 89 const tLatency = tBlocking; 90 promise.resolve({latency: tLatency, blocking: tBlocking}); 91 }, 0); 92 return promise; 93 } 94 95 function timeNonblockingReadback() { 96 writeToReadbackBuffer(); 97 const tLatency0 = performance.now(); 98 return fence().then(() => { 99 const tBlocking = timedGetBufferSubData(); 100 const tLatency = performance.now() - tLatency0; 101 return {latency: tLatency, blocking: tBlocking}; 102 }); 103 } 104 105 function timeReadbackWithUnrelatedCopy() { 106 writeToReadbackBuffer(); 107 const tLatency0 = performance.now(); 108 const f = fence(); 109 // copy to a buffer unrelated to the readback 110 gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.ARRAY_BUFFER, 0, 0, 8); 111 return f.then(() => { 112 const tBlocking = timedGetBufferSubData(); 113 const tLatency = performance.now() - tLatency0; 114 return {latency: tLatency, blocking: tBlocking}; 115 }); 116 } 117 118 function timeReadbackInterrupted() { 119 writeToReadbackBuffer(); 120 const tLatency0 = performance.now(); 121 const f = fence(); 122 // interrupt the readback by inserting another write 123 gl.copyBufferSubData(gl.UNIFORM_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 8); 124 return f.then(() => { 125 const tBlocking = timedGetBufferSubData(); 126 const tLatency = performance.now() - tLatency0; 127 return {latency: tLatency, blocking: tBlocking}; 128 }); 129 } 130 131 function computeMean(timings) { 132 let total = 0; 133 for (let i = 0; i < timings.length; ++i) { 134 total += timings[i]; 135 } 136 return total / timings.length; 137 } 138 139 function measureMean(fn, iterations) { 140 const timingsLatency = Array(iterations); 141 const timingsBlocking = Array(iterations); 142 143 // Chain together `iterations` promises to call `fn` sequentially. 144 let promise = Promise.resolve(); 145 for (let i = 0; i < iterations; ++i) { 146 promise = promise 147 .then(fn) 148 .then(t => { 149 timingsLatency[i] = t.latency; 150 timingsBlocking[i] = t.blocking; 151 }); 152 } 153 154 return promise.then(() => { 155 const meanLatency = computeMean(timingsLatency); 156 const meanBlocking = computeMean(timingsBlocking); 157 return { latency: meanLatency, blocking: meanBlocking }; 158 }); 159 } 160 161 let t_blocking, t_nonblocking; 162 let t_unrelated; 163 let t_interrupted; 164 Promise.resolve() 165 .then(() => { 166 let iterations = 500; 167 debug(`blocking readback: mean over ${iterations} iterations...`); 168 return measureMean(timeBlockingReadback, iterations); 169 }) 170 .then(t => { 171 t_blocking = t; 172 debug(`... latency = ${t.latency}ms, blocking = ${t.blocking}ms`); 173 }) 174 .then(() => shouldBeTrue("areArraysEqual(dest, srcData)")) 175 176 .then(() => debug("")) 177 .then(() => { 178 let iterations = 500; 179 debug(`nonblocking readback: mean over ${iterations} iterations...`); 180 return measureMean(timeNonblockingReadback, iterations); 181 }) 182 .then(t => { 183 t_nonblocking = t; 184 debug(`... latency = ${t.latency}ms, blocking = ${t.blocking}ms`); 185 }) 186 .then(() => shouldBeTrue("areArraysEqual(dest, srcData)")) 187 188 .then(() => debug("")) 189 .then(() => { 190 let iterations = 500; 191 debug(`readback interrupted by unrelated read from copy source: mean over ${iterations} iterations...`); 192 return measureMean(timeReadbackWithUnrelatedCopy, iterations); 193 }) 194 .then(t => { 195 t_unrelated = t; 196 debug(`... latency = ${t.latency}ms, blocking = ${t.blocking}ms`); 197 }) 198 .then(() => shouldBeTrue("areArraysEqual(dest, srcData)")) 199 200 .then(() => debug("")) 201 .then(() => { 202 let iterations = 500; 203 debug(`readback interrupted by write to readback source: mean over ${iterations} iterations...`); 204 return measureMean(timeReadbackInterrupted, iterations); 205 }) 206 .then(t => { 207 t_interrupted = t; 208 debug(`... latency = ${t.latency}ms, blocking = ${t.blocking}ms`); 209 }) 210 .then(() => shouldBeTrue("areArraysEqual(dest, zeroData)")) 211 212 .then(() => { 213 debug(""); 214 shouldBeTrue("t_nonblocking.blocking < t_blocking.blocking"); 215 shouldBeTrue("t_unrelated.blocking < t_blocking.blocking"); 216 shouldBeTrue("t_nonblocking.blocking < t_interrupted.blocking"); 217 }) 218 .then(finishTest); 219 220 var successfullyParsed = true; 221 </script> 222 </body> 223 </html>