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 } else { 19 surface = CanvasKit.MakeSWCanvasSurface('anim'); 20 if (!surface) { 21 window._error = 'Could not make CPU surface'; 22 return null; 23 } 24 } 25 return surface; 26} 27 28// Time the drawing and flushing of a frame drawing function on a given surface. 29// drawFn is expected to be a zero arg function making draw calls to a canvas 30// warmupFrames - Run this number of frames before starting to measure things. 31// This allows us to make sure the noise from the first few renders (e.g shader 32// compilation, caches) is removed from the data we capture. 33// Stops after timeoutMillis if provided 34// Teturns a promise that resolves with the dict of measurements. 35function startTimingFrames(drawFn, surface, warmupFrames, maxFrames, timeoutMillis) { 36 return new Promise((resolve, reject) => { 37 const totalFrame = new Float32Array(maxFrames); 38 const withFlush = new Float32Array(maxFrames); 39 const withoutFlush = new Float32Array(maxFrames); 40 let warmUp = warmupFrames > 0; 41 let idx = -1; 42 let previousFrame; 43 44 function drawFrame() { 45 let start, afterDraw, end; 46 try { 47 start = performance.now(); 48 drawFn(); 49 afterDraw = performance.now(); 50 surface.flush(); 51 end = performance.now(); 52 } catch (e) { 53 console.error(e); 54 window._error = e.stack || e.toString(); 55 return; 56 } 57 58 if (warmUp) { 59 idx++; 60 if (idx >= warmupFrames) { 61 idx = -1; 62 warmUp = false; 63 } 64 window.requestAnimationFrame(drawFrame); 65 return; 66 } 67 if (idx >= 0) { 68 // Fill out total time the previous frame took to draw. 69 totalFrame[idx] = start - previousFrame; 70 } 71 previousFrame = start; 72 idx++; 73 // If we have maxed out the frames we are measuring or have completed the animation, 74 // we stop benchmarking. 75 if (!window._perfData) { 76 window._perfData = {}; 77 } 78 if (idx >= withFlush.length) { 79 resolve({ 80 // The total time elapsed between the same point during the drawing of each frame. 81 // This is the most relevant measurement for normal drawing tests. 82 'total_frame_ms': Array.from(totalFrame).slice(0, idx), 83 // The time taken to run the code under test and call surface.flush() 84 'with_flush_ms': Array.from(withFlush).slice(0, idx), 85 // The time taken to run the code under test 86 // This is the most relevant measurement for non-drawing tests such as matrix inversion. 87 'without_flush_ms': Array.from(withoutFlush).slice(0, idx), 88 }); 89 return; 90 } 91 92 // We can fill out this frame's intermediate steps. 93 withFlush[idx] = end - start; 94 withoutFlush[idx] = afterDraw - start; 95 96 if (timeoutMillis && ((beginTest + timeoutMillis) < performance.now())) { 97 console.log(`test aborted due to timeout after ${idx} frames`); 98 reject(`test aborted due to timeout after ${idx} frames`); 99 return; 100 } 101 window.requestAnimationFrame(drawFrame); 102 } 103 const beginTest = performance.now(); 104 window.requestAnimationFrame(drawFrame); 105 }); // new promise 106} 107