1 <!-- This benchmark aims to accurately measure the time it takes for Skottie to load the JSON and
2 turn it into an animation, as well as the times for the first hundred frames (and, as a subcomponent
3 of that, the seek times of the first hundred frames). This is set to mimic how a real-world user
4 would display the animation (e.g. using clock time to determine where to seek, not frame numbers).
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">
24 <button id="start_bench">Start Benchmark</button>
26 <canvas id=anim width=1000 height=1000 style="height: 1000px; width: 1000px;"></canvas>
28 <script type="text/javascript" charset="utf-8">
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 = 50 * 1000; // in case something takes a while, stop after 50 seconds.
35 const LOTTIE_JSON_PATH = '/static/lottie.json';
36 const ASSETS_PATH = '/static/assets/';
39 const loadKit = CanvasKitInit({
40 locateFile: (file) => '/static/' + file,
43 const loadLottie = fetch(LOTTIE_JSON_PATH).then((resp) => {
47 const loadFontsAndAssets = loadLottie.then((jsonStr) => {
48 const lottie = JSON.parse(jsonStr);
50 promises.push(...loadFonts(lottie.fonts));
51 promises.push(...loadAssets(lottie.assets));
52 return Promise.all(promises);
55 Promise.all([loadKit, loadLottie, loadFontsAndAssets]).then((values) => {
56 const [CanvasKit, json, externalAssets] = values;
57 console.log(externalAssets);
59 for (const asset of externalAssets) {
61 assets[asset.name] = asset.bytes;
64 const loadStart = performance.now();
65 const animation = CanvasKit.MakeManagedAnimation(json, assets);
66 const loadTime = performance.now() - loadStart;
69 json_load_ms: loadTime,
72 const duration = animation.duration() * 1000;
73 const bounds = CanvasKit.LTRBRect(0, 0, WIDTH, HEIGHT);
75 const urlSearchParams = new URLSearchParams(window.location.search);
77 if (urlSearchParams.has('webgl1')) {
81 const surface = getSurface(CanvasKit, glversion);
83 console.error('Could not make surface', window._error);
86 const canvas = surface.getCanvas();
88 document.getElementById('start_bench').addEventListener('click', async () => {
89 const startTime = Date.now();
90 const damageRect = Float32Array.of(0, 0, 0, 0);
93 const seek = ((Date.now() - startTime) / duration) % 1.0;
94 const damage = animation.seek(seek, damageRect);
96 if (damage[2] > damage[0] && damage[3] > damage[1]) {
97 animation.render(canvas, bounds);
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;
109 console.log('Perf is ready');
110 window._perfReady = true;
114 function loadFonts(fonts) {
116 if (!fonts || !fonts.list) {
119 for (const font of fonts.list) {
121 promises.push(fetch(`${ASSETS_PATH}/${font.fName}.ttf`).then((resp) => {
122 // fetch does not reject on 404
124 console.error(`Could not load ${font.fName}.ttf: status ${resp.status}`);
127 return resp.arrayBuffer().then((buffer) => {
140 function loadAssets(assets) {
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:');
150 promises.push(fetch(`${ASSETS_PATH}/${asset.p}`)
152 // fetch does not reject on 404
154 console.error(`Could not load ${asset.p}: status ${resp.status}`);
157 return resp.arrayBuffer().then((buffer) => {