Subset decoding benchmarks

It was my goal to create benchmarks that could measure all
of the use cases that we have identified.  I think single
subsets, translating, and scaling are the important ones.

It might be a good idea to discuss the document in greater
detail as well.  I just wanted to share this to aid the
discussion.
https://docs.google.com/a/google.com/document/d/1OxW96GDMAlw6dnzNXmiNX-F9oDBBlGXzSsgd0DMIkbI/edit?usp=sharing

BUG=skia:

Review URL: https://codereview.chromium.org/1160953002
This commit is contained in:
msarett 2015-06-09 13:56:10 -07:00 committed by Commit bot
parent c15d9579d6
commit b23e6aa676
13 changed files with 792 additions and 160 deletions

View File

@ -1,73 +0,0 @@
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "DecodingSubsetBench.h"
#include "SkData.h"
#include "SkImageDecoder.h"
#include "SkOSFile.h"
#include "SkStream.h"
/*
*
* This benchmark is designed to test the performance of image subset decoding.
* It is invoked from the nanobench.cpp file.
*
*/
DecodingSubsetBench::DecodingSubsetBench(SkString path, SkColorType colorType,
const int divisor)
: fColorType(colorType)
, fDivisor(divisor)
{
// Parse filename and the color type to give the benchmark a useful name
SkString baseName = SkOSPath::Basename(path.c_str());
const char* colorName;
switch(colorType) {
case kN32_SkColorType:
colorName = "N32";
break;
case kRGB_565_SkColorType:
colorName = "565";
break;
case kAlpha_8_SkColorType:
colorName = "Alpha8";
break;
default:
colorName = "Unknown";
}
fName.printf("DecodeSubset_%dx%d_%s_%s", fDivisor, fDivisor,
baseName.c_str(), colorName);
// Perform the decode setup
SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(path.c_str()));
fStream.reset(new SkMemoryStream(encoded));
fDecoder.reset(SkImageDecoder::Factory(fStream));
}
const char* DecodingSubsetBench::onGetName() {
return fName.c_str();
}
bool DecodingSubsetBench::isSuitableFor(Backend backend) {
return kNonRendering_Backend == backend;
}
void DecodingSubsetBench::onDraw(const int n, SkCanvas* canvas) {
for (int i = 0; i < n; i++) {
int w, h;
fDecoder->buildTileIndex(fStream->duplicate(), &w, &h);
// Divide the image into subsets and decode each subset
const int sW = w / fDivisor;
const int sH = h / fDivisor;
for (int y = 0; y < h; y += sH) {
for (int x = 0; x < w; x += sW) {
SkBitmap bitmap;
SkIRect rect = SkIRect::MakeXYWH(x, y, sW, sH);
fDecoder->decodeSubset(&bitmap, rect, fColorType);
}
}
}
}

View File

@ -1,37 +0,0 @@
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Benchmark.h"
#include "SkImageDecoder.h"
#include "SkImageInfo.h"
#include "SkStream.h"
#include "SkString.h"
/*
*
* This benchmark is designed to test the performance of image subset decoding.
* It is invoked from the nanobench.cpp file.
*
*/
class DecodingSubsetBench : public Benchmark {
public:
DecodingSubsetBench(SkString path, SkColorType colorType,
const int divisor);
protected:
const char* onGetName() override;
bool isSuitableFor(Backend backend) override;
void onDraw(const int n, SkCanvas* canvas) override;
private:
SkString fName;
SkColorType fColorType;
const int fDivisor;
SkAutoTDelete<SkMemoryStream> fStream;
SkAutoTDelete<SkImageDecoder> fDecoder;
typedef Benchmark INHERITED;
};

View File

@ -13,13 +13,17 @@
#include "CodecBench.h"
#include "CrashHandler.h"
#include "DecodingBench.h"
#include "DecodingSubsetBench.h"
#include "GMBench.h"
#include "ProcStats.h"
#include "ResultsWriter.h"
#include "RecordingBench.h"
#include "SKPAnimationBench.h"
#include "SKPBench.h"
#include "SubsetBenchPriv.h"
#include "SubsetDivisorBench.h"
#include "SubsetSingleBench.h"
#include "SubsetTranslateBench.h"
#include "SubsetZoomBench.h"
#include "Stats.h"
#include "Timer.h"
@ -483,6 +487,56 @@ static void create_targets(SkTDArray<Target*>* targets, Benchmark* b,
}
}
/*
* Returns true if set up for a subset decode succeeds, false otherwise
* If the set-up succeeds, the width and height parameters will be set
*/
static bool valid_subset_bench(const SkString& path, SkColorType colorType, bool useCodec,
int* width, int* height) {
SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(path.c_str()));
SkAutoTDelete<SkMemoryStream> stream(new SkMemoryStream(encoded));
if (useCodec) {
SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream.detach()));
if (NULL == codec) {
SkDebugf("Could not create codec for %s. Skipping bench.\n", path.c_str());
return false;
}
// These will be initialized by SkCodec if the color type is kIndex8 and
// unused otherwise.
SkPMColor colors[256];
int colorCount;
const SkImageInfo info = codec->getInfo().makeColorType(colorType);
SkAutoTDeleteArray<uint8_t> row(SkNEW_ARRAY(uint8_t, info.minRowBytes()));
SkScanlineDecoder* scanlineDecoder = codec->getScanlineDecoder(info, NULL,
colors, &colorCount);
if (NULL == scanlineDecoder) {
SkDebugf("Could not create scanline decoder for %s with color type %s. "
"Skipping bench.\n", path.c_str(), get_color_name(colorType));
return false;
}
*width = info.width();
*height = info.height();
} else {
SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream));
if (NULL == decoder) {
SkDebugf("Could not create decoder for %s. Skipping bench.\n", path.c_str());
return false;
}
//FIXME: See skbug.com/3921
if (kIndex_8_SkColorType == colorType || kGray_8_SkColorType == colorType) {
SkDebugf("Cannot use image subset decoder for %s with color type %s. "
"Skipping bench.\n", path.c_str(), get_color_name(colorType));
return false;
}
if (!decoder->buildTileIndex(stream.detach(), width, height)) {
SkDebugf("Could not build tile index for %s. Skipping bench.\n", path.c_str());
return false;
}
}
return true;
}
class BenchmarkStream {
public:
@ -496,8 +550,9 @@ public:
, fCurrentImage(0)
, fCurrentSubsetImage(0)
, fCurrentColorType(0)
, fCurrentAnimSKP(0)
, fDivisor(2) {
, fCurrentSubsetType(0)
, fUseCodec(0)
, fCurrentAnimSKP(0) {
for (int i = 0; i < FLAGS_skps.count(); i++) {
if (SkStrEndsWith(FLAGS_skps[i], ".skp")) {
fSKPs.push_back() = FLAGS_skps[i];
@ -551,7 +606,11 @@ public:
// Choose the candidate color types for image decoding
const SkColorType colorTypes[] =
{ kN32_SkColorType, kRGB_565_SkColorType, kAlpha_8_SkColorType, kIndex_8_SkColorType };
{ kN32_SkColorType,
kRGB_565_SkColorType,
kAlpha_8_SkColorType,
kIndex_8_SkColorType,
kGray_8_SkColorType };
fColorTypes.push_back_n(SK_ARRAY_COUNT(colorTypes), colorTypes);
}
@ -738,56 +797,55 @@ public:
fCurrentImage++;
}
// Run the DecodingSubsetBenches
while (fCurrentSubsetImage < fImages.count()) {
while (fCurrentColorType < fColorTypes.count()) {
const SkString& path = fImages[fCurrentSubsetImage];
SkColorType colorType = fColorTypes[fCurrentColorType];
fCurrentColorType++;
// Check if the image decodes before creating the benchmark
SkAutoTUnref<SkData> encoded(
SkData::NewFromFileName(path.c_str()));
SkAutoTDelete<SkMemoryStream> stream(
new SkMemoryStream(encoded));
SkAutoTDelete<SkImageDecoder>
decoder(SkImageDecoder::Factory(stream.get()));
if (!decoder) {
SkDebugf("Cannot find decoder for %s\n", path.c_str());
} else {
stream->rewind();
int w, h;
bool success;
if (!decoder->buildTileIndex(stream.detach(), &w, &h)
|| w*h == 1) {
// This is not an error, but in this case we still
// do not want to run the benchmark.
success = false;
} else if (fDivisor > w || fDivisor > h) {
SkDebugf("Divisor %d is too big for %s %dx%d\n",
fDivisor, path.c_str(), w, h);
success = false;
} else {
const int sW = w / fDivisor;
const int sH = h / fDivisor;
SkBitmap bitmap;
success = true;
for (int y = 0; y < h; y += sH) {
for (int x = 0; x < w; x += sW) {
SkIRect rect = SkIRect::MakeXYWH(x, y, sW, sH);
success &= decoder->decodeSubset(&bitmap, rect,
colorType);
// Run the SubsetBenches
bool useCodecOpts[] = { true, false };
while (fUseCodec < 2) {
bool useCodec = useCodecOpts[fUseCodec];
while (fCurrentSubsetImage < fImages.count()) {
while (fCurrentColorType < fColorTypes.count()) {
const SkString& path = fImages[fCurrentSubsetImage];
SkColorType colorType = fColorTypes[fCurrentColorType];
while (fCurrentSubsetType <= kLast_SubsetType) {
int width = 0;
int height = 0;
int currentSubsetType = fCurrentSubsetType++;
if (valid_subset_bench(path, colorType, useCodec, &width, &height)) {
switch (currentSubsetType) {
case kTopLeft_SubsetType:
return new SubsetSingleBench(path, colorType, width/2,
height/2, 0, 0, useCodec);
case kTopRight_SubsetType:
return new SubsetSingleBench(path, colorType, width/2,
height/2, width/2, 0, useCodec);
case kBottomLeft_SubsetType:
return new SubsetSingleBench(path, colorType, width/2,
height/2, 0, height/2, useCodec);
case kBottomRight_SubsetType:
return new SubsetSingleBench(path, colorType, width/2,
height/2, width/2, height/2, useCodec);
case k2x2_SubsetType:
return new SubsetDivisorBench(path, colorType, 2, useCodec);
case k3x3_SubsetType:
return new SubsetDivisorBench(path, colorType, 3, useCodec);
case kTranslate_SubsetType:
return new SubsetTranslateBench(path, colorType, 512, 512,
useCodec);
case kZoom_SubsetType:
return new SubsetZoomBench(path, colorType, 512, 512,
useCodec);
}
} else {
break;
}
}
// Create the benchmark if successful
if (success) {
return new DecodingSubsetBench(path, colorType,
fDivisor);
}
fCurrentSubsetType = 0;
fCurrentColorType++;
}
fCurrentColorType = 0;
fCurrentSubsetImage++;
}
fCurrentColorType = 0;
fCurrentSubsetImage++;
fCurrentSubsetImage = 0;
fUseCodec++;
}
return NULL;
@ -813,6 +871,18 @@ public:
}
private:
enum SubsetType {
kTopLeft_SubsetType = 0,
kTopRight_SubsetType = 1,
kBottomLeft_SubsetType = 2,
kBottomRight_SubsetType = 3,
k2x2_SubsetType = 4,
k3x3_SubsetType = 5,
kTranslate_SubsetType = 6,
kZoom_SubsetType = 7,
kLast_SubsetType = kZoom_SubsetType
};
const BenchRegistry* fBenches;
const skiagm::GMRegistry* fGMs;
SkIRect fClip;
@ -836,8 +906,9 @@ private:
int fCurrentImage;
int fCurrentSubsetImage;
int fCurrentColorType;
int fCurrentSubsetType;
int fUseCodec;
int fCurrentAnimSKP;
const int fDivisor;
};
int nanobench_main();

View File

@ -0,0 +1,35 @@
/*
* Copyright 2015 The Android Open Source Project
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SubsetBenchPriv_DEFINED
#define SubsetBenchPriv_DEFINED
#include "SkCodec.h"
#include "SkData.h"
#include "SkImageGenerator.h"
/*
* Convert the color type to a string
*/
static const char* get_color_name(SkColorType colorType) {
switch(colorType) {
case kN32_SkColorType:
return "N32";
case kRGB_565_SkColorType:
return "565";
case kGray_8_SkColorType:
return "Gray8";
case kIndex_8_SkColorType:
return "Index8";
case kAlpha_8_SkColorType:
return "Alpha8";
default:
return "Unknown";
}
}
#endif // SubsetBenchPriv_DEFINED

View File

@ -0,0 +1,129 @@
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SubsetDivisorBench.h"
#include "SubsetBenchPriv.h"
#include "SkData.h"
#include "SkCodec.h"
#include "SkImageDecoder.h"
#include "SkOSFile.h"
#include "SkStream.h"
/*
*
* This benchmark is designed to test the performance of subset decoding.
* It uses a divisor to decode the entire image in a grid of divisor x divisor blocks.
*
*/
SubsetDivisorBench::SubsetDivisorBench(const SkString& path,
SkColorType colorType,
uint32_t divisor,
bool useCodec)
: fColorType(colorType)
, fDivisor(divisor)
, fUseCodec(useCodec)
{
// Parse the filename
SkString baseName = SkOSPath::Basename(path.c_str());
// Choose an informative color name
const char* colorName = get_color_name(fColorType);
fName.printf("%sSubsetDivisor_%dx%d_%s_%s", fUseCodec ? "Codec" : "Image", fDivisor, fDivisor,
baseName.c_str(), colorName);
// Perform the decode setup
SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(path.c_str()));
fStream.reset(new SkMemoryStream(encoded));
}
const char* SubsetDivisorBench::onGetName() {
return fName.c_str();
}
bool SubsetDivisorBench::isSuitableFor(Backend backend) {
return kNonRendering_Backend == backend;
}
void SubsetDivisorBench::onDraw(const int n, SkCanvas* canvas) {
// When the color type is kIndex8, we will need to store the color table. If it is
// used, it will be initialized by the codec.
int colorCount;
SkPMColor colors[256];
if (fUseCodec) {
for (int count = 0; count < n; count++) {
SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(fStream->duplicate()));
const SkImageInfo info = codec->getInfo().makeColorType(fColorType);
SkAutoTDeleteArray<uint8_t> row(SkNEW_ARRAY(uint8_t, info.minRowBytes()));
SkScanlineDecoder* scanlineDecoder = codec->getScanlineDecoder(
info, NULL, colors, &colorCount);
const uint32_t subsetWidth = info.width() / fDivisor;
const uint32_t subsetHeight = info.height() / fDivisor;
const uint32_t maxSubsetWidth = subsetWidth + info.width() % fDivisor;
const uint32_t maxSubsetHeight = subsetHeight + info.height() % fDivisor;
SkBitmap bitmap;
// Note that we use the same bitmap for all of the subsets.
// It might be slightly larger than necessary for some of the subsets.
bitmap.allocPixels(info.makeWH(maxSubsetWidth, maxSubsetHeight));
for (uint32_t blockX = 0; blockX < fDivisor; blockX++) {
for (uint32_t blockY = 0; blockY < fDivisor; blockY++) {
scanlineDecoder->skipScanlines(blockY * subsetHeight);
const uint32_t currSubsetWidth =
(blockX == fDivisor - 1) ? maxSubsetWidth : subsetWidth;
const uint32_t currSubsetHeight =
(blockY == fDivisor - 1) ? maxSubsetHeight : subsetHeight;
const uint32_t bpp = info.bytesPerPixel();
for (uint32_t y = 0; y < currSubsetHeight; y++) {
scanlineDecoder->getScanlines(row.get(), 1, 0);
memcpy(bitmap.getAddr(0, y), row.get() + blockX * subsetWidth * bpp,
currSubsetWidth * bpp);
}
}
}
}
} else {
// We create a color table here to satisfy allocPixels() when the output
// type is kIndex8. It's okay that this is uninitialized since we never
// use it.
SkColorTable* colorTable = SkNEW_ARGS(SkColorTable, (colors, 0));
for (int count = 0; count < n; count++) {
int width, height;
SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream));
decoder->buildTileIndex(fStream->duplicate(), &width, &height);
const uint32_t subsetWidth = width / fDivisor;
const uint32_t subsetHeight = height / fDivisor;
const uint32_t maxSubsetWidth = subsetWidth + width % fDivisor;
const uint32_t maxSubsetHeight = subsetHeight + height % fDivisor;
SkBitmap bitmap;
// Note that we use the same bitmap for all of the subsets.
// It might be slightly larger than necessary for some of the subsets.
// If we do not include this step, decodeSubset() would allocate space
// for the pixels automatically, but this would not allow us to reuse the
// same bitmap as the other subsets. We want to reuse the same bitmap
// because it gives a more fair comparison with SkCodec and is a common
// use case of BitmapRegionDecoder.
bitmap.allocPixels(SkImageInfo::Make(maxSubsetWidth, maxSubsetHeight,
fColorType, kOpaque_SkAlphaType), NULL, colorTable);
for (uint32_t blockX = 0; blockX < fDivisor; blockX++) {
for (uint32_t blockY = 0; blockY < fDivisor; blockY++) {
const uint32_t currSubsetWidth =
(blockX == fDivisor - 1) ? maxSubsetWidth : subsetWidth;
const uint32_t currSubsetHeight =
(blockY == fDivisor - 1) ? maxSubsetHeight : subsetHeight;
SkIRect rect = SkIRect::MakeXYWH(blockX * subsetWidth,
blockY * subsetHeight, currSubsetWidth, currSubsetHeight);
decoder->decodeSubset(&bitmap, rect, fColorType);
}
}
}
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Benchmark.h"
#include "SkImageDecoder.h"
#include "SkImageInfo.h"
#include "SkStream.h"
#include "SkString.h"
/*
*
* This benchmark is designed to test the performance of subset decoding.
* It uses a divisor to decode the entire image in a grid of divisor x divisor blocks.
*
*/
class SubsetDivisorBench : public Benchmark {
public:
SubsetDivisorBench(const SkString& path,
SkColorType colorType,
uint32_t divisor,
bool useCodec);
protected:
const char* onGetName() override;
bool isSuitableFor(Backend backend) override;
void onDraw(const int n, SkCanvas* canvas) override;
private:
SkString fName;
SkColorType fColorType;
const uint32_t fDivisor;
const bool fUseCodec;
SkAutoTDelete<SkMemoryStream> fStream;
typedef Benchmark INHERITED;
};

View File

@ -0,0 +1,94 @@
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SubsetSingleBench.h"
#include "SubsetBenchPriv.h"
#include "SkData.h"
#include "SkCodec.h"
#include "SkImageDecoder.h"
#include "SkOSFile.h"
#include "SkStream.h"
/*
*
* This benchmark is designed to test the performance of subset decoding.
* It uses an input width, height, left, and top to decode a single subset.
*
*/
SubsetSingleBench::SubsetSingleBench(const SkString& path,
SkColorType colorType,
uint32_t subsetWidth,
uint32_t subsetHeight,
uint32_t offsetLeft,
uint32_t offsetTop,
bool useCodec)
: fColorType(colorType)
, fSubsetWidth(subsetWidth)
, fSubsetHeight(subsetHeight)
, fOffsetLeft(offsetLeft)
, fOffsetTop(offsetTop)
, fUseCodec(useCodec)
{
// Parse the filename
SkString baseName = SkOSPath::Basename(path.c_str());
// Choose an informative color name
const char* colorName = get_color_name(fColorType);
fName.printf("%sSubsetSingle_%dx%d +%d_+%d_%s_%s", fUseCodec ? "Codec" : "Image", fSubsetWidth,
fSubsetHeight, fOffsetLeft, fOffsetTop, baseName.c_str(), colorName);
// Perform the decode setup
SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(path.c_str()));
fStream.reset(new SkMemoryStream(encoded));
}
const char* SubsetSingleBench::onGetName() {
return fName.c_str();
}
bool SubsetSingleBench::isSuitableFor(Backend backend) {
return kNonRendering_Backend == backend;
}
void SubsetSingleBench::onDraw(const int n, SkCanvas* canvas) {
// When the color type is kIndex8, we will need to store the color table. If it is
// used, it will be initialized by the codec.
int colorCount;
SkPMColor colors[256];
if (fUseCodec) {
for (int count = 0; count < n; count++) {
SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(fStream->duplicate()));
const SkImageInfo info = codec->getInfo().makeColorType(fColorType);
SkAutoTDeleteArray<uint8_t> row(SkNEW_ARRAY(uint8_t, info.minRowBytes()));
SkScanlineDecoder* scanlineDecoder = codec->getScanlineDecoder(
info, NULL, colors, &colorCount);
SkBitmap bitmap;
bitmap.allocPixels(info.makeWH(fSubsetWidth, fSubsetHeight));
scanlineDecoder->skipScanlines(fOffsetTop);
uint32_t bpp = info.bytesPerPixel();
for (uint32_t y = 0; y < fSubsetHeight; y++) {
scanlineDecoder->getScanlines(row.get(), 1, 0);
memcpy(bitmap.getAddr(0, y), row.get() + fOffsetLeft * bpp,
fSubsetWidth * bpp);
}
}
} else {
for (int count = 0; count < n; count++) {
SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream));
int width, height;
decoder->buildTileIndex(fStream->duplicate(), &width, &height);
SkBitmap bitmap;
SkIRect rect = SkIRect::MakeXYWH(fOffsetLeft, fOffsetTop, fSubsetWidth,
fSubsetHeight);
decoder->decodeSubset(&bitmap, rect, fColorType);
}
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Benchmark.h"
#include "SkImageDecoder.h"
#include "SkImageInfo.h"
#include "SkStream.h"
#include "SkString.h"
/*
*
* This benchmark is designed to test the performance of subset decoding.
* It uses an input width, height, left, and top to decode a single subset.
*
*/
class SubsetSingleBench : public Benchmark {
public:
SubsetSingleBench(const SkString& path,
SkColorType colorType,
uint32_t subsetWidth,
uint32_t subsetHeight,
uint32_t offsetLeft,
uint32_t offsetTop,
bool useCodec);
protected:
const char* onGetName() override;
bool isSuitableFor(Backend backend) override;
void onDraw(const int n, SkCanvas* canvas) override;
private:
SkString fName;
SkColorType fColorType;
const uint32_t fSubsetWidth;
const uint32_t fSubsetHeight;
const uint32_t fOffsetLeft;
const uint32_t fOffsetTop;
const bool fUseCodec;
SkAutoTDelete<SkMemoryStream> fStream;
typedef Benchmark INHERITED;
};

View File

@ -0,0 +1,124 @@
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SubsetTranslateBench.h"
#include "SubsetBenchPriv.h"
#include "SkData.h"
#include "SkCodec.h"
#include "SkImageDecoder.h"
#include "SkOSFile.h"
#include "SkStream.h"
/*
*
* This benchmark is designed to test the performance of subset decoding.
* It uses input dimensions to decode the entire image where each block is susbetW x subsetH.
*
*/
SubsetTranslateBench::SubsetTranslateBench(const SkString& path,
SkColorType colorType,
uint32_t subsetWidth,
uint32_t subsetHeight,
bool useCodec)
: fColorType(colorType)
, fSubsetWidth(subsetWidth)
, fSubsetHeight(subsetHeight)
, fUseCodec(useCodec)
{
// Parse the filename
SkString baseName = SkOSPath::Basename(path.c_str());
// Choose an informative color name
const char* colorName = get_color_name(fColorType);
fName.printf("%sSubsetTranslate_%dx%d_%s_%s", fUseCodec ? "Codec" : "Image", fSubsetWidth,
fSubsetHeight, baseName.c_str(), colorName);
// Perform the decode setup
SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(path.c_str()));
fStream.reset(new SkMemoryStream(encoded));
}
const char* SubsetTranslateBench::onGetName() {
return fName.c_str();
}
bool SubsetTranslateBench::isSuitableFor(Backend backend) {
return kNonRendering_Backend == backend;
}
void SubsetTranslateBench::onDraw(const int n, SkCanvas* canvas) {
// When the color type is kIndex8, we will need to store the color table. If it is
// used, it will be initialized by the codec.
int colorCount;
SkPMColor colors[256];
if (fUseCodec) {
for (int count = 0; count < n; count++) {
SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(fStream->duplicate()));
const SkImageInfo info = codec->getInfo().makeColorType(fColorType);
SkAutoTDeleteArray<uint8_t> row(SkNEW_ARRAY(uint8_t, info.minRowBytes()));
SkScanlineDecoder* scanlineDecoder = codec->getScanlineDecoder(
info, NULL, colors, &colorCount);
SkBitmap bitmap;
// Note that we use the same bitmap for all of the subsets.
// It might be larger than necessary for the end subsets.
bitmap.allocPixels(info.makeWH(fSubsetWidth, fSubsetHeight));
for (int x = 0; x < info.width(); x += fSubsetWidth) {
for (int y = 0; y < info.height(); y += fSubsetHeight) {
scanlineDecoder->skipScanlines(y);
const uint32_t currSubsetWidth =
x + (int) fSubsetWidth > info.width() ?
info.width() - x : fSubsetWidth;
const uint32_t currSubsetHeight =
y + (int) fSubsetHeight > info.height() ?
info.height() - y : fSubsetHeight;
const uint32_t bpp = info.bytesPerPixel();
for (uint32_t y = 0; y < currSubsetHeight; y++) {
scanlineDecoder->getScanlines(row.get(), 1, 0);
memcpy(bitmap.getAddr(0, y), row.get() + x * bpp,
currSubsetWidth * bpp);
}
}
}
}
} else {
// We create a color table here to satisfy allocPixels() when the output
// type is kIndex8. It's okay that this is uninitialized since we never
// use it.
SkColorTable* colorTable = SkNEW_ARGS(SkColorTable, (colors, 0));
for (int count = 0; count < n; count++) {
int width, height;
SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream));
decoder->buildTileIndex(fStream->duplicate(), &width, &height);
SkBitmap bitmap;
// Note that we use the same bitmap for all of the subsets.
// It might be larger than necessary for the end subsets.
// If we do not include this step, decodeSubset() would allocate space
// for the pixels automatically, but this would not allow us to reuse the
// same bitmap as the other subsets. We want to reuse the same bitmap
// because it gives a more fair comparison with SkCodec and is a common
// use case of BitmapRegionDecoder.
bitmap.allocPixels(SkImageInfo::Make(fSubsetWidth, fSubsetHeight,
fColorType, kOpaque_SkAlphaType), NULL, colorTable);
for (int x = 0; x < width; x += fSubsetWidth) {
for (int y = 0; y < height; y += fSubsetHeight) {
const uint32_t currSubsetWidth = x + (int) fSubsetWidth > width ?
width - x : fSubsetWidth;
const uint32_t currSubsetHeight = y + (int) fSubsetHeight > height ?
height - y : fSubsetHeight;
SkIRect rect = SkIRect::MakeXYWH(x, y, currSubsetWidth,
currSubsetHeight);
decoder->decodeSubset(&bitmap, rect, fColorType);
}
}
}
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Benchmark.h"
#include "SkImageDecoder.h"
#include "SkImageInfo.h"
#include "SkStream.h"
#include "SkString.h"
/*
*
* This benchmark is designed to test the performance of subset decoding.
* It uses input dimensions to decode the entire image where each block is susbetW x subsetH.
*
*/
class SubsetTranslateBench : public Benchmark {
public:
SubsetTranslateBench(const SkString& path,
SkColorType colorType,
uint32_t subsetWidth,
uint32_t subsetHeight,
bool useCodec);
protected:
const char* onGetName() override;
bool isSuitableFor(Backend backend) override;
void onDraw(const int n, SkCanvas* canvas) override;
private:
SkString fName;
SkColorType fColorType;
const uint32_t fSubsetWidth;
const uint32_t fSubsetHeight;
const bool fUseCodec;
SkAutoTDelete<SkMemoryStream> fStream;
typedef Benchmark INHERITED;
};

View File

@ -0,0 +1,117 @@
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SubsetZoomBench.h"
#include "SubsetBenchPriv.h"
#include "SkData.h"
#include "SkCodec.h"
#include "SkImageDecoder.h"
#include "SkOSFile.h"
#include "SkStream.h"
/*
*
* This benchmark is designed to test the performance of subset decoding.
* Choose subsets to mimic a user zooming in or out on a photo.
*
*/
SubsetZoomBench::SubsetZoomBench(const SkString& path,
SkColorType colorType,
uint32_t subsetWidth,
uint32_t subsetHeight,
bool useCodec)
: fColorType(colorType)
, fSubsetWidth(subsetWidth)
, fSubsetHeight(subsetHeight)
, fUseCodec(useCodec)
{
// Parse the filename
SkString baseName = SkOSPath::Basename(path.c_str());
// Choose an informative color name
const char* colorName = get_color_name(fColorType);
fName.printf("%sSubsetZoom_%dx%d_%s_%s", fUseCodec ? "Codec" : "Image", fSubsetWidth,
fSubsetHeight, baseName.c_str(), colorName);
// Perform the decode setup
SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(path.c_str()));
fStream.reset(new SkMemoryStream(encoded));
}
const char* SubsetZoomBench::onGetName() {
return fName.c_str();
}
bool SubsetZoomBench::isSuitableFor(Backend backend) {
return kNonRendering_Backend == backend;
}
void SubsetZoomBench::onDraw(const int n, SkCanvas* canvas) {
// When the color type is kIndex8, we will need to store the color table. If it is
// used, it will be initialized by the codec.
int colorCount;
SkPMColor colors[256];
if (fUseCodec) {
for (int count = 0; count < n; count++) {
SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(fStream->duplicate()));
const SkImageInfo info = codec->getInfo().makeColorType(fColorType);
SkAutoTDeleteArray<uint8_t> row(SkNEW_ARRAY(uint8_t, info.minRowBytes()));
SkScanlineDecoder* scanlineDecoder = codec->getScanlineDecoder(
info, NULL, colors, &colorCount);
const int centerX = info.width() / 2;
const int centerY = info.height() / 2;
int w = fSubsetWidth;
int h = fSubsetHeight;
do {
const int subsetStartX = SkTMax(0, centerX - w / 2);
const int subsetStartY = SkTMax(0, centerY - h / 2);
const int subsetWidth = SkTMin(w, info.width() - subsetStartX);
const int subsetHeight = SkTMin(h, info.height() - subsetStartY);
// Note that if we subsetted and scaled in a single step, we could use the
// same bitmap - as is often done in actual use cases.
SkBitmap bitmap;
bitmap.allocPixels(info.makeWH(subsetWidth, subsetHeight));
uint32_t bpp = info.bytesPerPixel();
scanlineDecoder->skipScanlines(subsetStartY);
for (int y = 0; y < subsetHeight; y++) {
scanlineDecoder->getScanlines(row.get(), 1, 0);
memcpy(bitmap.getAddr(0, y), row.get() + subsetStartX * bpp,
subsetWidth * bpp);
}
w <<= 1;
h <<= 1;
} while (w < 2 * info.width() || h < 2 * info.height());
}
} else {
for (int count = 0; count < n; count++) {
int width, height;
SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream));
decoder->buildTileIndex(fStream->duplicate(), &width, &height);
const int centerX = width / 2;
const int centerY = height / 2;
int w = fSubsetWidth;
int h = fSubsetHeight;
do {
const int subsetStartX = SkTMax(0, centerX - w / 2);
const int subsetStartY = SkTMax(0, centerY - h / 2);
const int subsetWidth = SkTMin(w, width - subsetStartX);
const int subsetHeight = SkTMin(h, height - subsetStartY);
SkBitmap bitmap;
SkIRect rect = SkIRect::MakeXYWH(subsetStartX, subsetStartY, subsetWidth,
subsetHeight);
decoder->decodeSubset(&bitmap, rect, fColorType);
w <<= 1;
h <<= 1;
} while (w < 2 * width || h < 2 * height);
}
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Benchmark.h"
#include "SkImageDecoder.h"
#include "SkImageInfo.h"
#include "SkStream.h"
#include "SkString.h"
/*
*
* This benchmark is designed to test the performance of subset decoding.
* Choose subsets to mimic a user zooming in or out on a photo.
*
*/
class SubsetZoomBench : public Benchmark {
public:
SubsetZoomBench(const SkString& path,
SkColorType colorType,
uint32_t subsetWidth,
uint32_t subsetHeight,
bool useCodec);
protected:
const char* onGetName() override;
bool isSuitableFor(Backend backend) override;
void onDraw(const int n, SkCanvas* canvas) override;
private:
SkString fName;
SkColorType fColorType;
const uint32_t fSubsetWidth;
const uint32_t fSubsetHeight;
const bool fUseCodec;
SkAutoTDelete<SkMemoryStream> fStream;
typedef Benchmark INHERITED;
};

View File

@ -4,6 +4,8 @@
# found in the LICENSE file.
{
'include_dirs': [
'../bench/subset',
'../bench',
'../src/core',
'../src/effects',
'../src/gpu',