Allow render_pictures to render using multiple threads.
authorscroggo@google.com <scroggo@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 3 Oct 2012 17:32:33 +0000 (17:32 +0000)
committerscroggo@google.com <scroggo@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 3 Oct 2012 17:32:33 +0000 (17:32 +0000)
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

tools/PictureRenderer.cpp
tools/PictureRenderer.h
tools/render_pictures_main.cpp

index 2eb89b2..2478752 100644 (file)
@@ -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;
index f8133ea..4829e1d 100644 (file)
@@ -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);
 
index da0740a..477272d 100644 (file)
@@ -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);