[canvaskit] Add drawRect4f as example 'fast path' API

I added 3 tests, one using the drawRect API naively,
one using drawRect with a Malloc'd array, and one using
the drawRect4f.

rough local measurements:
 - [baseline with tip of tree code]: 50ms
 - naive drawRect: 40ms
 - drawRect with malloc: 28ms
 - drawRect4f: 27ms

I also tried the benchmarks locally with taking in paint
as a const reference. I did not see any changes, but that
could just be small sample size. I plan to land the code
as is for now, collect a bit of data in Perf and then try
landing the const reference stuff and see if we get
something measurable.

To aid this, I added in a helper list of tests to only run
some benchmarks easily.

Change-Id: I2d8c5296e93f05be45fc12059955fb9d9e339d83
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/315143
Reviewed-by: Kevin Lubick <kjlubick@google.com>
This commit is contained in:
Kevin Lubick 2020-09-03 08:31:52 -04:00
parent f8823b5726
commit a1c2117004
6 changed files with 98 additions and 4 deletions

View File

@ -48,6 +48,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- `CanvasKit.LTRBiRect` and `CanvasKit.XYWHiRect` as helpers to create SkIRects.
- `SkCanvas.drawRect4f` as a somewhat experimental way to have array-free APIs for clients that
already have their own representation of Rect. This is experimental because we don't know
if it's faster/better under real-world use and because we don't want to commit to having these
for all Rect APIs (and for similar types) until it has baked in a bit.
## [0.17.3] - 2020-08-05

View File

@ -1053,6 +1053,12 @@ EMSCRIPTEN_BINDINGS(Skia) {
const SkRect* rect = reinterpret_cast<const SkRect*>(fPtr);
self.drawRect(*rect, paint);
}))
.function("drawRect4f", optional_override([](SkCanvas& self, SkScalar left, SkScalar top,
SkScalar right, SkScalar bottom,
const SkPaint paint)->void {
const SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
self.drawRect(rect, paint);
}))
.function("_drawShadow", optional_override([](SkCanvas& self, const SkPath& path,
const SkPoint3& zPlaneParams,
const SkPoint3& lightPos, SkScalar lightRadius,

View File

@ -209,6 +209,7 @@ var CanvasKit = {
drawParagraph: function() {},
drawPath: function() {},
drawPicture: function() {},
drawRect4f: function() {},
drawText: function() {},
drawTextBlob: function() {},
drawVertices: function() {},

View File

@ -259,7 +259,7 @@ describe('Canvas Behavior', () => {
paint.setColorFilter(lerp)
canvas.drawRect(CanvasKit.LTRBRect(50, 10, 100, 60), paint);
paint.setColorFilter(red)
canvas.drawRect(CanvasKit.LTRBRect(90, 10, 140, 60), paint);
canvas.drawRect4f(90, 10, 140, 60, paint);
const r = CanvasKit.SkColorMatrix.rotated(0, .707, -.707);
const b = CanvasKit.SkColorMatrix.rotated(2, .5, .866);

View File

@ -79,15 +79,23 @@
document.getElementById('start_bench').addEventListener('click', async () => {
window._perfData = {};
// canvas_perf.js should have defined a single object called tests that is an array of
// objects having:
// canvas_perf.js should have defined an array called tests whose objects have:
// setup: A function called once before testing begins, it is expected to make its
// own canvas and put it in ctx.
// test: A function called to draw one frame
// teardown: A function called after testing finishes
// description: A human readable description
// perfkey: A key used to save the results in perf.skia.org.
for (const t of tests) {
//
// For quick local bench testing, there is also an array called onlytests. This way
// a developer can replace tests.push with onlytests.push to just run one or two
// performance benchmarks they care about.
let testsToRun = tests;
if (onlytests.length) {
testsToRun = onlytests;
}
for (const t of testsToRun) {
let ctx = {
'surface': surface,
'files': externalFiles,

View File

@ -1,3 +1,4 @@
const onlytests = [];
const tests = [];
// In all tests, the canvas is 600 by 600 px.
// tests should NOT call ctx.surface.flush()
@ -93,6 +94,80 @@ tests.push({
perfKey: 'canvas_drawRRect',
});
tests.push({
description: 'Draw 10K colored rects',
setup: function(CanvasKit, ctx) {
ctx.canvas = ctx.surface.getCanvas();
ctx.paint = new CanvasKit.SkPaint();
ctx.paint.setAntiAlias(true);
ctx.paint.setStyle(CanvasKit.PaintStyle.Fill);
},
test: function(CanvasKit, ctx) {
for (let i=0; i<10000; i++) {
const x = Math.random()*550;
const y = Math.random()*550;
ctx.paint.setColor(randomColorTwo(CanvasKit, 1, 2));
ctx.canvas.drawRect(CanvasKit.LTRBRect(x, y, x+50, y+50), ctx.paint);
}
},
teardown: function(CanvasKit, ctx) {
ctx.paint.delete();
},
perfKey: 'canvas_drawRect',
});
tests.push({
description: "Draw 10K colored rects with malloc'd rect",
setup: function(CanvasKit, ctx) {
ctx.canvas = ctx.surface.getCanvas();
ctx.paint = new CanvasKit.SkPaint();
ctx.paint.setAntiAlias(true);
ctx.paint.setStyle(CanvasKit.PaintStyle.Fill);
ctx.rect = CanvasKit.Malloc(Float32Array, 4);
},
test: function(CanvasKit, ctx) {
for (let i=0; i<10000; i++) {
ctx.paint.setColor(randomColorTwo(CanvasKit, 1, 2));
const ta = ctx.rect.toTypedArray();
ta[0] = Math.random()*550; // x
ta[1] = Math.random()*550; // y
ta[2] = ta[0] + 50;
ta[3] = ta[1] + 50;
ctx.canvas.drawRect(ta, ctx.paint);
}
},
teardown: function(CanvasKit, ctx) {
ctx.paint.delete();
CanvasKit.Free(ctx.rect);
},
perfKey: 'canvas_drawRect_malloc',
});
tests.push({
description: 'Draw 10K colored rects using 4 float API',
setup: function(CanvasKit, ctx) {
ctx.canvas = ctx.surface.getCanvas();
ctx.paint = new CanvasKit.SkPaint();
ctx.paint.setAntiAlias(true);
ctx.paint.setStyle(CanvasKit.PaintStyle.Fill);
},
test: function(CanvasKit, ctx) {
for (let i=0; i<10000; i++) {
const x = Math.random()*550;
const y = Math.random()*550;
ctx.paint.setColor(randomColorTwo(CanvasKit, 1, 2));
ctx.canvas.drawRect4f(x, y, x+50, y+50, ctx.paint);
}
},
teardown: function(CanvasKit, ctx) {
ctx.paint.delete();
},
perfKey: 'canvas_drawRect4f',
});
tests.push({
description: 'Compute tonal colors',
setup: function(CanvasKit, ctx) {},