diff options
author | 2015-06-09 13:56:10 -0700 | |
---|---|---|
committer | 2015-06-09 13:56:10 -0700 | |
commit | b23e6aa6767816ac4bc6c191e78ef62d6e765671 (patch) | |
tree | eca20bbc78e59cf624be66d5fb3f5a71aefc5c12 /bench | |
parent | c15d9579d654f14cb16a3ced7158cd93950d48c8 (diff) |
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
Diffstat (limited to 'bench')
-rw-r--r-- | bench/DecodingSubsetBench.cpp | 73 | ||||
-rw-r--r-- | bench/DecodingSubsetBench.h | 37 | ||||
-rw-r--r-- | bench/nanobench.cpp | 171 | ||||
-rw-r--r-- | bench/subset/SubsetBenchPriv.h | 35 | ||||
-rw-r--r-- | bench/subset/SubsetDivisorBench.cpp | 129 | ||||
-rw-r--r-- | bench/subset/SubsetDivisorBench.h | 40 | ||||
-rw-r--r-- | bench/subset/SubsetSingleBench.cpp | 94 | ||||
-rw-r--r-- | bench/subset/SubsetSingleBench.h | 46 | ||||
-rw-r--r-- | bench/subset/SubsetTranslateBench.cpp | 124 | ||||
-rw-r--r-- | bench/subset/SubsetTranslateBench.h | 42 | ||||
-rw-r--r-- | bench/subset/SubsetZoomBench.cpp | 117 | ||||
-rw-r--r-- | bench/subset/SubsetZoomBench.h | 42 |
12 files changed, 790 insertions, 160 deletions
diff --git a/bench/DecodingSubsetBench.cpp b/bench/DecodingSubsetBench.cpp deleted file mode 100644 index dc3c9f39cc..0000000000 --- a/bench/DecodingSubsetBench.cpp +++ /dev/null @@ -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); - } - } - } -} diff --git a/bench/DecodingSubsetBench.h b/bench/DecodingSubsetBench.h deleted file mode 100644 index 4941aa10c1..0000000000 --- a/bench/DecodingSubsetBench.h +++ /dev/null @@ -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; -}; diff --git a/bench/nanobench.cpp b/bench/nanobench.cpp index cd15580afb..7f87f37928 100644 --- a/bench/nanobench.cpp +++ b/bench/nanobench.cpp @@ -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(); diff --git a/bench/subset/SubsetBenchPriv.h b/bench/subset/SubsetBenchPriv.h new file mode 100644 index 0000000000..e0eb2ff826 --- /dev/null +++ b/bench/subset/SubsetBenchPriv.h @@ -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 diff --git a/bench/subset/SubsetDivisorBench.cpp b/bench/subset/SubsetDivisorBench.cpp new file mode 100644 index 0000000000..0ec2579b6d --- /dev/null +++ b/bench/subset/SubsetDivisorBench.cpp @@ -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); + } + } + } + } +} diff --git a/bench/subset/SubsetDivisorBench.h b/bench/subset/SubsetDivisorBench.h new file mode 100644 index 0000000000..e5ea71ff51 --- /dev/null +++ b/bench/subset/SubsetDivisorBench.h @@ -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; +}; diff --git a/bench/subset/SubsetSingleBench.cpp b/bench/subset/SubsetSingleBench.cpp new file mode 100644 index 0000000000..68c94cd246 --- /dev/null +++ b/bench/subset/SubsetSingleBench.cpp @@ -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); + } + } +} diff --git a/bench/subset/SubsetSingleBench.h b/bench/subset/SubsetSingleBench.h new file mode 100644 index 0000000000..1484a51258 --- /dev/null +++ b/bench/subset/SubsetSingleBench.h @@ -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; +}; diff --git a/bench/subset/SubsetTranslateBench.cpp b/bench/subset/SubsetTranslateBench.cpp new file mode 100644 index 0000000000..620b6f4903 --- /dev/null +++ b/bench/subset/SubsetTranslateBench.cpp @@ -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); + } + } + } + } +} diff --git a/bench/subset/SubsetTranslateBench.h b/bench/subset/SubsetTranslateBench.h new file mode 100644 index 0000000000..c7c6febbcb --- /dev/null +++ b/bench/subset/SubsetTranslateBench.h @@ -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; +}; diff --git a/bench/subset/SubsetZoomBench.cpp b/bench/subset/SubsetZoomBench.cpp new file mode 100644 index 0000000000..3ce193b6ea --- /dev/null +++ b/bench/subset/SubsetZoomBench.cpp @@ -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); + } + } +} diff --git a/bench/subset/SubsetZoomBench.h b/bench/subset/SubsetZoomBench.h new file mode 100644 index 0000000000..6a8f62de46 --- /dev/null +++ b/bench/subset/SubsetZoomBench.h @@ -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; +}; |