In bench_pictures --multi, maintain thread local caches.

Builds on https://codereview.appspot.com/6718046/ by mtklein.

Previously, each iteration of drawing a picture started new threads to draw the picture. Since each thread is using thread local storage for the font cache, this means that each iteration had to start with an empty font cache.

The newly added MultiCorePictureRenderer, separated from TiledPictureRenderer, now starts the drawing threads at the beginning of the test using an SkThreadPool, and keeps them alive through all iterations, so the font cache can be reused.

For now, I have removed the pipe version of the threaded renderer.

Updated bench_pictures_main and render_pictures_main to use the new
renderer, and to unref a renderer before early exit.

Review URL: https://codereview.appspot.com/6777063

git-svn-id: http://skia.googlecode.com/svn/trunk@6285 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
scroggo@google.com 2012-11-02 21:28:12 +00:00
parent db87c96085
commit a62da2fee7
4 changed files with 268 additions and 300 deletions

View File

@ -220,16 +220,11 @@ bool SimplePictureRenderer::render(const SkString* path) {
/////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////
TiledPictureRenderer::TiledPictureRenderer() TiledPictureRenderer::TiledPictureRenderer()
: fUsePipe(false) : fTileWidth(kDefaultTileWidth)
, fTileWidth(kDefaultTileWidth)
, fTileHeight(kDefaultTileHeight) , fTileHeight(kDefaultTileHeight)
, fTileWidthPercentage(0.0) , fTileWidthPercentage(0.0)
, fTileHeightPercentage(0.0) , fTileHeightPercentage(0.0)
, fTileMinPowerOf2Width(0) , fTileMinPowerOf2Width(0) { }
, fTileCounter(0)
, fNumThreads(1)
, fPictureClones(NULL)
, fPipeController(NULL) { }
void TiledPictureRenderer::init(SkPicture* pict) { void TiledPictureRenderer::init(SkPicture* pict) {
SkASSERT(pict != NULL); SkASSERT(pict != NULL);
@ -242,9 +237,7 @@ void TiledPictureRenderer::init(SkPicture* pict) {
// used by bench_pictures. // used by bench_pictures.
fPicture = pict; fPicture = pict;
fPicture->ref(); fPicture->ref();
if (!fUsePipe) {
this->buildBBoxHierarchy(); this->buildBBoxHierarchy();
}
if (fTileWidthPercentage > 0) { if (fTileWidthPercentage > 0) {
fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100)); fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
@ -258,42 +251,13 @@ void TiledPictureRenderer::init(SkPicture* pict) {
} else { } else {
this->setupTiles(); this->setupTiles();
} }
if (this->multiThreaded()) {
for (int i = 0; i < fNumThreads; ++i) {
*fCanvasPool.append() = this->setupCanvas(fTileWidth, fTileHeight);
}
if (!fUsePipe) {
SkASSERT(NULL == fPictureClones);
// Only need to create fNumThreads - 1 clones, since one thread will use the base
// picture.
int numberOfClones = fNumThreads - 1;
// This will be deleted in end().
fPictureClones = SkNEW_ARRAY(SkPicture, numberOfClones);
fPicture->clone(fPictureClones, numberOfClones);
}
}
} }
void TiledPictureRenderer::end() { void TiledPictureRenderer::end() {
fTileRects.reset(); fTileRects.reset();
SkDELETE_ARRAY(fPictureClones);
fPictureClones = NULL;
fCanvasPool.unrefAll();
if (fPipeController != NULL) {
SkASSERT(fUsePipe);
SkDELETE(fPipeController);
fPipeController = NULL;
}
this->INHERITED::end(); this->INHERITED::end();
} }
TiledPictureRenderer::~TiledPictureRenderer() {
// end() must be called to delete fPictureClones and fPipeController
SkASSERT(NULL == fPictureClones);
SkASSERT(NULL == fPipeController);
}
void TiledPictureRenderer::setupTiles() { void TiledPictureRenderer::setupTiles() {
for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) { for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
for (int tile_x_start = 0; tile_x_start < fPicture->width(); tile_x_start += fTileWidth) { for (int tile_x_start = 0; tile_x_start < fPicture->width(); tile_x_start += fTileWidth) {
@ -364,129 +328,6 @@ static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playba
} }
/////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////
// Base class for data used both by pipe and clone picture multi threaded drawing.
struct ThreadData {
ThreadData(SkCanvas* target, int* tileCounter, SkTDArray<SkRect>* tileRects,
const SkString* path, bool* success)
: fCanvas(target)
, fPath(path)
, fSuccess(success)
, fTileCounter(tileCounter)
, fTileRects(tileRects) {
SkASSERT(target != NULL && tileCounter != NULL && tileRects != NULL);
// Success must start off true, and it will be set to false upon failure.
SkASSERT(success != NULL && *success);
}
int32_t nextTile(SkRect* rect) {
int32_t i = sk_atomic_inc(fTileCounter);
if (i < fTileRects->count()) {
SkASSERT(rect != NULL);
*rect = fTileRects->operator[](i);
return i;
}
return -1;
}
// All of these are pointers to objects owned elsewhere
SkCanvas* fCanvas;
const SkString* fPath;
bool* fSuccess;
private:
// Shared by all threads, this states which is the next tile to be drawn.
int32_t* fTileCounter;
// Points to the array of rectangles. The array is already created before any threads are
// started and then it is unmodified, so there is no danger of race conditions.
const SkTDArray<SkRect>* fTileRects;
};
///////////////////////////////////////////////////////////////////////////////////////////////
// Draw using Pipe
struct TileData : public ThreadData {
TileData(ThreadSafePipeController* controller, SkCanvas* canvas, int* tileCounter,
SkTDArray<SkRect>* tileRects, const SkString* path, bool* success)
: INHERITED(canvas, tileCounter, tileRects, path, success)
, fController(controller) {}
ThreadSafePipeController* fController;
typedef ThreadData INHERITED;
};
static void DrawTile(void* data) {
SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
TileData* tileData = static_cast<TileData*>(data);
SkRect tileRect;
int32_t i;
while ((i = tileData->nextTile(&tileRect)) != -1) {
DrawTileToCanvas(tileData->fCanvas, tileRect, tileData->fController);
if (NULL != tileData->fPath &&
!writeAppendNumber(tileData->fCanvas, tileData->fPath, i)) {
*tileData->fSuccess = false;
break;
}
}
SkDELETE(tileData);
}
///////////////////////////////////////////////////////////////////////////////////////////////
// Draw using Picture
struct CloneData : public ThreadData {
CloneData(SkPicture* clone, SkCanvas* target, int* tileCounter, SkTDArray<SkRect>* tileRects,
const SkString* path, bool* success)
: INHERITED(target, tileCounter, tileRects, path, success)
, fClone(clone) {}
SkPicture* fClone;
typedef ThreadData INHERITED;
};
static void DrawClonedTiles(void* data) {
SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
CloneData* cloneData = static_cast<CloneData*>(data);
SkRect tileRect;
int32_t i;
while ((i = cloneData->nextTile(&tileRect)) != -1) {
DrawTileToCanvas(cloneData->fCanvas, tileRect, cloneData->fClone);
if (NULL != cloneData->fPath &&
!writeAppendNumber(cloneData->fCanvas, cloneData->fPath, i)) {
*cloneData->fSuccess = false;
break;
}
}
SkDELETE(cloneData);
}
///////////////////////////////////////////////////////////////////////////////////////////////
void TiledPictureRenderer::setup() {
if (this->multiThreaded()) {
// Reset to zero so we start with the first tile.
fTileCounter = 0;
if (fUsePipe) {
// Record the picture into the pipe controller. It is done here because unlike
// SkPicture, the pipe is modified (bitmaps can be removed) by drawing.
// fPipeController is deleted here after each call to render() except the last one and
// in end() for the last one.
if (fPipeController != NULL) {
SkDELETE(fPipeController);
}
fPipeController = SkNEW_ARGS(ThreadSafePipeController, (fTileRects.count()));
SkGPipeWriter writer;
SkCanvas* pipeCanvas = writer.startRecording(fPipeController,
SkGPipeWriter::kSimultaneousReaders_Flag);
SkASSERT(fPicture != NULL);
fPicture->draw(pipeCanvas);
writer.endRecording();
}
}
}
bool TiledPictureRenderer::render(const SkString* path) { bool TiledPictureRenderer::render(const SkString* path) {
SkASSERT(fPicture != NULL); SkASSERT(fPicture != NULL);
@ -494,38 +335,7 @@ bool TiledPictureRenderer::render(const SkString* path) {
return false; return false;
} }
if (this->multiThreaded()) { // Reuse one canvas for all tiles.
SkASSERT(fCanvasPool.count() == fNumThreads);
SkTDArray<SkThread*> threads;
SkThread::entryPointProc proc = fUsePipe ? DrawTile : DrawClonedTiles;
bool success = true;
for (int i = 0; i < fNumThreads; ++i) {
// data will be deleted by the entryPointProc.
ThreadData* data;
if (fUsePipe) {
data = SkNEW_ARGS(TileData, (fPipeController, fCanvasPool[i], &fTileCounter,
&fTileRects, path, &success));
} else {
SkPicture* pic = (0 == i) ? fPicture : &fPictureClones[i-1];
data = SkNEW_ARGS(CloneData, (pic, fCanvasPool[i], &fTileCounter, &fTileRects, path,
&success));
}
SkThread* thread = SkNEW_ARGS(SkThread, (proc, data));
if (!thread->start()) {
SkDebugf("Could not start %s thread %i.\n", (fUsePipe ? "pipe" : "picture"), i);
}
*threads.append() = thread;
}
SkASSERT(threads.count() == fNumThreads);
for (int i = 0; i < fNumThreads; ++i) {
SkThread* thread = threads[i];
thread->join();
SkDELETE(thread);
}
threads.reset();
return success;
} else {
// For single thread, we really only need one canvas total.
SkCanvas* canvas = this->setupCanvas(fTileWidth, fTileHeight); SkCanvas* canvas = this->setupCanvas(fTileWidth, fTileHeight);
SkAutoUnref aur(canvas); SkAutoUnref aur(canvas);
@ -537,7 +347,6 @@ bool TiledPictureRenderer::render(const SkString* path) {
} }
} }
return success; return success;
}
} }
SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) { SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
@ -552,6 +361,128 @@ SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
canvas->clipRect(clip); canvas->clipRect(clip);
return canvas; return canvas;
} }
///////////////////////////////////////////////////////////////////////////////////////////////
// Holds all of the information needed to draw a set of tiles.
class CloneData : public SkRunnable {
public:
CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
SkRunnable* done)
: fClone(clone)
, fCanvas(canvas)
, fPath(NULL)
, fRects(rects)
, fStart(start)
, fEnd(end)
, fSuccess(NULL)
, fDone(done) {
SkASSERT(fDone != NULL);
}
virtual void run() SK_OVERRIDE {
SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
for (int i = fStart; i < fEnd; i++) {
DrawTileToCanvas(fCanvas, fRects[i], fClone);
if (fPath != NULL && !writeAppendNumber(fCanvas, fPath, i)
&& fSuccess != NULL) {
*fSuccess = false;
// If one tile fails to write to a file, do not continue drawing the rest.
break;
}
}
fDone->run();
}
void setPathAndSuccess(const SkString* path, bool* success) {
fPath = path;
fSuccess = success;
}
private:
// All pointers unowned.
SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
// is threadsafe.
SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
const SkString* fPath; // If non-null, path to write the result to as a PNG.
SkTDArray<SkRect>& fRects; // All tiles of the picture.
const int fStart; // Range of tiles drawn by this thread.
const int fEnd;
bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
// and only set to false upon failure to write to a PNG.
SkRunnable* fDone;
};
MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
: fNumThreads(threadCount)
, fThreadPool(threadCount)
, fCountdown(threadCount) {
// Only need to create fNumThreads - 1 clones, since one thread will use the base
// picture.
fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
}
void MultiCorePictureRenderer::init(SkPicture *pict) {
// Set fPicture and the tiles.
this->INHERITED::init(pict);
for (int i = 0; i < fNumThreads; ++i) {
*fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
}
// Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
fPicture->clone(fPictureClones, fNumThreads - 1);
// Populate each thread with the appropriate data.
// Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
for (int i = 0; i < fNumThreads; i++) {
SkPicture* pic;
if (i == fNumThreads-1) {
// The last set will use the original SkPicture.
pic = fPicture;
} else {
pic = &fPictureClones[i];
}
const int start = i * chunkSize;
const int end = SkMin32(start + chunkSize, fTileRects.count());
fCloneData[i] = SkNEW_ARGS(CloneData,
(pic, fCanvasPool[i], fTileRects, start, end, &fCountdown));
}
}
bool MultiCorePictureRenderer::render(const SkString *path) {
bool success = true;
if (path != NULL) {
for (int i = 0; i < fNumThreads-1; i++) {
fCloneData[i]->setPathAndSuccess(path, &success);
}
}
fCountdown.reset(fNumThreads);
for (int i = 0; i < fNumThreads; i++) {
fThreadPool.add(fCloneData[i]);
}
fCountdown.wait();
return success;
}
void MultiCorePictureRenderer::end() {
for (int i = 0; i < fNumThreads - 1; i++) {
SkDELETE(fCloneData[i]);
fCloneData[i] = NULL;
}
fCanvasPool.unrefAll();
this->INHERITED::end();
}
MultiCorePictureRenderer::~MultiCorePictureRenderer() {
// Each individual CloneData was deleted in end.
SkDELETE_ARRAY(fCloneData);
SkDELETE_ARRAY(fPictureClones);
}
/////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -7,13 +7,17 @@
#ifndef PictureRenderer_DEFINED #ifndef PictureRenderer_DEFINED
#define PictureRenderer_DEFINED #define PictureRenderer_DEFINED
#include "SkCountdown.h"
#include "SkMath.h" #include "SkMath.h"
#include "SkPicture.h" #include "SkPicture.h"
#include "SkTypes.h"
#include "SkTDArray.h"
#include "SkRect.h" #include "SkRect.h"
#include "SkRefCnt.h" #include "SkRefCnt.h"
#include "SkRunnable.h"
#include "SkString.h" #include "SkString.h"
#include "SkTDArray.h"
#include "SkThreadPool.h"
#include "SkTypes.h"
#if SK_SUPPORT_GPU #if SK_SUPPORT_GPU
#include "GrContextFactory.h" #include "GrContextFactory.h"
@ -23,7 +27,7 @@
class SkBitmap; class SkBitmap;
class SkCanvas; class SkCanvas;
class SkGLContext; class SkGLContext;
class ThreadSafePipeController; class SkThread;
namespace sk_tools { namespace sk_tools {
@ -41,6 +45,9 @@ public:
kRTree_BBoxHierarchyType, kRTree_BBoxHierarchyType,
}; };
/**
* Called with each new SkPicture to render.
*/
virtual void init(SkPicture* pict); virtual void init(SkPicture* pict);
/** /**
@ -60,7 +67,12 @@ public:
*/ */
virtual bool render(const SkString* path) = 0; virtual bool render(const SkString* path) = 0;
/**
* Called once finished with a particular SkPicture, before calling init again, and before
* being done with this Renderer.
*/
virtual void end(); virtual void end();
void resetState(); void resetState();
void setDeviceType(SkDeviceTypes deviceType) { void setDeviceType(SkDeviceTypes deviceType) {
@ -164,8 +176,6 @@ public:
virtual void init(SkPicture* pict) SK_OVERRIDE; virtual void init(SkPicture* pict) SK_OVERRIDE;
virtual void setup() SK_OVERRIDE;
/** /**
* Renders to tiles, rather than a single canvas. If a path is provided, a separate file is * Renders to tiles, rather than a single canvas. If a path is provided, a separate file is
* created for each tile, named "path0.png", "path1.png", etc. * created for each tile, named "path0.png", "path1.png", etc.
@ -220,43 +230,51 @@ public:
return fTileMinPowerOf2Width; return fTileMinPowerOf2Width;
} }
/** protected:
* Set the number of threads to use for drawing. Non-positive numbers will set it to 1. virtual SkCanvas* setupCanvas(int width, int height) SK_OVERRIDE;
*/ SkTDArray<SkRect> fTileRects;
void setNumberOfThreads(int num) {
fNumThreads = SkMax32(num, 1);
}
void setUsePipe(bool usePipe) {
fUsePipe = usePipe;
}
~TiledPictureRenderer();
private: private:
bool fUsePipe;
int fTileWidth; int fTileWidth;
int fTileHeight; int fTileHeight;
double fTileWidthPercentage; double fTileWidthPercentage;
double fTileHeightPercentage; double fTileHeightPercentage;
int fTileMinPowerOf2Width; int fTileMinPowerOf2Width;
SkTDArray<SkRect> fTileRects;
// These are only used for multithreaded rendering
int32_t fTileCounter;
int fNumThreads;
SkTDArray<SkCanvas*> fCanvasPool;
SkPicture* fPictureClones;
ThreadSafePipeController* fPipeController;
void setupTiles(); void setupTiles();
void setupPowerOf2Tiles(); void setupPowerOf2Tiles();
virtual SkCanvas* setupCanvas(int width, int height) SK_OVERRIDE;
bool multiThreaded() { return fNumThreads > 1; }
typedef PictureRenderer INHERITED; typedef PictureRenderer INHERITED;
}; };
class CloneData;
class MultiCorePictureRenderer : public TiledPictureRenderer {
public:
explicit MultiCorePictureRenderer(int threadCount);
~MultiCorePictureRenderer();
virtual void init(SkPicture* pict) SK_OVERRIDE;
/**
* Behaves like TiledPictureRenderer::render(), only using multiple threads.
*/
virtual bool render(const SkString* path) SK_OVERRIDE;
virtual void end() SK_OVERRIDE;
private:
const int fNumThreads;
SkTDArray<SkCanvas*> fCanvasPool;
SkThreadPool fThreadPool;
SkPicture* fPictureClones;
CloneData** fCloneData;
SkCountdown fCountdown;
typedef TiledPictureRenderer INHERITED;
};
/** /**
* This class does not do any rendering, but its render function executes turning an SkPictureRecord * This class does not do any rendering, but its render function executes turning an SkPictureRecord
* into an SkPicturePlayback, which we want to time. * into an SkPicturePlayback, which we want to time.

View File

@ -76,7 +76,7 @@ static void usage(const char* argv0) {
SkDebugf( SkDebugf(
" --multi numThreads : Set the number of threads for multi threaded drawing. Must be greater\n" " --multi numThreads : Set the number of threads for multi threaded drawing. Must be greater\n"
" than 1. Only works with tiled rendering.\n" " than 1. Only works with tiled rendering.\n"
" --pipe: Benchmark SkGPipe rendering. Compatible with tiled, multithreaded rendering.\n"); " --pipe: Benchmark SkGPipe rendering. Currently incompatible with \"mode\".\n");
SkDebugf( SkDebugf(
" --bbh bbhType: Set the bounding box hierarchy type to be used. Accepted\n" " --bbh bbhType: Set the bounding box hierarchy type to be used. Accepted\n"
" values are: none, rtree. Default value is none.\n" " values are: none, rtree. Default value is none.\n"
@ -135,6 +135,12 @@ static bool run_single_benchmark(const SkString& inputPath,
return true; return true;
} }
#define PRINT_USAGE_AND_EXIT \
do { \
usage(argv0); \
exit(-1); \
} while (0)
static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* inputs, static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* inputs,
sk_tools::PictureBenchmark* benchmark) { sk_tools::PictureBenchmark* benchmark) {
const char* argv0 = argv[0]; const char* argv0 = argv[0];
@ -144,7 +150,7 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
sk_tools::PictureRenderer::SkDeviceTypes deviceType = sk_tools::PictureRenderer::SkDeviceTypes deviceType =
sk_tools::PictureRenderer::kBitmap_DeviceType; sk_tools::PictureRenderer::kBitmap_DeviceType;
sk_tools::PictureRenderer* renderer = NULL; SkAutoTUnref<sk_tools::PictureRenderer> renderer(NULL);
// Create a string to show our current settings. // Create a string to show our current settings.
// TODO: Make it prettier. Currently it just repeats the command line. // TODO: Make it prettier. Currently it just repeats the command line.
@ -170,12 +176,11 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
repeats = atoi(*argv); repeats = atoi(*argv);
if (repeats < 1) { if (repeats < 1) {
gLogger.logError("--repeat must be given a value > 0\n"); gLogger.logError("--repeat must be given a value > 0\n");
exit(-1); PRINT_USAGE_AND_EXIT;
} }
} else { } else {
gLogger.logError("Missing arg for --repeat\n"); gLogger.logError("Missing arg for --repeat\n");
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(-1);
} }
} else if (0 == strcmp(*argv, "--pipe")) { } else if (0 == strcmp(*argv, "--pipe")) {
usePipe = true; usePipe = true;
@ -194,28 +199,24 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
} }
} else { } else {
gLogger.logError("Missing arg for --logFile\n"); gLogger.logError("Missing arg for --logFile\n");
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(-1);
} }
} else if (0 == strcmp(*argv, "--multi")) { } else if (0 == strcmp(*argv, "--multi")) {
++argv; ++argv;
if (argv >= stop) { if (argv >= stop) {
gLogger.logError("Missing arg for --multi\n"); gLogger.logError("Missing arg for --multi\n");
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(-1);
} }
numThreads = atoi(*argv); numThreads = atoi(*argv);
if (numThreads < 2) { if (numThreads < 2) {
gLogger.logError("Number of threads must be at least 2.\n"); gLogger.logError("Number of threads must be at least 2.\n");
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(-1);
} }
} else if (0 == strcmp(*argv, "--bbh")) { } else if (0 == strcmp(*argv, "--bbh")) {
++argv; ++argv;
if (argv >= stop) { if (argv >= stop) {
gLogger.logError("Missing value for --bbh\n"); gLogger.logError("Missing value for --bbh\n");
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(-1);
} }
if (0 == strcmp(*argv, "none")) { if (0 == strcmp(*argv, "none")) {
bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType; bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
@ -225,23 +226,25 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
SkString err; SkString err;
err.printf("%s is not a valid value for --bbhType\n", *argv); err.printf("%s is not a valid value for --bbhType\n", *argv);
gLogger.logError(err); gLogger.logError(err);
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(-1);
} }
} else if (0 == strcmp(*argv, "--mode")) { } else if (0 == strcmp(*argv, "--mode")) {
if (renderer.get() != NULL) {
SkDebugf("Cannot combine modes.\n");
PRINT_USAGE_AND_EXIT;
}
++argv; ++argv;
if (argv >= stop) { if (argv >= stop) {
gLogger.logError("Missing mode for --mode\n"); gLogger.logError("Missing mode for --mode\n");
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(-1);
} }
if (0 == strcmp(*argv, "record")) { if (0 == strcmp(*argv, "record")) {
renderer = SkNEW(sk_tools::RecordPictureRenderer); renderer.reset(SkNEW(sk_tools::RecordPictureRenderer));
} else if (0 == strcmp(*argv, "simple")) { } else if (0 == strcmp(*argv, "simple")) {
renderer = SkNEW(sk_tools::SimplePictureRenderer); renderer.reset(SkNEW(sk_tools::SimplePictureRenderer));
} else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))) { } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))) {
useTiles = true; useTiles = true;
mode = *argv; mode = *argv;
@ -255,33 +258,29 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
SkString err; SkString err;
err.printf("Missing width for --mode %s\n", mode); err.printf("Missing width for --mode %s\n", mode);
gLogger.logError(err); gLogger.logError(err);
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(-1);
} }
widthString = *argv; widthString = *argv;
++argv; ++argv;
if (argv >= stop) { if (argv >= stop) {
gLogger.logError("Missing height for --mode tile\n"); gLogger.logError("Missing height for --mode tile\n");
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(-1);
} }
heightString = *argv; heightString = *argv;
} else if (0 == strcmp(*argv, "playbackCreation")) { } else if (0 == strcmp(*argv, "playbackCreation")) {
renderer = SkNEW(sk_tools::PlaybackCreationRenderer); renderer.reset(SkNEW(sk_tools::PlaybackCreationRenderer));
} else { } else {
SkString err; SkString err;
err.printf("%s is not a valid mode for --mode\n", *argv); err.printf("%s is not a valid mode for --mode\n", *argv);
gLogger.logError(err); gLogger.logError(err);
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(-1);
} }
} else if (0 == strcmp(*argv, "--device")) { } else if (0 == strcmp(*argv, "--device")) {
++argv; ++argv;
if (argv >= stop) { if (argv >= stop) {
gLogger.logError("Missing mode for --device\n"); gLogger.logError("Missing mode for --device\n");
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(-1);
} }
if (0 == strcmp(*argv, "bitmap")) { if (0 == strcmp(*argv, "bitmap")) {
@ -296,8 +295,7 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
SkString err; SkString err;
err.printf("%s is not a valid mode for --device\n", *argv); err.printf("%s is not a valid mode for --device\n", *argv);
gLogger.logError(err); gLogger.logError(err);
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(-1);
} }
} else if (0 == strcmp(*argv, "--timers")) { } else if (0 == strcmp(*argv, "--timers")) {
++argv; ++argv;
@ -333,8 +331,7 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
truncatedTimerCpu, timerGpu); truncatedTimerCpu, timerGpu);
} else { } else {
gLogger.logError("Missing arg for --timers\n"); gLogger.logError("Missing arg for --timers\n");
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(-1);
} }
} else if (0 == strcmp(*argv, "--min")) { } else if (0 == strcmp(*argv, "--min")) {
benchmark->setPrintMin(true); benchmark->setPrintMin(true);
@ -345,12 +342,10 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
benchmark->setLogPerIter(log); benchmark->setLogPerIter(log);
} else { } else {
gLogger.logError("Missing arg for --logPerIter\n"); gLogger.logError("Missing arg for --logPerIter\n");
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(-1);
} }
} else if (0 == strcmp(*argv, "--help") || 0 == strcmp(*argv, "-h")) { } else if (0 == strcmp(*argv, "--help") || 0 == strcmp(*argv, "-h")) {
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(0);
} else { } else {
inputs->push_back(SkString(*argv)); inputs->push_back(SkString(*argv));
} }
@ -358,19 +353,22 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
if (numThreads > 1 && !useTiles) { if (numThreads > 1 && !useTiles) {
gLogger.logError("Multithreaded drawing requires tiled rendering.\n"); gLogger.logError("Multithreaded drawing requires tiled rendering.\n");
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(-1);
} }
if (usePipe && sk_tools::PictureRenderer::kNone_BBoxHierarchyType != bbhType) { if (usePipe && sk_tools::PictureRenderer::kNone_BBoxHierarchyType != bbhType) {
gLogger.logError("--pipe and --bbh cannot be used together\n"); gLogger.logError("--pipe and --bbh cannot be used together\n");
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(-1);
} }
if (useTiles) { if (useTiles) {
SkASSERT(NULL == renderer); SkASSERT(NULL == renderer);
sk_tools::TiledPictureRenderer* tiledRenderer = SkNEW(sk_tools::TiledPictureRenderer); sk_tools::TiledPictureRenderer* tiledRenderer;
if (numThreads > 1) {
tiledRenderer = SkNEW_ARGS(sk_tools::MultiCorePictureRenderer, (numThreads));
} else {
tiledRenderer = SkNEW(sk_tools::TiledPictureRenderer);
}
if (isPowerOf2Mode) { if (isPowerOf2Mode) {
int minWidth = atoi(widthString); int minWidth = atoi(widthString);
if (!SkIsPow2(minWidth) || minWidth < 0) { if (!SkIsPow2(minWidth) || minWidth < 0) {
@ -379,8 +377,7 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
err.printf("-mode %s must be given a width" err.printf("-mode %s must be given a width"
" value that is a power of two\n", mode); " value that is a power of two\n", mode);
gLogger.logError(err); gLogger.logError(err);
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(-1);
} }
tiledRenderer->setTileMinPowerOf2Width(minWidth); tiledRenderer->setTileMinPowerOf2Width(minWidth);
} else if (sk_tools::is_percentage(widthString)) { } else if (sk_tools::is_percentage(widthString)) {
@ -388,16 +385,14 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
if (!(tiledRenderer->getTileWidthPercentage() > 0)) { if (!(tiledRenderer->getTileWidthPercentage() > 0)) {
tiledRenderer->unref(); tiledRenderer->unref();
gLogger.logError("--mode tile must be given a width percentage > 0\n"); gLogger.logError("--mode tile must be given a width percentage > 0\n");
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(-1);
} }
} else { } else {
tiledRenderer->setTileWidth(atoi(widthString)); tiledRenderer->setTileWidth(atoi(widthString));
if (!(tiledRenderer->getTileWidth() > 0)) { if (!(tiledRenderer->getTileWidth() > 0)) {
tiledRenderer->unref(); tiledRenderer->unref();
gLogger.logError("--mode tile must be given a width > 0\n"); gLogger.logError("--mode tile must be given a width > 0\n");
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(-1);
} }
} }
@ -406,16 +401,14 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
if (!(tiledRenderer->getTileHeightPercentage() > 0)) { if (!(tiledRenderer->getTileHeightPercentage() > 0)) {
tiledRenderer->unref(); tiledRenderer->unref();
gLogger.logError("--mode tile must be given a height percentage > 0\n"); gLogger.logError("--mode tile must be given a height percentage > 0\n");
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(-1);
} }
} else { } else {
tiledRenderer->setTileHeight(atoi(heightString)); tiledRenderer->setTileHeight(atoi(heightString));
if (!(tiledRenderer->getTileHeight() > 0)) { if (!(tiledRenderer->getTileHeight() > 0)) {
tiledRenderer->unref(); tiledRenderer->unref();
gLogger.logError("--mode tile must be given a height > 0\n"); gLogger.logError("--mode tile must be given a height > 0\n");
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(-1);
} }
} }
if (numThreads > 1) { if (numThreads > 1) {
@ -423,29 +416,32 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
if (sk_tools::PictureRenderer::kGPU_DeviceType == deviceType) { if (sk_tools::PictureRenderer::kGPU_DeviceType == deviceType) {
tiledRenderer->unref(); tiledRenderer->unref();
gLogger.logError("GPU not compatible with multithreaded tiling.\n"); gLogger.logError("GPU not compatible with multithreaded tiling.\n");
usage(argv0); PRINT_USAGE_AND_EXIT;
exit(-1);
} }
#endif #endif
tiledRenderer->setNumberOfThreads(numThreads);
} }
tiledRenderer->setUsePipe(usePipe); renderer.reset(tiledRenderer);
renderer = tiledRenderer; if (usePipe) {
SkDebugf("Pipe rendering is currently not compatible with tiling.\n"
"Turning off pipe.\n");
}
} else if (usePipe) { } else if (usePipe) {
renderer = SkNEW(sk_tools::PipePictureRenderer); if (renderer.get() != NULL) {
SkDebugf("Pipe is incompatible with other modes.\n");
PRINT_USAGE_AND_EXIT;
}
renderer.reset(SkNEW(sk_tools::PipePictureRenderer));
} }
if (inputs->count() < 1) { if (inputs->count() < 1) {
SkSafeUnref(renderer); PRINT_USAGE_AND_EXIT;
usage(argv0);
exit(-1);
} }
if (NULL == renderer) { if (NULL == renderer) {
renderer = SkNEW(sk_tools::SimplePictureRenderer); renderer.reset(SkNEW(sk_tools::SimplePictureRenderer));
} }
renderer->setBBoxHierarchyType(bbhType); renderer->setBBoxHierarchyType(bbhType);
benchmark->setRenderer(renderer)->unref(); benchmark->setRenderer(renderer);
benchmark->setRepeats(repeats); benchmark->setRepeats(repeats);
benchmark->setDeviceType(deviceType); benchmark->setDeviceType(deviceType);
benchmark->setLogger(&gLogger); benchmark->setLogger(&gLogger);

View File

@ -63,7 +63,7 @@ static void usage(const char* argv0) {
SkDebugf( SkDebugf(
" --multi count : Set the number of threads for multi threaded drawing. Must be greater\n" " --multi count : Set the number of threads for multi threaded drawing. Must be greater\n"
" than 1. Only works with tiled rendering.\n" " than 1. Only works with tiled rendering.\n"
" --pipe: Benchmark SkGPipe rendering. Compatible with tiled, multithreaded rendering.\n"); " --pipe: Benchmark SkGPipe rendering. Currently incompatible with \"mode\".\n");
SkDebugf( SkDebugf(
" --device bitmap" " --device bitmap"
#if SK_SUPPORT_GPU #if SK_SUPPORT_GPU
@ -174,7 +174,12 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
for (++argv; argv < stop; ++argv) { for (++argv; argv < stop; ++argv) {
if (0 == strcmp(*argv, "--mode")) { if (0 == strcmp(*argv, "--mode")) {
SkDELETE(renderer); if (renderer != NULL) {
renderer->unref();
SkDebugf("Cannot combine modes.\n");
usage(argv0);
exit(-1);
}
++argv; ++argv;
if (argv >= stop) { if (argv >= stop) {
@ -218,12 +223,14 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
} else if (0 == strcmp(*argv, "--multi")) { } else if (0 == strcmp(*argv, "--multi")) {
++argv; ++argv;
if (argv >= stop) { if (argv >= stop) {
SkSafeUnref(renderer);
SkDebugf("Missing arg for --multi\n"); SkDebugf("Missing arg for --multi\n");
usage(argv0); usage(argv0);
exit(-1); exit(-1);
} }
numThreads = atoi(*argv); numThreads = atoi(*argv);
if (numThreads < 2) { if (numThreads < 2) {
SkSafeUnref(renderer);
SkDebugf("Number of threads must be at least 2.\n"); SkDebugf("Number of threads must be at least 2.\n");
usage(argv0); usage(argv0);
exit(-1); exit(-1);
@ -231,6 +238,7 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
} else if (0 == strcmp(*argv, "--device")) { } else if (0 == strcmp(*argv, "--device")) {
++argv; ++argv;
if (argv >= stop) { if (argv >= stop) {
SkSafeUnref(renderer);
SkDebugf("Missing mode for --device\n"); SkDebugf("Missing mode for --device\n");
usage(argv0); usage(argv0);
exit(-1); exit(-1);
@ -245,13 +253,14 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
} }
#endif #endif
else { else {
SkSafeUnref(renderer);
SkDebugf("%s is not a valid mode for --device\n", *argv); SkDebugf("%s is not a valid mode for --device\n", *argv);
usage(argv0); usage(argv0);
exit(-1); exit(-1);
} }
} else if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) { } else if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) {
SkDELETE(renderer); SkSafeUnref(renderer);
usage(argv0); usage(argv0);
exit(-1); exit(-1);
} else if (0 == strcmp(*argv, "-w")) { } else if (0 == strcmp(*argv, "-w")) {
@ -268,6 +277,7 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
} }
if (numThreads > 1 && !useTiles) { if (numThreads > 1 && !useTiles) {
SkSafeUnref(renderer);
SkDebugf("Multithreaded drawing requires tiled rendering.\n"); SkDebugf("Multithreaded drawing requires tiled rendering.\n");
usage(argv0); usage(argv0);
exit(-1); exit(-1);
@ -275,7 +285,12 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
if (useTiles) { if (useTiles) {
SkASSERT(NULL == renderer); SkASSERT(NULL == renderer);
sk_tools::TiledPictureRenderer* tiledRenderer = SkNEW(sk_tools::TiledPictureRenderer); sk_tools::TiledPictureRenderer* tiledRenderer;
if (numThreads > 1) {
tiledRenderer = SkNEW_ARGS(sk_tools::MultiCorePictureRenderer, (numThreads));
} else {
tiledRenderer = SkNEW(sk_tools::TiledPictureRenderer);
}
if (isPowerOf2Mode) { if (isPowerOf2Mode) {
int minWidth = atoi(widthString); int minWidth = atoi(widthString);
if (!SkIsPow2(minWidth) || minWidth < 0) { if (!SkIsPow2(minWidth) || minWidth < 0) {
@ -332,16 +347,24 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
exit(-1); exit(-1);
} }
#endif #endif
tiledRenderer->setNumberOfThreads(numThreads);
} }
tiledRenderer->setUsePipe(usePipe);
renderer = tiledRenderer; renderer = tiledRenderer;
if (usePipe) {
SkDebugf("Pipe rendering is currently not compatible with tiling.\n"
"Turning off pipe.\n");
}
} else if (usePipe) { } else if (usePipe) {
if (renderer != NULL) {
renderer->unref();
SkDebugf("Pipe is incompatible with other modes.\n");
usage(argv0);
exit(-1);
}
renderer = SkNEW(sk_tools::PipePictureRenderer); renderer = SkNEW(sk_tools::PipePictureRenderer);
} }
if (inputs->empty()) { if (inputs->empty()) {
SkDELETE(renderer); SkSafeUnref(renderer);
if (NULL != outputDir) { if (NULL != outputDir) {
SkDELETE(outputDir); SkDELETE(outputDir);
} }