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