2879619a29
CanvasKit.MakeImageFromEncoded, when used with Browser APIs for loading/decoding images. - `CanvasKit.MakeImageFromCanvasImageSource` takes either an HTMLImageElement, SVGImageElement, HTMLVideoElement, HTMLCanvasElement, ImageBitmap, or OffscreenCanvas and returns an SkImage. This function is an alternative to `CanvasKit.MakeImageFromEncoded` for creating SkImages when loading and decoding images. In the future, codesize of CanvasKit may be able to be reduced by removing image codecs in wasm, if browser APIs for decoding images are used along with `CanvasKit.MakeImageFromCanvasImageSource` instead of `CanvasKit.MakeImageFromEncoded`. - Three usage examples of `CanvasKit.MakeImageFromCanvasImageSource` in core.spec.ts. These examples use browser APIs to decode images including 2d canvas, bitmaprenderer canvas, HTMLImageElement and Blob. - Added support for asynchronous callbacks in perfs and tests. Here are notes on the image decoding approaches we tested and perfed in the process of finding ways to use Browser APIs to decode images: 1. pipeline: ArrayBuffer → ImageData → ctx.putImageData → context.getImageData → Uint8Array → CanvasKit.MakeImage ❌ Problem: ImageData constructor expects decoded bytes already. 2. interface.js - CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded (async function) pipeline: ArrayBuffer → Blob -> HTMLImageElement -> draw on Canvas2d -> context.getImageData → Uint8Array → CanvasKit.MakeImage ✅ Works ⏱ Performance: 3rd place (in my testing locally) 3. interface.js - CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded2 (async function) ArrayBuffer → Blob → ImageBitmap → draw on Canvas2d → context.getImageData → Uint8Array → CanvasKit.MakeImage ✅ Works ⏱ Performance: 2nd place (in my testing locally) 4. interface.js - CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded3 (async function) ArrayBuffer → Blob → ImageBitmap → draw on canvas 1 using bitmaprenderer context → draw canvas 1 on canvas 2 using drawImage → context2d.getImageData → Uint8Array → CanvasKit.MakeImage ✅ Works ⏱ Performance: 1st place (in my testing locally) - quite surprising, this in some ways seems to be a more roundabout way of CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded2, but it seems bitmaprenderer context is fairly fast. Bug: skia:10360 Change-Id: I6fe94b8196dfd1ad0d8929f04bb1697da537ca18 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/295390 Reviewed-by: Kevin Lubick <kjlubick@google.com>
127 lines
3.5 KiB
JavaScript
127 lines
3.5 KiB
JavaScript
const REPORT_URL = 'http://localhost:8081/report_perf_data'
|
|
// Set this to enforce that the perf server must be up.
|
|
// Typically used for debugging.
|
|
const fail_on_no_perf = false;
|
|
|
|
function benchmarkAndReport(benchName, setupFn, testFn, teardownFn) {
|
|
try {
|
|
let ctx = {};
|
|
// warmup 3 times (arbitrary choice)
|
|
setupFn(ctx);
|
|
testFn(ctx);
|
|
testFn(ctx);
|
|
testFn(ctx);
|
|
teardownFn(ctx);
|
|
|
|
ctx = {};
|
|
setupFn(ctx);
|
|
let start = Date.now();
|
|
let now = start;
|
|
times = 0;
|
|
// See how many times we can do it in 100ms (arbitrary choice)
|
|
while (now - start < 100) {
|
|
testFn(ctx);
|
|
now = Date.now();
|
|
times++;
|
|
}
|
|
|
|
teardownFn(ctx);
|
|
|
|
// Try to make it go for 2 seconds (arbitrarily chosen)
|
|
// Since the pre-try took 100ms, multiply by 20 to get
|
|
// approximate tries in 2s (unless now - start >> 100 ms)
|
|
let goalTimes = times * 20;
|
|
ctx = {};
|
|
setupFn(ctx);
|
|
times = 0;
|
|
start = Date.now();
|
|
while (times < goalTimes) {
|
|
testFn(ctx);
|
|
times++;
|
|
}
|
|
const end = Date.now();
|
|
teardownFn(ctx);
|
|
|
|
const us = (end - start) * 1000 / times;
|
|
console.log(benchName, `${us} microseconds`)
|
|
return _report(us, benchName);
|
|
} catch(e) {
|
|
console.error('caught error', e);
|
|
return Promise.reject(e);
|
|
}
|
|
}
|
|
|
|
// The same as benchmarkAndReport, except expects the third parameter, testFn, to return a promise
|
|
async function asyncBenchmarkAndReport(benchName, setupFn, testFn, teardownFn) {
|
|
try {
|
|
let ctx = {};
|
|
// warmup 3 times (arbitrary choice)
|
|
setupFn(ctx);
|
|
await testFn(ctx);
|
|
await testFn(ctx);
|
|
await testFn(ctx);
|
|
teardownFn(ctx);
|
|
|
|
ctx = {};
|
|
setupFn(ctx);
|
|
let start = Date.now();
|
|
let now = start;
|
|
times = 0;
|
|
// See how many times we can do it in 100ms (arbitrary choice)
|
|
while (now - start < 100) {
|
|
await testFn(ctx);
|
|
now = Date.now();
|
|
times++;
|
|
}
|
|
|
|
teardownFn(ctx);
|
|
|
|
// Try to make it go for 2 seconds (arbitrarily chosen)
|
|
// Since the pre-try took 100ms, multiply by 20 to get
|
|
// approximate tries in 2s (unless now - start >> 100 ms)
|
|
let goalTimes = times * 20;
|
|
ctx = {};
|
|
setupFn(ctx);
|
|
times = 0;
|
|
start = Date.now();
|
|
while (times < goalTimes) {
|
|
await testFn(ctx);
|
|
times++;
|
|
}
|
|
const end = Date.now();
|
|
teardownFn(ctx);
|
|
|
|
const us = (end - start) * 1000 / times;
|
|
console.log(benchName, `${us} microseconds`)
|
|
return _report(us, benchName);
|
|
} catch(e) {
|
|
console.error('caught error', e);
|
|
return Promise.reject(e);
|
|
}
|
|
}
|
|
|
|
|
|
function _report(microseconds, benchName) {
|
|
return fetch(REPORT_URL, {
|
|
method: 'POST',
|
|
mode: 'no-cors',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
'bench_name': benchName,
|
|
'time_us': microseconds,
|
|
})
|
|
}).then(() => console.log(`Successfully reported ${benchName} to perf aggregator`));
|
|
}
|
|
|
|
function reportError(done) {
|
|
return (e) => {
|
|
console.log("Error with fetching. Likely could not connect to aggegator server", e.message);
|
|
if (fail_on_no_perf) {
|
|
expect(e).toBeUndefined();
|
|
}
|
|
done();
|
|
};
|
|
}
|