51891392d8
For each skp in the corpus, we start a fresh instance of Chromium (via puppeteer), draw the skp and measure that time. This process is repeated a fixed amount of repetitions and the median, the average, and the std deviation is reported to perf (as well as the individual datapoints as an FYI). Importantly (and something we'll need to change about SkottieFrames), we measure the average time between frames after unlocking the framerate. This ensures we account for the time needed by the GPU to actually draw (flush() returns after the GPU has all the instructions, but not necessarily has been able to draw). This implementation is very similar to the SkottieFrames code; a notable deviation is the repetitions are handled outside of the html, i.e. a new chrome window per run. I explored using content_shell, but noticed that requires building Chromium, which our infrastructure is not set up to do well. Change-Id: I14fdbdc951604d3fdf06e81a4be7e614d0e53c03 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/295079 Commit-Queue: Kevin Lubick <kjlubick@google.com> Reviewed-by: Nathaniel Nifong <nifong@google.com> Reviewed-by: Eric Boren <borenet@google.com>
158 lines
5.6 KiB
HTML
158 lines
5.6 KiB
HTML
<!-- This benchmark aims to accurately measure the time it takes for CanvasKit to render
|
|
an SKP from our test corpus. It is very careful to measure the time between frames. This form
|
|
of measurement makes sure we are capturing the GPU draw time. CanvasKit.flush() returns after
|
|
it has sent all the instructions to the GPU, but we don't know the GPU is done until the next
|
|
frame is requested. Thus, we need to keep track of the time between frames in order to
|
|
accurately calculate draw time. Keeping track of the drawPicture and drawPicture+flush is still
|
|
useful for telling us how much time we are spending in WASM land and if our drawing is CPU
|
|
bound or GPU bound. If total_frame_ms is close to with_flush_ms, we are CPU bound; if
|
|
total_frame_ms >> with_flush_ms, we are GPU bound.
|
|
-->
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>CanvasKit SKP Perf</title>
|
|
<meta charset="utf-8" />
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<script src="/static/canvaskit.js" type="text/javascript" charset="utf-8"></script>
|
|
<style type="text/css" media="screen">
|
|
body {
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<main>
|
|
<button id="start_bench">Start Benchmark</button>
|
|
<br>
|
|
<canvas id=anim width=1000 height=1000 style="height: 1000px; width: 1000px;"></canvas>
|
|
</main>
|
|
<script type="text/javascript" charset="utf-8">
|
|
const WIDTH = 1000;
|
|
const HEIGHT = 1000;
|
|
// Run this number of frames before starting to measure things. This allows us to make sure
|
|
// the noise from the first few renders (e.g shader compilation, caches) is removed from the
|
|
// data we capture.
|
|
const WARM_UP_FRAMES = 10;
|
|
const MAX_FRAMES = 201; // This should be sufficient to have low noise.
|
|
|
|
const SKP_PATH = '/static/test.skp';
|
|
(function() {
|
|
|
|
const loadKit = CanvasKitInit({
|
|
locateFile: (file) => '/static/' + file,
|
|
});
|
|
|
|
const loadSKP = fetch(SKP_PATH).then((resp) => {
|
|
return resp.arrayBuffer();
|
|
});
|
|
|
|
Promise.all([loadKit, loadSKP]).then((values) => {
|
|
const [CanvasKit, skpBytes] = values;
|
|
const loadStart = performance.now();
|
|
const skp = CanvasKit.MakeSkPicture(skpBytes);
|
|
const loadTime = performance.now() - loadStart;
|
|
console.log('loaded skp', skp, loadTime);
|
|
if (!skp) {
|
|
window._error = 'could not read skp';
|
|
return;
|
|
}
|
|
|
|
const surface = getSurface(CanvasKit);
|
|
if (!surface) {
|
|
console.error('Could not make surface', window._error);
|
|
return;
|
|
}
|
|
const canvas = surface.getCanvas();
|
|
|
|
document.getElementById('start_bench').addEventListener('click', () => {
|
|
const clearColor = CanvasKit.WHITE;
|
|
const totalFrame = new Float32Array(MAX_FRAMES);
|
|
const withFlush = new Float32Array(MAX_FRAMES);
|
|
const withoutFlush = new Float32Array(MAX_FRAMES);
|
|
let warmUp = true;
|
|
let idx = 0;
|
|
|
|
let previousFrame;
|
|
|
|
function drawFrame() {
|
|
const start = performance.now();
|
|
canvas.clear(clearColor);
|
|
canvas.drawPicture(skp);
|
|
const afterDraw = performance.now();
|
|
surface.flush();
|
|
const end = performance.now();
|
|
|
|
if (warmUp) {
|
|
idx++;
|
|
if (idx >= WARM_UP_FRAMES) {
|
|
idx = -1;
|
|
warmUp = false;
|
|
}
|
|
window.requestAnimationFrame(drawFrame);
|
|
return;
|
|
}
|
|
if (idx >= 0) {
|
|
// Fill out total time the previous frame took to draw.
|
|
totalFrame[idx] = start - previousFrame;
|
|
}
|
|
previousFrame = start;
|
|
idx++;
|
|
// If we have maxed out the frames we are measuring or have completed the animation,
|
|
// we stop benchmarking.
|
|
if (idx >= withFlush.length) {
|
|
window._perfData = {
|
|
total_frame_ms: Array.from(totalFrame).slice(0, idx),
|
|
with_flush_ms: Array.from(withFlush).slice(0, idx),
|
|
without_flush_ms: Array.from(withoutFlush).slice(0, idx),
|
|
skp_load_ms: loadTime,
|
|
};
|
|
window._perfDone = true;
|
|
return;
|
|
}
|
|
|
|
// We can fill out this frame's intermediate steps.
|
|
withFlush[idx] = end - start;
|
|
withoutFlush[idx] = afterDraw - start;
|
|
window.requestAnimationFrame(drawFrame);
|
|
}
|
|
window.requestAnimationFrame(drawFrame);
|
|
});
|
|
console.log('Perf is ready');
|
|
window._perfReady = true;
|
|
});
|
|
}
|
|
)();
|
|
|
|
// TODO(kjlubick) make this configurable to return a WEBGL 1 or WEBGL 2 surface.
|
|
function getSurface(CanvasKit) {
|
|
let surface;
|
|
if (window.location.hash.indexOf('gpu') !== -1) {
|
|
surface = CanvasKit.MakeWebGLCanvasSurface('anim');
|
|
if (!surface) {
|
|
window._error = 'Could not make GPU surface';
|
|
return null;
|
|
}
|
|
let c = document.getElementById('anim');
|
|
// If CanvasKit was unable to instantiate a WebGL context, it will fallback
|
|
// to CPU and add a ck-replaced class to the canvas element.
|
|
if (c.classList.contains('ck-replaced')) {
|
|
window._error = 'fell back to CPU';
|
|
return null;
|
|
}
|
|
} else {
|
|
surface = CanvasKit.MakeSWCanvasSurface('anim');
|
|
if (!surface) {
|
|
window._error = 'Could not make CPU surface';
|
|
return null;
|
|
}
|
|
}
|
|
return surface;
|
|
}
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|