diff --git a/include/core/SkScalar.h b/include/core/SkScalar.h index ed419689d8..4c3bdb277f 100644 --- a/include/core/SkScalar.h +++ b/include/core/SkScalar.h @@ -336,6 +336,12 @@ static inline SkScalar SkScalarInterp(SkScalar A, SkScalar B, SkScalar t) { return A + SkScalarMul(B - A, t); } +static inline SkScalar SkScalarLog2(SkScalar x) { + static const SkScalar log2_conversion_factor = SkScalarDiv(1, SkScalarLog(2)); + + return SkScalarMul(SkScalarLog(x), log2_conversion_factor); +} + /** Interpolate along the function described by (keys[length], values[length]) for the passed searchKey. SearchKeys outside the range keys[0]-keys[Length] clamp to the min or max value. This function was inspired by a desire diff --git a/tools/PictureBenchmark.cpp b/tools/PictureBenchmark.cpp index 4f7cbac202..df30d5a561 100644 --- a/tools/PictureBenchmark.cpp +++ b/tools/PictureBenchmark.cpp @@ -189,8 +189,14 @@ void TiledPictureBenchmark::run(SkPicture* pict) { } SkString result; - result.printf("%i_tiles_%ix%i: msecs = %6.2f", fRenderer.numTiles(), fRenderer.getTileWidth(), - fRenderer.getTileHeight(), wall_time / fRepeats); + if (fRenderer.getTileMinPowerOf2Width() > 0) { + result.printf("%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); + } #if SK_SUPPORT_GPU if (fRenderer.isUsingGpuDevice()) { result.appendf(" gmsecs = %6.2f", gpu_time / fRepeats); diff --git a/tools/PictureBenchmark.h b/tools/PictureBenchmark.h index 1cbc5e763c..f020c8d7e7 100644 --- a/tools/PictureBenchmark.h +++ b/tools/PictureBenchmark.h @@ -117,6 +117,14 @@ public: return fRenderer.getTileHeightPercentage(); } + void setTileMinPowerOf2Width(int width) { + fRenderer.setTileMinPowerOf2Width(width); + } + + int getTileMinPowerOf2Width() { + return fRenderer.getTileMinPowerOf2Width(); + } + private: TiledPictureRenderer fRenderer; typedef PictureBenchmark INHERITED; diff --git a/tools/PictureRenderer.cpp b/tools/PictureRenderer.cpp index ab7f7b9fc9..b162abc340 100644 --- a/tools/PictureRenderer.cpp +++ b/tools/PictureRenderer.cpp @@ -175,7 +175,11 @@ void TiledPictureRenderer::init(SkPicture* pict) { fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100)); } - this->setupTiles(); + if (fTileMinPowerOf2Width > 0) { + this->setupPowerOf2Tiles(); + } else { + this->setupTiles(); + } } void TiledPictureRenderer::render() { @@ -205,8 +209,8 @@ void TiledPictureRenderer::clipTile(SkCanvas* tile) { tile->clipRect(clip); } -void TiledPictureRenderer::addTile(int tile_x_start, int tile_y_start) { - SkCanvas* tile = this->setupCanvas(fTileWidth, fTileHeight); +void TiledPictureRenderer::addTile(int tile_x_start, int tile_y_start, int width, int height) { + SkCanvas* tile = this->setupCanvas(width, height); tile->translate(SkIntToScalar(-tile_x_start), SkIntToScalar(-tile_y_start)); this->clipTile(tile); @@ -219,7 +223,43 @@ void TiledPictureRenderer::setupTiles() { tile_y_start += fTileHeight) { for (int tile_x_start = 0; tile_x_start < fPicture->width(); tile_x_start += fTileWidth) { - this->addTile(tile_x_start, tile_y_start); + this->addTile(tile_x_start, tile_y_start, fTileWidth, fTileHeight); + } + } +} + +// The goal of the powers of two tiles is to minimize the amount of wasted tile +// space in the width-wise direction and then minimize the number of tiles. The +// 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 +// 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. +void TiledPictureRenderer::setupPowerOf2Tiles() { + int rounded_value = fPicture->width(); + if (fPicture->width() % fTileMinPowerOf2Width != 0) { + rounded_value = fPicture->width() - (fPicture->width() % fTileMinPowerOf2Width) + + fTileMinPowerOf2Width; + } + + int num_bits = SkScalarCeilToInt(SkScalarLog2(fPicture->width())); + int largest_possible_tile_size = 1 << num_bits; + + // The tile height is constant for a particular picture. + for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) { + int tile_x_start = 0; + int current_width = largest_possible_tile_size; + + while (current_width >= fTileMinPowerOf2Width) { + // It is very important this is a bitwise AND. + if (current_width & rounded_value) { + this->addTile(tile_x_start, tile_y_start, current_width, fTileHeight); + tile_x_start += current_width; + } + + current_width >>= 1; } } } diff --git a/tools/PictureRenderer.h b/tools/PictureRenderer.h index cd6e18e373..25e2ba4721 100644 --- a/tools/PictureRenderer.h +++ b/tools/PictureRenderer.h @@ -7,6 +7,7 @@ #ifndef PictureRenderer_DEFINED #define PictureRenderer_DEFINED +#include "SkMath.h" #include "SkTypes.h" #include "SkTDArray.h" #include "SkRefCnt.h" @@ -145,6 +146,19 @@ public: return fTileHeightPercentage; } + void setTileMinPowerOf2Width(int width) { + SkASSERT(SkIsPow2(width) && width > 0); + if (!SkIsPow2(width) || width <= 0) { + return; + } + + fTileMinPowerOf2Width = width; + } + + int getTileMinPowerOf2Width() const { + return fTileMinPowerOf2Width; + } + int numTiles() const { return fTiles.count(); } @@ -159,6 +173,7 @@ private: int fTileHeight; double fTileWidthPercentage; double fTileHeightPercentage; + int fTileMinPowerOf2Width; SkTDArray fTiles; @@ -167,8 +182,9 @@ private: // 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. void clipTile(SkCanvas* tile); - void addTile(int tile_x_start, int tile_y_start); + void addTile(int tile_x_start, int tile_y_start, int width, int height); void setupTiles(); + void setupPowerOf2Tiles(); void deleteTiles(); void copyTilesToCanvas(); diff --git a/tools/bench_pictures_main.cpp b/tools/bench_pictures_main.cpp index 88b54227e2..1579f692f4 100644 --- a/tools/bench_pictures_main.cpp +++ b/tools/bench_pictures_main.cpp @@ -8,6 +8,7 @@ #include "BenchTimer.h" #include "PictureBenchmark.h" #include "SkCanvas.h" +#include "SkMath.h" #include "SkOSFile.h" #include "SkPicture.h" #include "SkStream.h" @@ -22,7 +23,8 @@ static void usage(const char* argv0) { "Usage: \n" " %s ...\n" " [--repeat] \n" -" [--mode pipe | record | simple | tile width[%] height[%] | unflatten]\n" +" [--mode pipe | pow2tile minWidth height[%] | record | simple\n" +" | tile width[%] height[%] | unflatten]\n" " [--device bitmap" #if SK_SUPPORT_GPU " | gpu" @@ -34,11 +36,22 @@ 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 | record | simple | tile width[%] height[%] | unflatten: Run\n" -" in the corresponding mode. Default is simple.\n"); +" --mode pipe | pow2tile minWidht height[%] | record | simple\n" +" | tile width[%] height[%] | unflatten: 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"); + SkDebugf( " record, Benchmark picture to picture recording.\n"); SkDebugf( " simple, Benchmark a simple rendering.\n"); @@ -131,29 +144,47 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* benchmark = SkNEW(sk_tools::RecordPictureBenchmark); } else if (0 == strcmp(*argv, "simple")) { benchmark = SkNEW(sk_tools::SimplePictureBenchmark); - } else if (0 == strcmp(*argv, "tile")) { + } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))) { + char* mode = *argv; + bool isPowerOf2Mode = false; + + 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 tile\n"); + SkDebugf("Missing width for --mode %s\n", mode); usage(argv0); exit(-1); } - if (sk_tools::is_percentage(*argv)) { + 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 tile must be given a width percentage > 0\n"); + 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 tile must be given a width > 0\n"); + SkDebugf("--mode %s must be given a width > 0\n", mode); exit(-1); } } @@ -161,7 +192,7 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* ++argv; if (argv >= stop) { SkDELETE(tileBenchmark); - SkDebugf("Missing height for --mode tile\n"); + SkDebugf("Missing height for --mode %s\n", mode); usage(argv0); exit(-1); } @@ -170,14 +201,14 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* tileBenchmark->setTileHeightPercentage(atof(*argv)); if (!(tileBenchmark->getTileHeightPercentage() > 0)) { SkDELETE(tileBenchmark); - SkDebugf("--mode tile must be given a height percentage > 0\n"); + SkDebugf("--mode %s must be given a height percentage > 0\n", mode); exit(-1); } } else { tileBenchmark->setTileHeight(atoi(*argv)); if (!(tileBenchmark->getTileHeight() > 0)) { SkDELETE(tileBenchmark); - SkDebugf("--mode tile must be given a height > 0\n"); + SkDebugf("--mode %s must be given a height > 0\n", mode); exit(-1); } } diff --git a/tools/render_pictures_main.cpp b/tools/render_pictures_main.cpp index 998123051c..ec0969659e 100644 --- a/tools/render_pictures_main.cpp +++ b/tools/render_pictures_main.cpp @@ -8,6 +8,7 @@ #include "SkBitmap.h" #include "SkCanvas.h" #include "SkDevice.h" +#include "SkMath.h" #include "SkOSFile.h" #include "SkPicture.h" #include "SkStream.h" @@ -21,7 +22,8 @@ static void usage(const char* argv0) { SkDebugf("\n" "Usage: \n" " %s ... \n" -" [--mode pipe | simple | tile width[%] height[%]]\n" +" [--mode pipe | pow2tile minWidth height[%] | simple\n" +" | tile width[%] height[%]]\n" " [--device bitmap" #if SK_SUPPORT_GPU " | gpu" @@ -35,11 +37,21 @@ static void usage(const char* argv0) { SkDebugf( " outputDir: directory to write the rendered images.\n\n"); SkDebugf( -" --mode pipe | simple | tile width[%] height[%]: Run in the\n" -" corresponding mode. Default is simple.\n"); +" --mode pipe | 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" +" amount of wasted tile space.\n" +" minWidth is the minimum width\n" +" of these tiles and must be a\n" +" power of two. A simple render\n" +" is done with these tiles.\n"); + SkDebugf( " simple, Render using the default rendering method.\n"); SkDebugf( " tile width[%] height[%], Do a simple render using tiles\n" @@ -139,29 +151,47 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* renderer = SkNEW(sk_tools::PipePictureRenderer); } else if (0 == strcmp(*argv, "simple")) { renderer = SkNEW(sk_tools::SimplePictureRenderer); - } else if (0 == strcmp(*argv, "tile")) { + } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))) { + char* mode = *argv; + bool isPowerOf2Mode = false; + + 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 tile\n"); + SkDebugf("Missing width for --mode %s\n", mode); usage(argv0); exit(-1); } - if (sk_tools::is_percentage(*argv)) { + 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 tile must be given a width percentage > 0\n"); + 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 tile must be given a width > 0\n"); + SkDebugf("--mode %s must be given a width > 0\n", mode); exit(-1); } } @@ -169,7 +199,7 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* ++argv; if (argv >= stop) { SkDELETE(tileRenderer); - SkDebugf("Missing height for --mode tile\n"); + SkDebugf("Missing height for --mode %s\n", mode); usage(argv0); exit(-1); } @@ -179,14 +209,14 @@ static void parse_commandline(int argc, char* const argv[], SkTArray* if (!(tileRenderer->getTileHeightPercentage() > 0)) { SkDELETE(tileRenderer); SkDebugf( - "--mode tile must be given a height percentage > 0\n"); + "--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 tile must be given a height > 0\n"); + SkDebugf("--mode %s must be given a height > 0\n", mode); exit(-1); } }