From 81f9d2e05be4902993345dac93337158345c660b Mon Sep 17 00:00:00 2001 From: "scroggo@google.com" Date: Thu, 20 Sep 2012 14:54:21 +0000 Subject: [PATCH] In render_pictures tiled rendering, draw a separate PNG for each tile. Since the passed in picture may represent an image which is too large to be represented on the GPU, never create such a large canvas. Instead, after drawing to each tile, create a file showing just that tile. Review URL: https://codereview.appspot.com/6532056 git-svn-id: http://skia.googlecode.com/svn/trunk@5603 2bbb7eff-a529-9590-31e7-b0007b416f81 --- tools/PictureBenchmark.cpp | 2 +- tools/PictureRenderer.cpp | 55 +++++++++++++++++++--------------- tools/PictureRenderer.h | 37 +++++++++++++++-------- tools/render_pictures_main.cpp | 25 ++++++---------- 4 files changed, 66 insertions(+), 53 deletions(-) diff --git a/tools/PictureBenchmark.cpp b/tools/PictureBenchmark.cpp index 7879414de..0b4f56ebe 100644 --- a/tools/PictureBenchmark.cpp +++ b/tools/PictureBenchmark.cpp @@ -71,7 +71,7 @@ void PictureBenchmark::run(SkPicture* pict) { // We throw this away to remove first time effects (such as paging in this program) fRenderer->setup(); - fRenderer->render(false); + fRenderer->render(NULL); fRenderer->resetState(); BenchTimer* timer = this->setupTimer(); diff --git a/tools/PictureRenderer.cpp b/tools/PictureRenderer.cpp index 96649de3b..85bd194bb 100644 --- a/tools/PictureRenderer.cpp +++ b/tools/PictureRenderer.cpp @@ -99,38 +99,43 @@ void PictureRenderer::resetState() { #endif } -bool PictureRenderer::write(const SkString& path) const { - SkASSERT(fCanvas.get() != NULL); +bool PictureRenderer::write(SkCanvas* canvas, SkString path) const { + SkASSERT(canvas != NULL); SkASSERT(fPicture != NULL); - if (NULL == fCanvas.get() || NULL == fPicture) { + if (NULL == canvas || NULL == fPicture) { return false; } SkBitmap bitmap; - sk_tools::setup_bitmap(&bitmap, fPicture->width(), fPicture->height()); + SkISize size = canvas->getDeviceSize(); + sk_tools::setup_bitmap(&bitmap, size.width(), size.height()); - fCanvas->readPixels(&bitmap, 0, 0); + canvas->readPixels(&bitmap, 0, 0); sk_tools::force_all_opaque(bitmap); + // Since path is passed in by value, it is okay to modify it. + path.append(".png"); return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100); } /////////////////////////////////////////////////////////////////////////////////////////////// -void RecordPictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) { +bool RecordPictureRenderer::render(const SkString*) { SkPicture replayer; SkCanvas* recorder = replayer.beginRecording(fPicture->width(), fPicture->height()); fPicture->draw(recorder); replayer.endRecording(); + // Since this class does not actually render, return false. + return false; } /////////////////////////////////////////////////////////////////////////////////////////////// -void PipePictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) { +bool PipePictureRenderer::render(const SkString* path) { SkASSERT(fCanvas.get() != NULL); SkASSERT(fPicture != NULL); if (NULL == fCanvas.get() || NULL == fPicture) { - return; + return false; } PipeController pipeController(fCanvas.get()); @@ -139,19 +144,21 @@ void PipePictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) { pipeCanvas->drawPicture(*fPicture); writer.endRecording(); fCanvas->flush(); + return path != NULL && this->write(fCanvas, *path); } /////////////////////////////////////////////////////////////////////////////////////////////// -void SimplePictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) { +bool SimplePictureRenderer::render(const SkString* path) { SkASSERT(fCanvas.get() != NULL); SkASSERT(fPicture != NULL); if (NULL == fCanvas.get() || NULL == fPicture) { - return; + return false; } fCanvas->drawPicture(*fPicture); fCanvas->flush(); + return path != NULL && this->write(fCanvas, *path); } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -398,16 +405,10 @@ void TiledPictureRenderer::setup() { } } -void TiledPictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) { +bool TiledPictureRenderer::render(const SkString* path) { SkASSERT(fPicture != NULL); if (NULL == fPicture) { - return; - } - - if (doExtraWorkToDrawToBaseCanvas) { - if (NULL == fCanvas.get()) { - fCanvas.reset(this->INHERITED::setupCanvas()); - } + return false; } if (this->multiThreaded()) { @@ -437,6 +438,8 @@ void TiledPictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) { SkDELETE(thread); } threads.reset(); + // Currently multithreaded is not an option for render_pictures + return false; } else { // For single thread, we really only need one canvas total. SkCanvas* canvas = this->setupCanvas(fTileWidth, fTileHeight); @@ -444,13 +447,15 @@ void TiledPictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) { for (int i = 0; i < fTileRects.count(); ++i) { DrawTileToCanvas(canvas, fTileRects[i], fPicture); - if (doExtraWorkToDrawToBaseCanvas) { - SkASSERT(fCanvas.get() != NULL); - SkBitmap source = canvas->getDevice()->accessBitmap(false); - fCanvas->drawBitmap(source, fTileRects[i].fLeft, fTileRects[i].fTop); - fCanvas->flush(); + if (path != NULL) { + SkString tilePath(*path); + tilePath.appendf("%i", i); + if (!this->write(canvas, tilePath)) { + return false; + } } } + return path != NULL; } } @@ -474,8 +479,10 @@ void PlaybackCreationRenderer::setup() { fPicture->draw(recorder); } -void PlaybackCreationRenderer::render(bool doExtraWorkToDrawToBaseCanvas) { +bool PlaybackCreationRenderer::render(const SkString*) { fReplayer.endRecording(); + // Since this class does not actually render, return false. + return false; } } diff --git a/tools/PictureRenderer.h b/tools/PictureRenderer.h index 845aa9f73..f8133ea4a 100644 --- a/tools/PictureRenderer.h +++ b/tools/PictureRenderer.h @@ -47,12 +47,12 @@ public: /** * Perform work that is to be timed. Typically this is rendering, but is also used for recording * and preparing picture for playback by the subclasses which do those. - * @param doExtraWorkToDrawToBaseCanvas Perform extra work to draw to fCanvas. Some subclasses - * will automatically draw to fCanvas, but in the tiled - * case, for example, true needs to be passed so that - * the tiles will be stitched together on fCanvas. + * If path is non-null, subclass implementations should call write(). + * @param path If non-null, also write the output to the file specified by path. path should + * have no extension; it will be added by write(). + * @return bool True if path is non-null and the output is successfully written to a file. */ - virtual void render(bool doExtraWorkToDrawToBaseCanvas) = 0; + virtual bool render(const SkString* path) = 0; virtual void end(); void resetState(); @@ -95,9 +95,14 @@ public: #endif {} - bool write(const SkString& path) const; - 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); @@ -119,7 +124,7 @@ private: * to time. */ class RecordPictureRenderer : public PictureRenderer { - virtual void render(bool doExtraWorkToDrawToBaseCanvas) SK_OVERRIDE; + virtual bool render(const SkString*) SK_OVERRIDE; virtual SkString getPerIterTimeFormat() SK_OVERRIDE { return SkString("%.4f"); } @@ -128,7 +133,7 @@ class RecordPictureRenderer : public PictureRenderer { class PipePictureRenderer : public PictureRenderer { public: - virtual void render(bool doExtraWorkToDrawToBaseCanvas) SK_OVERRIDE; + virtual bool render(const SkString*) SK_OVERRIDE; private: typedef PictureRenderer INHERITED; @@ -136,7 +141,7 @@ private: class SimplePictureRenderer : public PictureRenderer { public: - virtual void render(bool doExtraWorkToDrawToBaseCanvas) SK_OVERRIDE; + virtual bool render(const SkString*) SK_OVERRIDE; private: typedef PictureRenderer INHERITED; @@ -147,8 +152,16 @@ public: TiledPictureRenderer(); virtual void init(SkPicture* pict) SK_OVERRIDE; + virtual void setup() SK_OVERRIDE; - virtual void render(bool doExtraWorkToDrawToBaseCanvas) SK_OVERRIDE; + + /** + * Renders to tiles, rather than a single canvas. If a path is provided, a separate file is + * created for each tile, named "path0.png", "path1.png", etc. + * Multithreaded mode currently does not support writing to a file. + */ + virtual bool render(const SkString* path) SK_OVERRIDE; + virtual void end() SK_OVERRIDE; void setTileWidth(int width) { @@ -241,7 +254,7 @@ class PlaybackCreationRenderer : public PictureRenderer { public: virtual void setup() SK_OVERRIDE; - virtual void render(bool doExtraWorkToDrawToBaseCanvas) SK_OVERRIDE; + virtual bool render(const SkString*) SK_OVERRIDE; virtual SkString getPerIterTimeFormat() SK_OVERRIDE { return SkString("%.4f"); } diff --git a/tools/render_pictures_main.cpp b/tools/render_pictures_main.cpp index 178d9ba43..5a7ff44e0 100644 --- a/tools/render_pictures_main.cpp +++ b/tools/render_pictures_main.cpp @@ -75,19 +75,8 @@ static void usage(const char* argv0) { static void make_output_filepath(SkString* path, const SkString& dir, const SkString& name) { sk_tools::make_filepath(path, dir, name); - path->remove(path->size() - 3, 3); - path->append("png"); -} - -static bool write_output(const SkString& outputDir, const SkString& inputFilename, - const sk_tools::PictureRenderer& renderer) { - SkString outputPath; - make_output_filepath(&outputPath, outputDir, inputFilename); - bool isWritten = renderer.write(outputPath); - if (!isWritten) { - SkDebugf("Could not write to file %s\n", outputPath.c_str()); - } - return isWritten; + // Remove ".skp" + path->remove(path->size() - 4, 4); } static bool render_picture(const SkString& inputPath, const SkString& outputDir, @@ -118,12 +107,16 @@ static bool render_picture(const SkString& inputPath, const SkString& outputDir, renderer.init(aur); - renderer.render(true); + SkString outputPath; + make_output_filepath(&outputPath, outputDir, inputFilename); + + success = renderer.render(&outputPath); + if (!success) { + SkDebugf("Could not write to file %s\n", outputPath.c_str()); + } renderer.resetState(); - success = write_output(outputDir, inputFilename, renderer); - renderer.end(); return success; }