9a3cdbb3d0
First bench: Simply render from a compressed ETC1 bitmap. This is roughly equal between compressed and uncompressed data. Second bench: Notify the bitmap that the pixels have changed (that's right, like a liar) to force a re-upload to the GPU. On this bench, decompressed textures seem to inexplicably do an order of magnitude better than compressed textures when it should be the other way around, but that investigation is reserved for a future CL. R=robertphillips@google.com Author: krajcevski@google.com Review URL: https://codereview.chromium.org/317023002
234 lines
7.7 KiB
C++
234 lines
7.7 KiB
C++
/*
|
|
* Copyright 2014 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "SkBenchmark.h"
|
|
#include "SkCanvas.h"
|
|
#include "SkData.h"
|
|
#include "SkDecodingImageGenerator.h"
|
|
#include "SkImageDecoder.h"
|
|
#include "SkOSFile.h"
|
|
#include "SkPixelRef.h"
|
|
|
|
#ifndef SK_IGNORE_ETC1_SUPPORT
|
|
|
|
#include "etc1.h"
|
|
|
|
// This takes the etc1 data pointed to by orig, and copies it `factor` times in each
|
|
// dimension. The return value is the new data or NULL on error.
|
|
static etc1_byte* create_expanded_etc1_bitmap(const uint8_t* orig, int factor) {
|
|
SkASSERT(NULL != orig);
|
|
SkASSERT(factor > 1);
|
|
|
|
const etc1_byte* origData = reinterpret_cast<const etc1_byte*>(orig);
|
|
if (!etc1_pkm_is_valid(orig)) {
|
|
return NULL;
|
|
}
|
|
|
|
etc1_uint32 origWidth = etc1_pkm_get_width(origData);
|
|
etc1_uint32 origHeight = etc1_pkm_get_height(origData);
|
|
|
|
// The width and height must be aligned along block boundaries
|
|
static const etc1_uint32 kETC1BlockWidth = 4;
|
|
static const etc1_uint32 kETC1BlockHeight = 4;
|
|
if ((origWidth % kETC1BlockWidth) != 0 ||
|
|
(origHeight % kETC1BlockHeight) != 0) {
|
|
return NULL;
|
|
}
|
|
|
|
// The picture must be at least as large as a block.
|
|
if (origWidth <= kETC1BlockWidth || origHeight <= kETC1BlockHeight) {
|
|
return NULL;
|
|
}
|
|
|
|
etc1_uint32 newWidth = origWidth * factor;
|
|
etc1_uint32 newHeight = origHeight * factor;
|
|
|
|
etc1_uint32 newDataSz = etc1_get_encoded_data_size(newWidth, newHeight);
|
|
etc1_byte* newData = reinterpret_cast<etc1_byte *>(
|
|
sk_malloc_throw(newDataSz + ETC_PKM_HEADER_SIZE));
|
|
etc1_pkm_format_header(newData, newWidth, newHeight);
|
|
|
|
etc1_byte* copyInto = newData;
|
|
|
|
copyInto += ETC_PKM_HEADER_SIZE;
|
|
origData += ETC_PKM_HEADER_SIZE;
|
|
|
|
etc1_uint32 origBlocksX = (origWidth >> 2);
|
|
etc1_uint32 origBlocksY = (origHeight >> 2);
|
|
etc1_uint32 newBlocksY = (newHeight >> 2);
|
|
etc1_uint32 origRowSzInBytes = origBlocksX * ETC1_ENCODED_BLOCK_SIZE;
|
|
|
|
for (etc1_uint32 j = 0; j < newBlocksY; ++j) {
|
|
const etc1_byte* rowStart = origData + ((j % origBlocksY) * origRowSzInBytes);
|
|
for(etc1_uint32 i = 0; i < newWidth; i += origWidth) {
|
|
memcpy(copyInto, rowStart, origRowSzInBytes);
|
|
copyInto += origRowSzInBytes;
|
|
}
|
|
}
|
|
return newData;
|
|
}
|
|
|
|
// This is the base class for all of the benches in this file. In general
|
|
// the ETC1 benches should all be working on the same data. Due to the
|
|
// simplicity of the PKM file, that data is the 128x128 mandrill etc1
|
|
// compressed texture repeated by some factor (currently 8 -> 1024x1024)
|
|
class ETCBitmapBenchBase : public SkBenchmark {
|
|
public:
|
|
ETCBitmapBenchBase() : fPKMData(loadPKM()) {
|
|
if (NULL == fPKMData) {
|
|
SkDebugf("Could not load PKM data!");
|
|
}
|
|
}
|
|
|
|
protected:
|
|
SkAutoDataUnref fPKMData;
|
|
|
|
private:
|
|
SkData *loadPKM() {
|
|
SkString filename = SkOSPath::SkPathJoin(
|
|
INHERITED::GetResourcePath().c_str(), "mandrill_128.pkm");
|
|
|
|
// Expand the data
|
|
SkAutoDataUnref fileData(SkData::NewFromFileName(filename.c_str()));
|
|
if (NULL == fileData) {
|
|
SkDebugf("Could not open the file. Did you forget to set the resourcePath?\n");
|
|
return NULL;
|
|
}
|
|
|
|
const etc1_uint32 kExpansionFactor = 8;
|
|
etc1_byte* expandedETC1 =
|
|
create_expanded_etc1_bitmap(fileData->bytes(), kExpansionFactor);
|
|
if (NULL == expandedETC1) {
|
|
SkDebugf("Error expanding ETC1 data by factor of %d\n", kExpansionFactor);
|
|
return NULL;
|
|
}
|
|
|
|
etc1_uint32 width = etc1_pkm_get_width(expandedETC1);
|
|
etc1_uint32 height = etc1_pkm_get_width(expandedETC1);
|
|
etc1_uint32 dataSz = ETC_PKM_HEADER_SIZE + etc1_get_encoded_data_size(width, height);
|
|
return SkData::NewFromMalloc(expandedETC1, dataSz);
|
|
}
|
|
|
|
typedef SkBenchmark INHERITED;
|
|
};
|
|
|
|
// This is the rendering benchmark. Prior to rendering the data, create a
|
|
// bitmap using the etc1 data.
|
|
class ETCBitmapBench : public ETCBitmapBenchBase {
|
|
public:
|
|
ETCBitmapBench(bool decompress, Backend backend)
|
|
: fDecompress(decompress), fBackend(backend) { }
|
|
|
|
virtual bool isSuitableFor(Backend backend) SK_OVERRIDE {
|
|
return backend == this->fBackend;
|
|
}
|
|
|
|
protected:
|
|
virtual const char* onGetName() SK_OVERRIDE {
|
|
if (kGPU_Backend == this->fBackend) {
|
|
if (this->fDecompress) {
|
|
return "etc1bitmap_render_gpu_decompressed";
|
|
} else {
|
|
return "etc1bitmap_render_gpu_compressed";
|
|
}
|
|
} else {
|
|
SkASSERT(kRaster_Backend == this->fBackend);
|
|
if (this->fDecompress) {
|
|
return "etc1bitmap_render_raster_decompressed";
|
|
} else {
|
|
return "etc1bitmap_render_raster_compressed";
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void onPreDraw() SK_OVERRIDE {
|
|
if (NULL == fPKMData) {
|
|
SkDebugf("Failed to load PKM data!\n");
|
|
return;
|
|
}
|
|
|
|
// Install pixel ref
|
|
if (!SkInstallDiscardablePixelRef(
|
|
SkDecodingImageGenerator::Create(
|
|
fPKMData, SkDecodingImageGenerator::Options()), &(this->fBitmap))) {
|
|
SkDebugf("Could not install discardable pixel ref.\n");
|
|
return;
|
|
}
|
|
|
|
// Decompress it if necessary
|
|
if (this->fDecompress) {
|
|
this->fBitmap.lockPixels();
|
|
}
|
|
}
|
|
|
|
virtual void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
|
|
for (int i = 0; i < loops; ++i) {
|
|
canvas->drawBitmap(this->fBitmap, 0, 0, NULL);
|
|
}
|
|
}
|
|
|
|
protected:
|
|
SkBitmap fBitmap;
|
|
bool decompress() const { return fDecompress; }
|
|
Backend backend() const { return fBackend; }
|
|
private:
|
|
const bool fDecompress;
|
|
const Backend fBackend;
|
|
typedef ETCBitmapBenchBase INHERITED;
|
|
};
|
|
|
|
// This benchmark is identical to the previous benchmark, but it explicitly forces
|
|
// an upload to the GPU before each draw call. We do this by notifying the bitmap
|
|
// that the pixels have changed (even though they haven't).
|
|
class ETCBitmapUploadBench : public ETCBitmapBench {
|
|
public:
|
|
ETCBitmapUploadBench(bool decompress, Backend backend)
|
|
: ETCBitmapBench(decompress, backend) { }
|
|
|
|
protected:
|
|
virtual const char* onGetName() SK_OVERRIDE {
|
|
if (kGPU_Backend == this->backend()) {
|
|
if (this->decompress()) {
|
|
return "etc1bitmap_upload_gpu_decompressed";
|
|
} else {
|
|
return "etc1bitmap_upload_gpu_compressed";
|
|
}
|
|
} else {
|
|
SkASSERT(kRaster_Backend == this->backend());
|
|
if (this->decompress()) {
|
|
return "etc1bitmap_upload_raster_decompressed";
|
|
} else {
|
|
return "etc1bitmap_upload_raster_compressed";
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
|
|
for (int i = 0; i < loops; ++i) {
|
|
this->fBitmap.pixelRef()->notifyPixelsChanged();
|
|
canvas->drawBitmap(this->fBitmap, 0, 0, NULL);
|
|
}
|
|
}
|
|
|
|
private:
|
|
typedef ETCBitmapBench INHERITED;
|
|
};
|
|
|
|
DEF_BENCH(return new ETCBitmapBench(false, SkBenchmark::kRaster_Backend);)
|
|
DEF_BENCH(return new ETCBitmapBench(true, SkBenchmark::kRaster_Backend);)
|
|
|
|
DEF_BENCH(return new ETCBitmapBench(false, SkBenchmark::kGPU_Backend);)
|
|
DEF_BENCH(return new ETCBitmapBench(true, SkBenchmark::kGPU_Backend);)
|
|
|
|
DEF_BENCH(return new ETCBitmapUploadBench(false, SkBenchmark::kRaster_Backend);)
|
|
DEF_BENCH(return new ETCBitmapUploadBench(true, SkBenchmark::kRaster_Backend);)
|
|
|
|
DEF_BENCH(return new ETCBitmapUploadBench(false, SkBenchmark::kGPU_Backend);)
|
|
DEF_BENCH(return new ETCBitmapUploadBench(true, SkBenchmark::kGPU_Backend);)
|
|
|
|
#endif // SK_IGNORE_ETC1_SUPPORT
|