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
This commit is contained in:
parent
1dfe88e00a
commit
b6e806bf17
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user