xref: /aosp_15_r20/external/skia/tools/perf-canvaskit-puppeteer/skottie-frames.html (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1<!-- This benchmark aims to accurately measure the time it takes for Skottie to load the JSON and
2turn it into an animation, as well as the times for the first hundred frames (and, as a subcomponent
3of that, the seek times of the first hundred frames). This is set to mimic how a real-world user
4would display the animation (e.g. using clock time to determine where to seek, not frame numbers).
5-->
6<!DOCTYPE html>
7<html>
8<head>
9  <title>Skottie-WASM Perf</title>
10  <meta charset="utf-8" />
11  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
12  <meta name="viewport" content="width=device-width, initial-scale=1.0">
13  <script src="/static/canvaskit.js" type="text/javascript" charset="utf-8"></script>
14  <script src="/static/benchmark.js" type="text/javascript" charset="utf-8"></script>
15  <style type="text/css" media="screen">
16    body {
17      margin: 0;
18      padding: 0;
19    }
20  </style>
21</head>
22<body>
23  <main>
24    <button id="start_bench">Start Benchmark</button>
25    <br>
26    <canvas id=anim width=1000 height=1000 style="height: 1000px; width: 1000px;"></canvas>
27  </main>
28  <script type="text/javascript" charset="utf-8">
29    const WIDTH  = 1000;
30    const HEIGHT = 1000;
31    const WARM_UP_FRAMES = 0; // No warmup, so that the jank of initial frames gets measured.
32    // We sample MAX_FRAMES or until MAX_SAMPLE_SECONDS has elapsed.
33    const MAX_FRAMES = 600; // ~10s at 60fps
34    const MAX_SAMPLE_MS = 90 * 1000; // in case something takes a while, stop after 90 seconds.
35    const LOTTIE_JSON_PATH = '/static/lottie.json';
36    const ASSETS_PATH = '/static/assets/';
37    (function() {
38
39      const loadKit = CanvasKitInit({
40        locateFile: (file) => '/static/' + file,
41      });
42
43      const loadLottie = fetch(LOTTIE_JSON_PATH).then((resp) => {
44        return resp.text();
45      });
46
47      const loadFontsAndAssets = loadLottie.then((jsonStr) => {
48        const lottie = JSON.parse(jsonStr);
49        const promises = [];
50        promises.push(...loadFonts(lottie.fonts));
51        promises.push(...loadAssets(lottie.assets));
52        return Promise.all(promises);
53      });
54
55      Promise.all([loadKit, loadLottie, loadFontsAndAssets]).then((values) => {
56        const [CanvasKit, json, externalAssets] = values;
57        console.log(externalAssets);
58        const assets = {};
59        for (const asset of externalAssets) {
60          if (asset) {
61            assets[asset.name] = asset.bytes;
62          }
63        }
64        const loadStart = performance.now();
65        const animation = CanvasKit.MakeManagedAnimation(json, assets);
66        const loadTime = performance.now() - loadStart;
67
68        window._perfData = {
69          json_load_ms: loadTime,
70        };
71
72        const duration = animation.duration() * 1000;
73        const bounds = CanvasKit.LTRBRect(0, 0, WIDTH, HEIGHT);
74
75        const urlSearchParams = new URLSearchParams(window.location.search);
76        let glversion = 2;
77        if (urlSearchParams.has('webgl1')) {
78          glversion = 1;
79        }
80
81        const surface = getSurface(CanvasKit, glversion);
82        if (!surface) {
83          console.error('Could not make surface', window._error);
84          return;
85        }
86        const canvas = surface.getCanvas();
87
88        document.getElementById('start_bench').addEventListener('click', async () => {
89          const startTime = Date.now();
90          const damageRect = Float32Array.of(0, 0, 0, 0);
91
92          function draw() {
93            const seek = ((Date.now() - startTime) / duration) % 1.0;
94            const damage = animation.seek(seek, damageRect);
95
96            if (damage[2] > damage[0] && damage[3] > damage[1]) {
97              animation.render(canvas, bounds);
98            }
99          }
100
101          startTimingFrames(draw, surface, WARM_UP_FRAMES, MAX_FRAMES, MAX_SAMPLE_MS).then((results) => {
102            Object.assign(window._perfData, results);
103            window._perfDone = true;
104          }).catch((error) => {
105            window._error = error;
106          });
107
108        });
109        console.log('Perf is ready');
110        window._perfReady = true;
111      });
112    })();
113
114  function loadFonts(fonts) {
115    const promises = [];
116    if (!fonts || !fonts.list) {
117      return promises;
118    }
119    for (const font of fonts.list) {
120      if (font.fName) {
121        promises.push(fetch(`${ASSETS_PATH}/${font.fName}.ttf`).then((resp) => {
122            // fetch does not reject on 404
123            if (!resp.ok) {
124              console.error(`Could not load ${font.fName}.ttf: status ${resp.status}`);
125              return null;
126            }
127            return resp.arrayBuffer().then((buffer) => {
128              return {
129                'name': font.fName,
130                'bytes': buffer
131              };
132            });
133          })
134        );
135      }
136    }
137    return promises;
138  }
139
140  function loadAssets(assets) {
141    const promises = [];
142    if (!assets) {
143      return [];
144    }
145    for (const asset of assets) {
146      // asset.p is the filename, if it's an image.
147      // Don't try to load inline/dataURI images.
148      const should_load = asset.p && asset.p.startsWith && !asset.p.startsWith('data:');
149      if (should_load) {
150        promises.push(fetch(`${ASSETS_PATH}/${asset.p}`)
151          .then((resp) => {
152            // fetch does not reject on 404
153            if (!resp.ok) {
154              console.error(`Could not load ${asset.p}: status ${resp.status}`);
155              return null;
156            }
157            return resp.arrayBuffer().then((buffer) => {
158              return {
159                'name': asset.p,
160                'bytes': buffer
161              };
162            });
163          })
164        );
165      }
166    }
167    return promises;
168  }
169  </script>
170</body>
171</html>
172