aboutsummaryrefslogtreecommitdiffstats
path: root/bench
diff options
context:
space:
mode:
Diffstat (limited to 'bench')
-rw-r--r--bench/Android.mk6
-rw-r--r--bench/MatrixBench.cpp229
-rw-r--r--bench/bench_compare.py140
-rw-r--r--bench/benchmain.cpp146
4 files changed, 456 insertions, 65 deletions
diff --git a/bench/Android.mk b/bench/Android.mk
index b20aef9..71523af 100644
--- a/bench/Android.mk
+++ b/bench/Android.mk
@@ -6,6 +6,9 @@ LOCAL_SRC_FILES := \
BitmapBench.cpp \
DecodeBench.cpp \
FPSBench.cpp \
+ GradientBench.cpp \
+ MatrixBench.cpp \
+ PathBench.cpp \
RectBench.cpp \
RepeatTileBench.cpp \
TextBench.cpp \
@@ -17,7 +20,8 @@ LOCAL_SRC_FILES += \
../src/utils/SkNWayCanvas.cpp \
../src/utils/SkParse.cpp
-LOCAL_SHARED_LIBRARIES := libcutils libskia
+LOCAL_SHARED_LIBRARIES := libcutils libskia libGLESv2
+LOCAL_STATIC_LIBRARIES := libskiagpu
LOCAL_C_INCLUDES := \
external/skia/include/config \
external/skia/include/core \
diff --git a/bench/MatrixBench.cpp b/bench/MatrixBench.cpp
new file mode 100644
index 0000000..d963bc7
--- /dev/null
+++ b/bench/MatrixBench.cpp
@@ -0,0 +1,229 @@
+#include "SkBenchmark.h"
+#include "SkMatrix.h"
+#include "SkRandom.h"
+#include "SkString.h"
+
+class MatrixBench : public SkBenchmark {
+ SkString fName;
+ enum { N = 100000 };
+public:
+ MatrixBench(void* param, const char name[]) : INHERITED(param) {
+ fName.printf("matrix_%s", name);
+ }
+
+ virtual void performTest() = 0;
+
+protected:
+ virtual int mulLoopCount() const { return 1; }
+
+ virtual const char* onGetName() {
+ return fName.c_str();
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ int n = N * this->mulLoopCount();
+ for (int i = 0; i < n; i++) {
+ this->performTest();
+ }
+ }
+
+private:
+ typedef SkBenchmark INHERITED;
+};
+
+// we want to stop the compiler from eliminating code that it thinks is a no-op
+// so we have a non-static global we increment, hoping that will convince the
+// compiler to execute everything
+int gMatrixBench_NonStaticGlobal;
+
+#define always_do(pred) \
+ do { \
+ if (pred) { \
+ ++gMatrixBench_NonStaticGlobal; \
+ } \
+ } while (0)
+
+class EqualsMatrixBench : public MatrixBench {
+public:
+ EqualsMatrixBench(void* param) : INHERITED(param, "equals") {}
+protected:
+ virtual void performTest() {
+ SkMatrix m0, m1, m2;
+
+ m0.reset();
+ m1.reset();
+ m2.reset();
+ always_do(m0 == m1);
+ always_do(m1 == m2);
+ always_do(m2 == m0);
+ always_do(m0.getType());
+ always_do(m1.getType());
+ always_do(m2.getType());
+ }
+private:
+ typedef MatrixBench INHERITED;
+};
+
+class ScaleMatrixBench : public MatrixBench {
+public:
+ ScaleMatrixBench(void* param) : INHERITED(param, "scale") {
+
+ fM0.reset();
+ fM1.setScale(fSX, fSY);
+ fM2.setTranslate(fSX, fSY);
+ fSX = fSY = SkFloatToScalar(1.5f);
+ }
+protected:
+ virtual void performTest() {
+ SkMatrix m;
+ m = fM0; m.preScale(fSX, fSY);
+ m = fM1; m.preScale(fSX, fSY);
+ m = fM2; m.preScale(fSX, fSY);
+ }
+private:
+ SkMatrix fM0, fM1, fM2;
+ SkScalar fSX, fSY;
+ typedef MatrixBench INHERITED;
+};
+
+// having unknown values in our arrays can throw off the timing a lot, perhaps
+// handling NaN values is a lot slower. Anyway, this guy is just meant to put
+// reasonable values in our arrays.
+template <typename T> void init9(T array[9]) {
+ SkRandom rand;
+ for (int i = 0; i < 9; i++) {
+ array[i] = rand.nextSScalar1();
+ }
+}
+
+// Test the performance of setConcat() non-perspective case:
+// using floating point precision only.
+class FloatConcatMatrixBench : public MatrixBench {
+public:
+ FloatConcatMatrixBench(void* p) : INHERITED(p, "concat_floatfloat") {
+ init9(mya);
+ init9(myb);
+ init9(myr);
+ }
+protected:
+ virtual int mulLoopCount() const { return 4; }
+
+ static inline void muladdmul(float a, float b, float c, float d,
+ float* result) {
+ *result = a * b + c * d;
+ }
+ virtual void performTest() {
+ const float* a = mya;
+ const float* b = myb;
+ float* r = myr;
+ muladdmul(a[0], b[0], a[1], b[3], &r[0]);
+ muladdmul(a[0], b[1], a[1], b[4], &r[1]);
+ muladdmul(a[0], b[2], a[1], b[5], &r[2]);
+ r[2] += a[2];
+ muladdmul(a[3], b[0], a[4], b[3], &r[3]);
+ muladdmul(a[3], b[1], a[4], b[4], &r[4]);
+ muladdmul(a[3], b[2], a[4], b[5], &r[5]);
+ r[5] += a[5];
+ r[6] = r[7] = 0.0f;
+ r[8] = 1.0f;
+ }
+private:
+ float mya [9];
+ float myb [9];
+ float myr [9];
+ typedef MatrixBench INHERITED;
+};
+
+static inline float SkDoubleToFloat(double x) {
+ return static_cast<float>(x);
+}
+
+// Test the performance of setConcat() non-perspective case:
+// using floating point precision but casting up to float for
+// intermediate results during computations.
+class FloatDoubleConcatMatrixBench : public MatrixBench {
+public:
+ FloatDoubleConcatMatrixBench(void* p) : INHERITED(p, "concat_floatdouble") {
+ init9(mya);
+ init9(myb);
+ init9(myr);
+ }
+protected:
+ virtual int mulLoopCount() const { return 4; }
+
+ static inline void muladdmul(float a, float b, float c, float d,
+ float* result) {
+ *result = SkDoubleToFloat((double)a * b + (double)c * d);
+ }
+ virtual void performTest() {
+ const float* a = mya;
+ const float* b = myb;
+ float* r = myr;
+ muladdmul(a[0], b[0], a[1], b[3], &r[0]);
+ muladdmul(a[0], b[1], a[1], b[4], &r[1]);
+ muladdmul(a[0], b[2], a[1], b[5], &r[2]);
+ r[2] += a[2];
+ muladdmul(a[3], b[0], a[4], b[3], &r[3]);
+ muladdmul(a[3], b[1], a[4], b[4], &r[4]);
+ muladdmul(a[3], b[2], a[4], b[5], &r[5]);
+ r[5] += a[5];
+ r[6] = r[7] = 0.0f;
+ r[8] = 1.0f;
+ }
+private:
+ float mya [9];
+ float myb [9];
+ float myr [9];
+ typedef MatrixBench INHERITED;
+};
+
+// Test the performance of setConcat() non-perspective case:
+// using double precision only.
+class DoubleConcatMatrixBench : public MatrixBench {
+public:
+ DoubleConcatMatrixBench(void* p) : INHERITED(p, "concat_double") {
+ init9(mya);
+ init9(myb);
+ init9(myr);
+ }
+protected:
+ virtual int mulLoopCount() const { return 4; }
+
+ static inline void muladdmul(double a, double b, double c, double d,
+ double* result) {
+ *result = a * b + c * d;
+ }
+ virtual void performTest() {
+ const double* a = mya;
+ const double* b = myb;
+ double* r = myr;
+ muladdmul(a[0], b[0], a[1], b[3], &r[0]);
+ muladdmul(a[0], b[1], a[1], b[4], &r[1]);
+ muladdmul(a[0], b[2], a[1], b[5], &r[2]);
+ r[2] += a[2];
+ muladdmul(a[3], b[0], a[4], b[3], &r[3]);
+ muladdmul(a[3], b[1], a[4], b[4], &r[4]);
+ muladdmul(a[3], b[2], a[4], b[5], &r[5]);
+ r[5] += a[5];
+ r[6] = r[7] = 0.0;
+ r[8] = 1.0;
+ }
+private:
+ double mya [9];
+ double myb [9];
+ double myr [9];
+ typedef MatrixBench INHERITED;
+};
+
+
+static SkBenchmark* M0(void* p) { return new EqualsMatrixBench(p); }
+static SkBenchmark* M1(void* p) { return new ScaleMatrixBench(p); }
+static SkBenchmark* M2(void* p) { return new FloatConcatMatrixBench(p); }
+static SkBenchmark* M3(void* p) { return new FloatDoubleConcatMatrixBench(p); }
+static SkBenchmark* M4(void* p) { return new DoubleConcatMatrixBench(p); }
+
+static BenchRegistry gReg0(M0);
+static BenchRegistry gReg1(M1);
+static BenchRegistry gReg2(M2);
+static BenchRegistry gReg3(M3);
+static BenchRegistry gReg4(M4);
diff --git a/bench/bench_compare.py b/bench/bench_compare.py
new file mode 100644
index 0000000..f6909b1
--- /dev/null
+++ b/bench/bench_compare.py
@@ -0,0 +1,140 @@
+'''
+Created on May 16, 2011
+
+@author: bungeman
+'''
+import sys
+import getopt
+import re
+
+def parse(lines):
+ """Takes iterable lines of bench output, returns {bench:{config:time}}."""
+
+ benches = {}
+ current_bench = None
+
+ for line in lines:
+ #see if this line starts a new bench
+ new_bench = re.search('running bench \[\d+ \d+\] (.{28})', line)
+ if new_bench:
+ current_bench = new_bench.group(1)
+
+ #add configs on this line to the current bench
+ if current_bench:
+ for new_config in re.finditer(' (.{4}): msecs = (\d+\.\d+)', line):
+ current_config = new_config.group(1)
+ current_time = float(new_config.group(2))
+ if current_bench in benches:
+ benches[current_bench][current_config] = current_time
+ else:
+ benches[current_bench] = {current_config : current_time}
+
+ return benches
+
+def usage():
+ """Prints simple usage information."""
+
+ print '-o <file> the old bench output file.'
+ print '-n <file> the new bench output file.'
+ print '-h causes headers to be output.'
+ print '-f <fieldSpec> which fields to output and in what order.'
+ print ' Not specifying is the same as -f "bcondp".'
+ print ' b: bench'
+ print ' c: config'
+ print ' o: old time'
+ print ' n: new time'
+ print ' d: diff'
+ print ' p: percent diff'
+
+
+def main():
+ """Parses command line and writes output."""
+
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "f:o:n:h")
+ except getopt.GetoptError, err:
+ print str(err)
+ usage()
+ sys.exit(2)
+
+ column_formats = {
+ 'b' : '{bench: >28} ',
+ 'c' : '{config: <4} ',
+ 'o' : '{old_time: >10.2f} ',
+ 'n' : '{new_time: >10.2f} ',
+ 'd' : '{diff: >+10.2f} ',
+ 'p' : '{diffp: >+7.1%} ',
+ }
+ header_formats = {
+ 'b' : '{bench: >28} ',
+ 'c' : '{config: <4} ',
+ 'o' : '{old_time: >10} ',
+ 'n' : '{new_time: >10} ',
+ 'd' : '{diff: >10} ',
+ 'p' : '{diffp: >7} ',
+ }
+
+ old = None
+ new = None
+ column_format = ""
+ header_format = ""
+ columns = 'bcondp'
+ header = False
+
+ for option, value in opts:
+ if option == "-o":
+ old = value
+ elif option == "-n":
+ new = value
+ elif option == "-h":
+ header = True
+ elif option == "-f":
+ columns = value
+ else:
+ usage()
+ assert False, "unhandled option"
+
+ if old is None or new is None:
+ usage()
+ sys.exit(2)
+
+ for column_char in columns:
+ if column_formats[column_char]:
+ column_format += column_formats[column_char]
+ header_format += header_formats[column_char]
+ else:
+ usage()
+ sys.exit(2)
+
+ if header:
+ print header_format.format(
+ bench='bench'
+ , config='conf'
+ , old_time='old'
+ , new_time='new'
+ , diff='diff'
+ , diffp='diffP'
+ )
+
+ old_benches = parse(open(old, 'r'))
+ new_benches = parse(open(new, 'r'))
+
+ for old_bench, old_configs in old_benches.items():
+ if old_bench in new_benches:
+ new_configs = new_benches[old_bench]
+ for old_config, old_time in old_configs.items():
+ if old_config in new_configs:
+ new_time = new_configs[old_config]
+ old_time = old_configs[old_config]
+ print column_format.format(
+ bench=old_bench.strip()
+ , config=old_config.strip()
+ , old_time=old_time
+ , new_time=new_time
+ , diff=(old_time - new_time)
+ , diffp=((old_time-new_time)/old_time)
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/bench/benchmain.cpp b/bench/benchmain.cpp
index 4f6d81c..066573a 100644
--- a/bench/benchmain.cpp
+++ b/bench/benchmain.cpp
@@ -6,6 +6,9 @@
#include "SkPicture.h"
#include "SkString.h"
#include "SkTime.h"
+#include "GrContext.h"
+#include "SkGpuDevice.h"
+#include "SkEGLContext.h"
#include "SkBenchmark.h"
@@ -30,6 +33,7 @@ static void erase(SkBitmap& bm) {
}
}
+#if 0
static bool equal(const SkBitmap& bm1, const SkBitmap& bm2) {
if (bm1.width() != bm2.width() ||
bm1.height() != bm2.height() ||
@@ -43,9 +47,9 @@ static bool equal(const SkBitmap& bm1, const SkBitmap& bm2) {
return false;
}
}
-
return true;
}
+#endif
class Iter {
public:
@@ -62,7 +66,7 @@ public:
}
return NULL;
}
-
+
private:
const BenchRegistry* fBench;
void* fParam;
@@ -102,7 +106,7 @@ static void saveFile(const char name[], const char config[], const char dir[],
*p++ = c | (SK_A32_MASK << SK_A32_SHIFT);
}
}
-
+
SkString str;
make_filename(name, &str);
str.appendf("_%s.png", config);
@@ -118,7 +122,7 @@ static void performClip(SkCanvas* canvas, int w, int h) {
r.set(SkIntToScalar(10), SkIntToScalar(10),
SkIntToScalar(w*2/3), SkIntToScalar(h*2/3));
canvas->clipRect(r, SkRegion::kIntersect_Op);
-
+
r.set(SkIntToScalar(w/3), SkIntToScalar(h/3),
SkIntToScalar(w-10), SkIntToScalar(h-10));
canvas->clipRect(r, SkRegion::kXOR_Op);
@@ -143,21 +147,6 @@ static void performScale(SkCanvas* canvas, int w, int h) {
canvas->translate(-x, -y);
}
-static void compare_pict_to_bitmap(SkPicture* pict, const SkBitmap& bm) {
- SkBitmap bm2;
-
- bm2.setConfig(bm.config(), bm.width(), bm.height());
- bm2.allocPixels();
- erase(bm2);
-
- SkCanvas canvas(bm2);
- canvas.drawPicture(*pict);
-
- if (!equal(bm, bm2)) {
- SkDebugf("----- compare_pict_to_bitmap failed\n");
- }
-}
-
static bool parse_bool_arg(char * const* argv, char* const* stop, bool* var) {
if (argv < stop) {
*var = atoi(*argv) != 0;
@@ -166,16 +155,43 @@ static bool parse_bool_arg(char * const* argv, char* const* stop, bool* var) {
return false;
}
+enum Backend {
+ kRaster_Backend,
+ kGPU_Backend,
+ kPDF_Backend,
+};
+
+static SkDevice* make_device(SkBitmap::Config config, const SkIPoint& size,
+ Backend backend, GrContext* context) {
+ SkDevice* device = NULL;
+ SkBitmap bitmap;
+ bitmap.setConfig(config, size.fX, size.fY);
+
+ switch (backend) {
+ case kRaster_Backend:
+ bitmap.allocPixels();
+ erase(bitmap);
+ device = new SkDevice(NULL, bitmap, true);
+ break;
+ case kGPU_Backend:
+ device = new SkGpuDevice(context, bitmap, SkGpuDevice::Current3DApiRenderTarget());
+// device->clear(0xFFFFFFFF);
+ break;
+ case kPDF_Backend:
+ default:
+ SkASSERT(!"unsupported");
+ }
+ return device;
+}
+
static const struct {
SkBitmap::Config fConfig;
const char* fName;
+ Backend fBackend;
} gConfigs[] = {
- { SkBitmap::kARGB_8888_Config, "8888" },
- { SkBitmap::kRGB_565_Config, "565", },
-#if 0
- { SkBitmap::kARGB_4444_Config, "4444", },
- { SkBitmap::kA8_Config, "A8", }
-#endif
+ { SkBitmap::kARGB_8888_Config, "8888", kRaster_Backend },
+ { SkBitmap::kRGB_565_Config, "565", kRaster_Backend },
+ { SkBitmap::kARGB_8888_Config, "GPU", kGPU_Backend },
};
static int findConfig(const char config[]) {
@@ -189,7 +205,7 @@ static int findConfig(const char config[]) {
int main (int argc, char * const argv[]) {
SkAutoGraphics ag;
-
+
SkTDict<const char*> defineDict(1024);
int repeatDraw = 1;
int forceAlpha = 0xFF;
@@ -199,16 +215,16 @@ int main (int argc, char * const argv[]) {
bool doScale = false;
bool doRotate = false;
bool doClip = false;
- bool doPict = false;
const char* matchStr = NULL;
bool hasStrokeWidth = false;
float strokeWidth;
-
+
SkString outDir;
SkBitmap::Config outConfig = SkBitmap::kNo_Config;
const char* configName = "";
+ Backend backend = kRaster_Backend; // for warning
int configCount = SK_ARRAY_COUNT(gConfigs);
-
+
char* const* stop = argv + argc;
for (++argv; argv < stop; ++argv) {
if (strcmp(*argv, "-o") == 0) {
@@ -219,8 +235,6 @@ int main (int argc, char * const argv[]) {
outDir.append("/");
}
}
- } else if (strcmp(*argv, "-pict") == 0) {
- doPict = true;
} else if (strcmp(*argv, "-repeat") == 0) {
argv++;
if (argv < stop) {
@@ -290,6 +304,7 @@ int main (int argc, char * const argv[]) {
if (index >= 0) {
outConfig = gConfigs[index].fConfig;
configName = gConfigs[index].fName;
+ backend = gConfigs[index].fBackend;
configCount = 1;
} else {
SkString str;
@@ -316,7 +331,7 @@ int main (int argc, char * const argv[]) {
return -1;
}
}
-
+
// report our current settings
{
SkString str;
@@ -324,7 +339,13 @@ int main (int argc, char * const argv[]) {
forceAlpha, forceAA, forceFilter);
log_progress(str);
}
-
+
+ GrContext* context = NULL;
+ SkEGLContext eglContext;
+ if (eglContext.init(1024, 1024)) {
+ context = GrContext::CreateGLShaderContext();
+ }
+
Iter iter(&defineDict);
SkBenchmark* bench;
while ((bench = iter.next()) != NULL) {
@@ -340,32 +361,34 @@ int main (int argc, char * const argv[]) {
if (hasStrokeWidth) {
bench->setStrokeWidth(strokeWidth);
}
-
+
// only run benchmarks if their name contains matchStr
if (matchStr && strstr(bench->getName(), matchStr) == NULL) {
continue;
}
-
+
{
SkString str;
str.printf("running bench [%d %d] %28s", dim.fX, dim.fY,
bench->getName());
log_progress(str);
}
-
+
for (int configIndex = 0; configIndex < configCount; configIndex++) {
if (configCount > 1) {
outConfig = gConfigs[configIndex].fConfig;
configName = gConfigs[configIndex].fName;
+ backend = gConfigs[configIndex].fBackend;
}
- SkBitmap bm;
- bm.setConfig(outConfig, dim.fX, dim.fY);
- bm.allocPixels();
- erase(bm);
-
- SkCanvas canvas(bm);
-
+ if (kGPU_Backend == backend && NULL == context) {
+ continue;
+ }
+
+ SkDevice* device = make_device(outConfig, dim, backend, context);
+ SkCanvas canvas(device);
+ device->unref();
+
if (doClip) {
performClip(&canvas, dim.fX, dim.fY);
}
@@ -375,33 +398,27 @@ int main (int argc, char * const argv[]) {
if (doRotate) {
performRotate(&canvas, dim.fX, dim.fY);
}
-
+
+ //warm up caches if needed
if (repeatDraw > 1) {
SkAutoCanvasRestore acr(&canvas, true);
bench->draw(&canvas);
+ if (kGPU_Backend == backend && context) {
+ context->flush();
+ glFinish();
+ }
}
-
+
SkMSec now = SkTime::GetMSecs();
for (int i = 0; i < repeatDraw; i++) {
- SkCanvas* c = &canvas;
-
- SkNWayCanvas nway;
- SkPicture* pict = NULL;
- if (doPict) {
- pict = new SkPicture;
- nway.addCanvas(pict->beginRecording(bm.width(), bm.height()));
- nway.addCanvas(&canvas);
- c = &nway;
- }
-
- SkAutoCanvasRestore acr(c, true);
- bench->draw(c);
-
- if (pict) {
- compare_pict_to_bitmap(pict, bm);
- pict->unref();
- }
+ SkAutoCanvasRestore acr(&canvas, true);
+ bench->draw(&canvas);
+ }
+ if (kGPU_Backend == backend && context) {
+ context->flush();
+ glFinish();
}
+
if (repeatDraw > 1) {
double duration = SkTime::GetMSecs() - now;
SkString str;
@@ -409,7 +426,8 @@ int main (int argc, char * const argv[]) {
log_progress(str);
}
if (outDir.size() > 0) {
- saveFile(bench->getName(), configName, outDir.c_str(), bm);
+ saveFile(bench->getName(), configName, outDir.c_str(),
+ device->accessBitmap(false));
}
}
log_progress("\n");