Provides output like the following:
running bench [1236 12045] androidpolice.skp
tile_256x256: tile [0,0] out of [5,48]: msecs = 1.00
tile_256x256: tile [1,0] out of [5,48]: msecs = 1.50
tile_256x256: tile [2,0] out of [5,48]: msecs = 1.00
tile_256x256: tile [3,0] out of [5,48]: msecs = 1.50
tile_256x256: tile [4,0] out of [5,48]: msecs = 2.50
tile_256x256: tile [0,1] out of [5,48]: msecs = 2.00
tile_256x256: tile [1,1] out of [5,48]: msecs = 3.50
tile_256x256: tile [2,1] out of [5,48]: msecs = 3.50
tile_256x256: tile [3,1] out of [5,48]: msecs = 6.00
tile_256x256: tile [4,1] out of [5,48]: msecs = 2.50
tile_256x256: tile [0,2] out of [5,48]: msecs = 2.00
BUG=https://code.google.com/p/skia/issues/detail?id=1016
Review URL: https://codereview.appspot.com/6937047
git-svn-id: http://skia.googlecode.com/svn/trunk@6805
2bbb7eff-a529-9590-31e7-
b0007b416f81
, fShowCpuTime(true)
, fShowTruncatedCpuTime(false)
, fShowGpuTime(false)
+, fTimeIndividualTiles(false)
{}
PictureBenchmark::~PictureBenchmark() {
#if SK_SUPPORT_GPU
if (fRenderer != NULL && fRenderer->isUsingGpuDevice()) {
return SkNEW_ARGS(BenchTimer, (fRenderer->getGLContext()));
- } else {
- return SkNEW_ARGS(BenchTimer, (NULL));
}
-#else
- return SkNEW_ARGS(BenchTimer, (NULL));
#endif
+ return SkNEW_ARGS(BenchTimer, (NULL));
}
void PictureBenchmark::logProgress(const char msg[]) {
usingGpu = fRenderer->isUsingGpuDevice();
#endif
- TimerData timerData(fRenderer->getPerIterTimeFormat(), fRenderer->getNormalTimeFormat());
- for (int i = 0; i < fRepeats; ++i) {
- fRenderer->setup();
-
- timer->start();
- fRenderer->render(NULL);
- timer->truncatedEnd();
-
- // Finishes gl context
- fRenderer->resetState();
- timer->end();
-
- timerData.appendTimes(timer, fRepeats - 1 == i);
+ if (fTimeIndividualTiles) {
+ TiledPictureRenderer* tiledRenderer = fRenderer->getTiledRenderer();
+ SkASSERT(tiledRenderer);
+ if (NULL == tiledRenderer) {
+ return;
+ }
+ int xTiles, yTiles;
+ if (!tiledRenderer->tileDimensions(xTiles, yTiles)) {
+ return;
+ }
+
+ // Insert a newline so that each tile is reported on its own line (separate from the line
+ // that describes the skp being run).
+ this->logProgress("\n");
+
+ int x, y;
+ while (tiledRenderer->nextTile(x, y)) {
+ TimerData timerData(tiledRenderer->getPerIterTimeFormat(),
+ tiledRenderer->getNormalTimeFormat());
+ for (int i = 0; i < fRepeats; ++i) {
+ timer->start();
+ tiledRenderer->drawCurrentTile();
+ timer->truncatedEnd();
+ tiledRenderer->resetState();
+ timer->end();
+ timerData.appendTimes(timer, fRepeats - 1 == i);
+ }
+ SkString configName = tiledRenderer->getConfigName();
+ configName.appendf(": tile [%i,%i] out of [%i,%i]", x, y, xTiles, yTiles);
+ SkString result = timerData.getResult(fLogPerIter, fPrintMin, fRepeats,
+ configName.c_str(), fShowWallTime,
+ fShowTruncatedWallTime, fShowCpuTime,
+ fShowTruncatedCpuTime, usingGpu && fShowGpuTime);
+ result.append("\n");
+ this->logProgress(result.c_str());
+ }
+ } else {
+ TimerData timerData(fRenderer->getPerIterTimeFormat(), fRenderer->getNormalTimeFormat());
+ for (int i = 0; i < fRepeats; ++i) {
+ fRenderer->setup();
+
+ timer->start();
+ fRenderer->render(NULL);
+ timer->truncatedEnd();
+
+ // Finishes gl context
+ fRenderer->resetState();
+ timer->end();
+
+ timerData.appendTimes(timer, fRepeats - 1 == i);
+ }
+
+ SkString configName = fRenderer->getConfigName();
+ SkString result = timerData.getResult(fLogPerIter, fPrintMin, fRepeats,
+ configName.c_str(), fShowWallTime,
+ fShowTruncatedWallTime, fShowCpuTime,
+ fShowTruncatedCpuTime, usingGpu && fShowGpuTime);
+ result.append("\n");
+ this->logProgress(result.c_str());
}
- SkString configName = fRenderer->getConfigName();
- SkString result = timerData.getResult(fLogPerIter, fPrintMin, fRepeats,
- configName.c_str(), fShowWallTime, fShowTruncatedWallTime,
- fShowCpuTime, fShowTruncatedCpuTime,
- usingGpu && fShowGpuTime);
- result.append("\n");
- this->logProgress(result.c_str());
-
fRenderer->end();
SkDELETE(timer);
}
~PictureBenchmark();
+ /**
+ * Draw the provided SkPicture fRepeats times while collecting timing data, and log the output
+ * via fLogger.
+ */
void run(SkPicture* pict);
void setRepeats(int repeats) {
fRepeats = repeats;
}
+ /**
+ * If true, tells run to log separate timing data for each individual tile. Each tile will be
+ * drawn fRepeats times. Requires the PictureRenderer set by setRenderer to be a
+ * TiledPictureRenderer.
+ */
+ void setTimeIndividualTiles(bool indiv) { fTimeIndividualTiles = true; }
+
+ bool timeIndividualTiles() { return fTimeIndividualTiles; }
+
PictureRenderer* setRenderer(PictureRenderer*);
void setDeviceType(PictureRenderer::SkDeviceTypes deviceType) {
bool fShowCpuTime;
bool fShowTruncatedCpuTime;
bool fShowGpuTime;
+ bool fTimeIndividualTiles;
void logProgress(const char msg[]);
, fTileHeight(kDefaultTileHeight)
, fTileWidthPercentage(0.0)
, fTileHeightPercentage(0.0)
- , fTileMinPowerOf2Width(0) { }
+ , fTileMinPowerOf2Width(0)
+ , fCurrentTileOffset(-1)
+ , fTilesX(0)
+ , fTilesY(0) { }
void TiledPictureRenderer::init(SkPicture* pict) {
SkASSERT(pict != NULL);
} else {
this->setupTiles();
}
+ fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight));
+ // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the
+ // first call to drawCurrentTile.
+ fCurrentTileOffset = -1;
}
void TiledPictureRenderer::end() {
const int width = this->getViewWidth();
const int height = this->getViewHeight();
+ fTilesX = fTilesY = 0;
for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
+ fTilesY++;
for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) {
+ if (0 == tile_y_start) {
+ // Only count tiles in the X direction on the first pass.
+ fTilesX++;
+ }
*fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
SkIntToScalar(tile_y_start),
SkIntToScalar(fTileWidth),
}
}
+bool TiledPictureRenderer::tileDimensions(int &x, int &y) {
+ if (fTileRects.count() == 0 || NULL == fPicture) {
+ return false;
+ }
+ x = fTilesX;
+ y = fTilesY;
+ return true;
+}
+
// The goal of the powers of two tiles is to minimize the amount of wasted tile
// space in the width-wise direction and then minimize the number of tiles. The
// constraints are that every tile must have a pixel width that is a power of
int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(width)));
int largest_possible_tile_size = 1 << num_bits;
+ fTilesX = fTilesY = 0;
// The tile height is constant for a particular picture.
for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
+ fTilesY++;
int tile_x_start = 0;
int current_width = largest_possible_tile_size;
// Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
while (current_width >= fTileMinPowerOf2Width) {
// It is very important this is a bitwise AND.
if (current_width & rounded_value) {
+ if (0 == tile_y_start) {
+ // Only count tiles in the X direction on the first pass.
+ fTilesX++;
+ }
*fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
SkIntToScalar(tile_y_start),
SkIntToScalar(current_width),
///////////////////////////////////////////////////////////////////////////////////////////////
+bool TiledPictureRenderer::nextTile(int &i, int &j) {
+ if (++fCurrentTileOffset < fTileRects.count()) {
+ i = fCurrentTileOffset % fTilesX;
+ j = fCurrentTileOffset / fTilesX;
+ return true;
+ }
+ return false;
+}
+
+void TiledPictureRenderer::drawCurrentTile() {
+ SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count());
+ DrawTileToCanvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture);
+}
+
bool TiledPictureRenderer::render(const SkString* path) {
SkASSERT(fPicture != NULL);
if (NULL == fPicture) {
return false;
}
- // Reuse one canvas for all tiles.
- SkCanvas* canvas = this->setupCanvas(fTileWidth, fTileHeight);
- SkAutoUnref aur(canvas);
-
bool success = true;
for (int i = 0; i < fTileRects.count(); ++i) {
- DrawTileToCanvas(canvas, fTileRects[i], fPicture);
+ DrawTileToCanvas(fCanvas, fTileRects[i], fPicture);
if (NULL != path) {
- success &= writeAppendNumber(canvas, path, i);
+ success &= writeAppendNumber(fCanvas, path, i);
}
}
return success;
namespace sk_tools {
+class TiledPictureRenderer;
+
class PictureRenderer : public SkRefCnt {
+
public:
enum SkDeviceTypes {
kBitmap_DeviceType,
*/
virtual void end();
+ /**
+ * If this PictureRenderer is actually a TiledPictureRender, return a pointer to this as a
+ * TiledPictureRender so its methods can be called.
+ */
+ virtual TiledPictureRenderer* getTiledRenderer() { return NULL; }
+
void resetState();
void setDeviceType(SkDeviceTypes deviceType) {
return fTileMinPowerOf2Width;
}
+ virtual TiledPictureRenderer* getTiledRenderer() SK_OVERRIDE { return this; }
+
+ /**
+ * Report the number of tiles in the x and y directions. Must not be called before init.
+ * @param x Output parameter identifying the number of tiles in the x direction.
+ * @param y Output parameter identifying the number of tiles in the y direction.
+ * @return True if the tiles have been set up, and x and y are meaningful. If false, x and y are
+ * unmodified.
+ */
+ bool tileDimensions(int& x, int&y);
+
+ /**
+ * Move to the next tile and return its indices. Must be called before calling drawCurrentTile
+ * for the first time.
+ * @param i Output parameter identifying the column of the next tile to be drawn on the next
+ * call to drawNextTile.
+ * @param j Output parameter identifying the row of the next tile to be drawn on the next call
+ * to drawNextTile.
+ * @param True if the tiles have been created and the next tile to be drawn by drawCurrentTile
+ * is within the range of tiles. If false, i and j are unmodified.
+ */
+ bool nextTile(int& i, int& j);
+
+ /**
+ * Render one tile. This will draw the same tile each time it is called until nextTile is
+ * called. The tile rendered will depend on how many calls have been made to nextTile.
+ * It is an error to call this without first calling nextTile, or if nextTile returns false.
+ */
+ void drawCurrentTile();
+
protected:
SkTDArray<SkRect> fTileRects;
virtual SkString getConfigNameInternal() SK_OVERRIDE;
private:
- int fTileWidth;
- int fTileHeight;
- double fTileWidthPercentage;
- double fTileHeightPercentage;
- int fTileMinPowerOf2Width;
+ int fTileWidth;
+ int fTileHeight;
+ double fTileWidthPercentage;
+ double fTileHeightPercentage;
+ int fTileMinPowerOf2Width;
+
+ // These variables are only used for timing individual tiles.
+ // Next tile to draw in fTileRects.
+ int fCurrentTileOffset;
+ // Number of tiles in the x direction.
+ int fTilesX;
+ // Number of tiles in the y direction.
+ int fTilesY;
void setupTiles();
void setupPowerOf2Tiles();
"Usage: \n"
" %s <inputDir>...\n"
" [--logFile filename][--timers [wcgWC]*][--logPerIter 1|0][--min]\n"
-" [--repeat] \n"
+" [--repeat][--timeIndividualTiles] \n"
" [--mode pow2tile minWidth height | record | simple\n"
" | tile width height | playbackCreation]\n"
" [--pipe]\n"
SkDebugf(" --min : Print the minimum times (instead of average).\n");
SkDebugf(" --timers [wcgWC]* : "
"Display wall, cpu, gpu, truncated wall or truncated cpu time for each picture.\n");
+ SkDebugf(" --timeIndividualTiles : Report times for drawing individual tiles, rather than\n"
+" times for drawing the whole page.\n"
+" Requires --mode tile\n");
SkDebugf(
" --mode pow2tile minWidth height | copyTile width height | record | simple\n"
" | tile width height | playbackCreation:\n"
gLogger.logError("Missing arg for --timers\n");
PRINT_USAGE_AND_EXIT;
}
+ } else if (0 == strcmp(*argv, "--timeIndividualTiles")) {
+ benchmark->setTimeIndividualTiles(true);
} else if (0 == strcmp(*argv, "--min")) {
benchmark->setPrintMin(true);
} else if (0 == strcmp(*argv, "--logPerIter")) {
x = y = 4;
}
tiledRenderer = SkNEW_ARGS(sk_tools::CopyTilesRenderer, (x, y));
+ if (benchmark->timeIndividualTiles()) {
+ gLogger.logError("timeIndividualTiles is not compatible with copyTile\n");
+ PRINT_USAGE_AND_EXIT;
+ }
} else if (numThreads > 1) {
tiledRenderer = SkNEW_ARGS(sk_tools::MultiCorePictureRenderer, (numThreads));
} else {
}
renderer.reset(tiledRenderer);
if (usePipe) {
- SkDebugf("Pipe rendering is currently not compatible with tiling.\n"
+ gLogger.logError("Pipe rendering is currently not compatible with tiling.\n"
"Turning off pipe.\n");
}
- } else if (usePipe) {
- if (renderer.get() != NULL) {
- SkDebugf("Pipe is incompatible with other modes.\n");
+ } else {
+ if (benchmark->timeIndividualTiles()) {
+ gLogger.logError("timeIndividualTiles requires tiled rendering.\n");
PRINT_USAGE_AND_EXIT;
}
- renderer.reset(SkNEW(sk_tools::PipePictureRenderer));
+ if (usePipe) {
+ if (renderer.get() != NULL) {
+ gLogger.logError("Pipe is incompatible with other modes.\n");
+ PRINT_USAGE_AND_EXIT;
+ }
+ renderer.reset(SkNEW(sk_tools::PipePictureRenderer));
+ }
}
if (inputs->count() < 1) {
PRINT_USAGE_AND_EXIT;