Implemented power of two tiling.

Review URL: https://codereview.appspot.com/6485056

git-svn-id: http://skia.googlecode.com/svn/trunk@5274 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
keyar@chromium.org 2012-08-23 20:53:25 +00:00
parent ac4b86cdcf
commit f4959ab118
7 changed files with 166 additions and 29 deletions

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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<SkCanvas*> 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();

View File

@ -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 <inputDir>...\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<SkString>*
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<SkString>*
++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<SkString>*
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);
}
}

View File

@ -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 <input>... <outputDir> \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<SkString>*
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<SkString>*
++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<SkString>*
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);
}
}