diff --git a/tools/PictureRenderer.cpp b/tools/PictureRenderer.cpp index 064557b8b2..745bec00f1 100644 --- a/tools/PictureRenderer.cpp +++ b/tools/PictureRenderer.cpp @@ -220,16 +220,11 @@ bool SimplePictureRenderer::render(const SkString* path) { /////////////////////////////////////////////////////////////////////////////////////////////// TiledPictureRenderer::TiledPictureRenderer() - : fUsePipe(false) - , fTileWidth(kDefaultTileWidth) + : fTileWidth(kDefaultTileWidth) , fTileHeight(kDefaultTileHeight) , fTileWidthPercentage(0.0) , fTileHeightPercentage(0.0) - , fTileMinPowerOf2Width(0) - , fTileCounter(0) - , fNumThreads(1) - , fPictureClones(NULL) - , fPipeController(NULL) { } + , fTileMinPowerOf2Width(0) { } void TiledPictureRenderer::init(SkPicture* pict) { SkASSERT(pict != NULL); @@ -242,9 +237,7 @@ void TiledPictureRenderer::init(SkPicture* pict) { // used by bench_pictures. fPicture = pict; fPicture->ref(); - if (!fUsePipe) { - this->buildBBoxHierarchy(); - } + this->buildBBoxHierarchy(); if (fTileWidthPercentage > 0) { fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100)); @@ -258,42 +251,13 @@ void TiledPictureRenderer::init(SkPicture* pict) { } else { 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() { fTileRects.reset(); - SkDELETE_ARRAY(fPictureClones); - fPictureClones = NULL; - fCanvasPool.unrefAll(); - if (fPipeController != NULL) { - SkASSERT(fUsePipe); - SkDELETE(fPipeController); - fPipeController = NULL; - } this->INHERITED::end(); } -TiledPictureRenderer::~TiledPictureRenderer() { - // end() must be called to delete fPictureClones and fPipeController - SkASSERT(NULL == fPictureClones); - SkASSERT(NULL == fPipeController); -} - void TiledPictureRenderer::setupTiles() { 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) { @@ -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* 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* fTileRects; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////// -// Draw using Pipe - -struct TileData : public ThreadData { - TileData(ThreadSafePipeController* controller, SkCanvas* canvas, int* tileCounter, - SkTDArray* 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(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* 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(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) { SkASSERT(fPicture != NULL); @@ -494,50 +335,18 @@ bool TiledPictureRenderer::render(const SkString* path) { return false; } - if (this->multiThreaded()) { - SkASSERT(fCanvasPool.count() == fNumThreads); - SkTDArray 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); - SkAutoUnref aur(canvas); + // 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); - if (NULL != path) { - success &= writeAppendNumber(canvas, path, i); - } + bool success = true; + for (int i = 0; i < fTileRects.count(); ++i) { + DrawTileToCanvas(canvas, fTileRects[i], fPicture); + if (NULL != path) { + success &= writeAppendNumber(canvas, path, i); } - return success; } + return success; } SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) { @@ -552,6 +361,128 @@ SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) { canvas->clipRect(clip); 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& 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& 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); +} /////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/tools/PictureRenderer.h b/tools/PictureRenderer.h index 9cafdc8c54..3ff8967d44 100644 --- a/tools/PictureRenderer.h +++ b/tools/PictureRenderer.h @@ -7,13 +7,17 @@ #ifndef PictureRenderer_DEFINED #define PictureRenderer_DEFINED + +#include "SkCountdown.h" #include "SkMath.h" #include "SkPicture.h" -#include "SkTypes.h" -#include "SkTDArray.h" #include "SkRect.h" #include "SkRefCnt.h" +#include "SkRunnable.h" #include "SkString.h" +#include "SkTDArray.h" +#include "SkThreadPool.h" +#include "SkTypes.h" #if SK_SUPPORT_GPU #include "GrContextFactory.h" @@ -23,7 +27,7 @@ class SkBitmap; class SkCanvas; class SkGLContext; -class ThreadSafePipeController; +class SkThread; namespace sk_tools { @@ -41,6 +45,9 @@ public: kRTree_BBoxHierarchyType, }; + /** + * Called with each new SkPicture to render. + */ virtual void init(SkPicture* pict); /** @@ -60,7 +67,12 @@ public: */ 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(); + void resetState(); void setDeviceType(SkDeviceTypes deviceType) { @@ -164,8 +176,6 @@ public: 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 * created for each tile, named "path0.png", "path1.png", etc. @@ -220,43 +230,51 @@ public: return fTileMinPowerOf2Width; } - /** - * Set the number of threads to use for drawing. Non-positive numbers will set it to 1. - */ - void setNumberOfThreads(int num) { - fNumThreads = SkMax32(num, 1); - } - - void setUsePipe(bool usePipe) { - fUsePipe = usePipe; - } - - ~TiledPictureRenderer(); +protected: + virtual SkCanvas* setupCanvas(int width, int height) SK_OVERRIDE; + SkTDArray fTileRects; private: - bool fUsePipe; int fTileWidth; int fTileHeight; double fTileWidthPercentage; double fTileHeightPercentage; int fTileMinPowerOf2Width; - SkTDArray fTileRects; - - // These are only used for multithreaded rendering - int32_t fTileCounter; - int fNumThreads; - SkTDArray fCanvasPool; - SkPicture* fPictureClones; - ThreadSafePipeController* fPipeController; void setupTiles(); void setupPowerOf2Tiles(); - virtual SkCanvas* setupCanvas(int width, int height) SK_OVERRIDE; - bool multiThreaded() { return fNumThreads > 1; } 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 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 * into an SkPicturePlayback, which we want to time. diff --git a/tools/bench_pictures_main.cpp b/tools/bench_pictures_main.cpp index 25fd5fca7f..87149e9af9 100644 --- a/tools/bench_pictures_main.cpp +++ b/tools/bench_pictures_main.cpp @@ -76,7 +76,7 @@ static void usage(const char* argv0) { SkDebugf( " --multi numThreads : Set the number of threads for multi threaded drawing. Must be greater\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( " --bbh bbhType: Set the bounding box hierarchy type to be used. Accepted\n" " values are: none, rtree. Default value is none.\n" @@ -135,6 +135,12 @@ static bool run_single_benchmark(const SkString& inputPath, return true; } +#define PRINT_USAGE_AND_EXIT \ + do { \ + usage(argv0); \ + exit(-1); \ + } while (0) + static void parse_commandline(int argc, char* const argv[], SkTArray* inputs, sk_tools::PictureBenchmark* benchmark) { const char* argv0 = argv[0]; @@ -144,7 +150,7 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* sk_tools::PictureRenderer::SkDeviceTypes deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType; - sk_tools::PictureRenderer* renderer = NULL; + SkAutoTUnref renderer(NULL); // Create a string to show our current settings. // 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* repeats = atoi(*argv); if (repeats < 1) { gLogger.logError("--repeat must be given a value > 0\n"); - exit(-1); + PRINT_USAGE_AND_EXIT; } } else { gLogger.logError("Missing arg for --repeat\n"); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--pipe")) { usePipe = true; @@ -194,28 +199,24 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* } } else { gLogger.logError("Missing arg for --logFile\n"); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--multi")) { ++argv; if (argv >= stop) { gLogger.logError("Missing arg for --multi\n"); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } numThreads = atoi(*argv); if (numThreads < 2) { gLogger.logError("Number of threads must be at least 2.\n"); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--bbh")) { ++argv; if (argv >= stop) { gLogger.logError("Missing value for --bbh\n"); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } if (0 == strcmp(*argv, "none")) { bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType; @@ -225,23 +226,25 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* SkString err; err.printf("%s is not a valid value for --bbhType\n", *argv); gLogger.logError(err); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--mode")) { + if (renderer.get() != NULL) { + SkDebugf("Cannot combine modes.\n"); + PRINT_USAGE_AND_EXIT; + } ++argv; if (argv >= stop) { gLogger.logError("Missing mode for --mode\n"); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } if (0 == strcmp(*argv, "record")) { - renderer = SkNEW(sk_tools::RecordPictureRenderer); + renderer.reset(SkNEW(sk_tools::RecordPictureRenderer)); } 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"))) { useTiles = true; mode = *argv; @@ -255,33 +258,29 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* SkString err; err.printf("Missing width for --mode %s\n", mode); gLogger.logError(err); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } widthString = *argv; ++argv; if (argv >= stop) { gLogger.logError("Missing height for --mode tile\n"); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } heightString = *argv; } else if (0 == strcmp(*argv, "playbackCreation")) { - renderer = SkNEW(sk_tools::PlaybackCreationRenderer); + renderer.reset(SkNEW(sk_tools::PlaybackCreationRenderer)); } else { SkString err; err.printf("%s is not a valid mode for --mode\n", *argv); gLogger.logError(err); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--device")) { ++argv; if (argv >= stop) { gLogger.logError("Missing mode for --device\n"); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } if (0 == strcmp(*argv, "bitmap")) { @@ -296,8 +295,7 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* SkString err; err.printf("%s is not a valid mode for --device\n", *argv); gLogger.logError(err); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--timers")) { ++argv; @@ -333,8 +331,7 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* truncatedTimerCpu, timerGpu); } else { gLogger.logError("Missing arg for --timers\n"); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--min")) { benchmark->setPrintMin(true); @@ -345,12 +342,10 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* benchmark->setLogPerIter(log); } else { gLogger.logError("Missing arg for --logPerIter\n"); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--help") || 0 == strcmp(*argv, "-h")) { - usage(argv0); - exit(0); + PRINT_USAGE_AND_EXIT; } else { inputs->push_back(SkString(*argv)); } @@ -358,19 +353,22 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* if (numThreads > 1 && !useTiles) { gLogger.logError("Multithreaded drawing requires tiled rendering.\n"); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } if (usePipe && sk_tools::PictureRenderer::kNone_BBoxHierarchyType != bbhType) { gLogger.logError("--pipe and --bbh cannot be used together\n"); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } if (useTiles) { 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) { int minWidth = atoi(widthString); if (!SkIsPow2(minWidth) || minWidth < 0) { @@ -379,8 +377,7 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* err.printf("-mode %s must be given a width" " value that is a power of two\n", mode); gLogger.logError(err); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } tiledRenderer->setTileMinPowerOf2Width(minWidth); } else if (sk_tools::is_percentage(widthString)) { @@ -388,16 +385,14 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* if (!(tiledRenderer->getTileWidthPercentage() > 0)) { tiledRenderer->unref(); gLogger.logError("--mode tile must be given a width percentage > 0\n"); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } } else { tiledRenderer->setTileWidth(atoi(widthString)); if (!(tiledRenderer->getTileWidth() > 0)) { tiledRenderer->unref(); gLogger.logError("--mode tile must be given a width > 0\n"); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } } @@ -406,16 +401,14 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* if (!(tiledRenderer->getTileHeightPercentage() > 0)) { tiledRenderer->unref(); gLogger.logError("--mode tile must be given a height percentage > 0\n"); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } } else { tiledRenderer->setTileHeight(atoi(heightString)); if (!(tiledRenderer->getTileHeight() > 0)) { tiledRenderer->unref(); gLogger.logError("--mode tile must be given a height > 0\n"); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } } if (numThreads > 1) { @@ -423,29 +416,32 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* if (sk_tools::PictureRenderer::kGPU_DeviceType == deviceType) { tiledRenderer->unref(); gLogger.logError("GPU not compatible with multithreaded tiling.\n"); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } #endif - tiledRenderer->setNumberOfThreads(numThreads); } - tiledRenderer->setUsePipe(usePipe); - renderer = tiledRenderer; + renderer.reset(tiledRenderer); + if (usePipe) { + SkDebugf("Pipe rendering is currently not compatible with tiling.\n" + "Turning off pipe.\n"); + } } 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) { - SkSafeUnref(renderer); - usage(argv0); - exit(-1); + PRINT_USAGE_AND_EXIT; } if (NULL == renderer) { - renderer = SkNEW(sk_tools::SimplePictureRenderer); + renderer.reset(SkNEW(sk_tools::SimplePictureRenderer)); } renderer->setBBoxHierarchyType(bbhType); - benchmark->setRenderer(renderer)->unref(); + benchmark->setRenderer(renderer); benchmark->setRepeats(repeats); benchmark->setDeviceType(deviceType); benchmark->setLogger(&gLogger); diff --git a/tools/render_pictures_main.cpp b/tools/render_pictures_main.cpp index ae9e94d4cc..521bbf8209 100644 --- a/tools/render_pictures_main.cpp +++ b/tools/render_pictures_main.cpp @@ -63,7 +63,7 @@ static void usage(const char* argv0) { SkDebugf( " --multi count : Set the number of threads for multi threaded drawing. Must be greater\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( " --device bitmap" #if SK_SUPPORT_GPU @@ -174,7 +174,12 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* for (++argv; argv < stop; ++argv) { if (0 == strcmp(*argv, "--mode")) { - SkDELETE(renderer); + if (renderer != NULL) { + renderer->unref(); + SkDebugf("Cannot combine modes.\n"); + usage(argv0); + exit(-1); + } ++argv; if (argv >= stop) { @@ -218,12 +223,14 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* } else if (0 == strcmp(*argv, "--multi")) { ++argv; if (argv >= stop) { + SkSafeUnref(renderer); SkDebugf("Missing arg for --multi\n"); usage(argv0); exit(-1); } numThreads = atoi(*argv); if (numThreads < 2) { + SkSafeUnref(renderer); SkDebugf("Number of threads must be at least 2.\n"); usage(argv0); exit(-1); @@ -231,6 +238,7 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* } else if (0 == strcmp(*argv, "--device")) { ++argv; if (argv >= stop) { + SkSafeUnref(renderer); SkDebugf("Missing mode for --device\n"); usage(argv0); exit(-1); @@ -245,13 +253,14 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* } #endif else { + SkSafeUnref(renderer); SkDebugf("%s is not a valid mode for --device\n", *argv); usage(argv0); exit(-1); } } else if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) { - SkDELETE(renderer); + SkSafeUnref(renderer); usage(argv0); exit(-1); } else if (0 == strcmp(*argv, "-w")) { @@ -268,6 +277,7 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* } if (numThreads > 1 && !useTiles) { + SkSafeUnref(renderer); SkDebugf("Multithreaded drawing requires tiled rendering.\n"); usage(argv0); exit(-1); @@ -275,7 +285,12 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* if (useTiles) { 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) { int minWidth = atoi(widthString); if (!SkIsPow2(minWidth) || minWidth < 0) { @@ -332,16 +347,24 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* exit(-1); } #endif - tiledRenderer->setNumberOfThreads(numThreads); } - tiledRenderer->setUsePipe(usePipe); renderer = tiledRenderer; + if (usePipe) { + SkDebugf("Pipe rendering is currently not compatible with tiling.\n" + "Turning off pipe.\n"); + } } 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); } if (inputs->empty()) { - SkDELETE(renderer); + SkSafeUnref(renderer); if (NULL != outputDir) { SkDELETE(outputDir); }