1cb93a386Sopenharmony_ci/** 2cb93a386Sopenharmony_ci * Command line application to run Skottie-WASM perf on a Lottie file in the 3cb93a386Sopenharmony_ci * browser and then exporting the result. 4cb93a386Sopenharmony_ci * 5cb93a386Sopenharmony_ci */ 6cb93a386Sopenharmony_ciconst puppeteer = require('puppeteer'); 7cb93a386Sopenharmony_ciconst express = require('express'); 8cb93a386Sopenharmony_ciconst fs = require('fs'); 9cb93a386Sopenharmony_ciconst commandLineArgs = require('command-line-args'); 10cb93a386Sopenharmony_ciconst commandLineUsage= require('command-line-usage'); 11cb93a386Sopenharmony_ciconst fetch = require('node-fetch'); 12cb93a386Sopenharmony_ci 13cb93a386Sopenharmony_ciconst opts = [ 14cb93a386Sopenharmony_ci { 15cb93a386Sopenharmony_ci name: 'canvaskit_js', 16cb93a386Sopenharmony_ci typeLabel: '{underline file}', 17cb93a386Sopenharmony_ci description: 'The path to canvaskit.js.' 18cb93a386Sopenharmony_ci }, 19cb93a386Sopenharmony_ci { 20cb93a386Sopenharmony_ci name: 'canvaskit_wasm', 21cb93a386Sopenharmony_ci typeLabel: '{underline file}', 22cb93a386Sopenharmony_ci description: 'The path to canvaskit.wasm.' 23cb93a386Sopenharmony_ci }, 24cb93a386Sopenharmony_ci { 25cb93a386Sopenharmony_ci name: 'input', 26cb93a386Sopenharmony_ci typeLabel: '{underline file}', 27cb93a386Sopenharmony_ci description: 'The Lottie JSON file to process.' 28cb93a386Sopenharmony_ci }, 29cb93a386Sopenharmony_ci { 30cb93a386Sopenharmony_ci name: 'output', 31cb93a386Sopenharmony_ci typeLabel: '{underline file}', 32cb93a386Sopenharmony_ci description: 'The perf file to write. Defaults to perf.json', 33cb93a386Sopenharmony_ci }, 34cb93a386Sopenharmony_ci { 35cb93a386Sopenharmony_ci name: 'use_gpu', 36cb93a386Sopenharmony_ci description: 'Whether we should run in non-headless mode with GPU.', 37cb93a386Sopenharmony_ci type: Boolean, 38cb93a386Sopenharmony_ci }, 39cb93a386Sopenharmony_ci { 40cb93a386Sopenharmony_ci name: 'port', 41cb93a386Sopenharmony_ci description: 'The port number to use, defaults to 8081.', 42cb93a386Sopenharmony_ci type: Number, 43cb93a386Sopenharmony_ci }, 44cb93a386Sopenharmony_ci { 45cb93a386Sopenharmony_ci name: 'help', 46cb93a386Sopenharmony_ci alias: 'h', 47cb93a386Sopenharmony_ci type: Boolean, 48cb93a386Sopenharmony_ci description: 'Print this usage guide.' 49cb93a386Sopenharmony_ci }, 50cb93a386Sopenharmony_ci]; 51cb93a386Sopenharmony_ci 52cb93a386Sopenharmony_ciconst usage = [ 53cb93a386Sopenharmony_ci { 54cb93a386Sopenharmony_ci header: 'Skottie WASM Perf', 55cb93a386Sopenharmony_ci content: "Command line application to run Skottie-WASM perf." 56cb93a386Sopenharmony_ci }, 57cb93a386Sopenharmony_ci { 58cb93a386Sopenharmony_ci header: 'Options', 59cb93a386Sopenharmony_ci optionList: opts, 60cb93a386Sopenharmony_ci }, 61cb93a386Sopenharmony_ci]; 62cb93a386Sopenharmony_ci 63cb93a386Sopenharmony_ci// Parse and validate flags. 64cb93a386Sopenharmony_ciconst options = commandLineArgs(opts); 65cb93a386Sopenharmony_ci 66cb93a386Sopenharmony_ciif (!options.output) { 67cb93a386Sopenharmony_ci options.output = 'perf.json'; 68cb93a386Sopenharmony_ci} 69cb93a386Sopenharmony_ciif (!options.port) { 70cb93a386Sopenharmony_ci options.port = 8081; 71cb93a386Sopenharmony_ci} 72cb93a386Sopenharmony_ci 73cb93a386Sopenharmony_ciif (options.help) { 74cb93a386Sopenharmony_ci console.log(commandLineUsage(usage)); 75cb93a386Sopenharmony_ci process.exit(0); 76cb93a386Sopenharmony_ci} 77cb93a386Sopenharmony_ci 78cb93a386Sopenharmony_ciif (!options.canvaskit_js) { 79cb93a386Sopenharmony_ci console.error('You must supply path to canvaskit.js.'); 80cb93a386Sopenharmony_ci console.log(commandLineUsage(usage)); 81cb93a386Sopenharmony_ci process.exit(1); 82cb93a386Sopenharmony_ci} 83cb93a386Sopenharmony_ci 84cb93a386Sopenharmony_ciif (!options.canvaskit_wasm) { 85cb93a386Sopenharmony_ci console.error('You must supply path to canvaskit.wasm.'); 86cb93a386Sopenharmony_ci console.log(commandLineUsage(usage)); 87cb93a386Sopenharmony_ci process.exit(1); 88cb93a386Sopenharmony_ci} 89cb93a386Sopenharmony_ci 90cb93a386Sopenharmony_ciif (!options.input) { 91cb93a386Sopenharmony_ci console.error('You must supply a Lottie JSON filename.'); 92cb93a386Sopenharmony_ci console.log(commandLineUsage(usage)); 93cb93a386Sopenharmony_ci process.exit(1); 94cb93a386Sopenharmony_ci} 95cb93a386Sopenharmony_ci 96cb93a386Sopenharmony_ci// Start up a web server to serve the three files we need. 97cb93a386Sopenharmony_cilet canvasKitJS = fs.readFileSync(options.canvaskit_js, 'utf8'); 98cb93a386Sopenharmony_cilet canvasKitWASM = fs.readFileSync(options.canvaskit_wasm, 'binary'); 99cb93a386Sopenharmony_cilet driverHTML = fs.readFileSync('skottie-wasm-perf.html', 'utf8'); 100cb93a386Sopenharmony_cilet lottieJSON = fs.readFileSync(options.input, 'utf8'); 101cb93a386Sopenharmony_ci 102cb93a386Sopenharmony_ciconst app = express(); 103cb93a386Sopenharmony_ciapp.get('/', (req, res) => res.send(driverHTML)); 104cb93a386Sopenharmony_ciapp.get('/res/canvaskit.wasm', function(req, res) { 105cb93a386Sopenharmony_ci res.type('application/wasm'); 106cb93a386Sopenharmony_ci res.send(new Buffer(canvasKitWASM, 'binary')); 107cb93a386Sopenharmony_ci}); 108cb93a386Sopenharmony_ciapp.get('/res/canvaskit.js', (req, res) => res.send(canvasKitJS)); 109cb93a386Sopenharmony_ciapp.get('/res/lottie.json', (req, res) => res.send(lottieJSON)); 110cb93a386Sopenharmony_ciapp.listen(options.port, () => console.log('- Local web server started.')) 111cb93a386Sopenharmony_ci 112cb93a386Sopenharmony_ci// Utility function. 113cb93a386Sopenharmony_ciasync function wait(ms) { 114cb93a386Sopenharmony_ci await new Promise(resolve => setTimeout(() => resolve(), ms)); 115cb93a386Sopenharmony_ci return ms; 116cb93a386Sopenharmony_ci} 117cb93a386Sopenharmony_ci 118cb93a386Sopenharmony_cilet hash = "#cpu"; 119cb93a386Sopenharmony_ciif (options.use_gpu) { 120cb93a386Sopenharmony_ci hash = "#gpu"; 121cb93a386Sopenharmony_ci} 122cb93a386Sopenharmony_ciconst targetURL = `http://localhost:${options.port}/${hash}`; 123cb93a386Sopenharmony_ciconst viewPort = {width: 1000, height: 1000}; 124cb93a386Sopenharmony_ci 125cb93a386Sopenharmony_ci// Drive chrome to load the web page from the server we have running. 126cb93a386Sopenharmony_ciasync function driveBrowser() { 127cb93a386Sopenharmony_ci console.log('- Launching chrome for ' + options.input); 128cb93a386Sopenharmony_ci let browser; 129cb93a386Sopenharmony_ci let page; 130cb93a386Sopenharmony_ci const headless = !options.use_gpu; 131cb93a386Sopenharmony_ci let browser_args = [ 132cb93a386Sopenharmony_ci '--no-sandbox', 133cb93a386Sopenharmony_ci '--disable-setuid-sandbox', 134cb93a386Sopenharmony_ci '--window-size=' + viewPort.width + ',' + viewPort.height, 135cb93a386Sopenharmony_ci ]; 136cb93a386Sopenharmony_ci if (options.use_gpu) { 137cb93a386Sopenharmony_ci browser_args.push('--ignore-gpu-blacklist'); 138cb93a386Sopenharmony_ci browser_args.push('--ignore-gpu-blocklist'); 139cb93a386Sopenharmony_ci browser_args.push('--enable-gpu-rasterization'); 140cb93a386Sopenharmony_ci } 141cb93a386Sopenharmony_ci console.log("Running with headless: " + headless + " args: " + browser_args); 142cb93a386Sopenharmony_ci try { 143cb93a386Sopenharmony_ci browser = await puppeteer.launch({headless: headless, args: browser_args}); 144cb93a386Sopenharmony_ci page = await browser.newPage(); 145cb93a386Sopenharmony_ci await page.setViewport(viewPort); 146cb93a386Sopenharmony_ci } catch (e) { 147cb93a386Sopenharmony_ci console.log('Could not open the browser.', e); 148cb93a386Sopenharmony_ci process.exit(1); 149cb93a386Sopenharmony_ci } 150cb93a386Sopenharmony_ci console.log("Loading " + targetURL); 151cb93a386Sopenharmony_ci try { 152cb93a386Sopenharmony_ci // Start trace. 153cb93a386Sopenharmony_ci await page.tracing.start({ 154cb93a386Sopenharmony_ci path: options.output, 155cb93a386Sopenharmony_ci screenshots: false, 156cb93a386Sopenharmony_ci categories: ["blink", "cc", "gpu"] 157cb93a386Sopenharmony_ci }); 158cb93a386Sopenharmony_ci 159cb93a386Sopenharmony_ci await page.goto(targetURL, { 160cb93a386Sopenharmony_ci timeout: 60000, 161cb93a386Sopenharmony_ci waitUntil: 'networkidle0' 162cb93a386Sopenharmony_ci }); 163cb93a386Sopenharmony_ci 164cb93a386Sopenharmony_ci console.log('Waiting 90s for run to be done'); 165cb93a386Sopenharmony_ci await page.waitForFunction(`(window._skottieDone === true) || window._error`, { 166cb93a386Sopenharmony_ci timeout: 90000, 167cb93a386Sopenharmony_ci }); 168cb93a386Sopenharmony_ci 169cb93a386Sopenharmony_ci const err = await page.evaluate('window._error'); 170cb93a386Sopenharmony_ci if (err) { 171cb93a386Sopenharmony_ci console.log(`ERROR: ${err}`) 172cb93a386Sopenharmony_ci process.exit(1); 173cb93a386Sopenharmony_ci } 174cb93a386Sopenharmony_ci 175cb93a386Sopenharmony_ci // Stop Trace. 176cb93a386Sopenharmony_ci await page.tracing.stop(); 177cb93a386Sopenharmony_ci } catch(e) { 178cb93a386Sopenharmony_ci console.log('Timed out while loading or drawing. Either the JSON file was ' + 179cb93a386Sopenharmony_ci 'too big or hit a bug.', e); 180cb93a386Sopenharmony_ci await browser.close(); 181cb93a386Sopenharmony_ci process.exit(1); 182cb93a386Sopenharmony_ci } 183cb93a386Sopenharmony_ci 184cb93a386Sopenharmony_ci await browser.close(); 185cb93a386Sopenharmony_ci // Need to call exit() because the web server is still running. 186cb93a386Sopenharmony_ci process.exit(0); 187cb93a386Sopenharmony_ci} 188cb93a386Sopenharmony_ci 189cb93a386Sopenharmony_cidriveBrowser(); 190