diff options
author | 2012-10-03 17:32:33 +0000 | |
---|---|---|
committer | 2012-10-03 17:32:33 +0000 | |
commit | b6e806bf17ffcca0c147a7dab4fba104fe928a58 (patch) | |
tree | 42ad6993942fdad4132bb1ccfb3d6c994f8271c1 /tools | |
parent | 1dfe88e00aeddf20690fd2469fd17e43f670ee3a (diff) |
Allow render_pictures to render using multiple threads.
Make write() a static function so it can be used by the
thread entry functions.
Add a helper function to append a number to a string and
call write to share code.
Review URL: https://codereview.appspot.com/6589062
git-svn-id: http://skia.googlecode.com/svn/trunk@5789 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'tools')
-rw-r--r-- | tools/PictureRenderer.cpp | 98 | ||||
-rw-r--r-- | tools/PictureRenderer.h | 7 | ||||
-rw-r--r-- | tools/render_pictures_main.cpp | 172 |
3 files changed, 178 insertions, 99 deletions
diff --git a/tools/PictureRenderer.cpp b/tools/PictureRenderer.cpp index 2eb89b2370..247875250d 100644 --- a/tools/PictureRenderer.cpp +++ b/tools/PictureRenderer.cpp @@ -99,10 +99,16 @@ void PictureRenderer::resetState() { #endif } -bool PictureRenderer::write(SkCanvas* canvas, SkString path) const { +/** + * Write the canvas to the specified path. + * @param canvas Must be non-null. Canvas to be written to a file. + * @param path Path for the file to be written. Should have no extension; write() will append + * an appropriate one. Passed in by value so it can be modified. + * @return bool True if the Canvas is written to a file. + */ +static bool write(SkCanvas* canvas, SkString path) { SkASSERT(canvas != NULL); - SkASSERT(fPicture != NULL); - if (NULL == canvas || NULL == fPicture) { + if (NULL == canvas) { return false; } @@ -118,6 +124,19 @@ bool PictureRenderer::write(SkCanvas* canvas, SkString path) const { return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100); } +/** + * If path is non NULL, append number to it, and call write(SkCanvas*, SkString) to write the + * provided canvas to a file. Returns true if path is NULL or if write() succeeds. + */ +static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number) { + if (NULL == path) { + return true; + } + SkString pathWithNumber(*path); + pathWithNumber.appendf("%i", number); + return write(canvas, pathWithNumber); +} + /////////////////////////////////////////////////////////////////////////////////////////////// bool RecordPictureRenderer::render(const SkString*) { @@ -144,7 +163,7 @@ bool PipePictureRenderer::render(const SkString* path) { pipeCanvas->drawPicture(*fPicture); writer.endRecording(); fCanvas->flush(); - return path != NULL && this->write(fCanvas, *path); + return path != NULL && write(fCanvas, *path); } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -158,7 +177,7 @@ bool SimplePictureRenderer::render(const SkString* path) { fCanvas->drawPicture(*fPicture); fCanvas->flush(); - return path != NULL && this->write(fCanvas, *path); + return path != NULL && write(fCanvas, *path); } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -307,23 +326,32 @@ 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) + ThreadData(SkCanvas* target, int* tileCounter, SkTDArray<SkRect>* tileRects, + const SkString* path, bool* success) : fCanvas(target) , fTileCounter(tileCounter) - , fTileRects(tileRects) { + , fTileRects(tileRects) + , fPath(path) + , fSuccess(success) { 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); } - const SkRect* nextTile() { + int32_t nextTile(SkRect* rect) { int32_t i = sk_atomic_inc(fTileCounter); if (i < fTileRects->count()) { - return &fTileRects->operator[](i); + SkASSERT(rect != NULL); + *rect = fTileRects->operator[](i); + return i; } - return NULL; + 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; @@ -337,8 +365,8 @@ private: struct TileData : public ThreadData { TileData(ThreadSafePipeController* controller, SkCanvas* canvas, int* tileCounter, - SkTDArray<SkRect>* tileRects) - : INHERITED(canvas, tileCounter, tileRects) + SkTDArray<SkRect>* tileRects, const SkString* path, bool* success) + : INHERITED(canvas, tileCounter, tileRects, path, success) , fController(controller) {} ThreadSafePipeController* fController; @@ -350,9 +378,14 @@ static void DrawTile(void* data) { SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024); TileData* tileData = static_cast<TileData*>(data); - const SkRect* tileRect; - while ((tileRect = tileData->nextTile()) != NULL) { - DrawTileToCanvas(tileData->fCanvas, *tileRect, tileData->fController); + SkRect tileRect; + int32_t i; + while ((i = tileData->nextTile(&tileRect)) != -1) { + DrawTileToCanvas(tileData->fCanvas, tileRect, tileData->fController); + if (!writeAppendNumber(tileData->fCanvas, tileData->fPath, i)) { + *tileData->fSuccess = false; + break; + } } SkDELETE(tileData); } @@ -361,8 +394,9 @@ static void DrawTile(void* data) { // Draw using Picture struct CloneData : public ThreadData { - CloneData(SkPicture* clone, SkCanvas* target, int* tileCounter, SkTDArray<SkRect>* tileRects) - : INHERITED(target, tileCounter, tileRects) + 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; @@ -374,9 +408,14 @@ static void DrawClonedTiles(void* data) { SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024); CloneData* cloneData = static_cast<CloneData*>(data); - const SkRect* tileRect; - while ((tileRect = cloneData->nextTile()) != NULL) { - DrawTileToCanvas(cloneData->fCanvas, *tileRect, cloneData->fClone); + SkRect tileRect; + int32_t i; + while ((i = cloneData->nextTile(&tileRect)) != -1) { + DrawTileToCanvas(cloneData->fCanvas, tileRect, cloneData->fClone); + if (!writeAppendNumber(cloneData->fCanvas, cloneData->fPath, i)) { + *cloneData->fSuccess = false; + break; + } } SkDELETE(cloneData); } @@ -416,15 +455,17 @@ bool TiledPictureRenderer::render(const SkString* path) { 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)); + 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)); + data = SkNEW_ARGS(CloneData, (pic, fCanvasPool[i], &fTileCounter, &fTileRects, path, + &success)); } SkThread* thread = SkNEW_ARGS(SkThread, (proc, data)); if (!thread->start()) { @@ -439,8 +480,7 @@ bool TiledPictureRenderer::render(const SkString* path) { SkDELETE(thread); } threads.reset(); - // Currently multithreaded is not an option for render_pictures - return false; + return success; } else { // For single thread, we really only need one canvas total. SkCanvas* canvas = this->setupCanvas(fTileWidth, fTileHeight); @@ -448,12 +488,8 @@ bool TiledPictureRenderer::render(const SkString* path) { for (int i = 0; i < fTileRects.count(); ++i) { DrawTileToCanvas(canvas, fTileRects[i], fPicture); - if (path != NULL) { - SkString tilePath(*path); - tilePath.appendf("%i", i); - if (!this->write(canvas, tilePath)) { - return false; - } + if (!writeAppendNumber(canvas, path, i)) { + return false; } } return path != NULL; diff --git a/tools/PictureRenderer.h b/tools/PictureRenderer.h index f8133ea4a4..4829e1df87 100644 --- a/tools/PictureRenderer.h +++ b/tools/PictureRenderer.h @@ -96,13 +96,6 @@ public: {} protected: - /** - * Write the canvas to the specified path. - * @param canvas Must be non-null. Canvas to be written to a file. - * @param path Path for the file to be written. Should have no extension; write() will append - * an appropriate one. - */ - bool write(SkCanvas* canvas, SkString path) const; SkCanvas* setupCanvas(); virtual SkCanvas* setupCanvas(int width, int height); diff --git a/tools/render_pictures_main.cpp b/tools/render_pictures_main.cpp index da0740a45b..477272df25 100644 --- a/tools/render_pictures_main.cpp +++ b/tools/render_pictures_main.cpp @@ -23,8 +23,10 @@ static void usage(const char* argv0) { SkDebugf("\n" "Usage: \n" " %s <input>... <outputDir> \n" -" [--mode pipe | pow2tile minWidth height[%] | simple\n" +" [--mode pow2tile minWidth height[%] | simple\n" " | tile width[%] height[%]]\n" +" [--pipe]\n" +" [--multi count]\n" " [--device bitmap" #if SK_SUPPORT_GPU " | gpu" @@ -38,12 +40,10 @@ static void usage(const char* argv0) { SkDebugf( " outputDir: directory to write the rendered images.\n\n"); SkDebugf( -" --mode pipe | pow2tile minWidth height[%] | simple\n" +" --mode pow2tile minWidth height[%] | simple\n" " | tile width[%] height[%]: Run in the corresponding mode.\n" " Default is simple.\n"); SkDebugf( -" pipe, Render using a SkGPipe.\n"); - SkDebugf( " pow2tile minWidth height[%], Creates tiles with widths\n" " that are all a power of two\n" " such that they minimize the\n" @@ -59,6 +59,10 @@ static void usage(const char* argv0) { " with the given dimensions.\n"); SkDebugf("\n"); 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"); + SkDebugf( " --device bitmap" #if SK_SUPPORT_GPU " | gpu" @@ -152,6 +156,14 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* sk_tools::PictureRenderer::SkDeviceTypes deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType; + bool usePipe = false; + int numThreads = 1; + bool useTiles = false; + const char* widthString = NULL; + const char* heightString = NULL; + bool isPowerOf2Mode = false; + const char* mode = NULL; + for (++argv; argv < stop; ++argv) { if (0 == strcmp(*argv, "--mode")) { SkDELETE(renderer); @@ -163,90 +175,55 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* exit(-1); } - if (0 == strcmp(*argv, "pipe")) { - renderer = SkNEW(sk_tools::PipePictureRenderer); - } else if (0 == strcmp(*argv, "simple")) { + if (0 == strcmp(*argv, "simple")) { renderer = SkNEW(sk_tools::SimplePictureRenderer); } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))) { - char* mode = *argv; - bool isPowerOf2Mode = false; + useTiles = true; + mode = *argv; if (0 == strcmp(*argv, "pow2tile")) { isPowerOf2Mode = true; } - sk_tools::TiledPictureRenderer* tileRenderer = - SkNEW(sk_tools::TiledPictureRenderer); ++argv; if (argv >= stop) { - SkDELETE(tileRenderer); SkDebugf("Missing width for --mode %s\n", mode); usage(argv0); exit(-1); } - if (isPowerOf2Mode) { - int minWidth = atoi(*argv); - - if (!SkIsPow2(minWidth) || minWidth <= 0) { - SkDELETE(tileRenderer); - SkDebugf("--mode %s must be given a width" - " value that is a power of two\n", mode); - exit(-1); - } - - tileRenderer->setTileMinPowerOf2Width(minWidth); - } else if (sk_tools::is_percentage(*argv)) { - tileRenderer->setTileWidthPercentage(atof(*argv)); - if (!(tileRenderer->getTileWidthPercentage() > 0)) { - SkDELETE(tileRenderer); - SkDebugf("--mode %s must be given a width percentage > 0\n", mode); - exit(-1); - } - } else { - tileRenderer->setTileWidth(atoi(*argv)); - if (!(tileRenderer->getTileWidth() > 0)) { - SkDELETE(tileRenderer); - SkDebugf("--mode %s must be given a width > 0\n", mode); - exit(-1); - } - } - + widthString = *argv; ++argv; if (argv >= stop) { - SkDELETE(tileRenderer); - SkDebugf("Missing height for --mode %s\n", mode); + SkDebugf("Missing height for --mode tile\n"); usage(argv0); exit(-1); } - - if (sk_tools::is_percentage(*argv)) { - tileRenderer->setTileHeightPercentage(atof(*argv)); - if (!(tileRenderer->getTileHeightPercentage() > 0)) { - SkDELETE(tileRenderer); - SkDebugf( - "--mode %s must be given a height percentage > 0\n", mode); - exit(-1); - } - } else { - tileRenderer->setTileHeight(atoi(*argv)); - if (!(tileRenderer->getTileHeight() > 0)) { - SkDELETE(tileRenderer); - SkDebugf("--mode %s must be given a height > 0\n", mode); - exit(-1); - } - } - - renderer = tileRenderer; + heightString = *argv; } else { SkDebugf("%s is not a valid mode for --mode\n", *argv); usage(argv0); exit(-1); } + } else if (0 == strcmp(*argv, "--pipe")) { + usePipe = true; + } else if (0 == strcmp(*argv, "--multi")) { + ++argv; + if (argv >= stop) { + SkDebugf("Missing arg for --multi\n"); + usage(argv0); + exit(-1); + } + numThreads = atoi(*argv); + if (numThreads < 2) { + SkDebugf("Number of threads must be at least 2.\n"); + usage(argv0); + exit(-1); + } } else if (0 == strcmp(*argv, "--device")) { ++argv; if (argv >= stop) { - SkDebugf("Missing mode for --deivce\n"); + SkDebugf("Missing mode for --device\n"); usage(argv0); exit(-1); } @@ -274,6 +251,79 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* } } + if (numThreads > 1 && !useTiles) { + SkDebugf("Multithreaded drawing requires tiled rendering.\n"); + usage(argv0); + exit(-1); + } + + if (useTiles) { + SkASSERT(NULL == renderer); + sk_tools::TiledPictureRenderer* tiledRenderer = SkNEW(sk_tools::TiledPictureRenderer); + if (isPowerOf2Mode) { + int minWidth = atoi(widthString); + if (!SkIsPow2(minWidth) || minWidth < 0) { + tiledRenderer->unref(); + SkString err; + err.printf("-mode %s must be given a width" + " value that is a power of two\n", mode); + SkDebugf(err.c_str()); + usage(argv0); + exit(-1); + } + tiledRenderer->setTileMinPowerOf2Width(minWidth); + } else if (sk_tools::is_percentage(widthString)) { + tiledRenderer->setTileWidthPercentage(atof(widthString)); + if (!(tiledRenderer->getTileWidthPercentage() > 0)) { + tiledRenderer->unref(); + SkDebugf("--mode tile must be given a width percentage > 0\n"); + usage(argv0); + exit(-1); + } + } else { + tiledRenderer->setTileWidth(atoi(widthString)); + if (!(tiledRenderer->getTileWidth() > 0)) { + tiledRenderer->unref(); + SkDebugf("--mode tile must be given a width > 0\n"); + usage(argv0); + exit(-1); + } + } + + if (sk_tools::is_percentage(heightString)) { + tiledRenderer->setTileHeightPercentage(atof(heightString)); + if (!(tiledRenderer->getTileHeightPercentage() > 0)) { + tiledRenderer->unref(); + SkDebugf("--mode tile must be given a height percentage > 0\n"); + usage(argv0); + exit(-1); + } + } else { + tiledRenderer->setTileHeight(atoi(heightString)); + if (!(tiledRenderer->getTileHeight() > 0)) { + tiledRenderer->unref(); + SkDebugf("--mode tile must be given a height > 0\n"); + usage(argv0); + exit(-1); + } + } + if (numThreads > 1) { +#if SK_SUPPORT_GPU + if (sk_tools::PictureRenderer::kGPU_DeviceType == deviceType) { + tiledRenderer->unref(); + SkDebugf("GPU not compatible with multithreaded tiling.\n"); + usage(argv0); + exit(-1); + } +#endif + tiledRenderer->setNumberOfThreads(numThreads); + } + tiledRenderer->setUsePipe(usePipe); + renderer = tiledRenderer; + } else if (usePipe) { + renderer = SkNEW(sk_tools::PipePictureRenderer); + } + if (inputs->count() < 2) { SkDELETE(renderer); usage(argv0); |