From 58b4ead36c62d8c0256ee4da554f3df2744d904c Mon Sep 17 00:00:00 2001 From: "scroggo@google.com" Date: Fri, 31 Aug 2012 16:15:22 +0000 Subject: Perform multi core rendering in bench_pictures. Add a flag in SkGPipeWriter for threadsafe drawing. Add a deferred pipe controller to SamplePipeControllers, which can be called to play back in multiple threads. Depends on http://codereview.appspot.com/6459105/ Review URL: https://codereview.appspot.com/6482068 git-svn-id: http://skia.googlecode.com/svn/trunk@5371 2bbb7eff-a529-9590-31e7-b0007b416f81 --- tools/PictureBenchmark.cpp | 13 ++-- tools/PictureBenchmark.h | 8 +++ tools/PictureRenderer.cpp | 108 +++++++++++++++++++++++++--- tools/PictureRenderer.h | 18 +++++ tools/bench_pictures_main.cpp | 162 ++++++++++++++++++++++++------------------ tools/picture_utils.cpp | 2 +- tools/picture_utils.h | 2 +- 7 files changed, 226 insertions(+), 87 deletions(-) (limited to 'tools') diff --git a/tools/PictureBenchmark.cpp b/tools/PictureBenchmark.cpp index d24bb8b550..29152f8d69 100644 --- a/tools/PictureBenchmark.cpp +++ b/tools/PictureBenchmark.cpp @@ -194,13 +194,16 @@ void TiledPictureBenchmark::run(SkPicture* pict) { } SkString result; + if (fRenderer.isMultiThreaded()) { + result.printf("multithreaded using %s ", (fRenderer.isUsePipe() ? "pipe" : "picture")); + } if (fRenderer.getTileMinPowerOf2Width() > 0) { - result.printf("%i_pow2tiles_%iminx%i: msecs = %6.2f", fRenderer.numTiles(), - fRenderer.getTileMinPowerOf2Width(), fRenderer.getTileHeight(), - wall_time / fRepeats); + result.appendf("%i_pow2tiles_%iminx%i: msecs = %6.2f", fRenderer.numTiles(), + fRenderer.getTileMinPowerOf2Width(), fRenderer.getTileHeight(), + wall_time / fRepeats); } else { - result.printf("%i_tiles_%ix%i: msecs = %6.2f", fRenderer.numTiles(), - fRenderer.getTileWidth(), fRenderer.getTileHeight(), wall_time / fRepeats); + result.appendf("%i_tiles_%ix%i: msecs = %6.2f", fRenderer.numTiles(), + fRenderer.getTileWidth(), fRenderer.getTileHeight(), wall_time / fRepeats); } #if SK_SUPPORT_GPU if (fRenderer.isUsingGpuDevice()) { diff --git a/tools/PictureBenchmark.h b/tools/PictureBenchmark.h index f020c8d7e7..2a14ea2888 100644 --- a/tools/PictureBenchmark.h +++ b/tools/PictureBenchmark.h @@ -125,6 +125,14 @@ public: return fRenderer.getTileMinPowerOf2Width(); } + void setThreading(bool multi) { + fRenderer.setMultiThreaded(multi); + } + + void setUsePipe(bool usePipe) { + fRenderer.setUsePipe(usePipe); + } + private: TiledPictureRenderer fRenderer; typedef PictureBenchmark INHERITED; diff --git a/tools/PictureRenderer.cpp b/tools/PictureRenderer.cpp index b7958e2d2e..d5b72ab7ce 100644 --- a/tools/PictureRenderer.cpp +++ b/tools/PictureRenderer.cpp @@ -6,22 +6,24 @@ */ #include "PictureRenderer.h" +#include "picture_utils.h" #include "SamplePipeControllers.h" #include "SkCanvas.h" #include "SkDevice.h" -#include "SkImageEncoder.h" #include "SkGPipe.h" +#if SK_SUPPORT_GPU +#include "SkGpuDevice.h" +#endif +#include "SkGraphics.h" +#include "SkImageEncoder.h" #include "SkMatrix.h" #include "SkPicture.h" #include "SkScalar.h" #include "SkString.h" +#include "SkTemplates.h" #include "SkTDArray.h" +#include "SkThreadUtils.h" #include "SkTypes.h" -#include "picture_utils.h" - -#if SK_SUPPORT_GPU -#include "SkGpuDevice.h" -#endif namespace sk_tools { @@ -156,7 +158,9 @@ void SimplePictureRenderer::render() { } TiledPictureRenderer::TiledPictureRenderer() - : fTileWidth(kDefaultTileWidth) + : fMultiThreaded(false) + , fUsePipe(false) + , fTileWidth(kDefaultTileWidth) , fTileHeight(kDefaultTileHeight) , fTileMinPowerOf2Width(0) , fTileHeightPercentage(0.0) @@ -236,7 +240,7 @@ void TiledPictureRenderer::setupTiles() { // constraints are that every tile must have a pixel width that is a power of // two and also be of some minimal width (that is also a power of two). // -// This is sovled by first taking our picture size and rounding it up to the +// This is solved by first taking our picture size and rounding it up to the // multiple of the minimal width. The binary representation of this rounded // value gives us the tiles we need: a bit of value one means we need a tile of // that size. @@ -275,9 +279,93 @@ void TiledPictureRenderer::deleteTiles() { fTiles.reset(); } +/////////////////////////////////////////////////////////////////////////////////////////////// +// Draw using Pipe + +struct TileData { + TileData(SkCanvas* canvas, DeferredPipeController* controller); + SkCanvas* fCanvas; + DeferredPipeController* fController; + SkThread fThread; +}; + +static void DrawTile(void* data) { + SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024); + TileData* tileData = static_cast(data); + tileData->fController->playback(tileData->fCanvas); +} + +TileData::TileData(SkCanvas* canvas, DeferredPipeController* controller) +: fCanvas(canvas) +, fController(controller) +, fThread(&DrawTile, static_cast(this)) {} + +/////////////////////////////////////////////////////////////////////////////////////////////// +// Draw using Picture + +struct CloneData { + CloneData(SkCanvas* target, SkPicture* original); + SkCanvas* fCanvas; + SkPicture* fClone; + SkThread fThread; +}; + +static void DrawClonedTile(void* data) { + SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024); + CloneData* cloneData = static_cast(data); + cloneData->fCanvas->drawPicture(*cloneData->fClone); +} + +CloneData::CloneData(SkCanvas* target, SkPicture* clone) +: fCanvas(target) +, fClone(clone) +, fThread(&DrawClonedTile, static_cast(this)) {} + +/////////////////////////////////////////////////////////////////////////////////////////////// + void TiledPictureRenderer::drawTiles() { - for (int i = 0; i < fTiles.count(); ++i) { - fTiles[i]->drawPicture(*(fPicture)); + if (fMultiThreaded) { + if (fUsePipe) { + // First, draw into a pipe controller + SkGPipeWriter writer; + DeferredPipeController controller(fTiles.count()); + SkCanvas* pipeCanvas = writer.startRecording(&controller, + SkGPipeWriter::kSimultaneousReaders_Flag); + pipeCanvas->drawPicture(*(fPicture)); + writer.endRecording(); + + // Create and start the threads. + TileData* tileData[fTiles.count()]; + for (int i = 0; i < fTiles.count(); i++) { + tileData[i] = SkNEW_ARGS(TileData, (fTiles[i], &controller)); + if (!tileData[i]->fThread.start()) { + SkDebugf("could not start thread %i\n", i); + } + } + for (int i = 0; i < fTiles.count(); i++) { + tileData[i]->fThread.join(); + SkDELETE(tileData[i]); + } + } else { + SkPicture* clones = SkNEW_ARRAY(SkPicture, fTiles.count()); + SkAutoTDeleteArray autodelete(clones); + fPicture->clone(clones, fTiles.count()); + CloneData* cloneData[fTiles.count()]; + for (int i = 0; i < fTiles.count(); i++) { + cloneData[i] = SkNEW_ARGS(CloneData, (fTiles[i], &clones[i])); + if (!cloneData[i]->fThread.start()) { + SkDebugf("Could not start picture thread %i\n", i); + } + } + for (int i = 0; i < fTiles.count(); i++) { + cloneData[i]->fThread.join(); + SkDELETE(cloneData[i]); + } + } + } else { + for (int i = 0; i < fTiles.count(); ++i) { + fTiles[i]->drawPicture(*(fPicture)); + } } } diff --git a/tools/PictureRenderer.h b/tools/PictureRenderer.h index 25e2ba4721..c901903cfe 100644 --- a/tools/PictureRenderer.h +++ b/tools/PictureRenderer.h @@ -163,12 +163,30 @@ public: return fTiles.count(); } + void setMultiThreaded(bool multi) { + fMultiThreaded = multi; + } + + bool isMultiThreaded() const { + return fMultiThreaded; + } + + void setUsePipe(bool usePipe) { + fUsePipe = usePipe; + } + + bool isUsePipe() const { + return fUsePipe; + } + ~TiledPictureRenderer(); protected: virtual void finishDraw(); private: + bool fMultiThreaded; + bool fUsePipe; int fTileWidth; int fTileHeight; double fTileWidthPercentage; diff --git a/tools/bench_pictures_main.cpp b/tools/bench_pictures_main.cpp index 1579f692f4..084a971acd 100644 --- a/tools/bench_pictures_main.cpp +++ b/tools/bench_pictures_main.cpp @@ -23,8 +23,9 @@ static void usage(const char* argv0) { "Usage: \n" " %s ...\n" " [--repeat] \n" -" [--mode pipe | pow2tile minWidth height[%] | record | simple\n" -" | tile width[%] height[%] | unflatten]\n" +" [--mode pow2tile minWidth height[] (multi) | record | simple\n" +" | tile width[] height[] (multi) | unflatten]\n" +" [--pipe]\n" " [--device bitmap" #if SK_SUPPORT_GPU " | gpu" @@ -36,32 +37,37 @@ static void usage(const char* argv0) { " inputDir: A list of directories and files to use as input. Files are\n" " expected to have the .skp extension.\n\n"); SkDebugf( -" --mode pipe | pow2tile minWidht height[%] | record | simple\n" -" | tile width[%] height[%] | unflatten: Run in the corresponding mode.\n" -" Default is simple.\n"); +" --mode pow2tile minWidht height[] (multi) | record | simple\n" +" | tile width[] height[] (multi) | unflatten:\n" +" Run in the corresponding mode.\n" +" Default is simple.\n"); SkDebugf( -" pipe, Benchmark SkGPipe rendering.\n"); - SkDebugf( -" pow2tile minWidth height[%], Creates tiles with widths\n" -" that are all a power of two\n" -" such that they minimize the\n" -" amount of wasted tile space.\n" -" minWidth is the minimum width\n" -" of these tiles and must be a\n" -" power of two. Simple\n" -" rendering using these tiles\n" -" is benchmarked.\n"); +" pow2tile minWidth height[], Creates tiles with widths\n" +" that are all a power of two\n" +" such that they minimize the\n" +" amount of wasted tile space.\n" +" minWidth is the minimum width\n" +" of these tiles and must be a\n" +" power of two. Simple\n" +" rendering using these tiles\n" +" is benchmarked.\n" +" Append \"multi\" for multithreaded\n" +" drawing.\n"); SkDebugf( " record, Benchmark picture to picture recording.\n"); SkDebugf( " simple, Benchmark a simple rendering.\n"); SkDebugf( -" tile width[%] height[%], Benchmark simple rendering using\n" -" tiles with the given dimensions.\n"); +" tile width[] height[], Benchmark simple rendering using\n" +" tiles with the given dimensions.\n" +" Append \"multi\" for multithreaded\n" +" drawing.\n"); SkDebugf( " unflatten, Benchmark picture unflattening.\n"); SkDebugf("\n"); SkDebugf( +" --pipe: Benchmark SkGPipe rendering. Compatible with tiled, multithreaded rendering.\n"); + SkDebugf( " --device bitmap" #if SK_SUPPORT_GPU " | gpu" @@ -112,6 +118,13 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* sk_tools::PictureRenderer::SkDeviceTypes deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType; + bool usePipe = false; + bool multiThreaded = false; + 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, "--repeat")) { ++argv; @@ -128,6 +141,8 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* usage(argv0); exit(-1); } + } else if (0 == strcmp(*argv, "--pipe")) { + usePipe = true; } else if (0 == strcmp(*argv, "--mode")) { SkDELETE(benchmark); @@ -138,82 +153,40 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* exit(-1); } - if (0 == strcmp(*argv, "pipe")) { - benchmark = SkNEW(sk_tools::PipePictureBenchmark); - } else if (0 == strcmp(*argv, "record")) { + if (0 == strcmp(*argv, "record")) { benchmark = SkNEW(sk_tools::RecordPictureBenchmark); } else if (0 == strcmp(*argv, "simple")) { benchmark = SkNEW(sk_tools::SimplePictureBenchmark); } 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::TiledPictureBenchmark* tileBenchmark = - SkNEW(sk_tools::TiledPictureBenchmark); ++argv; if (argv >= stop) { - SkDELETE(tileBenchmark); SkDebugf("Missing width for --mode %s\n", mode); usage(argv0); exit(-1); } - if (isPowerOf2Mode) { - int minWidth = atoi(*argv); - - if (!SkIsPow2(minWidth) || minWidth <= 0) { - SkDELETE(tileBenchmark); - SkDebugf("--mode %s must be given a width" - " value that is a power of two\n", mode); - exit(-1); - } - - tileBenchmark->setTileMinPowerOf2Width(minWidth); - } else if (sk_tools::is_percentage(*argv)) { - tileBenchmark->setTileWidthPercentage(atof(*argv)); - if (!(tileBenchmark->getTileWidthPercentage() > 0)) { - SkDELETE(tileBenchmark); - SkDebugf("--mode %s must be given a width percentage > 0\n", mode); - exit(-1); - } - } else { - tileBenchmark->setTileWidth(atoi(*argv)); - if (!(tileBenchmark->getTileWidth() > 0)) { - SkDELETE(tileBenchmark); - SkDebugf("--mode %s must be given a width > 0\n", mode); - exit(-1); - } - } - + widthString = *argv; ++argv; if (argv >= stop) { - SkDELETE(tileBenchmark); - SkDebugf("Missing height for --mode %s\n", mode); + SkDebugf("Missing height for --mode tile\n"); usage(argv0); exit(-1); } + heightString = *argv; - if (sk_tools::is_percentage(*argv)) { - tileBenchmark->setTileHeightPercentage(atof(*argv)); - if (!(tileBenchmark->getTileHeightPercentage() > 0)) { - SkDELETE(tileBenchmark); - SkDebugf("--mode %s must be given a height percentage > 0\n", mode); - exit(-1); - } + ++argv; + if (argv < stop && 0 == strcmp(*argv, "multi")) { + multiThreaded = true; } else { - tileBenchmark->setTileHeight(atoi(*argv)); - if (!(tileBenchmark->getTileHeight() > 0)) { - SkDELETE(tileBenchmark); - SkDebugf("--mode %s must be given a height > 0\n", mode); - exit(-1); - } + --argv; } - - benchmark = tileBenchmark; } else if (0 == strcmp(*argv, "unflatten")) { benchmark = SkNEW(sk_tools::UnflattenPictureBenchmark); } else { @@ -252,6 +225,55 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* } } + if (useTiles) { + sk_tools::TiledPictureBenchmark* tileBenchmark = SkNEW(sk_tools::TiledPictureBenchmark); + if (isPowerOf2Mode) { + int minWidth = atoi(widthString); + if (!SkIsPow2(minWidth) || minWidth < 0) { + SkDELETE(tileBenchmark); + SkDebugf("--mode %s must be given a width" + " value that is a power of two\n", mode); + exit(-1); + } + tileBenchmark->setTileMinPowerOf2Width(minWidth); + } else if (sk_tools::is_percentage(widthString)) { + tileBenchmark->setTileWidthPercentage(atof(widthString)); + if (!(tileBenchmark->getTileWidthPercentage() > 0)) { + SkDELETE(tileBenchmark); + SkDebugf("--mode tile must be given a width percentage > 0\n"); + exit(-1); + } + } else { + tileBenchmark->setTileWidth(atoi(widthString)); + if (!(tileBenchmark->getTileWidth() > 0)) { + SkDELETE(tileBenchmark); + SkDebugf("--mode tile must be given a width > 0\n"); + exit(-1); + } + } + + if (sk_tools::is_percentage(heightString)) { + tileBenchmark->setTileHeightPercentage(atof(heightString)); + if (!(tileBenchmark->getTileHeightPercentage() > 0)) { + SkDELETE(tileBenchmark); + SkDebugf("--mode tile must be given a height percentage > 0\n"); + exit(-1); + } + } else { + tileBenchmark->setTileHeight(atoi(heightString)); + if (!(tileBenchmark->getTileHeight() > 0)) { + SkDELETE(tileBenchmark); + SkDebugf("--mode tile must be given a height > 0\n"); + exit(-1); + } + } + tileBenchmark->setThreading(multiThreaded); + tileBenchmark->setUsePipe(usePipe); + benchmark = tileBenchmark; + } else if (usePipe) { + SkDELETE(benchmark); + benchmark = SkNEW(sk_tools::PipePictureBenchmark); + } if (inputs->count() < 1) { SkDELETE(benchmark); usage(argv0); diff --git a/tools/picture_utils.cpp b/tools/picture_utils.cpp index d5caf0c7dd..fb5f37cbc3 100644 --- a/tools/picture_utils.cpp +++ b/tools/picture_utils.cpp @@ -80,7 +80,7 @@ namespace sk_tools { basename->set(path.c_str(), end + 1); } - bool is_percentage(char* const string) { + bool is_percentage(const char* const string) { SkString skString(string); return skString.endsWith("%"); } diff --git a/tools/picture_utils.h b/tools/picture_utils.h index 18352f2bdf..bbde7f2789 100644 --- a/tools/picture_utils.h +++ b/tools/picture_utils.h @@ -34,7 +34,7 @@ namespace sk_tools { void get_basename(SkString* basename, const SkString& path); // Returns true if the string ends with % - bool is_percentage(char* const string); + bool is_percentage(const char* const string); // Prints to STDOUT so that test results can be easily seperated from the // error stream. Note, that this still prints to the same stream as SkDebugf -- cgit v1.2.3