skia2/tools/perf-canvaskit-puppeteer/benchmark.js
Kevin Lubick f611404816 [canvaskit] Remove the need for users to keep track of contexts.
We'll switch to the correct context when necessary (e.g. before
calls that talk to the GPU). This is achieved by adding in
calls at the JS layer to switch the context before making a call
that is known to talk to the GPU (e.g. draw calls on SkCanvas).

Another implementation that was considered was to add a C++
shim in GrGLInterface that would switch the context before
every call in the GPU - however, that seemed too difficult
and would add extra overhead if a single draw* call talks
to the GPU multiple times.

Bug: skia:12255
Change-Id: I96e4c6b41a5bfcc9913aeaca7ccb125358048ad3
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/432136
Reviewed-by: Brian Salomon <bsalomon@google.com>
2021-08-25 18:23:06 +00:00

107 lines
3.8 KiB
JavaScript

// Shared benchmarking functions such as surface creation, measurement, publishing to perf.skia.org
function getSurface(CanvasKit, webglversion) {
let surface;
if (window.location.hash.indexOf('gpu') !== -1) {
surface = CanvasKit.MakeWebGLCanvasSurface('anim', null /* colorspace */, {'majorVersion': webglversion});
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;
}
// Time the drawing and flushing of a frame drawing function on a given surface.
// drawFn is expected to be a zero arg function making draw calls to a canvas
// warmupFrames - 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.
// Stops after timeoutMillis if provided
// Teturns a promise that resolves with the dict of measurements.
function startTimingFrames(drawFn, surface, warmupFrames, maxFrames, timeoutMillis) {
return new Promise((resolve, reject) => {
const totalFrame = new Float32Array(maxFrames);
const withFlush = new Float32Array(maxFrames);
const withoutFlush = new Float32Array(maxFrames);
let warmUp = warmupFrames > 0;
let idx = -1;
let previousFrame;
function drawFrame() {
let start, afterDraw, end;
try {
start = performance.now();
drawFn();
afterDraw = performance.now();
surface.flush();
end = performance.now();
} catch (e) {
console.error(e);
window._error = e.stack || e.toString();
return;
}
if (warmUp) {
idx++;
if (idx >= warmupFrames) {
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 (!window._perfData) {
window._perfData = {};
}
if (idx >= withFlush.length) {
resolve({
// The total time elapsed between the same point during the drawing of each frame.
// This is the most relevant measurement for normal drawing tests.
'total_frame_ms': Array.from(totalFrame).slice(0, idx),
// The time taken to run the code under test and call surface.flush()
'with_flush_ms': Array.from(withFlush).slice(0, idx),
// The time taken to run the code under test
// This is the most relevant measurement for non-drawing tests such as matrix inversion.
'without_flush_ms': Array.from(withoutFlush).slice(0, idx),
});
return;
}
// We can fill out this frame's intermediate steps.
withFlush[idx] = end - start;
withoutFlush[idx] = afterDraw - start;
if (timeoutMillis && ((beginTest + timeoutMillis) < performance.now())) {
console.log(`test aborted due to timeout after ${idx} frames`);
reject(`test aborted due to timeout after ${idx} frames`);
return;
}
window.requestAnimationFrame(drawFrame);
}
const beginTest = performance.now();
window.requestAnimationFrame(drawFrame);
}); // new promise
}