/* * Copyright 2011 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "Benchmark.h" #include "SkCanvas.h" #include "SkColor.h" #include "SkPaint.h" #include "SkPicture.h" #include "SkPictureRecorder.h" #include "SkPoint.h" #include "SkRandom.h" #include "SkRect.h" #include "SkString.h" // This is designed to emulate about 4 screens of textual content class PicturePlaybackBench : public Benchmark { public: PicturePlaybackBench(const char name[]) { fName.printf("picture_playback_%s", name); fPictureWidth = SkIntToScalar(PICTURE_WIDTH); fPictureHeight = SkIntToScalar(PICTURE_HEIGHT); fTextSize = SkIntToScalar(TEXT_SIZE); } enum { PICTURE_WIDTH = 1000, PICTURE_HEIGHT = 4000, TEXT_SIZE = 10 }; protected: virtual const char* onGetName() { return fName.c_str(); } virtual void onDraw(const int loops, SkCanvas* canvas) { SkPictureRecorder recorder; SkCanvas* pCanvas = recorder.beginRecording(PICTURE_WIDTH, PICTURE_HEIGHT, NULL, 0); this->recordCanvas(pCanvas); SkAutoTUnref picture(recorder.endRecording()); const SkPoint translateDelta = getTranslateDelta(loops); for (int i = 0; i < loops; i++) { picture->playback(canvas); canvas->translate(translateDelta.fX, translateDelta.fY); } } virtual void recordCanvas(SkCanvas* canvas) = 0; virtual SkPoint getTranslateDelta(int N) { SkIPoint canvasSize = onGetSize(); return SkPoint::Make(SkIntToScalar((PICTURE_WIDTH - canvasSize.fX)/N), SkIntToScalar((PICTURE_HEIGHT- canvasSize.fY)/N)); } SkString fName; SkScalar fPictureWidth; SkScalar fPictureHeight; SkScalar fTextSize; private: typedef Benchmark INHERITED; }; class TextPlaybackBench : public PicturePlaybackBench { public: TextPlaybackBench() : INHERITED("drawText") { } protected: virtual void recordCanvas(SkCanvas* canvas) SK_OVERRIDE { SkPaint paint; paint.setTextSize(fTextSize); paint.setColor(SK_ColorBLACK); const char* text = "Hamburgefons"; size_t len = strlen(text); const SkScalar textWidth = paint.measureText(text, len); for (SkScalar x = 0; x < fPictureWidth; x += textWidth) { for (SkScalar y = 0; y < fPictureHeight; y += fTextSize) { canvas->drawText(text, len, x, y, paint); } } } private: typedef PicturePlaybackBench INHERITED; }; class PosTextPlaybackBench : public PicturePlaybackBench { public: PosTextPlaybackBench(bool drawPosH) : INHERITED(drawPosH ? "drawPosTextH" : "drawPosText") , fDrawPosH(drawPosH) { } protected: virtual void recordCanvas(SkCanvas* canvas) SK_OVERRIDE { SkPaint paint; paint.setTextSize(fTextSize); paint.setColor(SK_ColorBLACK); const char* text = "Hamburgefons"; size_t len = strlen(text); const SkScalar textWidth = paint.measureText(text, len); SkScalar* adv = new SkScalar[len]; paint.getTextWidths(text, len, adv); for (SkScalar x = 0; x < fPictureWidth; x += textWidth) { for (SkScalar y = 0; y < fPictureHeight; y += fTextSize) { SkPoint* pos = new SkPoint[len]; SkScalar advX = 0; for (size_t i = 0; i < len; i++) { if (fDrawPosH) pos[i].set(x + advX, y); else pos[i].set(x + advX, y + i); advX += adv[i]; } canvas->drawPosText(text, len, pos, paint); delete[] pos; } } delete[] adv; } private: bool fDrawPosH; typedef PicturePlaybackBench INHERITED; }; /////////////////////////////////////////////////////////////////////////////// DEF_BENCH( return new TextPlaybackBench(); ) DEF_BENCH( return new PosTextPlaybackBench(true); ) DEF_BENCH( return new PosTextPlaybackBench(false); ) // Chrome draws into small tiles with impl-side painting. // This benchmark measures the relative performance of our bounding-box hierarchies, // both when querying tiles perfectly and when not. enum BBH { kNone, kRTree, kTileGrid }; enum Mode { kTiled, kRandom }; class TiledPlaybackBench : public Benchmark { public: TiledPlaybackBench(BBH bbh, Mode mode) : fBBH(bbh), fMode(mode), fName("tiled_playback") { switch (fBBH) { case kNone: fName.append("_none" ); break; case kRTree: fName.append("_rtree" ); break; case kTileGrid: fName.append("_tilegrid"); break; } switch (fMode) { case kTiled: fName.append("_tiled" ); break; case kRandom: fName.append("_random"); break; } } virtual const char* onGetName() SK_OVERRIDE { return fName.c_str(); } virtual SkIPoint onGetSize() SK_OVERRIDE { return SkIPoint::Make(1024,1024); } virtual void onPreDraw() SK_OVERRIDE { SkTileGridFactory::TileGridInfo info = { { 256, 256 }, {0,0}, {0,0} }; SkAutoTDelete factory; switch (fBBH) { case kNone: break; case kRTree: factory.reset(new SkRTreeFactory); break; case kTileGrid: factory.reset(new SkTileGridFactory(info)); break; } SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(1024, 1024, factory); SkRandom rand; for (int i = 0; i < 10000; i++) { SkScalar x = rand.nextRangeScalar(0, 1024), y = rand.nextRangeScalar(0, 1024), w = rand.nextRangeScalar(0, 128), h = rand.nextRangeScalar(0, 128); SkPaint paint; paint.setColor(rand.nextU()); paint.setAlpha(0xFF); canvas->drawRect(SkRect::MakeXYWH(x,y,w,h), paint); } fPic.reset(recorder.endRecording()); } virtual void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE { for (int i = 0; i < loops; i++) { // This inner loop guarantees we make the same choices for all bench variants. SkRandom rand; for (int j = 0; j < 10; j++) { SkScalar x = 0, y = 0; switch (fMode) { case kTiled: x = SkScalar(256 * rand.nextULessThan(4)); y = SkScalar(256 * rand.nextULessThan(4)); break; case kRandom: x = rand.nextRangeScalar(0, 768); y = rand.nextRangeScalar(0, 768); break; } SkAutoCanvasRestore ar(canvas, true/*save now*/); canvas->clipRect(SkRect::MakeXYWH(x,y,256,256)); fPic->playback(canvas); } } } private: BBH fBBH; Mode fMode; SkString fName; SkAutoTDelete fPic; }; DEF_BENCH( return new TiledPlaybackBench(kNone, kRandom); ) DEF_BENCH( return new TiledPlaybackBench(kNone, kTiled ); ) DEF_BENCH( return new TiledPlaybackBench(kRTree, kRandom); ) DEF_BENCH( return new TiledPlaybackBench(kRTree, kTiled ); ) DEF_BENCH( return new TiledPlaybackBench(kTileGrid, kRandom); ) DEF_BENCH( return new TiledPlaybackBench(kTileGrid, kTiled ); )