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