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:
parent
c15d9579d6
commit
b23e6aa676
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
};
|
@ -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();
|
||||
|
35
bench/subset/SubsetBenchPriv.h
Normal file
35
bench/subset/SubsetBenchPriv.h
Normal 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
|
129
bench/subset/SubsetDivisorBench.cpp
Normal file
129
bench/subset/SubsetDivisorBench.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
40
bench/subset/SubsetDivisorBench.h
Normal file
40
bench/subset/SubsetDivisorBench.h
Normal 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;
|
||||
};
|
94
bench/subset/SubsetSingleBench.cpp
Normal file
94
bench/subset/SubsetSingleBench.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
46
bench/subset/SubsetSingleBench.h
Normal file
46
bench/subset/SubsetSingleBench.h
Normal 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;
|
||||
};
|
124
bench/subset/SubsetTranslateBench.cpp
Normal file
124
bench/subset/SubsetTranslateBench.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
42
bench/subset/SubsetTranslateBench.h
Normal file
42
bench/subset/SubsetTranslateBench.h
Normal 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;
|
||||
};
|
117
bench/subset/SubsetZoomBench.cpp
Normal file
117
bench/subset/SubsetZoomBench.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
42
bench/subset/SubsetZoomBench.h
Normal file
42
bench/subset/SubsetZoomBench.h
Normal 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;
|
||||
};
|
@ -4,6 +4,8 @@
|
||||
# found in the LICENSE file.
|
||||
{
|
||||
'include_dirs': [
|
||||
'../bench/subset',
|
||||
'../bench',
|
||||
'../src/core',
|
||||
'../src/effects',
|
||||
'../src/gpu',
|
||||
|
Loading…
Reference in New Issue
Block a user