| // Shared benchmarking functions such as surface creation, measurement, publishing to perf.skia.org |
| |
| function getSurface(CanvasKit, webglversion) { |
| let surface; |
| if (window.location.hash.indexOf('gpu') !== -1) { |
| surface = CanvasKit.MakeWebGLCanvasSurface('anim', null /* colorspace */, {'majorVersion': webglversion}); |
| if (!surface) { |
| window._error = 'Could not make GPU surface'; |
| return null; |
| } |
| let c = document.getElementById('anim'); |
| // If CanvasKit was unable to instantiate a WebGL context, it will fallback |
| // to CPU and add a ck-replaced class to the canvas element. |
| if (c.classList.contains('ck-replaced')) { |
| window._error = 'fell back to CPU'; |
| return null; |
| } |
| if (webglversion != surface.openGLversion) { |
| window._error = 'Want WebGL version '+webglversion+' but got '+surface.openGLversion; |
| return null; |
| } |
| } else { |
| surface = CanvasKit.MakeSWCanvasSurface('anim'); |
| if (!surface) { |
| window._error = 'Could not make CPU surface'; |
| return null; |
| } |
| } |
| return surface; |
| } |
| |
| // Time the drawing and flushing of a frame drawing function on a given surface. |
| // drawFn is expected to be a zero arg function making draw calls to a canvas |
| // warmupFrames - Run this number of frames before starting to measure things. |
| // This allows us to make sure the noise from the first few renders (e.g shader |
| // compilation, caches) is removed from the data we capture. |
| // Stops after timeoutMillis if provided |
| // Teturns a promise that resolves with the dict of measurements. |
| function startTimingFrames(drawFn, surface, warmupFrames, maxFrames, timeoutMillis) { |
| return new Promise((resolve, reject) => { |
| const totalFrame = new Float32Array(maxFrames); |
| const withFlush = new Float32Array(maxFrames); |
| const withoutFlush = new Float32Array(maxFrames); |
| let warmUp = warmupFrames > 0; |
| let idx = -1; |
| let previousFrame; |
| |
| function drawFrame() { |
| const start = performance.now(); |
| drawFn(); |
| const afterDraw = performance.now(); |
| surface.flush(); |
| const end = performance.now(); |
| |
| if (warmUp) { |
| idx++; |
| if (idx >= warmupFrames) { |
| idx = -1; |
| warmUp = false; |
| } |
| window.requestAnimationFrame(drawFrame); |
| return; |
| } |
| if (idx >= 0) { |
| // Fill out total time the previous frame took to draw. |
| totalFrame[idx] = start - previousFrame; |
| } |
| previousFrame = start; |
| idx++; |
| // If we have maxed out the frames we are measuring or have completed the animation, |
| // we stop benchmarking. |
| if (!window._perfData) { |
| window._perfData = {}; |
| } |
| if (idx >= withFlush.length) { |
| resolve({ |
| // The total time elapsed between the same point during the drawing of each frame. |
| // This is the most relevant measurement for normal drawing tests. |
| 'total_frame_ms': Array.from(totalFrame).slice(0, idx), |
| // The time taken to run the code under test and call surface.flush() |
| 'with_flush_ms': Array.from(withFlush).slice(0, idx), |
| // The time taken to run the code under test |
| // This is the most relevant measurement for non-drawing tests such as matrix inversion. |
| 'without_flush_ms': Array.from(withoutFlush).slice(0, idx), |
| }); |
| return; |
| } |
| |
| // We can fill out this frame's intermediate steps. |
| withFlush[idx] = end - start; |
| withoutFlush[idx] = afterDraw - start; |
| |
| if (timeoutMillis && ((beginTest + timeoutMillis) < performance.now())) { |
| console.log('test aborted due to timeout'); |
| reject('test aborted due to timeout'); |
| return; |
| } |
| window.requestAnimationFrame(drawFrame); |
| } |
| const beginTest = performance.now(); |
| window.requestAnimationFrame(drawFrame); |
| }); // new promise |
| } |