From 0b15698a8c76bb8abc1b555c1d91892669b4118f Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Mon, 6 Jun 2011 17:02:24 -0400 Subject: Skia Merge (revision 1510) This CL includes bug fixes and closely mirrors the version of Skia used in Chrome M13, which is likely to be our baseline for ICS. The CL also adds source files for the SampleApp which will allow us to execute basic skia tests. The SampleApp requires the utils/views directory in order to run. Finally, we have included the PDF backend for Skia in order to experiment with using it to generate PDF files for certain applications. Note: The SampleApp and PDF code are not built as part of libskia. Change-Id: I1895ccfbd8074e25f19148cc7bd1b4af571fb307 --- bench/Android.mk | 6 +- bench/MatrixBench.cpp | 229 +++++++++++++++++++++++++++++++++++++++++++++++++ bench/bench_compare.py | 140 ++++++++++++++++++++++++++++++ bench/benchmain.cpp | 146 +++++++++++++++++-------------- 4 files changed, 456 insertions(+), 65 deletions(-) create mode 100644 bench/MatrixBench.cpp create mode 100644 bench/bench_compare.py (limited to 'bench') 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 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(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 the old bench output file.' + print '-n the new bench output file.' + print '-h causes headers to be output.' + print '-f 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 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"); -- cgit v1.1