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