1// Shared benchmarking functions such as surface creation, measurement, publishing to perf.skia.org 2 3function getSurface(CanvasKit, webglversion) { 4 let surface; 5 if (window.location.hash.indexOf('gpu') !== -1) { 6 surface = CanvasKit.MakeWebGLCanvasSurface('anim', null /* colorspace */, {'majorVersion': webglversion}); 7 if (!surface) { 8 window._error = 'Could not make GPU surface'; 9 return null; 10 } 11 let c = document.getElementById('anim'); 12 // If CanvasKit was unable to instantiate a WebGL context, it will fallback 13 // to CPU and add a ck-replaced class to the canvas element. 14 if (c.classList.contains('ck-replaced')) { 15 window._error = 'fell back to CPU'; 16 return null; 17 } 18 if (webglversion !== surface.openGLversion) { 19 window._error = 'Want WebGL version '+webglversion+' but got '+surface.openGLversion; 20 return null; 21 } 22 } else { 23 surface = CanvasKit.MakeSWCanvasSurface('anim'); 24 if (!surface) { 25 window._error = 'Could not make CPU surface'; 26 return null; 27 } 28 } 29 return surface; 30} 31 32// Time the drawing and flushing of a frame drawing function on a given surface. 33// drawFn is expected to be a zero arg function making draw calls to a canvas 34// warmupFrames - Run this number of frames before starting to measure things. 35// This allows us to make sure the noise from the first few renders (e.g shader 36// compilation, caches) is removed from the data we capture. 37// Stops after timeoutMillis if provided 38// Teturns a promise that resolves with the dict of measurements. 39function startTimingFrames(drawFn, surface, warmupFrames, maxFrames, timeoutMillis) { 40 return new Promise((resolve, reject) => { 41 const totalFrame = new Float32Array(maxFrames); 42 const withFlush = new Float32Array(maxFrames); 43 const withoutFlush = new Float32Array(maxFrames); 44 let warmUp = warmupFrames > 0; 45 let idx = -1; 46 let previousFrame; 47 48 function drawFrame() { 49 const start = performance.now(); 50 drawFn(); 51 const afterDraw = performance.now(); 52 surface.flush(); 53 const end = performance.now(); 54 55 if (warmUp) { 56 idx++; 57 if (idx >= warmupFrames) { 58 idx = -1; 59 warmUp = false; 60 } 61 window.requestAnimationFrame(drawFrame); 62 return; 63 } 64 if (idx >= 0) { 65 // Fill out total time the previous frame took to draw. 66 totalFrame[idx] = start - previousFrame; 67 } 68 previousFrame = start; 69 idx++; 70 // If we have maxed out the frames we are measuring or have completed the animation, 71 // we stop benchmarking. 72 if (!window._perfData) { 73 window._perfData = {}; 74 } 75 if (idx >= withFlush.length) { 76 resolve({ 77 // The total time elapsed between the same point during the drawing of each frame. 78 // This is the most relevant measurement for normal drawing tests. 79 'total_frame_ms': Array.from(totalFrame).slice(0, idx), 80 // The time taken to run the code under test and call surface.flush() 81 'with_flush_ms': Array.from(withFlush).slice(0, idx), 82 // The time taken to run the code under test 83 // This is the most relevant measurement for non-drawing tests such as matrix inversion. 84 'without_flush_ms': Array.from(withoutFlush).slice(0, idx), 85 }); 86 return; 87 } 88 89 // We can fill out this frame's intermediate steps. 90 withFlush[idx] = end - start; 91 withoutFlush[idx] = afterDraw - start; 92 93 if (timeoutMillis && ((beginTest + timeoutMillis) < performance.now())) { 94 console.log(`test aborted due to timeout after ${idx} frames`); 95 reject(`test aborted due to timeout after ${idx} frames`); 96 return; 97 } 98 window.requestAnimationFrame(drawFrame); 99 } 100 const beginTest = performance.now(); 101 window.requestAnimationFrame(drawFrame); 102 }); // new promise 103} 104