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