1cb93a386Sopenharmony_ci<!-- This benchmark aims to measure performance degredation related to 2cb93a386Sopenharmony_cimoving a complex path. May be related to caching an alpha mask of the path at 3cb93a386Sopenharmony_cisubpixel coordinates i.e. (25.234, 43.119) instead of (25, 43). 4cb93a386Sopenharmony_ciAs a consequence the cache may get full very quickly. Effect of paint opacity 5cb93a386Sopenharmony_ciand rotation transformations on performance can also be tested using the query param options. 6cb93a386Sopenharmony_ci 7cb93a386Sopenharmony_ciAvailable query param options: 8cb93a386Sopenharmony_ci - snap: Round all path translations to the nearest integer. This means subpixel coordinate. 9cb93a386Sopenharmony_ci translations will not be used. Only has an effect when the translating option is used. 10cb93a386Sopenharmony_ci - opacity: Use a transparent color to fill the path. If this option is 11cb93a386Sopenharmony_ci not included then opaque black is used. 12cb93a386Sopenharmony_ci - translate: The path will be randomly translated every frame. 13cb93a386Sopenharmony_ci - rotate: The path will be randomly rotated every frame. 14cb93a386Sopenharmony_ci--> 15cb93a386Sopenharmony_ci<!DOCTYPE html> 16cb93a386Sopenharmony_ci<html> 17cb93a386Sopenharmony_ci<head> 18cb93a386Sopenharmony_ci <title>Complex Path translation Perf</title> 19cb93a386Sopenharmony_ci <meta charset="utf-8" /> 20cb93a386Sopenharmony_ci <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> 21cb93a386Sopenharmony_ci <meta name="viewport" content="width=device-width, initial-scale=1.0"> 22cb93a386Sopenharmony_ci <script src="/static/canvaskit.js" type="text/javascript" charset="utf-8"></script> 23cb93a386Sopenharmony_ci <style type="text/css" media="screen"> 24cb93a386Sopenharmony_ci body { 25cb93a386Sopenharmony_ci margin: 0; 26cb93a386Sopenharmony_ci padding: 0; 27cb93a386Sopenharmony_ci } 28cb93a386Sopenharmony_ci #test-svg { 29cb93a386Sopenharmony_ci height: 0; 30cb93a386Sopenharmony_ci width: 0; 31cb93a386Sopenharmony_ci } 32cb93a386Sopenharmony_ci #complex-path { 33cb93a386Sopenharmony_ci height: 1000px; 34cb93a386Sopenharmony_ci width: 1000px; 35cb93a386Sopenharmony_ci } 36cb93a386Sopenharmony_ci </style> 37cb93a386Sopenharmony_ci</head> 38cb93a386Sopenharmony_ci<body> 39cb93a386Sopenharmony_ci <!-- Arbitrary svg for testing. Source: https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/gallardo.svg--> 40cb93a386Sopenharmony_ci <object type="image/svg+xml" data="/static/assets/car.svg" id="test-svg"> 41cb93a386Sopenharmony_ci Car image 42cb93a386Sopenharmony_ci </object> 43cb93a386Sopenharmony_ci 44cb93a386Sopenharmony_ci <main> 45cb93a386Sopenharmony_ci <button id="start_bench">Start Benchmark</button> 46cb93a386Sopenharmony_ci <br> 47cb93a386Sopenharmony_ci <canvas id=complex-path width=1000 height=1000></canvas> 48cb93a386Sopenharmony_ci </main> 49cb93a386Sopenharmony_ci <script type="text/javascript" charset="utf-8"> 50cb93a386Sopenharmony_ci const urlSearchParams = new URLSearchParams(window.location.search); 51cb93a386Sopenharmony_ci 52cb93a386Sopenharmony_ci // We sample MAX_FRAMES or until MAX_SAMPLE_SECONDS has elapsed. 53cb93a386Sopenharmony_ci const MAX_FRAMES = 60 * 30; // ~30s at 60fps 54cb93a386Sopenharmony_ci const MAX_SAMPLE_MS = 30 * 1000; // in case something takes a while, stop after 30 seconds. 55cb93a386Sopenharmony_ci const TRANSPARENT_PINK = new Float32Array([1,0,1,0.1]); 56cb93a386Sopenharmony_ci 57cb93a386Sopenharmony_ci const svgObjectElement = document.getElementById('test-svg'); 58cb93a386Sopenharmony_ci svgObjectElement.addEventListener('load', () => { 59cb93a386Sopenharmony_ci CanvasKitInit({ 60cb93a386Sopenharmony_ci locateFile: (file) => '/static/' + file, 61cb93a386Sopenharmony_ci }).then(run); 62cb93a386Sopenharmony_ci }); 63cb93a386Sopenharmony_ci 64cb93a386Sopenharmony_ci function run(CanvasKit) { 65cb93a386Sopenharmony_ci 66cb93a386Sopenharmony_ci const surface = getSurface(CanvasKit); 67cb93a386Sopenharmony_ci if (!surface) { 68cb93a386Sopenharmony_ci console.error('Could not make surface', window._error); 69cb93a386Sopenharmony_ci return; 70cb93a386Sopenharmony_ci } 71cb93a386Sopenharmony_ci const skcanvas = surface.getCanvas(); 72cb93a386Sopenharmony_ci const grContext = surface.grContext; 73cb93a386Sopenharmony_ci 74cb93a386Sopenharmony_ci document.getElementById('start_bench').addEventListener('click', () => { 75cb93a386Sopenharmony_ci // Initialize drawing related objects 76cb93a386Sopenharmony_ci const svgElement = svgObjectElement.contentDocument; 77cb93a386Sopenharmony_ci const svgPathAndFillColorPairs = svgToPathAndFillColorPairs(svgElement, CanvasKit); 78cb93a386Sopenharmony_ci 79cb93a386Sopenharmony_ci const paint = new CanvasKit.Paint(); 80cb93a386Sopenharmony_ci paint.setAntiAlias(true); 81cb93a386Sopenharmony_ci paint.setStyle(CanvasKit.PaintStyle.Fill); 82cb93a386Sopenharmony_ci let paintColor = CanvasKit.BLACK; 83cb93a386Sopenharmony_ci 84cb93a386Sopenharmony_ci // Path is large, scale canvas so entire path is visible 85cb93a386Sopenharmony_ci skcanvas.scale(0.5, 0.5); 86cb93a386Sopenharmony_ci 87cb93a386Sopenharmony_ci // Initialize perf data 88cb93a386Sopenharmony_ci let currentFrameNumber = 0; 89cb93a386Sopenharmony_ci const frameTimesMs = new Float32Array(MAX_FRAMES); 90cb93a386Sopenharmony_ci let startTimeMs = performance.now(); 91cb93a386Sopenharmony_ci let previousFrameTimeMs = performance.now(); 92cb93a386Sopenharmony_ci 93cb93a386Sopenharmony_ci const resourceCacheUsageBytes = new Float32Array(MAX_FRAMES); 94cb93a386Sopenharmony_ci const usedJSHeapSizesBytes = new Float32Array(MAX_FRAMES); 95cb93a386Sopenharmony_ci 96cb93a386Sopenharmony_ci function drawFrame() { 97cb93a386Sopenharmony_ci // Draw complex path with random translations and rotations. 98cb93a386Sopenharmony_ci let randomHorizontalTranslation = 0; 99cb93a386Sopenharmony_ci let randomVerticalTranslation = 0; 100cb93a386Sopenharmony_ci let randomRotation = 0; 101cb93a386Sopenharmony_ci 102cb93a386Sopenharmony_ci if (urlSearchParams.has('translate')) { 103cb93a386Sopenharmony_ci randomHorizontalTranslation = Math.random() * 50 - 25; 104cb93a386Sopenharmony_ci randomVerticalTranslation = Math.random() * 50 - 25; 105cb93a386Sopenharmony_ci } 106cb93a386Sopenharmony_ci if (urlSearchParams.has('snap')) { 107cb93a386Sopenharmony_ci randomHorizontalTranslation = Math.round(randomHorizontalTranslation); 108cb93a386Sopenharmony_ci randomVerticalTranslation = Math.round(randomVerticalTranslation); 109cb93a386Sopenharmony_ci } 110cb93a386Sopenharmony_ci if (urlSearchParams.has('opacity')) { 111cb93a386Sopenharmony_ci paintColor = TRANSPARENT_PINK; 112cb93a386Sopenharmony_ci } 113cb93a386Sopenharmony_ci if (urlSearchParams.has('rotate')) { 114cb93a386Sopenharmony_ci randomRotation = (Math.random() - 0.5) / 20; 115cb93a386Sopenharmony_ci } 116cb93a386Sopenharmony_ci 117cb93a386Sopenharmony_ci skcanvas.clear(CanvasKit.WHITE); 118cb93a386Sopenharmony_ci for (const [path, color] of svgPathAndFillColorPairs) { 119cb93a386Sopenharmony_ci path.transform([Math.cos(randomRotation), -Math.sin(randomRotation), randomHorizontalTranslation, 120cb93a386Sopenharmony_ci Math.sin(randomRotation), Math.cos(randomRotation), randomVerticalTranslation, 121cb93a386Sopenharmony_ci 0, 0, 1 ]); 122cb93a386Sopenharmony_ci paint.setColor(paintColor); 123cb93a386Sopenharmony_ci skcanvas.drawPath(path, paint); 124cb93a386Sopenharmony_ci } 125cb93a386Sopenharmony_ci surface.flush(); 126cb93a386Sopenharmony_ci 127cb93a386Sopenharmony_ci // Record perf data: measure frame times, memory usage 128cb93a386Sopenharmony_ci const currentFrameTimeMs = performance.now(); 129cb93a386Sopenharmony_ci frameTimesMs[currentFrameNumber] = currentFrameTimeMs - previousFrameTimeMs; 130cb93a386Sopenharmony_ci previousFrameTimeMs = currentFrameTimeMs; 131cb93a386Sopenharmony_ci 132cb93a386Sopenharmony_ci resourceCacheUsageBytes[currentFrameNumber] = grContext.getResourceCacheUsageBytes(); 133cb93a386Sopenharmony_ci usedJSHeapSizesBytes[currentFrameNumber] = window.performance.memory.totalJSHeapSize; 134cb93a386Sopenharmony_ci currentFrameNumber++; 135cb93a386Sopenharmony_ci 136cb93a386Sopenharmony_ci const timeSinceStart = performance.now() - startTimeMs; 137cb93a386Sopenharmony_ci if (currentFrameNumber >= MAX_FRAMES || timeSinceStart >= MAX_SAMPLE_MS) { 138cb93a386Sopenharmony_ci window._perfData = { 139cb93a386Sopenharmony_ci frames_ms: Array.from(frameTimesMs).slice(0, currentFrameNumber), 140cb93a386Sopenharmony_ci resourceCacheUsage_bytes: Array.from(resourceCacheUsageBytes).slice(0, currentFrameNumber), 141cb93a386Sopenharmony_ci usedJSHeapSizes_bytes: Array.from(usedJSHeapSizesBytes).slice(0, currentFrameNumber), 142cb93a386Sopenharmony_ci }; 143cb93a386Sopenharmony_ci window._perfDone = true; 144cb93a386Sopenharmony_ci return; 145cb93a386Sopenharmony_ci } 146cb93a386Sopenharmony_ci window.requestAnimationFrame(drawFrame); 147cb93a386Sopenharmony_ci } 148cb93a386Sopenharmony_ci window.requestAnimationFrame(drawFrame); 149cb93a386Sopenharmony_ci }); 150cb93a386Sopenharmony_ci 151cb93a386Sopenharmony_ci console.log('Perf is ready'); 152cb93a386Sopenharmony_ci window._perfReady = true; 153cb93a386Sopenharmony_ci } 154cb93a386Sopenharmony_ci 155cb93a386Sopenharmony_ci function svgToPathAndFillColorPairs(svgElement, CanvasKit) { 156cb93a386Sopenharmony_ci const pathElements = Array.from(svgElement.getElementsByTagName('path')); 157cb93a386Sopenharmony_ci return pathElements.map((path) => [ 158cb93a386Sopenharmony_ci CanvasKit.MakePathFromSVGString(path.getAttribute("d")), 159cb93a386Sopenharmony_ci CanvasKit.parseColorString(path.getAttribute("fill")??'#000000') 160cb93a386Sopenharmony_ci ]); 161cb93a386Sopenharmony_ci } 162cb93a386Sopenharmony_ci 163cb93a386Sopenharmony_ci function getSurface(CanvasKit) { 164cb93a386Sopenharmony_ci let surface; 165cb93a386Sopenharmony_ci if (window.location.hash.indexOf('gpu') !== -1) { 166cb93a386Sopenharmony_ci surface = CanvasKit.MakeWebGLCanvasSurface('complex-path'); 167cb93a386Sopenharmony_ci if (!surface) { 168cb93a386Sopenharmony_ci window._error = 'Could not make GPU surface'; 169cb93a386Sopenharmony_ci return null; 170cb93a386Sopenharmony_ci } 171cb93a386Sopenharmony_ci let c = document.getElementById('complex-path'); 172cb93a386Sopenharmony_ci // If CanvasKit was unable to instantiate a WebGL context, it will fallback 173cb93a386Sopenharmony_ci // to CPU and add a ck-replaced class to the canvas element. 174cb93a386Sopenharmony_ci if (c.classList.contains('ck-replaced')) { 175cb93a386Sopenharmony_ci window._error = 'fell back to CPU'; 176cb93a386Sopenharmony_ci return null; 177cb93a386Sopenharmony_ci } 178cb93a386Sopenharmony_ci } else { 179cb93a386Sopenharmony_ci surface = CanvasKit.MakeSWCanvasSurface('complex-path'); 180cb93a386Sopenharmony_ci if (!surface) { 181cb93a386Sopenharmony_ci window._error = 'Could not make CPU surface'; 182cb93a386Sopenharmony_ci return null; 183cb93a386Sopenharmony_ci } 184cb93a386Sopenharmony_ci } 185cb93a386Sopenharmony_ci return surface; 186cb93a386Sopenharmony_ci } 187cb93a386Sopenharmony_ci </script> 188cb93a386Sopenharmony_ci</body> 189cb93a386Sopenharmony_ci</html> 190