aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools
diff options
context:
space:
mode:
authorGravatar scroggo@google.com <scroggo@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-09-20 14:42:33 +0000
committerGravatar scroggo@google.com <scroggo@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-09-20 14:42:33 +0000
commitbcdf2ec50dfd170959cc2db67c49f6dac084be03 (patch)
treed6437193532ae252675b7f1de17f39a6fe418a0a /tools
parentb4ca9df976951adf632388371f9a8a9219d93014 (diff)
In bench_pictures, use a pool of tiles for multicore drawing.
Also includes some code cleanup and code sharing. Allow setting the number of threads on the command line. Rename ThreadSafePipeController::playback to ::draw, to be the same as SkPicture so DrawTileToCanvas can take a template parameter. Disallow multithreading with GPU turned on. Display help information with improper tiled arguments. BUG=https://code.google.com/p/skia/issues/detail?id=871 Review URL: https://codereview.appspot.com/6536050 git-svn-id: http://skia.googlecode.com/svn/trunk@5602 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'tools')
-rw-r--r--tools/PictureRenderer.cpp250
-rw-r--r--tools/PictureRenderer.h35
-rw-r--r--tools/bench_pictures_main.cpp64
3 files changed, 234 insertions, 115 deletions
diff --git a/tools/PictureRenderer.cpp b/tools/PictureRenderer.cpp
index cea7b5bff5..96649de3b8 100644
--- a/tools/PictureRenderer.cpp
+++ b/tools/PictureRenderer.cpp
@@ -157,13 +157,16 @@ void SimplePictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) {
///////////////////////////////////////////////////////////////////////////////////////////////
TiledPictureRenderer::TiledPictureRenderer()
- : fMultiThreaded(false)
- , fUsePipe(false)
+ : fUsePipe(false)
, fTileWidth(kDefaultTileWidth)
, fTileHeight(kDefaultTileHeight)
, fTileWidthPercentage(0.0)
, fTileHeightPercentage(0.0)
- , fTileMinPowerOf2Width(0) { }
+ , fTileMinPowerOf2Width(0)
+ , fTileCounter(0)
+ , fNumThreads(1)
+ , fPictureClones(NULL)
+ , fPipeController(NULL) { }
void TiledPictureRenderer::init(SkPicture* pict) {
SkASSERT(pict != NULL);
@@ -188,15 +191,40 @@ 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);
+ fPictureClones->clone(fPictureClones, numberOfClones);
+ }
+ }
}
void TiledPictureRenderer::end() {
- this->deleteTiles();
+ fTileRects.reset();
+ SkDELETE_ARRAY(fPictureClones);
+ fPictureClones = NULL;
+ fCanvasPool.unrefAll();
+ if (fPipeController != NULL) {
+ SkASSERT(fUsePipe);
+ SkDELETE(fPipeController);
+ fPipeController = NULL;
+ }
this->INHERITED::end();
}
TiledPictureRenderer::~TiledPictureRenderer() {
- this->deleteTiles();
+ // end() must be called to delete fPictureClones and fPipeController
+ SkASSERT(NULL == fPictureClones);
+ SkASSERT(NULL == fPipeController);
}
void TiledPictureRenderer::setupTiles() {
@@ -252,56 +280,124 @@ void TiledPictureRenderer::setupPowerOf2Tiles() {
}
}
-void TiledPictureRenderer::deleteTiles() {
- fTileRects.reset();
+/**
+ * Draw the specified playback to the canvas translated to rectangle provided, so that this mini
+ * canvas represents the rectangle's portion of the overall picture.
+ * Saves and restores so that the initial clip and matrix return to their state before this function
+ * is called.
+ */
+template<class T>
+static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
+ int saveCount = canvas->save();
+ // Translate so that we draw the correct portion of the picture
+ canvas->translate(-tileRect.fLeft, -tileRect.fTop);
+ playback->draw(canvas);
+ canvas->restoreToCount(saveCount);
+ canvas->flush();
}
///////////////////////////////////////////////////////////////////////////////////////////////
+// Base class for data used both by pipe and clone picture multi threaded drawing.
+
+struct ThreadData {
+ ThreadData(SkCanvas* target, int* tileCounter, SkTDArray<SkRect>* tileRects)
+ : fCanvas(target)
+ , fTileCounter(tileCounter)
+ , fTileRects(tileRects) {
+ SkASSERT(target != NULL && tileCounter != NULL && tileRects != NULL);
+ }
+
+ const SkRect* nextTile() {
+ if (int32_t i = sk_atomic_inc(fTileCounter) < fTileRects->count()) {
+ return &fTileRects->operator[](i);
+ }
+ return NULL;
+ }
+
+ // All of these are pointers to objects owned elsewhere
+ SkCanvas* fCanvas;
+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 {
- TileData(SkCanvas* canvas, ThreadSafePipeController* controller);
- SkCanvas* fCanvas;
+struct TileData : public ThreadData {
+ TileData(ThreadSafePipeController* controller, SkCanvas* canvas, int* tileCounter,
+ SkTDArray<SkRect>* tileRects)
+ : INHERITED(canvas, tileCounter, tileRects)
+ , fController(controller) {}
+
ThreadSafePipeController* fController;
- SkThread fThread;
+
+ typedef ThreadData INHERITED;
};
static void DrawTile(void* data) {
SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
TileData* tileData = static_cast<TileData*>(data);
- tileData->fController->playback(tileData->fCanvas);
- tileData->fCanvas->flush();
-}
-TileData::TileData(SkCanvas* canvas, ThreadSafePipeController* controller)
-: fCanvas(canvas)
-, fController(controller)
-, fThread(&DrawTile, static_cast<void*>(this)) {}
+ const SkRect* tileRect;
+ while ((tileRect = tileData->nextTile()) != NULL) {
+ DrawTileToCanvas(tileData->fCanvas, *tileRect, tileData->fController);
+ }
+ SkDELETE(tileData);
+}
///////////////////////////////////////////////////////////////////////////////////////////////
// Draw using Picture
-struct CloneData {
- CloneData(SkCanvas* target, SkPicture* original);
- SkCanvas* fCanvas;
+struct CloneData : public ThreadData {
+ CloneData(SkPicture* clone, SkCanvas* target, int* tileCounter, SkTDArray<SkRect>* tileRects)
+ : INHERITED(target, tileCounter, tileRects)
+ , fClone(clone) {}
+
SkPicture* fClone;
- SkThread fThread;
+
+ typedef ThreadData INHERITED;
};
-static void DrawClonedTile(void* data) {
+static void DrawClonedTiles(void* data) {
SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
CloneData* cloneData = static_cast<CloneData*>(data);
- cloneData->fCanvas->drawPicture(*cloneData->fClone);
- cloneData->fCanvas->flush();
-}
-CloneData::CloneData(SkCanvas* target, SkPicture* clone)
-: fCanvas(target)
-, fClone(clone)
-, fThread(&DrawClonedTile, static_cast<void*>(this)) {}
+ const SkRect* tileRect;
+ while ((tileRect = cloneData->nextTile()) != NULL) {
+ DrawTileToCanvas(cloneData->fCanvas, *tileRect, cloneData->fClone);
+ }
+ 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();
+ }
+ }
+}
+
void TiledPictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) {
SkASSERT(fPicture != NULL);
if (NULL == fPicture) {
@@ -310,71 +406,44 @@ void TiledPictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) {
if (doExtraWorkToDrawToBaseCanvas) {
if (NULL == fCanvas.get()) {
- fCanvas.reset(this->setupCanvas());
+ fCanvas.reset(this->INHERITED::setupCanvas());
}
}
- if (fMultiThreaded) {
- // FIXME: Turning off multi threading while we transition to using a pool of tiles
-/*
- if (fUsePipe) {
- // First, draw into a pipe controller
- SkGPipeWriter writer;
- ThreadSafePipeController controller(fTiles.count());
- SkCanvas* pipeCanvas = writer.startRecording(&controller,
- SkGPipeWriter::kSimultaneousReaders_Flag);
- pipeCanvas->drawPicture(*(fPicture));
- writer.endRecording();
-
- // Create and start the threads.
- TileData** tileData = SkNEW_ARRAY(TileData*, fTiles.count());
- SkAutoTDeleteArray<TileData*> deleteTileData(tileData);
- 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]);
+ if (this->multiThreaded()) {
+ SkASSERT(fCanvasPool.count() == fNumThreads);
+ SkTDArray<SkThread*> threads;
+ SkThread::entryPointProc proc = fUsePipe ? DrawTile : DrawClonedTiles;
+ 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));
+ } else {
+ SkPicture* pic = (0 == i) ? fPicture : &fPictureClones[i-1];
+ data = SkNEW_ARGS(CloneData, (pic, fCanvasPool[i], &fTileCounter, &fTileRects));
}
- } else {
- SkPicture* clones = SkNEW_ARRAY(SkPicture, fTiles.count());
- SkAutoTDeleteArray<SkPicture> autodelete(clones);
- fPicture->clone(clones, fTiles.count());
- CloneData** cloneData = SkNEW_ARRAY(CloneData*, fTiles.count());
- SkAutoTDeleteArray<CloneData*> deleteCloneData(cloneData);
- 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]);
+ 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();
} else {
- // For single thread, we really only need one canvas total
+ // For single thread, we really only need one canvas total.
SkCanvas* canvas = this->setupCanvas(fTileWidth, fTileHeight);
SkAutoUnref aur(canvas);
- // Clip the tile to an area that is completely in what the SkPicture says is the
- // drawn-to area. This is mostly important for tiles on the right and bottom edges
- // as they may go over this area and the picture may have some commands that
- // draw outside of this area and so should not actually be written.
- SkRect clip = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
- SkIntToScalar(fPicture->height()));
for (int i = 0; i < fTileRects.count(); ++i) {
- canvas->resetMatrix();
- canvas->clipRect(clip);
- // Translate so that we draw the correct portion of the picture
- canvas->translate(-fTileRects[i].fLeft, -fTileRects[i].fTop);
- canvas->drawPicture(*(fPicture));
- canvas->flush();
+ DrawTileToCanvas(canvas, fTileRects[i], fPicture);
if (doExtraWorkToDrawToBaseCanvas) {
SkASSERT(fCanvas.get() != NULL);
SkBitmap source = canvas->getDevice()->accessBitmap(false);
@@ -385,6 +454,19 @@ void TiledPictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) {
}
}
+SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
+ SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
+ SkASSERT(fPicture != NULL);
+ // Clip the tile to an area that is completely in what the SkPicture says is the
+ // drawn-to area. This is mostly important for tiles on the right and bottom edges
+ // as they may go over this area and the picture may have some commands that
+ // draw outside of this area and so should not actually be written.
+ SkRect clip = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
+ SkIntToScalar(fPicture->height()));
+ canvas->clipRect(clip);
+ return canvas;
+}
+
///////////////////////////////////////////////////////////////////////////////////////////////
void PlaybackCreationRenderer::setup() {
diff --git a/tools/PictureRenderer.h b/tools/PictureRenderer.h
index 5ae3f29443..845aa9f73e 100644
--- a/tools/PictureRenderer.h
+++ b/tools/PictureRenderer.h
@@ -23,6 +23,7 @@
class SkBitmap;
class SkCanvas;
class SkGLContext;
+class ThreadSafePipeController;
namespace sk_tools {
@@ -98,7 +99,7 @@ public:
protected:
SkCanvas* setupCanvas();
- SkCanvas* setupCanvas(int width, int height);
+ virtual SkCanvas* setupCanvas(int width, int height);
SkAutoTUnref<SkCanvas> fCanvas;
SkPicture* fPicture;
@@ -146,6 +147,7 @@ public:
TiledPictureRenderer();
virtual void init(SkPicture* pict) SK_OVERRIDE;
+ virtual void setup() SK_OVERRIDE;
virtual void render(bool doExtraWorkToDrawToBaseCanvas) SK_OVERRIDE;
virtual void end() SK_OVERRIDE;
@@ -194,8 +196,11 @@ public:
return fTileMinPowerOf2Width;
}
- void setMultiThreaded(bool multi) {
- fMultiThreaded = multi;
+ /**
+ * 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) {
@@ -205,19 +210,25 @@ public:
~TiledPictureRenderer();
private:
- bool fMultiThreaded;
- bool fUsePipe;
- int fTileWidth;
- int fTileHeight;
- double fTileWidthPercentage;
- double fTileHeightPercentage;
- int fTileMinPowerOf2Width;
-
+ bool fUsePipe;
+ int fTileWidth;
+ int fTileHeight;
+ double fTileWidthPercentage;
+ double fTileHeightPercentage;
+ 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 setupPowerOf2Tiles();
- void deleteTiles();
+ virtual SkCanvas* setupCanvas(int width, int height) SK_OVERRIDE;
+ bool multiThreaded() { return fNumThreads > 1; }
typedef PictureRenderer INHERITED;
};
diff --git a/tools/bench_pictures_main.cpp b/tools/bench_pictures_main.cpp
index 626ee52658..6cbb991e73 100644
--- a/tools/bench_pictures_main.cpp
+++ b/tools/bench_pictures_main.cpp
@@ -26,9 +26,10 @@ static void usage(const char* argv0) {
" %s <inputDir>...\n"
" [--logFile filename][--timers [wcgWC]*][--logPerIter 1|0][--min]\n"
" [--repeat] \n"
-" [--mode pow2tile minWidth height[] (multi) | record | simple\n"
-" | tile width[] height[] (multi) | playbackCreation]\n"
+" [--mode pow2tile minWidth height[] | record | simple\n"
+" | tile width[] height[] | playbackCreation]\n"
" [--pipe]\n"
+" [--multi numThreads]\n"
" [--device bitmap"
#if SK_SUPPORT_GPU
" | gpu"
@@ -46,8 +47,8 @@ static void usage(const char* argv0) {
SkDebugf(" --timers [wcgWC]* : "
"Display wall, cpu, gpu, truncated wall or truncated cpu time for each picture.\n");
SkDebugf(
-" --mode pow2tile minWidht height[] (multi) | record | simple\n"
-" | tile width[] height[] (multi) | playbackCreation:\n"
+" --mode pow2tile minWidht height[] | record | simple\n"
+" | tile width[] height[] | playbackCreation:\n"
" Run in the corresponding mode.\n"
" Default is simple.\n");
SkDebugf(
@@ -59,22 +60,20 @@ static void usage(const char* argv0) {
" 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");
+" is benchmarked.\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"
-" Append \"multi\" for multithreaded\n"
-" drawing.\n");
+" tiles with the given dimensions.\n");
SkDebugf(
" playbackCreation, Benchmark creation of the SkPicturePlayback.\n");
SkDebugf("\n");
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");
SkDebugf(
" --device bitmap"
@@ -154,7 +153,7 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
commandLine.append("\n");
bool usePipe = false;
- bool multiThreaded = false;
+ int numThreads = 1;
bool useTiles = false;
const char* widthString = NULL;
const char* heightString = NULL;
@@ -194,6 +193,19 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
usage(argv0);
exit(-1);
}
+ } else if (0 == strcmp(*argv, "--multi")) {
+ ++argv;
+ if (argv >= stop) {
+ gLogger.logError("Missing arg for --multi\n");
+ usage(argv0);
+ exit(-1);
+ }
+ numThreads = atoi(*argv);
+ if (numThreads < 2) {
+ gLogger.logError("Number of threads must be at least 2.\n");
+ usage(argv0);
+ exit(-1);
+ }
} else if (0 == strcmp(*argv, "--mode")) {
++argv;
@@ -232,13 +244,6 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
exit(-1);
}
heightString = *argv;
-
- ++argv;
- if (argv < stop && 0 == strcmp(*argv, "multi")) {
- multiThreaded = true;
- } else {
- --argv;
- }
} else if (0 == strcmp(*argv, "playbackCreation")) {
renderer = SkNEW(sk_tools::PlaybackCreationRenderer);
} else {
@@ -328,6 +333,12 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
}
}
+ if (numThreads > 1 && !useTiles) {
+ gLogger.logError("Multithreaded drawing requires tiled rendering.\n");
+ usage(argv0);
+ exit(-1);
+ }
+
if (useTiles) {
SkASSERT(NULL == renderer);
sk_tools::TiledPictureRenderer* tiledRenderer = SkNEW(sk_tools::TiledPictureRenderer);
@@ -339,6 +350,7 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
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);
}
tiledRenderer->setTileMinPowerOf2Width(minWidth);
@@ -347,6 +359,7 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
if (!(tiledRenderer->getTileWidthPercentage() > 0)) {
tiledRenderer->unref();
gLogger.logError("--mode tile must be given a width percentage > 0\n");
+ usage(argv0);
exit(-1);
}
} else {
@@ -354,6 +367,7 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
if (!(tiledRenderer->getTileWidth() > 0)) {
tiledRenderer->unref();
gLogger.logError("--mode tile must be given a width > 0\n");
+ usage(argv0);
exit(-1);
}
}
@@ -363,6 +377,7 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
if (!(tiledRenderer->getTileHeightPercentage() > 0)) {
tiledRenderer->unref();
gLogger.logError("--mode tile must be given a height percentage > 0\n");
+ usage(argv0);
exit(-1);
}
} else {
@@ -370,10 +385,21 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
if (!(tiledRenderer->getTileHeight() > 0)) {
tiledRenderer->unref();
gLogger.logError("--mode tile must be given a height > 0\n");
+ usage(argv0);
exit(-1);
}
}
- tiledRenderer->setMultiThreaded(multiThreaded);
+ if (numThreads > 1) {
+#if SK_SUPPORT_GPU
+ if (sk_tools::PictureRenderer::kGPU_DeviceType == deviceType) {
+ tiledRenderer->unref();
+ gLogger.logError("GPU not compatible with multithreaded tiling.\n");
+ usage(argv0);
+ exit(-1);
+ }
+#endif
+ tiledRenderer->setNumberOfThreads(numThreads);
+ }
tiledRenderer->setUsePipe(usePipe);
renderer = tiledRenderer;
} else if (usePipe) {