diff options
author | Derek Sollenberger <djsollen@google.com> | 2012-01-18 08:56:56 -0500 |
---|---|---|
committer | Derek Sollenberger <derek@android.com> | 2012-02-06 14:14:40 -0500 |
commit | 1cab2921ab279367f8206cdadc9259d12e603548 (patch) | |
tree | 2852f9dc2481f639122e18fc7831ae6ca43d6d5a /bench | |
parent | d7176fd5571bc9878d3cdac8696eaa35ec170d9d (diff) | |
download | external_skia-1cab2921ab279367f8206cdadc9259d12e603548.zip external_skia-1cab2921ab279367f8206cdadc9259d12e603548.tar.gz external_skia-1cab2921ab279367f8206cdadc9259d12e603548.tar.bz2 |
Skia merge (revision 3022)
This CL has companion changes to account for API updates in...
(1) frameworks/base
(2) external/webkit
Change-Id: Ibb989e76e8bd24313849f9631dbef42cdef9eb7d
Diffstat (limited to 'bench')
40 files changed, 3116 insertions, 571 deletions
diff --git a/bench/AAClipBench.cpp b/bench/AAClipBench.cpp new file mode 100644 index 0000000..39088c1 --- /dev/null +++ b/bench/AAClipBench.cpp @@ -0,0 +1,104 @@ +/* + * Copyright 2011 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 "SkAAClip.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkString.h" + +class AAClipBuilderBench : public SkBenchmark { + SkString fName; + SkPath fPath; + SkRect fRect; + SkRegion fRegion; + bool fDoPath; + bool fDoAA; + + enum { + N = SkBENCHLOOP(200), + }; + +public: + AAClipBuilderBench(void* param, bool doPath, bool doAA) : INHERITED(param) { + fDoPath = doPath; + fDoAA = doAA; + + fName.printf("aaclip_build_%s_%s", doPath ? "path" : "rect", + doAA ? "AA" : "BW"); + + fRegion.setRect(0, 0, 640, 480); + fRect.set(fRegion.getBounds()); + fRect.inset(SK_Scalar1/4, SK_Scalar1/4); + fPath.addRoundRect(fRect, SkIntToScalar(20), SkIntToScalar(20)); + } + +protected: + virtual const char* onGetName() { return fName.c_str(); } + virtual void onDraw(SkCanvas* canvas) { + SkPaint paint; + this->setupPaint(&paint); + + for (int i = 0; i < N; ++i) { + SkAAClip clip; + if (fDoPath) { + clip.setPath(fPath, &fRegion, fDoAA); + } else { + clip.setRect(fRect, fDoAA); + } + } + } +private: + typedef SkBenchmark INHERITED; +}; + +class AAClipRegionBench : public SkBenchmark { +public: + AAClipRegionBench(void* param) : INHERITED(param) { + SkPath path; + // test conversion of a complex clip to a aaclip + path.addCircle(0, 0, SkIntToScalar(200)); + path.addCircle(0, 0, SkIntToScalar(180)); + // evenodd means we've constructed basically a stroked circle + path.setFillType(SkPath::kEvenOdd_FillType); + + SkIRect bounds; + path.getBounds().roundOut(&bounds); + fRegion.setPath(path, SkRegion(bounds)); + } + +protected: + virtual const char* onGetName() { return "aaclip_setregion"; } + virtual void onDraw(SkCanvas* canvas) { + for (int i = 0; i < N; ++i) { + SkAAClip clip; + clip.setRegion(fRegion); + } + } + +private: + enum { + N = SkBENCHLOOP(400), + }; + SkRegion fRegion; + typedef SkBenchmark INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static SkBenchmark* Fact0(void* p) { return SkNEW_ARGS(AAClipBuilderBench, (p, false, false)); } +static SkBenchmark* Fact1(void* p) { return SkNEW_ARGS(AAClipBuilderBench, (p, false, true)); } +static SkBenchmark* Fact2(void* p) { return SkNEW_ARGS(AAClipBuilderBench, (p, true, false)); } +static SkBenchmark* Fact3(void* p) { return SkNEW_ARGS(AAClipBuilderBench, (p, true, true)); } + +static BenchRegistry gReg0(Fact0); +static BenchRegistry gReg1(Fact1); +static BenchRegistry gReg2(Fact2); +static BenchRegistry gReg3(Fact3); + +static SkBenchmark* Fact01(void* p) { return SkNEW_ARGS(AAClipRegionBench, (p)); } +static BenchRegistry gReg01(Fact01); diff --git a/bench/Android.mk b/bench/Android.mk index a0fe86c..ab2e588 100644 --- a/bench/Android.mk +++ b/bench/Android.mk @@ -3,36 +3,42 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - BenchGpuTimer_none.cpp \ - BenchSysTimer_posix.cpp \ + benchmain.cpp \ BenchTimer.cpp \ + BenchSysTimer_posix.cpp \ + BenchGpuTimer_gl.cpp \ + SkBenchmark.cpp + +LOCAL_SRC_FILES += \ + AAClipBench.cpp \ BitmapBench.cpp \ + BlurBench.cpp \ + ChromeBench.cpp \ DecodeBench.cpp \ - FPSBench.cpp \ + FontScalerBench.cpp \ GradientBench.cpp \ + MathBench.cpp \ MatrixBench.cpp \ + MutexBench.cpp \ PathBench.cpp \ RectBench.cpp \ RepeatTileBench.cpp \ + ScalarBench.cpp \ + ShaderMaskBench.cpp \ TextBench.cpp \ - SkBenchmark.cpp \ - benchmain.cpp - -# additional optional class for this tool -LOCAL_SRC_FILES += \ - ../src/utils/SkNWayCanvas.cpp \ - ../src/utils/SkParse.cpp + VertBench.cpp -LOCAL_SHARED_LIBRARIES := libcutils libskia libGLESv2 +LOCAL_SHARED_LIBRARIES := libcutils libskia libGLESv2 libEGL LOCAL_STATIC_LIBRARIES := libskiagpu LOCAL_C_INCLUDES := \ - external/skia/include/config \ external/skia/include/core \ + external/skia/include/config \ + external/skia/include/effects \ + external/skia/include/gpu \ external/skia/include/images \ external/skia/include/utils \ - external/skia/include/effects \ - external/skia/gpu/include \ - external/skia/include/gpu + external/skia/src/core \ + external/skia/src/gpu #LOCAL_CFLAGS := diff --git a/bench/BenchGpuTimer_gl.cpp b/bench/BenchGpuTimer_gl.cpp index ec2145d..885f7b2 100644 --- a/bench/BenchGpuTimer_gl.cpp +++ b/bench/BenchGpuTimer_gl.cpp @@ -1,159 +1,41 @@ -#include "BenchGpuTimer_gl.h" -#include <string.h> - -//GL -#define BENCH_GL_FUNCTION_TYPE -#if defined(SK_MESA) - #include <GL/osmesa.h> - #define SK_BENCH_CONTEXT_CHECK (NULL != OSMesaGetCurrentContext()) - - #define SK_GL_GET_PROC(F) gBenchGL.f ## F = (BenchGL ## F ## Proc) \ - OSMesaGetProcAddress("gl" #F); - #define SK_GL_GET_PROC_SUFFIX(F, S) gBenchGL.f ## F = (BenchGL##F##Proc)\ - OSMesaGetProcAddress("gl" #F #S); - -#elif defined(SK_BUILD_FOR_WIN32) - #define WIN32_LEAN_AND_MEAN 1 - #include <Windows.h> - #include <GL/GL.h> - #define SK_BENCH_CONTEXT_CHECK (NULL != wglGetCurrentContext()) - - #undef BENCH_GL_FUNCTION_TYPE - #define BENCH_GL_FUNCTION_TYPE __stdcall - #define SK_GL_GET_PROC(F) gBenchGL.f ## F = (BenchGL ## F ## Proc) \ - wglGetProcAddress("gl" #F); - #define SK_GL_GET_PROC_SUFFIX(F, S) gBenchGL.f ## F = (BenchGL##F##Proc)\ - wglGetProcAddress("gl" #F #S); - -#elif defined(SK_BUILD_FOR_MAC) - #include <OpenGL/gl.h> - #include <OpenGL/CGLCurrent.h> - #define SK_BENCH_CONTEXT_CHECK (NULL != CGLGetCurrentContext()) - -#elif defined(SK_BUILD_FOR_UNIX) - #include <GL/gl.h> - #include <GL/glx.h> - #define SK_BENCH_CONTEXT_CHECK (NULL != glXGetCurrentContext()) +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "BenchGpuTimer_gl.h" +#include "SkGLContext.h" + +BenchGpuTimer::BenchGpuTimer(const SkGLContext* glctx) { + fContext = glctx; + glctx->ref(); + glctx->makeCurrent(); + fStarted = false; + fSupported = GrGLGetVersion(glctx->gl()) > GR_GL_VER(3,3) || + GrGLHasExtension(glctx->gl(), "GL_ARB_timer_query") || + GrGLHasExtension(glctx->gl(), "GL_EXT_timer_query"); - #define SK_GL_GET_PROC(F) gBenchGL.f ## F = (BenchGL ## F ## Proc) \ - glXGetProcAddressARB(reinterpret_cast<const GLubyte*>("gl" #F)); - #define SK_GL_GET_PROC_SUFFIX(F, S) gBenchGL.f ## F = (BenchGL##F##Proc)\ - glXGetProcAddressARB(reinterpret_cast<const GLubyte*>("gl" #F #S)); -#else - #error unsupported platform -#endif - -#define BenchGL_TIME_ELAPSED 0x88BF -#define BenchGL_QUERY_RESULT 0x8866 -#define BenchGL_QUERY_RESULT_AVAILABLE 0x8867 - -#if defined(SK_BUILD_FOR_WIN32) -typedef UINT64 BenchGLuint64; -#else -#include <stdint.h> -typedef uint64_t BenchGLuint64; -#endif - -typedef void (BENCH_GL_FUNCTION_TYPE *BenchGLGenQueriesProc) (GLsizei n, GLuint *ids); -typedef void (BENCH_GL_FUNCTION_TYPE *BenchGLBeginQueryProc) (GLenum target, GLuint id); -typedef void (BENCH_GL_FUNCTION_TYPE *BenchGLEndQueryProc) (GLenum target); -typedef void (BENCH_GL_FUNCTION_TYPE *BenchGLDeleteQueriesProc) (GLsizei n, const GLuint *ids); -typedef void (BENCH_GL_FUNCTION_TYPE *BenchGLGetQueryObjectivProc) (GLuint id, GLenum pname, GLint *params); -typedef void (BENCH_GL_FUNCTION_TYPE *BenchGLGetQueryObjectui64vProc) (GLuint id, GLenum pname, BenchGLuint64 *params); - -struct BenchGLInterface { - bool fHasTimer; - BenchGLGenQueriesProc fGenQueries; - BenchGLBeginQueryProc fBeginQuery; - BenchGLEndQueryProc fEndQuery; - BenchGLDeleteQueriesProc fDeleteQueries; - BenchGLGetQueryObjectivProc fGetQueryObjectiv; - BenchGLGetQueryObjectui64vProc fGetQueryObjectui64v; -}; - -static bool BenchGLCheckExtension(const char* ext, - const char* extensionString) { - int extLength = strlen(ext); - - while (true) { - int n = strcspn(extensionString, " "); - if (n == extLength && 0 == strncmp(ext, extensionString, n)) { - return true; - } - if (0 == extensionString[n]) { - return false; - } - extensionString += n+1; - } - - return false; -} - -static BenchGLInterface gBenchGL; -static bool gBenchGLInterfaceInit = false; - -static void BenchGLSetDefaultGLInterface() { - gBenchGL.fHasTimer = false; - if (gBenchGLInterfaceInit || !SK_BENCH_CONTEXT_CHECK) return; - - const char* glExts = - reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)); - const GLboolean ext = - BenchGLCheckExtension("GL_EXT_timer_query", glExts); - const GLboolean arb = - BenchGLCheckExtension("GL_ARB_timer_query", glExts); - if (ext || arb) { -#if defined(SK_BUILD_FOR_MAC) - #if GL_EXT_timer_query || GL_ARB_timer_query - gBenchGL.fHasTimer = true; - gBenchGL.fGenQueries = glGenQueries; - gBenchGL.fBeginQuery = glBeginQuery; - gBenchGL.fEndQuery = glEndQuery; - gBenchGL.fDeleteQueries = glDeleteQueries; - gBenchGL.fGetQueryObjectiv = glGetQueryObjectiv; - #endif - #if GL_ARB_timer_query - gBenchGL.fGetQueryObjectui64v = glGetQueryObjectui64v; - #elif GL_EXT_timer_query - gBenchGL.fGetQueryObjectui64v = glGetQueryObjectui64vEXT; - #endif -#else - gBenchGL.fHasTimer = true; - SK_GL_GET_PROC(GenQueries) - SK_GL_GET_PROC(BeginQuery) - SK_GL_GET_PROC(EndQuery) - SK_GL_GET_PROC(DeleteQueries) - - SK_GL_GET_PROC(GetQueryObjectiv) - if (arb) { - SK_GL_GET_PROC(GetQueryObjectui64v) - } else { - SK_GL_GET_PROC_SUFFIX(GetQueryObjectui64v, EXT) - } -#endif - } - gBenchGLInterfaceInit = true; -} - -BenchGpuTimer::BenchGpuTimer() { - BenchGLSetDefaultGLInterface(); - if (gBenchGL.fHasTimer) { - gBenchGL.fGenQueries(1, &this->fQuery); + if (fSupported) { + SK_GL(*glctx, GenQueries(1, &fQuery)); } } BenchGpuTimer::~BenchGpuTimer() { - if (gBenchGL.fHasTimer) { - gBenchGL.fDeleteQueries(1, &this->fQuery); + if (fSupported) { + fContext->makeCurrent(); + SK_GL(*fContext, DeleteQueries(1, &fQuery)); } + fContext->unref(); } void BenchGpuTimer::startGpu() { - if (!gBenchGL.fHasTimer) return; - - this->fStarted = true; - gBenchGL.fBeginQuery(BenchGL_TIME_ELAPSED, this->fQuery); + if (fSupported) { + fContext->makeCurrent(); + fStarted = true; + SK_GL(*fContext, BeginQuery(GR_GL_TIME_ELAPSED, fQuery)); + } } /** @@ -161,21 +43,24 @@ void BenchGpuTimer::startGpu() { * as this will cpu wait for the gpu to finish. */ double BenchGpuTimer::endGpu() { - if (!gBenchGL.fHasTimer) return 0; - - this->fStarted = false; - gBenchGL.fEndQuery(BenchGL_TIME_ELAPSED); - - GLint available = 0; - while (!available) { - gBenchGL.fGetQueryObjectiv(this->fQuery - , BenchGL_QUERY_RESULT_AVAILABLE - , &available); + if (fSupported) { + fStarted = false; + fContext->makeCurrent(); + SK_GL(*fContext, EndQuery(GR_GL_TIME_ELAPSED)); + + GrGLint available = 0; + while (!available) { + SK_GL(*fContext, GetQueryObjectiv(fQuery, + GR_GL_QUERY_RESULT_AVAILABLE, + &available)); + } + GrGLuint64 totalGPUTimeElapsed = 0; + SK_GL(*fContext, GetQueryObjectui64v(fQuery, + GR_GL_QUERY_RESULT, + &totalGPUTimeElapsed)); + + return totalGPUTimeElapsed / 1000000.0; + } else { + return 0; } - BenchGLuint64 totalGPUTimeElapsed = 0; - gBenchGL.fGetQueryObjectui64v(this->fQuery - , BenchGL_QUERY_RESULT - , &totalGPUTimeElapsed); - - return totalGPUTimeElapsed / 1000000.0; } diff --git a/bench/BenchGpuTimer_gl.h b/bench/BenchGpuTimer_gl.h index ac23482..7c7b5c2 100644 --- a/bench/BenchGpuTimer_gl.h +++ b/bench/BenchGpuTimer_gl.h @@ -1,33 +1,26 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ #ifndef SkBenchGpuTimer_DEFINED #define SkBenchGpuTimer_DEFINED -#if defined(SK_MESA) - #include <GL/osmesa.h> - -#elif defined(SK_BUILD_FOR_WIN32) - #define WIN32_LEAN_AND_MEAN 1 - #include <Windows.h> - #include <GL/GL.h> - -#elif defined(SK_BUILD_FOR_MAC) - #include <OpenGL/gl.h> - -#elif defined(SK_BUILD_FOR_UNIX) - #include <GL/gl.h> - -#else - #error unsupported platform -#endif +class SkGLContext; class BenchGpuTimer { public: - BenchGpuTimer(); + BenchGpuTimer(const SkGLContext* glctx); ~BenchGpuTimer(); void startGpu(); double endGpu(); private: - GLuint fQuery; + unsigned fQuery; int fStarted; + const SkGLContext* fContext; + bool fSupported; }; #endif diff --git a/bench/BenchGpuTimer_none.cpp b/bench/BenchGpuTimer_none.cpp deleted file mode 100644 index 0dba6d7..0000000 --- a/bench/BenchGpuTimer_none.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "BenchGpuTimer_none.h" - -BenchGpuTimer::BenchGpuTimer() { -} - -BenchGpuTimer::~BenchGpuTimer() { -} - -void BenchGpuTimer::startGpu() { -} - -double BenchGpuTimer::endGpu() { - return -1.0; -} diff --git a/bench/BenchGpuTimer_none.h b/bench/BenchGpuTimer_none.h deleted file mode 100644 index 7069ca4..0000000 --- a/bench/BenchGpuTimer_none.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef SkBenchGpuTimer_DEFINED -#define SkBenchGpuTimer_DEFINED - -class BenchGpuTimer { -public: - BenchGpuTimer(); - ~BenchGpuTimer(); - void startGpu(); - double endGpu(); -}; - -#endif diff --git a/bench/BenchSysTimer_c.cpp b/bench/BenchSysTimer_c.cpp index fc0850b..f4cbd39 100644 --- a/bench/BenchSysTimer_c.cpp +++ b/bench/BenchSysTimer_c.cpp @@ -1,3 +1,10 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ #include "BenchSysTimer_c.h" //Time diff --git a/bench/BenchSysTimer_c.h b/bench/BenchSysTimer_c.h index c598f30..2ddc83d 100644 --- a/bench/BenchSysTimer_c.h +++ b/bench/BenchSysTimer_c.h @@ -1,3 +1,10 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ #ifndef SkBenchSysTimer_DEFINED #define SkBenchSysTimer_DEFINED diff --git a/bench/BenchSysTimer_mach.cpp b/bench/BenchSysTimer_mach.cpp index b23897c..c837ca3 100644 --- a/bench/BenchSysTimer_mach.cpp +++ b/bench/BenchSysTimer_mach.cpp @@ -1,3 +1,10 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ #include "BenchSysTimer_mach.h" //Time diff --git a/bench/BenchSysTimer_mach.h b/bench/BenchSysTimer_mach.h index da4fff0..44d0e5e 100644 --- a/bench/BenchSysTimer_mach.h +++ b/bench/BenchSysTimer_mach.h @@ -1,3 +1,10 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ #ifndef SkBenchSysTimer_DEFINED #define SkBenchSysTimer_DEFINED diff --git a/bench/BenchSysTimer_posix.cpp b/bench/BenchSysTimer_posix.cpp index 5d28f40..e6767e5 100644 --- a/bench/BenchSysTimer_posix.cpp +++ b/bench/BenchSysTimer_posix.cpp @@ -1,3 +1,10 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ #include "BenchSysTimer_posix.h" //Time diff --git a/bench/BenchSysTimer_posix.h b/bench/BenchSysTimer_posix.h index 09dfb0e..de793f3 100644 --- a/bench/BenchSysTimer_posix.h +++ b/bench/BenchSysTimer_posix.h @@ -1,3 +1,10 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ #ifndef SkBenchSysTimer_DEFINED #define SkBenchSysTimer_DEFINED diff --git a/bench/BenchSysTimer_windows.cpp b/bench/BenchSysTimer_windows.cpp index 923754c..3635ec5 100644 --- a/bench/BenchSysTimer_windows.cpp +++ b/bench/BenchSysTimer_windows.cpp @@ -1,3 +1,10 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ #include "BenchSysTimer_windows.h" //Time @@ -35,7 +42,7 @@ void BenchSysTimer::startCpu() { double BenchSysTimer::endCpu() { ULONGLONG end_cpu = winCpuTime(); - return (end_cpu - this->fStartCpu) / 10000; + return static_cast<double>((end_cpu - this->fStartCpu)) / 10000.0L; } double BenchSysTimer::endWall() { LARGE_INTEGER end_wall; @@ -48,8 +55,10 @@ double BenchSysTimer::endWall() { LARGE_INTEGER frequency; if (0 == ::QueryPerformanceFrequency(&frequency)) { - return 0; + return 0.0L; } else { - return (double)ticks_elapsed.QuadPart / frequency.QuadPart * 1000; + return static_cast<double>(ticks_elapsed.QuadPart) + / static_cast<double>(frequency.QuadPart) + * 1000.0L; } } diff --git a/bench/BenchSysTimer_windows.h b/bench/BenchSysTimer_windows.h index 72a3fb2..c3d0c9b 100644 --- a/bench/BenchSysTimer_windows.h +++ b/bench/BenchSysTimer_windows.h @@ -1,3 +1,10 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ #ifndef SkBenchSysTimer_DEFINED #define SkBenchSysTimer_DEFINED @@ -5,7 +12,7 @@ #define WIN32_LEAN_AND_MEAN 1 #include <Windows.h> -struct BenchSysTimer { +class BenchSysTimer { public: void startWall(); void startCpu(); diff --git a/bench/BenchTimer.cpp b/bench/BenchTimer.cpp index e7b0068..c3a1190 100644 --- a/bench/BenchTimer.cpp +++ b/bench/BenchTimer.cpp @@ -1,48 +1,55 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ #include "BenchTimer.h" #if defined(SK_BUILD_FOR_WIN32) #include "BenchSysTimer_windows.h" #elif defined(SK_BUILD_FOR_MAC) #include "BenchSysTimer_mach.h" -#elif defined(SK_BUILD_FOR_UNIX) +#elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID) #include "BenchSysTimer_posix.h" #else #include "BenchSysTimer_c.h" #endif -#if defined(SK_MESA) || \ - defined(SK_BUILD_FOR_WIN32) || \ - defined(SK_BUILD_FOR_MAC) || \ - defined(SK_BUILD_FOR_UNIX) - #include "BenchGpuTimer_gl.h" - -#else - #include "BenchGpuTimer_none.h" -#endif +#include "BenchGpuTimer_gl.h" -BenchTimer::BenchTimer() +BenchTimer::BenchTimer(SkGLContext* gl) : fCpu(-1.0) , fWall(-1.0) , fGpu(-1.0) { - this->fSysTimer = new BenchSysTimer(); - this->fGpuTimer = new BenchGpuTimer(); + fSysTimer = new BenchSysTimer(); + if (gl) { + fGpuTimer = new BenchGpuTimer(gl); + } else { + fGpuTimer = NULL; + } } BenchTimer::~BenchTimer() { - delete this->fSysTimer; - delete this->fGpuTimer; + delete fSysTimer; + delete fGpuTimer; } void BenchTimer::start() { - this->fSysTimer->startWall(); - this->fGpuTimer->startGpu(); - this->fSysTimer->startCpu(); + fSysTimer->startWall(); + if (fGpuTimer) { + fGpuTimer->startGpu(); + } + fSysTimer->startCpu(); } void BenchTimer::end() { - this->fCpu = this->fSysTimer->endCpu(); + fCpu = fSysTimer->endCpu(); //It is important to stop the cpu clocks first, //as the following will cpu wait for the gpu to finish. - this->fGpu = this->fGpuTimer->endGpu(); - this->fWall = this->fSysTimer->endWall(); + if (fGpuTimer) { + fGpu = fGpuTimer->endGpu(); + } + fWall = fSysTimer->endWall(); } diff --git a/bench/BenchTimer.h b/bench/BenchTimer.h index eae82d5..080bc6d 100644 --- a/bench/BenchTimer.h +++ b/bench/BenchTimer.h @@ -1,9 +1,21 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ #ifndef SkBenchTimer_DEFINED #define SkBenchTimer_DEFINED +#include <SkTypes.h> + + class BenchSysTimer; class BenchGpuTimer; +class SkGLContext; + /** * SysTimers and GpuTimers are implemented orthogonally. * This class combines a SysTimer and a GpuTimer into one single, @@ -11,7 +23,7 @@ class BenchGpuTimer; */ class BenchTimer { public: - BenchTimer(); + BenchTimer(SkGLContext* gl = NULL); ~BenchTimer(); void start(); void end(); diff --git a/bench/BitmapBench.cpp b/bench/BitmapBench.cpp index 77e7ade..3b16925 100644 --- a/bench/BitmapBench.cpp +++ b/bench/BitmapBench.cpp @@ -1,3 +1,10 @@ + +/* + * Copyright 2011 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 "SkBitmap.h" #include "SkPaint.h" @@ -93,13 +100,15 @@ class BitmapBench : public SkBenchmark { SkBitmap fBitmap; SkPaint fPaint; bool fIsOpaque; + bool fForceUpdate; //bitmap marked as dirty before each draw. forces bitmap to be updated on device cache int fTileX, fTileY; // -1 means don't use shader SkString fName; - enum { N = 300 }; + enum { N = SkBENCHLOOP(300) }; public: BitmapBench(void* param, bool isOpaque, SkBitmap::Config c, + bool forceUpdate = false, bool bitmapVolatile = false, int tx = -1, int ty = -1) - : INHERITED(param), fIsOpaque(isOpaque), fTileX(tx), fTileY(ty) { + : INHERITED(param), fIsOpaque(isOpaque), fForceUpdate(forceUpdate), fTileX(tx), fTileY(ty) { const int w = 128; const int h = 128; SkBitmap bm; @@ -124,6 +133,7 @@ public: fBitmap.getColorTable()->setIsOpaque(isOpaque); } fBitmap.setIsOpaque(isOpaque); + fBitmap.setIsVolatile(bitmapVolatile); } protected: @@ -137,6 +147,11 @@ protected: } fName.appendf("_%s%s", gConfigName[fBitmap.config()], fIsOpaque ? "" : "_A"); + if (fForceUpdate) + fName.append("_update"); + if (fBitmap.isVolatile()) + fName.append("_volatile"); + return fName.c_str(); } @@ -154,6 +169,10 @@ protected: for (int i = 0; i < N; i++) { SkScalar x = x0 + rand.nextUScalar1() * dim.fX; SkScalar y = y0 + rand.nextUScalar1() * dim.fY; + + if (fForceUpdate) + bitmap.notifyPixelsChanged(); + canvas->drawBitmap(bitmap, x, y, &paint); } } @@ -169,6 +188,8 @@ static SkBenchmark* Fact3(void* p) { return new BitmapBench(p, false, SkBitmap:: static SkBenchmark* Fact4(void* p) { return new BitmapBench(p, true, SkBitmap::kARGB_4444_Config); } static SkBenchmark* Fact5(void* p) { return new BitmapBench(p, false, SkBitmap::kIndex8_Config); } static SkBenchmark* Fact6(void* p) { return new BitmapBench(p, true, SkBitmap::kIndex8_Config); } +static SkBenchmark* Fact7(void* p) { return new BitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, true); } +static SkBenchmark* Fact8(void* p) { return new BitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, false); } static BenchRegistry gReg0(Fact0); static BenchRegistry gReg1(Fact1); @@ -177,3 +198,5 @@ static BenchRegistry gReg3(Fact3); static BenchRegistry gReg4(Fact4); static BenchRegistry gReg5(Fact5); static BenchRegistry gReg6(Fact6); +static BenchRegistry gReg7(Fact7); +static BenchRegistry gReg8(Fact8); diff --git a/bench/BlurBench.cpp b/bench/BlurBench.cpp new file mode 100644 index 0000000..de78fe1 --- /dev/null +++ b/bench/BlurBench.cpp @@ -0,0 +1,106 @@ + +/* + * Copyright 2011 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 "SkPaint.h" +#include "SkRandom.h" +#include "SkShader.h" +#include "SkString.h" +#include "SkBlurMaskFilter.h" + +#define SMALL SkIntToScalar(2) +#define REAL SkFloatToScalar(1.5f) +#define BIG SkIntToScalar(10) + +static const char* gStyleName[] = { + "normal", + "solid", + "outer", + "inner" +}; + +class BlurBench : public SkBenchmark { + SkScalar fRadius; + SkBlurMaskFilter::BlurStyle fStyle; + SkString fName; + +public: + BlurBench(void* param, SkScalar rad, SkBlurMaskFilter::BlurStyle bs) : INHERITED(param) { + fRadius = rad; + fStyle = bs; + const char* name = rad > 0 ? gStyleName[bs] : "none"; + if (SkScalarFraction(rad) != 0) { + fName.printf("blur_%.2f_%s", SkScalarToFloat(rad), name); + } else { + fName.printf("blur_%d_%s", SkScalarRound(rad), name); + } + } + +protected: + virtual const char* onGetName() { + return fName.c_str(); + } + + virtual void onDraw(SkCanvas* canvas) { + SkPaint paint; + this->setupPaint(&paint); + + paint.setAntiAlias(true); + + SkRandom rand; + for (int i = 0; i < SkBENCHLOOP(10); i++) { + SkRect r = SkRect::MakeWH(rand.nextUScalar1() * 400, + rand.nextUScalar1() * 400); + r.offset(fRadius, fRadius); + + if (fRadius > 0) { + SkMaskFilter* mf = SkBlurMaskFilter::Create(fRadius, fStyle, 0); + paint.setMaskFilter(mf)->unref(); + } + canvas->drawOval(r, paint); + } + } + +private: + typedef SkBenchmark INHERITED; +}; + +static SkBenchmark* Fact00(void* p) { return new BlurBench(p, SMALL, SkBlurMaskFilter::kNormal_BlurStyle); } +static SkBenchmark* Fact01(void* p) { return new BlurBench(p, SMALL, SkBlurMaskFilter::kSolid_BlurStyle); } +static SkBenchmark* Fact02(void* p) { return new BlurBench(p, SMALL, SkBlurMaskFilter::kOuter_BlurStyle); } +static SkBenchmark* Fact03(void* p) { return new BlurBench(p, SMALL, SkBlurMaskFilter::kInner_BlurStyle); } + +static SkBenchmark* Fact10(void* p) { return new BlurBench(p, BIG, SkBlurMaskFilter::kNormal_BlurStyle); } +static SkBenchmark* Fact11(void* p) { return new BlurBench(p, BIG, SkBlurMaskFilter::kSolid_BlurStyle); } +static SkBenchmark* Fact12(void* p) { return new BlurBench(p, BIG, SkBlurMaskFilter::kOuter_BlurStyle); } +static SkBenchmark* Fact13(void* p) { return new BlurBench(p, BIG, SkBlurMaskFilter::kInner_BlurStyle); } + +static SkBenchmark* Fact20(void* p) { return new BlurBench(p, REAL, SkBlurMaskFilter::kNormal_BlurStyle); } +static SkBenchmark* Fact21(void* p) { return new BlurBench(p, REAL, SkBlurMaskFilter::kSolid_BlurStyle); } +static SkBenchmark* Fact22(void* p) { return new BlurBench(p, REAL, SkBlurMaskFilter::kOuter_BlurStyle); } +static SkBenchmark* Fact23(void* p) { return new BlurBench(p, REAL, SkBlurMaskFilter::kInner_BlurStyle); } + +static SkBenchmark* FactNone(void* p) { return new BlurBench(p, 0, SkBlurMaskFilter::kNormal_BlurStyle); } + +static BenchRegistry gReg00(Fact00); +static BenchRegistry gReg01(Fact01); +static BenchRegistry gReg02(Fact02); +static BenchRegistry gReg03(Fact03); + +static BenchRegistry gReg10(Fact10); +static BenchRegistry gReg11(Fact11); +static BenchRegistry gReg12(Fact12); +static BenchRegistry gReg13(Fact13); + +static BenchRegistry gReg20(Fact20); +static BenchRegistry gReg21(Fact21); +static BenchRegistry gReg22(Fact22); +static BenchRegistry gReg23(Fact23); + +static BenchRegistry gRegNone(FactNone); + diff --git a/bench/ChromeBench.cpp b/bench/ChromeBench.cpp new file mode 100644 index 0000000..fc73d53 --- /dev/null +++ b/bench/ChromeBench.cpp @@ -0,0 +1,498 @@ +/* + * Copyright 2011 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 "SkPaint.h" +#include "SkString.h" + +/** + Benchmarks that try to emulate a particular Skia call pattern observed in Chrome. +*/ + +/// blitRect() calls emitted by Chrome while scrolling through gmail: count, width, height. +int gmailScrollingRectSpec [431*3] = { + 1, 1254, 1160, + 1, 64, 112, + 1, 1034, 261, + 1, 1166, 1, + 1, 1166, 20, + 1, 1254, 40, + 1, 140, 20, + 1, 22, 30, + 1, 22, 39, + 1, 294, 29, + 1, 336, 25, + 1, 336, 5, + 1, 37, 3, + 1, 37, 4, + 1, 37, 5, + 1, 41, 29, + 1, 57, 15, + 1, 72, 5, + 1, 72, 8, + 1, 76, 29, + 1, 981, 88, + 1, 990, 2, + 1, 990, 6, + 2, 220, 88, + 2, 294, 1, + 2, 37, 6, + 2, 391, 55, + 2, 57, 11, + 2, 57, 14, + 2, 57, 7, + 2, 981, 30, + 2, 990, 15, + 2, 990, 19, + 3, 114, 16, + 3, 1166, 39, + 3, 1254, 154, + 3, 12, 12, + 3, 162, 7, + 3, 164, 479, + 3, 167, 449, + 3, 16, 24, + 3, 204, 497, + 3, 205, 434, + 3, 220, 1127, + 3, 220, 1132, + 3, 220, 931, + 3, 220, 933, + 3, 220, 934, + 3, 297, 8, + 3, 72, 25, + 3, 87, 30, + 3, 981, 1, + 3, 981, 126, + 3, 990, 27, + 3, 990, 36, + 3, 991, 29, + 4, 1254, 306, + 4, 1254, 36, + 4, 1, 1, + 4, 1, 14, + 4, 1, 19, + 4, 1, 7, + 4, 21, 21, + 4, 220, 30, + 4, 46, 949, + 4, 509, 30, + 4, 57, 2, + 4, 57, 6, + 4, 990, 11, + 5, 13, 8, + 5, 198, 24, + 5, 24, 24, + 5, 25, 24, + 5, 2, 24, + 5, 37, 33, + 5, 57, 4, + 5, 599, 24, + 5, 90, 24, + 5, 981, 19, + 5, 990, 23, + 5, 990, 8, + 6, 101, 29, + 6, 117, 29, + 6, 1254, 88, + 6, 139, 29, + 6, 13, 12, + 6, 15, 15, + 6, 164, 25, + 6, 16, 16, + 6, 198, 7, + 6, 1, 12, + 6, 1, 15, + 6, 1, 27, + 6, 220, 936, + 6, 24, 7, + 6, 25, 7, + 6, 2, 7, + 6, 326, 29, + 6, 336, 29, + 6, 599, 7, + 6, 86, 29, + 6, 90, 7, + 6, 96, 29, + 6, 991, 31, + 7, 198, 12, + 7, 198, 20, + 7, 198, 33, + 7, 198, 35, + 7, 24, 12, + 7, 24, 20, + 7, 24, 33, + 7, 24, 35, + 7, 25, 12, + 7, 25, 20, + 7, 25, 33, + 7, 25, 35, + 7, 2, 12, + 7, 2, 20, + 7, 2, 33, + 7, 2, 35, + 7, 304, 1, + 7, 38, 29, + 7, 51, 29, + 7, 599, 12, + 7, 599, 20, + 7, 599, 33, + 7, 599, 35, + 7, 90, 12, + 7, 90, 20, + 7, 90, 33, + 7, 90, 35, + 8, 13, 5, + 8, 198, 13, + 8, 198, 23, + 8, 220, 1, + 8, 24, 13, + 8, 24, 23, + 8, 25, 13, + 8, 25, 23, + 8, 2, 13, + 8, 2, 23, + 8, 329, 28, + 8, 57, 10, + 8, 599, 13, + 8, 599, 23, + 8, 90, 13, + 8, 90, 23, + 9, 198, 17, + 9, 198, 19, + 9, 198, 37, + 9, 198, 5, + 9, 198, 8, + 9, 24, 17, + 9, 24, 19, + 9, 24, 37, + 9, 24, 5, + 9, 24, 8, + 9, 25, 17, + 9, 25, 19, + 9, 25, 37, + 9, 25, 5, + 9, 25, 8, + 9, 2, 17, + 9, 2, 19, + 9, 2, 37, + 9, 2, 5, + 9, 2, 8, + 9, 599, 17, + 9, 599, 19, + 9, 599, 37, + 9, 599, 5, + 9, 599, 8, + 9, 72, 29, + 9, 90, 17, + 9, 90, 19, + 9, 90, 37, + 9, 90, 5, + 9, 90, 8, + 10, 13, 11, + 10, 13, 9, + 10, 198, 26, + 10, 198, 28, + 10, 1, 23, + 10, 1, 4, + 10, 1, 6, + 10, 24, 26, + 10, 24, 28, + 10, 25, 26, + 10, 25, 28, + 10, 26, 24, + 10, 2, 26, + 10, 2, 28, + 10, 599, 26, + 10, 599, 28, + 10, 90, 26, + 10, 90, 28, + 11, 198, 27, + 11, 24, 27, + 11, 25, 27, + 11, 2, 27, + 11, 599, 27, + 11, 90, 27, + 12, 198, 14, + 12, 198, 21, + 12, 198, 3, + 12, 1, 11, + 12, 1, 2, + 12, 1, 8, + 12, 24, 14, + 12, 24, 21, + 12, 24, 3, + 12, 25, 14, + 12, 25, 21, + 12, 25, 3, + 12, 26, 7, + 12, 2, 14, + 12, 2, 21, + 12, 2, 3, + 12, 329, 14, + 12, 38, 2, + 12, 599, 14, + 12, 599, 21, + 12, 599, 3, + 12, 90, 14, + 12, 90, 21, + 12, 90, 3, + 13, 198, 11, + 13, 198, 15, + 13, 198, 31, + 13, 24, 11, + 13, 24, 15, + 13, 24, 31, + 13, 25, 11, + 13, 25, 15, + 13, 25, 31, + 13, 2, 11, + 13, 2, 15, + 13, 2, 31, + 13, 57, 13, + 13, 599, 11, + 13, 599, 15, + 13, 599, 31, + 13, 71, 29, + 13, 90, 11, + 13, 90, 15, + 13, 90, 31, + 14, 13, 2, + 14, 198, 10, + 14, 24, 10, + 14, 25, 10, + 14, 26, 12, + 14, 26, 20, + 14, 26, 33, + 14, 26, 35, + 14, 2, 10, + 14, 336, 1, + 14, 45, 29, + 14, 599, 10, + 14, 63, 29, + 14, 90, 10, + 15, 13, 3, + 15, 198, 2, + 15, 198, 29, + 15, 198, 34, + 15, 24, 2, + 15, 24, 29, + 15, 24, 34, + 15, 25, 2, + 15, 25, 29, + 15, 25, 34, + 15, 2, 2, + 15, 2, 29, + 15, 2, 34, + 15, 599, 2, + 15, 599, 29, + 15, 599, 34, + 15, 90, 2, + 15, 90, 29, + 15, 90, 34, + 16, 13, 4, + 16, 13, 6, + 16, 198, 16, + 16, 198, 9, + 16, 1, 10, + 16, 24, 16, + 16, 24, 9, + 16, 25, 16, + 16, 25, 9, + 16, 26, 13, + 16, 26, 23, + 16, 2, 16, + 16, 2, 9, + 16, 599, 16, + 16, 599, 9, + 16, 90, 16, + 16, 90, 9, + 17, 13, 7, + 17, 198, 18, + 17, 24, 18, + 17, 25, 18, + 17, 2, 18, + 17, 599, 18, + 17, 90, 18, + 18, 198, 22, + 18, 198, 32, + 18, 198, 36, + 18, 198, 4, + 18, 24, 22, + 18, 24, 32, + 18, 24, 36, + 18, 24, 4, + 18, 25, 22, + 18, 25, 32, + 18, 25, 36, + 18, 25, 4, + 18, 26, 17, + 18, 26, 19, + 18, 26, 37, + 18, 26, 5, + 18, 26, 8, + 18, 2, 22, + 18, 2, 32, + 18, 2, 36, + 18, 2, 4, + 18, 599, 22, + 18, 599, 32, + 18, 599, 36, + 18, 599, 4, + 18, 90, 22, + 18, 90, 32, + 18, 90, 36, + 18, 90, 4, + 19, 13, 10, + 20, 1254, 30, + 20, 16, 1007, + 20, 26, 26, + 20, 26, 28, + 21, 198, 6, + 21, 24, 6, + 21, 25, 6, + 21, 2, 6, + 21, 599, 6, + 21, 90, 6, + 22, 198, 38, + 22, 22, 40, + 22, 24, 38, + 22, 25, 38, + 22, 26, 27, + 22, 2, 38, + 22, 599, 38, + 22, 90, 38, + 23, 1254, 1160, + 24, 220, 930, + 24, 26, 14, + 24, 26, 21, + 24, 26, 3, + 26, 11, 11, + 26, 1, 13, + 26, 26, 11, + 26, 26, 15, + 26, 26, 31, + 28, 26, 10, + 30, 176, 60, + 30, 26, 2, + 30, 26, 29, + 30, 26, 34, + 32, 26, 16, + 32, 26, 9, + 34, 26, 18, + 36, 26, 22, + 36, 26, 32, + 36, 26, 36, + 36, 26, 4, + 36, 37, 26, + 42, 26, 6, + 43, 115, 29, + 44, 198, 25, + 44, 24, 25, + 44, 25, 25, + 44, 26, 38, + 44, 2, 25, + 44, 599, 25, + 44, 90, 25, + 46, 22, 1, + 47, 198, 30, + 47, 25, 30, + 47, 2, 30, + 47, 599, 30, + 47, 90, 30, + 48, 24, 30, + 52, 176, 30, + 58, 140, 24, + 58, 4, 30, + 63, 990, 29, + 64, 1254, 1, + 88, 26, 25, + 92, 198, 39, + 92, 25, 39, + 92, 2, 39, + 92, 599, 39, + 92, 90, 39, + 93, 24, 39, + 94, 26, 30, + 108, 1254, 1051, + 117, 140, 1, + 119, 160, 1, + 126, 1, 29, + 132, 135, 16, + 147, 72, 16, + 184, 26, 39, + 238, 990, 1, + 376, 11, 1007, + 380, 11, 487, + 1389, 1034, 1007, + 1870, 57, 16, + 4034, 1, 16, + 8521, 198, 40, + 8521, 25, 40, + 8521, 2, 40, + 8521, 599, 40, + 8521, 90, 40, + 8543, 24, 40, + 8883, 13, 13, + 17042, 26, 40, + 17664, 198, 1, + 17664, 25, 1, + 17664, 2, 1, + 17664, 599, 1, + 17664, 90, 1, + 17710, 24, 1, + 35328, 26, 1, +}; + +/// Emulates the mix of rects blitted by gmail during scrolling +class ScrollGmailBench : public SkBenchmark { + enum { + W = 1254, + H = 1160, + N = 431 + }; +public: + ScrollGmailBench(void* param) : INHERITED(param) { } + +protected: + + virtual const char* onGetName() { return "chrome_scrollGmail"; } + virtual void onDraw(SkCanvas* canvas) { + SkDEBUGCODE(this->validateBounds(canvas)); + SkPaint paint; + this->setupPaint(&paint); + for (int i = 0; i < N; i++) { + SkRect current; + setRectangle(current, i); + for (int j = 0; j < SkBENCHLOOP(gmailScrollingRectSpec[i*3]); j++) { + canvas->drawRect(current, paint); + } + } + } + virtual SkIPoint onGetSize() { return SkIPoint::Make(W, H); } + + void setRectangle(SkRect& current, int i) { + current.set(0, 0, + SkIntToScalar(gmailScrollingRectSpec[i*3+1]), SkIntToScalar(gmailScrollingRectSpec[i*3+2])); + } + void validateBounds(SkCanvas* canvas) { + SkIRect bounds; + canvas->getClipDeviceBounds(&bounds); + SkASSERT(bounds.right()-bounds.left() >= W); + SkASSERT(bounds.bottom()-bounds.top() >= H); + } + + +private: + typedef SkBenchmark INHERITED; +}; + +static SkBenchmark* ScrollGmailFactory(void* p) { + return SkNEW_ARGS(ScrollGmailBench, (p)); +} + +static BenchRegistry gScrollGmailReg(ScrollGmailFactory); diff --git a/bench/DecodeBench.cpp b/bench/DecodeBench.cpp index ac11089..6761690 100644 --- a/bench/DecodeBench.cpp +++ b/bench/DecodeBench.cpp @@ -1,3 +1,10 @@ + +/* + * Copyright 2011 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 "SkBitmap.h" #include "SkImageDecoder.h" @@ -11,7 +18,7 @@ class DecodeBench : public SkBenchmark { const char* fFilename; SkBitmap::Config fPrefConfig; SkString fName; - enum { N = 10 }; + enum { N = SkBENCHLOOP(10) }; public: DecodeBench(void* param, SkBitmap::Config c) : SkBenchmark(param) { fFilename = this->findDefine("decode-filename"); diff --git a/bench/FPSBench.cpp b/bench/FPSBench.cpp deleted file mode 100644 index 28668fe..0000000 --- a/bench/FPSBench.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include "SkBenchmark.h" -#include "SkCanvas.h" -#include "SkPaint.h" -#include "SkRandom.h" -#include "SkString.h" - -class FPSBench : public SkBenchmark { - int32_t fWidth; - int32_t fHeight; -public: - FPSBench(void* p) : INHERITED(p) { - fWidth = 640; - (void)this->findDefine32("width", &fWidth); - fHeight = 480; - (void)this->findDefine32("height", &fHeight); - } - - int width() const { return fWidth; } - int height() const { return fHeight; } - -protected: - virtual SkIPoint onGetSize() { return SkIPoint::Make(fWidth, fHeight); } - -private: - typedef SkBenchmark INHERITED; -}; - -/////////////////////////////////////////////////////////////////////////////// - -class Color_FPSBench : public FPSBench { -public: - Color_FPSBench(void* p, SkColor c, const char name[]) : INHERITED(p) { - fColor = c; - fName = name; - } - -protected: - virtual const char* onGetName() { return fName; } - virtual void onDraw(SkCanvas* canvas) { - canvas->drawColor(fColor); - } - -private: - const char* fName; - SkColor fColor; - - typedef FPSBench INHERITED; -}; - -class Bitmap_FPSBench : public FPSBench { -public: - Bitmap_FPSBench(void* p, SkBitmap::Config config, bool doOpaque, bool doScale) : INHERITED(p) { - fBitmap.setConfig(config, this->width(), this->height()); - fBitmap.allocPixels(); - fBitmap.eraseColor(0xFFFF0000); - if (doOpaque) { - fBitmap.setIsOpaque(true); - } - - const char* configStr = "565"; - if (config == SkBitmap::kARGB_8888_Config) { - if (doOpaque) { - configStr = "X888"; - } else { - configStr = "8888"; - } - } - fName.printf("fps_bitmap_%s_%s", configStr, - doScale ? "scale" : "noscale"); - - fMatrix.reset(); - if (doScale) { - fMatrix.setScale(SkIntToScalar(3)/2, SkIntToScalar(3)/2); - } - } - -protected: - virtual const char* onGetName() { return fName.c_str(); } - virtual void onDraw(SkCanvas* canvas) { - canvas->drawBitmapMatrix(fBitmap, fMatrix); - } - -private: - SkBitmap fBitmap; - SkMatrix fMatrix; - SkString fName; - - typedef FPSBench INHERITED; -}; - -static SkBenchmark* FillFactory(void* p) { return SkNEW_ARGS(Color_FPSBench, (p, 0xFFFF0000, "fps_fill")); } -static SkBenchmark* BlendFactory(void* p) { return SkNEW_ARGS(Color_FPSBench, (p, 0x80FF0000, "fps_blend")); } -static SkBenchmark* BMFactory0(void* p) { return SkNEW_ARGS(Bitmap_FPSBench, (p, SkBitmap::kARGB_8888_Config, false, false)); } -static SkBenchmark* BMFactory1(void* p) { return SkNEW_ARGS(Bitmap_FPSBench, (p, SkBitmap::kARGB_8888_Config, false, true)); } -static SkBenchmark* BMFactory2(void* p) { return SkNEW_ARGS(Bitmap_FPSBench, (p, SkBitmap::kARGB_8888_Config, true, false)); } -static SkBenchmark* BMFactory3(void* p) { return SkNEW_ARGS(Bitmap_FPSBench, (p, SkBitmap::kARGB_8888_Config, true, true)); } -static SkBenchmark* BMFactory4(void* p) { return SkNEW_ARGS(Bitmap_FPSBench, (p, SkBitmap::kRGB_565_Config, false, false)); } -static SkBenchmark* BMFactory5(void* p) { return SkNEW_ARGS(Bitmap_FPSBench, (p, SkBitmap::kRGB_565_Config, false, true)); } - -static BenchRegistry gFillReg(FillFactory); -static BenchRegistry gBlendReg(BlendFactory); -static BenchRegistry gBMReg0(BMFactory0); -static BenchRegistry gBMReg1(BMFactory1); -static BenchRegistry gBMReg2(BMFactory2); -static BenchRegistry gBMReg3(BMFactory3); -static BenchRegistry gBMReg4(BMFactory4); -static BenchRegistry gBMReg5(BMFactory5); - diff --git a/bench/FontScalerBench.cpp b/bench/FontScalerBench.cpp new file mode 100644 index 0000000..4ac6a35 --- /dev/null +++ b/bench/FontScalerBench.cpp @@ -0,0 +1,60 @@ +/* + * Copyright 2011 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 "SkGraphics.h" +#include "SkPaint.h" +#include "SkRandom.h" +#include "SkString.h" + +extern bool gSkSuppressFontCachePurgeSpew; + +class FontScalerBench : public SkBenchmark { + SkString fName; + SkString fText; + bool fDoLCD; +public: + FontScalerBench(void* param, bool doLCD) : INHERITED(param) { + fName.printf("fontscaler_%s", doLCD ? "lcd" : "aa"); + fText.set("abcdefghijklmnopqrstuvwxyz01234567890"); + fDoLCD = doLCD; + } + +protected: + virtual const char* onGetName() { return fName.c_str(); } + virtual void onDraw(SkCanvas* canvas) { + SkPaint paint; + this->setupPaint(&paint); + paint.setLCDRenderText(fDoLCD); + + bool prev = gSkSuppressFontCachePurgeSpew; + gSkSuppressFontCachePurgeSpew = true; + + // this is critical - we want to time the creation process, so we + // explicitly flush our cache before each run + SkGraphics::PurgeFontCache(); + + for (int ps = 9; ps <= 24; ps += 2) { + paint.setTextSize(SkIntToScalar(ps)); + canvas->drawText(fText.c_str(), fText.size(), + 0, SkIntToScalar(20), paint); + } + + gSkSuppressFontCachePurgeSpew = prev; + } +private: + typedef SkBenchmark INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static SkBenchmark* Fact0(void* p) { return SkNEW_ARGS(FontScalerBench, (p, false)); } +static SkBenchmark* Fact1(void* p) { return SkNEW_ARGS(FontScalerBench, (p, true)); } + +static BenchRegistry gReg0(Fact0); +static BenchRegistry gReg1(Fact1); diff --git a/bench/GradientBench.cpp b/bench/GradientBench.cpp index 5ecca6b..a5032ea 100644 --- a/bench/GradientBench.cpp +++ b/bench/GradientBench.cpp @@ -1,3 +1,10 @@ + +/* + * Copyright 2011 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 "SkBitmap.h" #include "SkCanvas.h" @@ -31,23 +38,29 @@ static const GradData gGradData[] = { { 5, gColors, gPos2 } }; +/// Ignores scale static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data, - SkShader::TileMode tm, SkUnitMapper* mapper) { + SkShader::TileMode tm, SkUnitMapper* mapper, + float scale) { return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos, data.fCount, tm, mapper); } static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data, - SkShader::TileMode tm, SkUnitMapper* mapper) { + SkShader::TileMode tm, SkUnitMapper* mapper, + float scale) { SkPoint center; center.set(SkScalarAve(pts[0].fX, pts[1].fX), SkScalarAve(pts[0].fY, pts[1].fY)); - return SkGradientShader::CreateRadial(center, center.fX, data.fColors, + return SkGradientShader::CreateRadial(center, center.fX * scale, + data.fColors, data.fPos, data.fCount, tm, mapper); } +/// Ignores scale static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data, - SkShader::TileMode tm, SkUnitMapper* mapper) { + SkShader::TileMode tm, SkUnitMapper* mapper, + float scale) { SkPoint center; center.set(SkScalarAve(pts[0].fX, pts[1].fX), SkScalarAve(pts[0].fY, pts[1].fY)); @@ -55,21 +68,24 @@ static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data, data.fPos, data.fCount, mapper); } +/// Ignores scale static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data, - SkShader::TileMode tm, SkUnitMapper* mapper) { + SkShader::TileMode tm, SkUnitMapper* mapper, + float scale) { SkPoint center0, center1; center0.set(SkScalarAve(pts[0].fX, pts[1].fX), SkScalarAve(pts[0].fY, pts[1].fY)); center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); return SkGradientShader::CreateTwoPointRadial( - center1, (pts[1].fX - pts[0].fX) / 7, - center0, (pts[1].fX - pts[0].fX) / 2, - data.fColors, data.fPos, data.fCount, tm, mapper); + center1, (pts[1].fX - pts[0].fX) / 7, + center0, (pts[1].fX - pts[0].fX) / 2, + data.fColors, data.fPos, data.fCount, tm, mapper); } typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data, - SkShader::TileMode tm, SkUnitMapper* mapper); + SkShader::TileMode tm, SkUnitMapper* mapper, + float scale); static const struct { GradMaker fMaker; @@ -89,6 +105,37 @@ enum GradType { // these must match the order in gGrads kRadial2_GradType }; +enum GeomType { + kRect_GeomType, + kOval_GeomType +}; + +static const char* tilemodename(SkShader::TileMode tm) { + switch (tm) { + case SkShader::kClamp_TileMode: + return "clamp"; + case SkShader::kRepeat_TileMode: + return "repeat"; + case SkShader::kMirror_TileMode: + return "mirror"; + default: + SkASSERT(!"unknown tilemode"); + return "error"; + } +} + +static const char* geomtypename(GeomType gt) { + switch (gt) { + case kRect_GeomType: + return "rectangle"; + case kOval_GeomType: + return "oval"; + default: + SkASSERT(!"unknown geometry type"); + return "error"; + } +} + /////////////////////////////////////////////////////////////////////////////// class GradientBench : public SkBenchmark { @@ -101,17 +148,26 @@ class GradientBench : public SkBenchmark { N = 1 }; public: - GradientBench(void* param, GradType gt) : INHERITED(param) { - fName.printf("gradient_%s", gGrads[gt].fName); + GradientBench(void* param, GradType gradType, + SkShader::TileMode tm = SkShader::kClamp_TileMode, + GeomType geomType = kRect_GeomType, + float scale = 1.0f) + : INHERITED(param) { + fName.printf("gradient_%s_%s", gGrads[gradType].fName, + tilemodename(tm)); + if (geomType != kRect_GeomType) { + fName.append("_"); + fName.append(geomtypename(geomType)); + } const SkPoint pts[2] = { { 0, 0 }, { SkIntToScalar(W), SkIntToScalar(H) } }; - fCount = N * gGrads[gt].fRepeat; - fShader = gGrads[gt].fMaker(pts, gGradData[0], - SkShader::kClamp_TileMode, NULL); + fCount = SkBENCHLOOP(N * gGrads[gradType].fRepeat); + fShader = gGrads[gradType].fMaker(pts, gGradData[0], tm, NULL, scale); + fGeomType = geomType; } virtual ~GradientBench() { @@ -131,21 +187,86 @@ protected: SkRect r = { 0, 0, SkIntToScalar(W), SkIntToScalar(H) }; for (int i = 0; i < fCount; i++) { - canvas->drawRect(r, paint); + switch (fGeomType) { + case kRect_GeomType: + canvas->drawRect(r, paint); + break; + case kOval_GeomType: + canvas->drawOval(r, paint); + break; + } } } private: typedef SkBenchmark INHERITED; + + GeomType fGeomType; +}; + +class Gradient2Bench : public SkBenchmark { +public: + Gradient2Bench(void* param) : INHERITED(param) {} + +protected: + virtual const char* onGetName() { + return "gradient_create"; + } + + virtual void onDraw(SkCanvas* canvas) { + SkPaint paint; + this->setupPaint(&paint); + + const SkRect r = { 0, 0, SkIntToScalar(4), SkIntToScalar(4) }; + const SkPoint pts[] = { + { 0, 0 }, + { SkIntToScalar(100), SkIntToScalar(100) }, + }; + + for (int i = 0; i < SkBENCHLOOP(1000); i++) { + const int a = i % 256; + SkColor colors[] = { + SK_ColorBLACK, + SkColorSetARGB(a, a, a, a), + SK_ColorWHITE }; + SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, + SK_ARRAY_COUNT(colors), + SkShader::kClamp_TileMode); + paint.setShader(s)->unref(); + canvas->drawRect(r, paint); + } + } + +private: + typedef SkBenchmark INHERITED; }; static SkBenchmark* Fact0(void* p) { return new GradientBench(p, kLinear_GradType); } -static SkBenchmark* Fact1(void* p) { return new GradientBench(p, kRadial_GradType); } +static SkBenchmark* Fact01(void* p) { return new GradientBench(p, kLinear_GradType, SkShader::kMirror_TileMode); } + +// Draw a radial gradient of radius 1/2 on a rectangle; half the lines should +// be completely pinned, the other half should pe partially pinned +static SkBenchmark* Fact1(void* p) { return new GradientBench(p, kRadial_GradType, SkShader::kClamp_TileMode, kRect_GeomType, 0.5f); } + +// Draw a radial gradient on a circle of equal size; all the lines should +// hit the unpinned fast path (so long as GradientBench.W == H) +static SkBenchmark* Fact1o(void* p) { return new GradientBench(p, kRadial_GradType, SkShader::kClamp_TileMode, kOval_GeomType); } + +static SkBenchmark* Fact11(void* p) { return new GradientBench(p, kRadial_GradType, SkShader::kMirror_TileMode); } static SkBenchmark* Fact2(void* p) { return new GradientBench(p, kSweep_GradType); } static SkBenchmark* Fact3(void* p) { return new GradientBench(p, kRadial2_GradType); } +static SkBenchmark* Fact31(void* p) { return new GradientBench(p, kRadial2_GradType, SkShader::kMirror_TileMode); } + +static SkBenchmark* Fact4(void* p) { return new Gradient2Bench(p); } static BenchRegistry gReg0(Fact0); +static BenchRegistry gReg01(Fact01); static BenchRegistry gReg1(Fact1); +static BenchRegistry gReg1o(Fact1o); +static BenchRegistry gReg11(Fact11); static BenchRegistry gReg2(Fact2); static BenchRegistry gReg3(Fact3); +static BenchRegistry gReg31(Fact31); + +static BenchRegistry gReg4(Fact4); diff --git a/bench/Makefile.am b/bench/Makefile.am deleted file mode 100644 index 2b67862..0000000 --- a/bench/Makefile.am +++ /dev/null @@ -1,20 +0,0 @@ -AM_CPPFLAGS = -I$(top_builddir)/include/core -I$(top_builddir)/include/images -AM_LDFLAGS = -lpng -lpthread - -bin_PROGRAMS = Bench -Bench_SOURCES = RectBench.cpp \ - FPSBench.cpp \ - SkBenchmark.cpp \ - BenchTool/main.cpp \ - $(top_builddir)/src/images/SkImageDecoder.cpp \ - $(top_builddir)/src/images/SkImageDecoder_libpng.cpp \ - $(top_builddir)/src/images/SkScaledBitmapSampler.cpp \ - $(top_builddir)/src/ports/SkGlobals_global.cpp \ - $(top_builddir)/src/ports/SkOSFile_stdio.cpp \ - $(top_builddir)/src/ports/SkThread_pthread.cpp \ - $(top_builddir)/src/ports/SkTime_Unix.cpp \ - $(top_builddir)/src/ports/SkFontHost_none.cpp - - -Bench_LDADD = $(top_builddir)/src/core/libskia.a - diff --git a/bench/MathBench.cpp b/bench/MathBench.cpp new file mode 100644 index 0000000..9feb5af --- /dev/null +++ b/bench/MathBench.cpp @@ -0,0 +1,316 @@ +#include "SkBenchmark.h" +#include "SkColorPriv.h" +#include "SkMatrix.h" +#include "SkRandom.h" +#include "SkString.h" +#include "SkPaint.h" + +class MathBench : public SkBenchmark { + enum { + kBuffer = 100, + kLoop = 10000 + }; + SkString fName; + float fSrc[kBuffer], fDst[kBuffer]; +public: + MathBench(void* param, const char name[]) : INHERITED(param) { + fName.printf("math_%s", name); + + SkRandom rand; + for (int i = 0; i < kBuffer; ++i) { + fSrc[i] = rand.nextSScalar1(); + } + } + + virtual void performTest(float dst[], const float src[], int count) = 0; + +protected: + virtual int mulLoopCount() const { return 1; } + + virtual const char* onGetName() { + return fName.c_str(); + } + + virtual void onDraw(SkCanvas* canvas) { + int n = SkBENCHLOOP(kLoop * this->mulLoopCount()); + for (int i = 0; i < n; i++) { + this->performTest(fDst, fSrc, kBuffer); + } + } + +private: + typedef SkBenchmark INHERITED; +}; + +class MathBenchU32 : public MathBench { +public: + MathBenchU32(void* param, const char name[]) : INHERITED(param, name) {} + +protected: + virtual void performITest(uint32_t* dst, const uint32_t* src, int count) = 0; + + virtual void performTest(float* SK_RESTRICT dst, const float* SK_RESTRICT src, + int count) SK_OVERRIDE { + uint32_t* d = SkTCast<uint32_t*>(dst); + const uint32_t* s = SkTCast<const uint32_t*>(src); + this->performITest(d, s, count); + } +private: + typedef MathBench INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class NoOpMathBench : public MathBench { +public: + NoOpMathBench(void* param) : INHERITED(param, "noOp") {} +protected: + virtual void performTest(float dst[], const float src[], int count) { + for (int i = 0; i < count; ++i) { + dst[i] = src[i] + 1; + } + } +private: + typedef MathBench INHERITED; +}; + +class SlowISqrtMathBench : public MathBench { +public: + SlowISqrtMathBench(void* param) : INHERITED(param, "slowIsqrt") {} +protected: + virtual void performTest(float dst[], const float src[], int count) { + for (int i = 0; i < count; ++i) { + dst[i] = 1.0f / sk_float_sqrt(src[i]); + } + } +private: + typedef MathBench INHERITED; +}; + +static inline float SkFastInvSqrt(float x) { + float xhalf = 0.5f*x; + int i = *(int*)&x; + i = 0x5f3759df - (i>>1); + x = *(float*)&i; + x = x*(1.5f-xhalf*x*x); +// x = x*(1.5f-xhalf*x*x); // this line takes err from 10^-3 to 10^-6 + return x; +} + +class FastISqrtMathBench : public MathBench { +public: + FastISqrtMathBench(void* param) : INHERITED(param, "fastIsqrt") {} +protected: + virtual void performTest(float dst[], const float src[], int count) { + for (int i = 0; i < count; ++i) { + dst[i] = SkFastInvSqrt(src[i]); + } + } +private: + typedef MathBench INHERITED; +}; + +static inline uint32_t QMul64(uint32_t value, U8CPU alpha) { + SkASSERT((uint8_t)alpha == alpha); + const uint32_t mask = 0xFF00FF; + + uint64_t tmp = value; + tmp = (tmp & mask) | ((tmp & ~mask) << 24); + tmp *= alpha; + return ((tmp >> 8) & mask) | ((tmp >> 32) & ~mask); +} + +class QMul64Bench : public MathBenchU32 { +public: + QMul64Bench(void* param) : INHERITED(param, "qmul64") {} +protected: + virtual void performITest(uint32_t* SK_RESTRICT dst, + const uint32_t* SK_RESTRICT src, + int count) SK_OVERRIDE { + for (int i = 0; i < count; ++i) { + dst[i] = QMul64(src[i], (uint8_t)i); + } + } +private: + typedef MathBenchU32 INHERITED; +}; + +class QMul32Bench : public MathBenchU32 { +public: + QMul32Bench(void* param) : INHERITED(param, "qmul32") {} +protected: + virtual void performITest(uint32_t* SK_RESTRICT dst, + const uint32_t* SK_RESTRICT src, + int count) SK_OVERRIDE { + for (int i = 0; i < count; ++i) { + dst[i] = SkAlphaMulQ(src[i], (uint8_t)i); + } + } +private: + typedef MathBenchU32 INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static bool isFinite_int(float x) { + uint32_t bits = SkFloat2Bits(x); // need unsigned for our shifts + int exponent = bits << 1 >> 24; + return exponent != 0xFF; +} + +static bool isFinite_float(float x) { + return sk_float_isfinite(x); +} + +static bool isFinite_mulzero(float x) { + float y = x * 0; + return y == y; +} + +static bool isfinite_and_int(const float data[4]) { + return isFinite_int(data[0]) && isFinite_int(data[1]) && isFinite_int(data[2]) && isFinite_int(data[3]); +} + +static bool isfinite_and_float(const float data[4]) { + return isFinite_float(data[0]) && isFinite_float(data[1]) && isFinite_float(data[2]) && isFinite_float(data[3]); +} + +static bool isfinite_and_mulzero(const float data[4]) { + return isFinite_mulzero(data[0]) && isFinite_mulzero(data[1]) && isFinite_mulzero(data[2]) && isFinite_mulzero(data[3]); +} + +#define mulzeroadd(data) (data[0]*0 + data[1]*0 + data[2]*0 + data[3]*0) + +static bool isfinite_plus_int(const float data[4]) { + return isFinite_int(mulzeroadd(data)); +} + +static bool isfinite_plus_float(const float data[4]) { + return !sk_float_isnan(mulzeroadd(data)); +} + +static bool isfinite_plus_mulzero(const float data[4]) { + float x = mulzeroadd(data); + return x == x; +} + +typedef bool (*IsFiniteProc)(const float[]); + +#define MAKEREC(name) { name, #name } + +static const struct { + IsFiniteProc fProc; + const char* fName; +} gRec[] = { + MAKEREC(isfinite_and_int), + MAKEREC(isfinite_and_float), + MAKEREC(isfinite_and_mulzero), + MAKEREC(isfinite_plus_int), + MAKEREC(isfinite_plus_float), + MAKEREC(isfinite_plus_mulzero), +}; + +#undef MAKEREC + +static bool isFinite(const SkRect& r) { + // x * 0 will be NaN iff x is infinity or NaN. + // a + b will be NaN iff either a or b is NaN. + float value = r.fLeft * 0 + r.fTop * 0 + r.fRight * 0 + r.fBottom * 0; + + // value is either NaN or it is finite (zero). + // value==value will be true iff value is not NaN + return value == value; +} + +class IsFiniteBench : public SkBenchmark { + enum { + N = SkBENCHLOOP(1000), + NN = SkBENCHLOOP(1000), + }; + float fData[N]; +public: + + IsFiniteBench(void* param, int index) : INHERITED(param) { + SkRandom rand; + + for (int i = 0; i < N; ++i) { + fData[i] = rand.nextSScalar1(); + } + + if (index < 0) { + fProc = NULL; + fName = "isfinite_rect"; + } else { + fProc = gRec[index].fProc; + fName = gRec[index].fName; + } + } + +protected: + virtual void onDraw(SkCanvas* canvas) { + IsFiniteProc proc = fProc; + const float* data = fData; + // do this so the compiler won't throw away the function call + int counter = 0; + + if (proc) { + for (int j = 0; j < NN; ++j) { + for (int i = 0; i < N - 4; ++i) { + counter += proc(&data[i]); + } + } + } else { + for (int j = 0; j < NN; ++j) { + for (int i = 0; i < N - 4; ++i) { + const SkRect* r = reinterpret_cast<const SkRect*>(&data[i]); + counter += r->isFinite(); + } + } + } + + SkPaint paint; + if (paint.getAlpha() == 0) { + SkDebugf("%d\n", counter); + } + } + + virtual const char* onGetName() { + return fName; + } + +private: + IsFiniteProc fProc; + const char* fName; + + typedef SkBenchmark INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static SkBenchmark* M0(void* p) { return new NoOpMathBench(p); } +static SkBenchmark* M1(void* p) { return new SlowISqrtMathBench(p); } +static SkBenchmark* M2(void* p) { return new FastISqrtMathBench(p); } +static SkBenchmark* M3(void* p) { return new QMul64Bench(p); } +static SkBenchmark* M4(void* p) { return new QMul32Bench(p); } + +static SkBenchmark* M5neg1(void* p) { return new IsFiniteBench(p, -1); } +static SkBenchmark* M50(void* p) { return new IsFiniteBench(p, 0); } +static SkBenchmark* M51(void* p) { return new IsFiniteBench(p, 1); } +static SkBenchmark* M52(void* p) { return new IsFiniteBench(p, 2); } +static SkBenchmark* M53(void* p) { return new IsFiniteBench(p, 3); } +static SkBenchmark* M54(void* p) { return new IsFiniteBench(p, 4); } +static SkBenchmark* M55(void* p) { return new IsFiniteBench(p, 5); } + +static BenchRegistry gReg0(M0); +static BenchRegistry gReg1(M1); +static BenchRegistry gReg2(M2); +static BenchRegistry gReg3(M3); +static BenchRegistry gReg4(M4); + +static BenchRegistry gReg5neg1(M5neg1); +static BenchRegistry gReg50(M50); +static BenchRegistry gReg51(M51); +static BenchRegistry gReg52(M52); +static BenchRegistry gReg53(M53); +static BenchRegistry gReg54(M54); +static BenchRegistry gReg55(M55); diff --git a/bench/MatrixBench.cpp b/bench/MatrixBench.cpp index dce0358..a2e459a 100644 --- a/bench/MatrixBench.cpp +++ b/bench/MatrixBench.cpp @@ -1,3 +1,10 @@ + +/* + * Copyright 2011 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 "SkMatrix.h" #include "SkRandom.h" @@ -21,7 +28,7 @@ protected: } virtual void onDraw(SkCanvas* canvas) { - int n = N * this->mulLoopCount(); + int n = SkBENCHLOOP(N * this->mulLoopCount()); for (int i = 0; i < n; i++) { this->performTest(); } @@ -56,9 +63,6 @@ protected: 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; @@ -214,26 +218,71 @@ private: typedef MatrixBench INHERITED; }; +class GetTypeMatrixBench : public MatrixBench { +public: + GetTypeMatrixBench(void* param) + : INHERITED(param, "gettype") { + fArray[0] = (float) fRnd.nextS(); + fArray[1] = (float) fRnd.nextS(); + fArray[2] = (float) fRnd.nextS(); + fArray[3] = (float) fRnd.nextS(); + fArray[4] = (float) fRnd.nextS(); + fArray[5] = (float) fRnd.nextS(); + fArray[6] = (float) fRnd.nextS(); + fArray[7] = (float) fRnd.nextS(); + fArray[8] = (float) fRnd.nextS(); + } +protected: + // Putting random generation of the matrix inside performTest() + // would help us avoid anomalous runs, but takes up 25% or + // more of the function time. + virtual void performTest() { + fMatrix.setAll(fArray[0], fArray[1], fArray[2], + fArray[3], fArray[4], fArray[5], + fArray[6], fArray[7], fArray[8]); + always_do(fMatrix.getType()); + fMatrix.dirtyMatrixTypeCache(); + always_do(fMatrix.getType()); + fMatrix.dirtyMatrixTypeCache(); + always_do(fMatrix.getType()); + fMatrix.dirtyMatrixTypeCache(); + always_do(fMatrix.getType()); + fMatrix.dirtyMatrixTypeCache(); + always_do(fMatrix.getType()); + fMatrix.dirtyMatrixTypeCache(); + always_do(fMatrix.getType()); + fMatrix.dirtyMatrixTypeCache(); + always_do(fMatrix.getType()); + fMatrix.dirtyMatrixTypeCache(); + always_do(fMatrix.getType()); + } +private: + SkMatrix fMatrix; + float fArray[9]; + SkRandom fRnd; + typedef MatrixBench INHERITED; +}; + #ifdef SK_SCALAR_IS_FLOAT class ScaleTransMixedMatrixBench : public MatrixBench { public: ScaleTransMixedMatrixBench(void* p) : INHERITED(p, "scaletrans_mixed"), fCount (16) { - fMatrix.setAll(fRandom.nextS(), fRandom.nextS(), fRandom.nextS(), - fRandom.nextS(), fRandom.nextS(), fRandom.nextS(), - fRandom.nextS(), fRandom.nextS(), fRandom.nextS()); + fMatrix.setAll(fRandom.nextSScalar1(), fRandom.nextSScalar1(), fRandom.nextSScalar1(), + fRandom.nextSScalar1(), fRandom.nextSScalar1(), fRandom.nextSScalar1(), + fRandom.nextSScalar1(), fRandom.nextSScalar1(), fRandom.nextSScalar1()); int i; - for (i = 0; i < fCount; i++) { - fSrc[i].fX = fRandom.nextS(); - fSrc[i].fY = fRandom.nextS(); - fDst[i].fX = fRandom.nextS(); - fDst[i].fY = fRandom.nextS(); + for (i = 0; i < SkBENCHLOOP(fCount); i++) { + fSrc[i].fX = fRandom.nextSScalar1(); + fSrc[i].fY = fRandom.nextSScalar1(); + fDst[i].fX = fRandom.nextSScalar1(); + fDst[i].fY = fRandom.nextSScalar1(); } } protected: virtual void performTest() { SkPoint* dst = fDst; const SkPoint* src = fSrc; - int count = fCount; + int count = SkBENCHLOOP(fCount); float mx = fMatrix[SkMatrix::kMScaleX]; float my = fMatrix[SkMatrix::kMScaleY]; float tx = fMatrix[SkMatrix::kMTransX]; @@ -254,29 +303,28 @@ class ScaleTransMixedMatrixBench : public MatrixBench { typedef MatrixBench INHERITED; }; - class ScaleTransDoubleMatrixBench : public MatrixBench { public: ScaleTransDoubleMatrixBench(void* p) : INHERITED(p, "scaletrans_double"), fCount (16) { init9(fMatrix); int i; - for (i = 0; i < fCount; i++) { - fSrc[i].fX = fRandom.nextS(); - fSrc[i].fY = fRandom.nextS(); - fDst[i].fX = fRandom.nextS(); - fDst[i].fY = fRandom.nextS(); + for (i = 0; i < SkBENCHLOOP(fCount); i++) { + fSrc[i].fX = fRandom.nextSScalar1(); + fSrc[i].fY = fRandom.nextSScalar1(); + fDst[i].fX = fRandom.nextSScalar1(); + fDst[i].fY = fRandom.nextSScalar1(); } } protected: virtual void performTest() { SkPoint* dst = fDst; const SkPoint* src = fSrc; - int count = fCount; + int count = SkBENCHLOOP(fCount); // As doubles, on Z600 Linux systems this is 2.5x as expensive as mixed mode - float mx = fMatrix[SkMatrix::kMScaleX]; - float my = fMatrix[SkMatrix::kMScaleY]; - float tx = fMatrix[SkMatrix::kMTransX]; - float ty = fMatrix[SkMatrix::kMTransY]; + float mx = (float) fMatrix[SkMatrix::kMScaleX]; + float my = (float) fMatrix[SkMatrix::kMScaleY]; + float tx = (float) fMatrix[SkMatrix::kMTransX]; + float ty = (float) fMatrix[SkMatrix::kMTransY]; do { dst->fY = src->fY * my + ty; dst->fX = src->fX * mx + tx; @@ -303,12 +351,14 @@ 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 SkBenchmark* M5(void* p) { return new GetTypeMatrixBench(p); } static BenchRegistry gReg0(M0); static BenchRegistry gReg1(M1); static BenchRegistry gReg2(M2); static BenchRegistry gReg3(M3); static BenchRegistry gReg4(M4); +static BenchRegistry gReg5(M5); #ifdef SK_SCALAR_IS_FLOAT static SkBenchmark* FlM0(void* p) { return new ScaleTransMixedMatrixBench(p); } diff --git a/bench/MutexBench.cpp b/bench/MutexBench.cpp new file mode 100644 index 0000000..d9b427b --- /dev/null +++ b/bench/MutexBench.cpp @@ -0,0 +1,43 @@ +/* + * Copyright 2011 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 "SkThread.h" + +class MutexBench : public SkBenchmark { + enum { + N = SkBENCHLOOP(80), + M = SkBENCHLOOP(200) + }; +public: + MutexBench(void* param) : INHERITED(param) { + + } +protected: + virtual const char* onGetName() { + return "mutex"; + } + + virtual void onDraw(SkCanvas* canvas) { + for (int i = 0; i < N; i++) { + SkMutex mu; + for (int j = 0; j < M; j++) { + mu.acquire(); + mu.release(); + } + } + } + +private: + typedef SkBenchmark INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static SkBenchmark* Fact(void* p) { return new MutexBench(p); } + +static BenchRegistry gReg01(Fact); + diff --git a/bench/PathBench.cpp b/bench/PathBench.cpp index 19e3aae..d3e01b7 100644 --- a/bench/PathBench.cpp +++ b/bench/PathBench.cpp @@ -1,8 +1,16 @@ + +/* + * Copyright 2011 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 "SkBitmap.h" #include "SkCanvas.h" #include "SkColorPriv.h" #include "SkPaint.h" +#include "SkRandom.h" #include "SkShader.h" #include "SkString.h" @@ -20,7 +28,7 @@ class PathBench : public SkBenchmark { SkPaint fPaint; SkString fName; Flags fFlags; - enum { N = 1000 }; + enum { N = SkBENCHLOOP(1000) }; public: PathBench(void* param, Flags flags) : INHERITED(param), fFlags(flags) { fPaint.setStyle(flags & kStroke_Flag ? SkPaint::kStroke_Style : @@ -31,7 +39,7 @@ public: virtual void appendName(SkString*) = 0; virtual void makePath(SkPath*) = 0; - virtual bool iscomplex() { return false; } + virtual int complexity() { return 0; } protected: virtual const char* onGetName() { @@ -58,9 +66,7 @@ protected: if (fFlags & kBig_Flag) { count >>= 2; } - if (this->iscomplex()) { - count >>= 3; - } + count >>= (3 * complexity()); for (int i = 0; i < count; i++) { canvas->drawPath(path, paint); @@ -146,11 +152,38 @@ public: path->lineTo(x0, y + 2 * dy); path->close(); } - virtual bool iscomplex() { return true; } + virtual int complexity() { return 1; } private: typedef PathBench INHERITED; }; +class LongCurvedPathBench : public PathBench { +public: + LongCurvedPathBench(void * param, Flags flags) + : INHERITED(param, flags) { + } + + virtual void appendName(SkString* name) { + name->append("long_curved"); + } + virtual void makePath(SkPath* path) { + SkRandom rand (12); + int i; + for (i = 0; i < 100; i++) { + path->quadTo(SkScalarMul(rand.nextUScalar1(), SkIntToScalar(640)), + SkScalarMul(rand.nextUScalar1(), SkIntToScalar(480)), + SkScalarMul(rand.nextUScalar1(), SkIntToScalar(640)), + SkScalarMul(rand.nextUScalar1(), SkIntToScalar(480))); + } + path->close(); + } + virtual int complexity() { return 2; } +private: + typedef PathBench INHERITED; +}; + + + static SkBenchmark* FactT00(void* p) { return new TrianglePathBench(p, FLAGS00); } static SkBenchmark* FactT01(void* p) { return new TrianglePathBench(p, FLAGS01); } static SkBenchmark* FactT10(void* p) { return new TrianglePathBench(p, FLAGS10); } @@ -169,6 +202,13 @@ static SkBenchmark* FactO11(void* p) { return new OvalPathBench(p, FLAGS11); } static SkBenchmark* FactS00(void* p) { return new SawToothPathBench(p, FLAGS00); } static SkBenchmark* FactS01(void* p) { return new SawToothPathBench(p, FLAGS01); } +static SkBenchmark* FactLC00(void* p) { + return new LongCurvedPathBench(p, FLAGS00); +} +static SkBenchmark* FactLC01(void* p) { + return new LongCurvedPathBench(p, FLAGS01); +} + static BenchRegistry gRegT00(FactT00); static BenchRegistry gRegT01(FactT01); static BenchRegistry gRegT10(FactT10); @@ -187,3 +227,6 @@ static BenchRegistry gRegO11(FactO11); static BenchRegistry gRegS00(FactS00); static BenchRegistry gRegS01(FactS01); +static BenchRegistry gRegLC00(FactLC00); +static BenchRegistry gRegLC01(FactLC01); + diff --git a/bench/RectBench.cpp b/bench/RectBench.cpp index fb54640..cef2e22 100644 --- a/bench/RectBench.cpp +++ b/bench/RectBench.cpp @@ -1,3 +1,10 @@ + +/* + * Copyright 2011 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 "SkPaint.h" @@ -11,7 +18,7 @@ public: enum { W = 640, H = 480, - N = 300 + N = SkBENCHLOOP(300) }; SkRect fRects[N]; SkColor fColors[N]; diff --git a/bench/RepeatTileBench.cpp b/bench/RepeatTileBench.cpp index 97bbeb8..8470bed 100644 --- a/bench/RepeatTileBench.cpp +++ b/bench/RepeatTileBench.cpp @@ -1,3 +1,10 @@ + +/* + * Copyright 2011 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 "SkBitmap.h" #include "SkCanvas.h" @@ -79,7 +86,7 @@ static void convertToIndex666(const SkBitmap& src, SkBitmap* dst) { class RepeatTileBench : public SkBenchmark { SkPaint fPaint; SkString fName; - enum { N = 20 }; + enum { N = SkBENCHLOOP(20) }; public: RepeatTileBench(void* param, SkBitmap::Config c) : INHERITED(param) { const int w = 50; diff --git a/bench/ScalarBench.cpp b/bench/ScalarBench.cpp index 29fe5c4..8bedfbd 100644 --- a/bench/ScalarBench.cpp +++ b/bench/ScalarBench.cpp @@ -1,3 +1,10 @@ + +/* + * Copyright 2011 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 "SkFloatBits.h" #include "SkRandom.h" @@ -21,7 +28,7 @@ protected: } virtual void onDraw(SkCanvas* canvas) { - int n = N * this->mulLoopCount(); + int n = SkBENCHLOOP(N * this->mulLoopCount()); for (int i = 0; i < n; i++) { this->performTest(); } diff --git a/bench/ShaderMaskBench.cpp b/bench/ShaderMaskBench.cpp new file mode 100644 index 0000000..62be6c5 --- /dev/null +++ b/bench/ShaderMaskBench.cpp @@ -0,0 +1,109 @@ + +/* + * Copyright 2011 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 "SkColorShader.h" +#include "SkFontHost.h" +#include "SkPaint.h" +#include "SkRandom.h" +#include "SkSfntUtils.h" +#include "SkString.h" +#include "SkTemplates.h" + +#define STR "Hamburgefons" + +enum FontQuality { + kBW, + kAA, + kLCD +}; + +static const char* fontQualityName(const SkPaint& paint) { + if (!paint.isAntiAlias()) { + return "BW"; + } + if (paint.isLCDRenderText()) { + return "LCD"; + } + return "AA"; +} + +class ShaderMaskBench : public SkBenchmark { + SkPaint fPaint; + SkString fText; + SkString fName; + FontQuality fFQ; + enum { N = SkBENCHLOOP(500) }; +public: + ShaderMaskBench(void* param, bool isOpaque, FontQuality fq) : INHERITED(param) { + fFQ = fq; + fText.set(STR); + + fPaint.setAntiAlias(kBW != fq); + fPaint.setLCDRenderText(kLCD == fq); + fPaint.setAlpha(isOpaque ? 0xFF : 0x80); + fPaint.setShader(new SkColorShader)->unref(); + } + +protected: + virtual const char* onGetName() { + fName.printf("shadermask", SkScalarToFloat(fPaint.getTextSize())); + fName.appendf("_%s", fontQualityName(fPaint)); + fName.appendf("_%02X", fPaint.getAlpha()); + return fName.c_str(); + } + + virtual void onDraw(SkCanvas* canvas) { + const SkIPoint dim = this->getSize(); + SkRandom rand; + + SkPaint paint(fPaint); + this->setupPaint(&paint); + // explicitly need these + paint.setAlpha(fPaint.getAlpha()); + paint.setAntiAlias(kBW != fFQ); + paint.setLCDRenderText(kLCD == fFQ); + + const SkScalar x0 = SkIntToScalar(-10); + const SkScalar y0 = SkIntToScalar(-10); + + paint.setTextSize(SkIntToScalar(12)); + for (int i = 0; i < N; i++) { + SkScalar x = x0 + rand.nextUScalar1() * dim.fX; + SkScalar y = y0 + rand.nextUScalar1() * dim.fY; + canvas->drawText(fText.c_str(), fText.size(), x, y, paint); + } + + paint.setTextSize(SkIntToScalar(48)); + for (int i = 0; i < N/4; i++) { + SkScalar x = x0 + rand.nextUScalar1() * dim.fX; + SkScalar y = y0 + rand.nextUScalar1() * dim.fY; + canvas->drawText(fText.c_str(), fText.size(), x, y, paint); + } + } + +private: + typedef SkBenchmark INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static SkBenchmark* Fact00(void* p) { return new ShaderMaskBench(p, true, kBW); } +static SkBenchmark* Fact01(void* p) { return new ShaderMaskBench(p, false, kBW); } +static SkBenchmark* Fact10(void* p) { return new ShaderMaskBench(p, true, kAA); } +static SkBenchmark* Fact11(void* p) { return new ShaderMaskBench(p, false, kAA); } +static SkBenchmark* Fact20(void* p) { return new ShaderMaskBench(p, true, kLCD); } +static SkBenchmark* Fact21(void* p) { return new ShaderMaskBench(p, false, kLCD); } + +static BenchRegistry gReg00(Fact00); +static BenchRegistry gReg01(Fact01); +static BenchRegistry gReg10(Fact10); +static BenchRegistry gReg11(Fact11); +static BenchRegistry gReg20(Fact20); +static BenchRegistry gReg21(Fact21); + diff --git a/bench/SkBenchmark.cpp b/bench/SkBenchmark.cpp index 230a7af..a69402b 100644 --- a/bench/SkBenchmark.cpp +++ b/bench/SkBenchmark.cpp @@ -1,3 +1,10 @@ + +/* + * Copyright 2011 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 "SkPaint.h" #include "SkParse.h" diff --git a/bench/SkBenchmark.h b/bench/SkBenchmark.h index 945db80..5019b23 100644 --- a/bench/SkBenchmark.h +++ b/bench/SkBenchmark.h @@ -1,3 +1,10 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ #ifndef SkBenchmark_DEFINED #define SkBenchmark_DEFINED @@ -6,6 +13,12 @@ #include "SkTDict.h" #include "SkTRegistry.h" +#ifdef SK_DEBUG + #define SkBENCHLOOP(n) 1 +#else + #define SkBENCHLOOP(n) n +#endif + class SkCanvas; class SkPaint; diff --git a/bench/TextBench.cpp b/bench/TextBench.cpp index f2b9604..63a7167 100644 --- a/bench/TextBench.cpp +++ b/bench/TextBench.cpp @@ -1,3 +1,10 @@ + +/* + * Copyright 2011 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 "SkFontHost.h" @@ -7,6 +14,22 @@ #include "SkString.h" #include "SkTemplates.h" +enum FontQuality { + kBW, + kAA, + kLCD +}; + +static const char* fontQualityName(const SkPaint& paint) { + if (!paint.isAntiAlias()) { + return "BW"; + } + if (paint.isLCDRenderText()) { + return "LCD"; + } + return "AA"; +} + /* Some considerations for performance: short -vs- long strings (measuring overhead) tiny -vs- large pointsize (measure blit -vs- overhead) @@ -18,34 +41,35 @@ */ class TextBench : public SkBenchmark { SkPaint fPaint; - int fCount; - SkPoint* fPos; SkString fText; SkString fName; - enum { N = 600 }; + FontQuality fFQ; + bool fDoPos; + SkPoint* fPos; + enum { N = SkBENCHLOOP(800) }; public: - TextBench(void* param, const char text[], int ps, bool linearText, - bool posText, SkColor color = SK_ColorBLACK) : INHERITED(param) { + TextBench(void* param, const char text[], int ps, + SkColor color, FontQuality fq, bool doPos = false) : INHERITED(param) { + fFQ = fq; + fDoPos = doPos; fText.set(text); - fPaint.setAntiAlias(true); + fPaint.setAntiAlias(kBW != fq); + fPaint.setLCDRenderText(kLCD == fq); fPaint.setTextSize(SkIntToScalar(ps)); - fPaint.setLinearText(linearText); fPaint.setColor(color); - if (posText) { - SkAutoTArray<SkScalar> storage(fText.size()); - SkScalar* widths = storage.get(); - fCount = fPaint.getTextWidths(fText.c_str(), fText.size(), widths); - fPos = new SkPoint[fCount]; + if (doPos) { + size_t len = strlen(text); + SkScalar* adv = new SkScalar[len]; + fPaint.getTextWidths(text, len, adv); + fPos = new SkPoint[len]; SkScalar x = 0; - for (int i = 0; i < fCount; i++) { - fPos[i].set(x, 0); - x += widths[i]; + for (size_t i = 0; i < len; ++i) { + fPos[i].set(x, SkIntToScalar(50)); + x += adv[i]; } - } else { - fCount = 0; - fPos = NULL; + delete[] adv; } } @@ -56,15 +80,14 @@ public: protected: virtual const char* onGetName() { fName.printf("text_%g", SkScalarToFloat(fPaint.getTextSize())); - if (fPaint.isLinearText()) { - fName.append("_linear"); - } - if (fPos) { - fName.append("_pos"); + if (fDoPos) { + fName.appendf("_pos"); } - + fName.appendf("_%s", fontQualityName(fPaint)); if (SK_ColorBLACK != fPaint.getColor()) { fName.appendf("_%02X", fPaint.getAlpha()); + } else { + fName.appendf("_BK"); } return fName.c_str(); } @@ -75,20 +98,26 @@ protected: SkPaint paint(fPaint); this->setupPaint(&paint); - paint.setColor(fPaint.getColor()); // need our specified color + // explicitly need these + paint.setColor(fPaint.getColor()); + paint.setAntiAlias(kBW != fFQ); + paint.setLCDRenderText(kLCD == fFQ); const SkScalar x0 = SkIntToScalar(-10); const SkScalar y0 = SkIntToScalar(-10); + if (fDoPos) { + // realistically, the matrix is often at least translated, so we + // do that since it exercises different code in drawPosText. + canvas->translate(SK_Scalar1, SK_Scalar1); + } + for (int i = 0; i < N; i++) { - SkScalar x = x0 + rand.nextUScalar1() * dim.fX; - SkScalar y = y0 + rand.nextUScalar1() * dim.fY; - if (fPos) { - canvas->save(SkCanvas::kMatrix_SaveFlag); - canvas->translate(x, y); + if (fDoPos) { canvas->drawPosText(fText.c_str(), fText.size(), fPos, paint); - canvas->restore(); } else { + SkScalar x = x0 + rand.nextUScalar1() * dim.fX; + SkScalar y = y0 + rand.nextUScalar1() * dim.fY; canvas->drawText(fText.c_str(), fText.size(), x, y, paint); } } @@ -101,35 +130,31 @@ private: /////////////////////////////////////////////////////////////////////////////// #define STR "Hamburgefons" -#define SMALL 9 -#define BIG 48 -static SkBenchmark* Fact0(void* p) { return new TextBench(p, STR, SMALL, false, false); } -static SkBenchmark* Fact01(void* p) { return new TextBench(p, STR, SMALL, false, false, 0xFFFF0000); } -static SkBenchmark* Fact02(void* p) { return new TextBench(p, STR, SMALL, false, false, 0x88FF0000); } +static SkBenchmark* Fact01(void* p) { return new TextBench(p, STR, 16, 0xFF000000, kBW); } +static SkBenchmark* Fact02(void* p) { return new TextBench(p, STR, 16, 0xFFFF0000, kBW); } +static SkBenchmark* Fact03(void* p) { return new TextBench(p, STR, 16, 0x88FF0000, kBW); } -static SkBenchmark* Fact1(void* p) { return new TextBench(p, STR, SMALL, false, true); } -static SkBenchmark* Fact2(void* p) { return new TextBench(p, STR, SMALL, true, false); } -static SkBenchmark* Fact3(void* p) { return new TextBench(p, STR, SMALL, true, true); } +static SkBenchmark* Fact11(void* p) { return new TextBench(p, STR, 16, 0xFF000000, kAA); } +static SkBenchmark* Fact12(void* p) { return new TextBench(p, STR, 16, 0xFFFF0000, kAA); } +static SkBenchmark* Fact13(void* p) { return new TextBench(p, STR, 16, 0x88FF0000, kAA); } -static SkBenchmark* Fact4(void* p) { return new TextBench(p, STR, BIG, false, false); } -static SkBenchmark* Fact41(void* p) { return new TextBench(p, STR, BIG, false, false, 0xFFFF0000); } -static SkBenchmark* Fact42(void* p) { return new TextBench(p, STR, BIG, false, false, 0x88FF0000); } +static SkBenchmark* Fact21(void* p) { return new TextBench(p, STR, 16, 0xFF000000, kLCD); } +static SkBenchmark* Fact22(void* p) { return new TextBench(p, STR, 16, 0xFFFF0000, kLCD); } +static SkBenchmark* Fact23(void* p) { return new TextBench(p, STR, 16, 0x88FF0000, kLCD); } -static SkBenchmark* Fact5(void* p) { return new TextBench(p, STR, BIG, false, true); } -static SkBenchmark* Fact6(void* p) { return new TextBench(p, STR, BIG, true, false); } -static SkBenchmark* Fact7(void* p) { return new TextBench(p, STR, BIG, true, true); } +static SkBenchmark* Fact111(void* p) { return new TextBench(p, STR, 16, 0xFF000000, kAA, true); } -static BenchRegistry gReg0(Fact0); static BenchRegistry gReg01(Fact01); static BenchRegistry gReg02(Fact02); -static BenchRegistry gReg1(Fact1); -static BenchRegistry gReg2(Fact2); -static BenchRegistry gReg3(Fact3); -static BenchRegistry gReg4(Fact4); -static BenchRegistry gReg41(Fact41); -static BenchRegistry gReg42(Fact42); -static BenchRegistry gReg5(Fact5); -static BenchRegistry gReg6(Fact6); -static BenchRegistry gReg7(Fact7); +static BenchRegistry gReg03(Fact03); + +static BenchRegistry gReg11(Fact11); +static BenchRegistry gReg12(Fact12); +static BenchRegistry gReg13(Fact13); + +static BenchRegistry gReg21(Fact21); +static BenchRegistry gReg22(Fact22); +static BenchRegistry gReg23(Fact23); +static BenchRegistry gReg111(Fact111); diff --git a/bench/VertBench.cpp b/bench/VertBench.cpp new file mode 100644 index 0000000..98df449 --- /dev/null +++ b/bench/VertBench.cpp @@ -0,0 +1,99 @@ +/* + * Copyright 2011 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 "SkPaint.h" +#include "SkRandom.h" +#include "SkString.h" +#include "SkShader.h" + +enum VertFlags { + kColors_VertFlag, + kTexture_VertFlag, +}; + +class VertBench : public SkBenchmark { + SkString fName; + enum { + W = 640, + H = 480, + ROW = 20, + COL = 20, + PTS = (ROW + 1) * (COL + 1), + IDX = ROW * COL * 6, + N = SkBENCHLOOP(10) + }; + + SkPoint fPts[PTS]; + SkColor fColors[PTS]; + SkPoint fTex[PTS]; + uint16_t fIdx[IDX]; + + static void load_2_tris(uint16_t idx[], int x, int y, int rb) { + int n = y * rb + x; + idx[0] = n; idx[1] = n + 1; idx[2] = rb + n + 1; + idx[3] = n; idx[4] = rb + n + 1; idx[5] = n + rb; + } + +public: + VertBench(void* param) : INHERITED(param) { + const SkScalar dx = SkIntToScalar(W) / COL; + const SkScalar dy = SkIntToScalar(H) / COL; + + SkPoint* pts = fPts; + uint16_t* idx = fIdx; + + SkScalar yy = 0; + for (int y = 0; y <= ROW; y++) { + SkScalar xx = 0; + for (int x = 0; x <= COL; ++x) { + pts->set(xx, yy); + pts += 1; + xx += dx; + + if (x < COL && y < ROW) { + load_2_tris(idx, x, y, COL + 1); + for (int i = 0; i < 6; i++) { + SkASSERT(idx[i] < PTS); + } + idx += 6; + } + } + yy += dy; + } + SkASSERT(PTS == pts - fPts); + SkASSERT(IDX == idx - fIdx); + + SkRandom rand; + for (int i = 0; i < PTS; ++i) { + fColors[i] = rand.nextU() | (0xFF << 24); + } + + fName.set("verts"); + } + +protected: + virtual const char* onGetName() { return fName.c_str(); } + virtual void onDraw(SkCanvas* canvas) { + SkPaint paint; + this->setupPaint(&paint); + + for (int i = 0; i < N; i++) { + canvas->drawVertices(SkCanvas::kTriangles_VertexMode, PTS, + fPts, NULL, fColors, NULL, fIdx, IDX, paint); + } + } +private: + typedef SkBenchmark INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static SkBenchmark* Fact(void* p) { return SkNEW_ARGS(VertBench, (p)); } + +static BenchRegistry gReg(Fact); diff --git a/bench/bench_compare.py b/bench/bench_compare.py index f6909b1..c1a1ff9 100644 --- a/bench/bench_compare.py +++ b/bench/bench_compare.py @@ -5,31 +5,7 @@ Created on May 16, 2011 ''' 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 +import bench_util def usage(): """Prints simple usage information.""" @@ -38,20 +14,39 @@ def usage(): 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 ' Not specifying is the same as -f "bctondp".' print ' b: bench' print ' c: config' + print ' t: time type' print ' o: old time' print ' n: new time' print ' d: diff' print ' p: percent diff' - +class BenchDiff: + """A compare between data points produced by bench. + + (BenchDataPoint, BenchDataPoint)""" + def __init__(self, old, new): + self.old = old + self.new = new + self.diff = old.time - new.time + diffp = 0 + if old.time != 0: + diffp = self.diff / old.time + self.diffp = diffp + + def __repr__(self): + return "BenchDiff(%s, %s)" % ( + str(self.new), + str(self.old), + ) + def main(): """Parses command line and writes output.""" try: - opts, args = getopt.getopt(sys.argv[1:], "f:o:n:h") + opts, _ = getopt.getopt(sys.argv[1:], "f:o:n:h") except getopt.GetoptError, err: print str(err) usage() @@ -60,25 +55,27 @@ def main(): column_formats = { 'b' : '{bench: >28} ', 'c' : '{config: <4} ', + 't' : '{time_type: <4} ', 'o' : '{old_time: >10.2f} ', 'n' : '{new_time: >10.2f} ', 'd' : '{diff: >+10.2f} ', - 'p' : '{diffp: >+7.1%} ', + 'p' : '{diffp: >+8.1%} ', } header_formats = { 'b' : '{bench: >28} ', 'c' : '{config: <4} ', + 't' : '{time_type: <4} ', 'o' : '{old_time: >10} ', 'n' : '{new_time: >10} ', 'd' : '{diff: >10} ', - 'p' : '{diffp: >7} ', + 'p' : '{diffp: >8} ', } old = None new = None column_format = "" header_format = "" - columns = 'bcondp' + columns = 'bctondp' header = False for option, value in opts: @@ -110,31 +107,43 @@ def main(): print header_format.format( bench='bench' , config='conf' + , time_type='time' , 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) - ) - + old_benches = bench_util.parse({}, open(old, 'r')) + new_benches = bench_util.parse({}, open(new, 'r')) + + bench_diffs = [] + for old_bench in old_benches: + #filter new_benches for benches that match old_bench + new_bench_match = [bench for bench in new_benches + if old_bench.bench == bench.bench and + old_bench.config == bench.config and + old_bench.time_type == bench.time_type + ] + if (len(new_bench_match) < 1): + continue + bench_diffs.append(BenchDiff(old_bench, new_bench_match[0])) + + bench_diffs.sort(key=lambda d : [d.diffp, + d.old.bench, + d.old.config, + d.old.time_type, + ]) + for bench_diff in bench_diffs: + print column_format.format( + bench=bench_diff.old.bench.strip() + , config=bench_diff.old.config.strip() + , time_type=bench_diff.old.time_type + , old_time=bench_diff.old.time + , new_time=bench_diff.new.time + , diff=bench_diff.diff + , diffp=bench_diff.diffp + ) if __name__ == "__main__": main() diff --git a/bench/bench_graph_svg.py b/bench/bench_graph_svg.py new file mode 100644 index 0000000..d8cd54b --- /dev/null +++ b/bench/bench_graph_svg.py @@ -0,0 +1,750 @@ +''' +Created on May 16, 2011 + +@author: bungeman +''' +import sys +import getopt +import re +import os +import bench_util +import json +import xml.sax.saxutils + +def usage(): + """Prints simple usage information.""" + + print '-d <dir> a directory containing bench_r<revision>_<scalar> files.' + print '-b <bench> the bench to show.' + print '-c <config> the config to show (GPU, 8888, 565, etc).' + print '-t <time> the time to show (w, c, g, etc).' + print '-s <setting>[=<value>] a setting to show (alpha, scalar, etc).' + print '-r <revision>[:<revision>] the revisions to show.' + print ' Negative <revision> is taken as offset from most recent revision.' + print '-f <revision>[:<revision>] the revisions to use for fitting.' + print ' Negative <revision> is taken as offset from most recent revision.' + print '-x <int> the desired width of the svg.' + print '-y <int> the desired height of the svg.' + print '-l <title> title to use for the output graph' + print '--default-setting <setting>[=<value>] setting for those without.' + + +class Label: + """The information in a label. + + (str, str, str, str, {str:str})""" + def __init__(self, bench, config, time_type, settings): + self.bench = bench + self.config = config + self.time_type = time_type + self.settings = settings + + def __repr__(self): + return "Label(%s, %s, %s, %s)" % ( + str(self.bench), + str(self.config), + str(self.time_type), + str(self.settings), + ) + + def __str__(self): + return "%s_%s_%s_%s" % ( + str(self.bench), + str(self.config), + str(self.time_type), + str(self.settings), + ) + + def __eq__(self, other): + return (self.bench == other.bench and + self.config == other.config and + self.time_type == other.time_type and + self.settings == other.settings) + + def __hash__(self): + return (hash(self.bench) ^ + hash(self.config) ^ + hash(self.time_type) ^ + hash(frozenset(self.settings.iteritems()))) + +def get_latest_revision(directory): + """Returns the latest revision number found within this directory. + """ + latest_revision_found = -1 + for bench_file in os.listdir(directory): + file_name_match = re.match('bench_r(\d+)_(\S+)', bench_file) + if (file_name_match is None): + continue + revision = int(file_name_match.group(1)) + if revision > latest_revision_found: + latest_revision_found = revision + if latest_revision_found < 0: + return None + else: + return latest_revision_found + +def parse_dir(directory, default_settings, oldest_revision, newest_revision): + """Parses bench data from files like bench_r<revision>_<scalar>. + + (str, {str, str}, Number, Number) -> {int:[BenchDataPoints]}""" + revision_data_points = {} # {revision : [BenchDataPoints]} + for bench_file in os.listdir(directory): + file_name_match = re.match('bench_r(\d+)_(\S+)', bench_file) + if (file_name_match is None): + continue + + revision = int(file_name_match.group(1)) + scalar_type = file_name_match.group(2) + + if (revision < oldest_revision or revision > newest_revision): + continue + + file_handle = open(directory + '/' + bench_file, 'r') + + if (revision not in revision_data_points): + revision_data_points[revision] = [] + default_settings['scalar'] = scalar_type + revision_data_points[revision].extend( + bench_util.parse(default_settings, file_handle)) + file_handle.close() + return revision_data_points + +def create_lines(revision_data_points, settings + , bench_of_interest, config_of_interest, time_of_interest): + """Convert revision data into sorted line data. + + ({int:[BenchDataPoints]}, {str:str}, str?, str?, str?) + -> {Label:[(x,y)] | [n].x <= [n+1].x}""" + revisions = revision_data_points.keys() + revisions.sort() + lines = {} # {Label:[(x,y)] | x[n] <= x[n+1]} + for revision in revisions: + for point in revision_data_points[revision]: + if (bench_of_interest is not None and + not bench_of_interest == point.bench): + continue + + if (config_of_interest is not None and + not config_of_interest == point.config): + continue + + if (time_of_interest is not None and + not time_of_interest == point.time_type): + continue + + skip = False + for key, value in settings.items(): + if key in point.settings and point.settings[key] != value: + skip = True + break + if skip: + continue + + line_name = Label(point.bench + , point.config + , point.time_type + , point.settings) + + if line_name not in lines: + lines[line_name] = [] + + lines[line_name].append((revision, point.time)) + + return lines + +def bounds(lines): + """Finds the bounding rectangle for the lines. + + {Label:[(x,y)]} -> ((min_x, min_y),(max_x,max_y))""" + min_x = bench_util.Max + min_y = bench_util.Max + max_x = bench_util.Min + max_y = bench_util.Min + + for line in lines.itervalues(): + for x, y in line: + min_x = min(min_x, x) + min_y = min(min_y, y) + max_x = max(max_x, x) + max_y = max(max_y, y) + + return ((min_x, min_y), (max_x, max_y)) + +def create_regressions(lines, start_x, end_x): + """Creates regression data from line segments. + + ({Label:[(x,y)] | [n].x <= [n+1].x}, Number, Number) + -> {Label:LinearRegression}""" + regressions = {} # {Label : LinearRegression} + + for label, line in lines.iteritems(): + regression_line = [p for p in line if start_x <= p[0] <= end_x] + + if (len(regression_line) < 2): + continue + regression = bench_util.LinearRegression(regression_line) + regressions[label] = regression + + return regressions + +def bounds_slope(regressions): + """Finds the extreme up and down slopes of a set of linear regressions. + + ({Label:LinearRegression}) -> (max_up_slope, min_down_slope)""" + max_up_slope = 0 + min_down_slope = 0 + for regression in regressions.itervalues(): + min_slope = regression.find_min_slope() + max_up_slope = max(max_up_slope, min_slope) + min_down_slope = min(min_down_slope, min_slope) + + return (max_up_slope, min_down_slope) + +def main(): + """Parses command line and writes output.""" + + try: + opts, _ = getopt.getopt(sys.argv[1:] + , "d:b:c:l:t:s:r:f:x:y:" + , "default-setting=") + except getopt.GetoptError, err: + print str(err) + usage() + sys.exit(2) + + directory = None + config_of_interest = None + bench_of_interest = None + time_of_interest = None + revision_range = '0:' + regression_range = '0:' + latest_revision = None + requested_height = None + requested_width = None + title = 'Bench graph' + settings = {} + default_settings = {} + + def parse_range(range): + """Takes '<old>[:<new>]' as a string and returns (old, new). + Any revision numbers that are dependent on the latest revision number + will be filled in based on latest_revision. + """ + old, _, new = range.partition(":") + old = int(old) + if old < 0: + old += latest_revision; + if not new: + new = latest_revision; + new = int(new) + if new < 0: + new += latest_revision; + return (old, new) + + def add_setting(settings, setting): + """Takes <key>[=<value>] adds {key:value} or {key:True} to settings.""" + name, _, value = setting.partition('=') + if not value: + settings[name] = True + else: + settings[name] = value + + try: + for option, value in opts: + if option == "-d": + directory = value + elif option == "-b": + bench_of_interest = value + elif option == "-c": + config_of_interest = value + elif option == "-t": + time_of_interest = value + elif option == "-s": + add_setting(settings, value) + elif option == "-r": + revision_range = value + elif option == "-f": + regression_range = value + elif option == "-x": + requested_width = int(value) + elif option == "-y": + requested_height = int(value) + elif option == "-l": + title = value + elif option == "--default-setting": + add_setting(default_settings, value) + else: + usage() + assert False, "unhandled option" + except ValueError: + usage() + sys.exit(2) + + if directory is None: + usage() + sys.exit(2) + + latest_revision = get_latest_revision(directory) + oldest_revision, newest_revision = parse_range(revision_range) + oldest_regression, newest_regression = parse_range(regression_range) + + revision_data_points = parse_dir(directory + , default_settings + , oldest_revision + , newest_revision) + + # Update oldest_revision and newest_revision based on the data we could find + all_revision_numbers = revision_data_points.keys() + oldest_revision = min(all_revision_numbers) + newest_revision = max(all_revision_numbers) + + lines = create_lines(revision_data_points + , settings + , bench_of_interest + , config_of_interest + , time_of_interest) + + regressions = create_regressions(lines + , oldest_regression + , newest_regression) + + output_xhtml(lines, oldest_revision, newest_revision, + regressions, requested_width, requested_height, title) + +def qa(out): + """Stringify input and quote as an xml attribute.""" + return xml.sax.saxutils.quoteattr(str(out)) +def qe(out): + """Stringify input and escape as xml data.""" + return xml.sax.saxutils.escape(str(out)) + +def create_select(qualifier, lines, select_id=None): + """Output select with options showing lines which qualifier maps to it. + + ((Label) -> str, {Label:_}, str?) -> _""" + options = {} #{ option : [Label]} + for label in lines.keys(): + option = qualifier(label) + if (option not in options): + options[option] = [] + options[option].append(label) + option_list = list(options.keys()) + option_list.sort() + print '<select class="lines"', + if select_id is not None: + print 'id=%s' % qa(select_id) + print 'multiple="true" size="10" onchange="updateSvg();">' + for option in option_list: + print '<option value=' + qa('[' + + reduce(lambda x,y:x+json.dumps(str(y))+',',options[option],"")[0:-1] + + ']') + '>'+qe(option)+'</option>' + print '</select>' + +def output_xhtml(lines, oldest_revision, newest_revision, + regressions, requested_width, requested_height, title): + """Outputs an svg/xhtml view of the data.""" + print '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"', + print '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' + print '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">' + print '<head>' + print '<title>%s</title>' % title + print '</head>' + print '<body>' + + output_svg(lines, regressions, requested_width, requested_height) + + #output the manipulation controls + print """ +<script type="text/javascript">//<![CDATA[ + function getElementsByClass(node, searchClass, tag) { + var classElements = new Array(); + var elements = node.getElementsByTagName(tag); + var pattern = new RegExp("^|\\s"+searchClass+"\\s|$"); + for (var i = 0, elementsFound = 0; i < elements.length; ++i) { + if (pattern.test(elements[i].className)) { + classElements[elementsFound] = elements[i]; + ++elementsFound; + } + } + return classElements; + } + function getAllLines() { + var selectElem = document.getElementById('benchSelect'); + var linesObj = {}; + for (var i = 0; i < selectElem.options.length; ++i) { + var lines = JSON.parse(selectElem.options[i].value); + for (var j = 0; j < lines.length; ++j) { + linesObj[lines[j]] = true; + } + } + return linesObj; + } + function getOptions(selectElem) { + var linesSelectedObj = {}; + for (var i = 0; i < selectElem.options.length; ++i) { + if (!selectElem.options[i].selected) continue; + + var linesSelected = JSON.parse(selectElem.options[i].value); + for (var j = 0; j < linesSelected.length; ++j) { + linesSelectedObj[linesSelected[j]] = true; + } + } + return linesSelectedObj; + } + function objectEmpty(obj) { + for (var p in obj) { + return false; + } + return true; + } + function markSelectedLines(selectElem, allLines) { + var linesSelected = getOptions(selectElem); + if (!objectEmpty(linesSelected)) { + for (var line in allLines) { + allLines[line] &= (linesSelected[line] == true); + } + } + } + function updateSvg() { + var allLines = getAllLines(); + + var selects = getElementsByClass(document, 'lines', 'select'); + for (var i = 0; i < selects.length; ++i) { + markSelectedLines(selects[i], allLines); + } + + for (var line in allLines) { + var svgLine = document.getElementById(line); + var display = (allLines[line] ? 'inline' : 'none'); + svgLine.setAttributeNS(null,'display', display); + } + } + + function mark(markerId) { + for (var line in getAllLines()) { + var svgLineGroup = document.getElementById(line); + var display = svgLineGroup.getAttributeNS(null,'display'); + if (display == null || display == "" || display != "none") { + var svgLine = document.getElementById(line+'_line'); + if (markerId == null) { + svgLine.removeAttributeNS(null,'marker-mid'); + } else { + svgLine.setAttributeNS(null,'marker-mid', markerId); + } + } + } + } +//]]></script>""" + + print '<table border="0" width="%s">' % requested_width + print """ +<form> +<tr valign="bottom" align="center"> +<td width="1">Bench Type</td> +<td width="1">Bitmap Config</td> +<td width="1">Timer Type (Cpu/Gpu/wall)</td> +<td width="1"><!--buttons--></td> +<td width="10%"><!--spacing--></td>""" + + print '<td>%s<br></br>revisions r%s - r%s</td>' % ( + title, + bench_util.CreateRevisionLink(oldest_revision), + bench_util.CreateRevisionLink(newest_revision)) + print '</tr><tr valign="top" align="center">' + print '<td width="1">' + create_select(lambda l: l.bench, lines, 'benchSelect') + print '</td><td width="1">' + create_select(lambda l: l.config, lines) + print '</td><td width="1">' + create_select(lambda l: l.time_type, lines) + + all_settings = {} + variant_settings = set() + for label in lines.keys(): + for key, value in label.settings.items(): + if key not in all_settings: + all_settings[key] = value + elif all_settings[key] != value: + variant_settings.add(key) + + for k in variant_settings: + create_select(lambda l: l.settings[k], lines) + + print '</td><td width="1"><button type="button"', + print 'onclick=%s' % qa("mark('url(#circleMark)'); return false;"), + print '>Mark Points</button>' + print '<button type="button" onclick="mark(null);">Clear Points</button>' + + print """ +</td> +<td width="10%"></td> +<td align="left"> +<p>Brighter red indicates tests that have gotten worse; brighter green +indicates tests that have gotten better.</p> +<p>To highlight individual tests, hold down CONTROL and mouse over +graph lines.</p> +<p>To highlight revision numbers, hold down SHIFT and mouse over +the graph area.</p> +<p>To only show certain tests on the graph, select any combination of +tests in the selectors at left. (To show all, select all.)</p> +<p>Use buttons at left to mark/clear points on the lines for selected +benchmarks.</p> +</td> +</tr> +</form> +</table> +</body> +</html>""" + +def compute_size(requested_width, requested_height, rev_width, time_height): + """Converts potentially empty requested size into a concrete size. + + (Number?, Number?) -> (Number, Number)""" + pic_width = 0 + pic_height = 0 + if (requested_width is not None and requested_height is not None): + pic_height = requested_height + pic_width = requested_width + + elif (requested_width is not None): + pic_width = requested_width + pic_height = pic_width * (float(time_height) / rev_width) + + elif (requested_height is not None): + pic_height = requested_height + pic_width = pic_height * (float(rev_width) / time_height) + + else: + pic_height = 800 + pic_width = max(rev_width*3 + , pic_height * (float(rev_width) / time_height)) + + return (pic_width, pic_height) + +def output_svg(lines, regressions, requested_width, requested_height): + """Outputs an svg view of the data.""" + + (global_min_x, _), (global_max_x, global_max_y) = bounds(lines) + max_up_slope, min_down_slope = bounds_slope(regressions) + + #output + global_min_y = 0 + x = global_min_x + y = global_min_y + w = global_max_x - global_min_x + h = global_max_y - global_min_y + font_size = 16 + line_width = 2 + + pic_width, pic_height = compute_size(requested_width, requested_height + , w, h) + + def cw(w1): + """Converts a revision difference to display width.""" + return (pic_width / float(w)) * w1 + def cx(x): + """Converts a revision to a horizontal display position.""" + return cw(x - global_min_x) + + def ch(h1): + """Converts a time difference to a display height.""" + return -(pic_height / float(h)) * h1 + def cy(y): + """Converts a time to a vertical display position.""" + return pic_height + ch(y - global_min_y) + + print '<svg', + print 'width=%s' % qa(str(pic_width)+'px') + print 'height=%s' % qa(str(pic_height)+'px') + print 'viewBox="0 0 %s %s"' % (str(pic_width), str(pic_height)) + print 'onclick=%s' % qa( + "var event = arguments[0] || window.event;" + " if (event.shiftKey) { highlightRevision(null); }" + " if (event.ctrlKey) { highlight(null); }" + " return false;") + print 'xmlns="http://www.w3.org/2000/svg"' + print 'xmlns:xlink="http://www.w3.org/1999/xlink">' + + print """ +<defs> + <marker id="circleMark" + viewBox="0 0 2 2" refX="1" refY="1" + markerUnits="strokeWidth" + markerWidth="2" markerHeight="2" + orient="0"> + <circle cx="1" cy="1" r="1"/> + </marker> +</defs>""" + + #output the revisions + print """ +<script type="text/javascript">//<![CDATA[ + var previousRevision; + var previousRevisionFill; + var previousRevisionStroke + function highlightRevision(id) { + if (previousRevision == id) return; + + document.getElementById('revision').firstChild.nodeValue = 'r' + id; + document.getElementById('rev_link').setAttribute('xlink:href', + 'http://code.google.com/p/skia/source/detail?r=' + id); + + var preRevision = document.getElementById(previousRevision); + if (preRevision) { + preRevision.setAttributeNS(null,'fill', previousRevisionFill); + preRevision.setAttributeNS(null,'stroke', previousRevisionStroke); + } + + var revision = document.getElementById(id); + previousRevision = id; + if (revision) { + previousRevisionFill = revision.getAttributeNS(null,'fill'); + revision.setAttributeNS(null,'fill','rgb(100%, 95%, 95%)'); + + previousRevisionStroke = revision.getAttributeNS(null,'stroke'); + revision.setAttributeNS(null,'stroke','rgb(100%, 90%, 90%)'); + } + } +//]]></script>""" + + def print_rect(x, y, w, h, revision): + """Outputs a revision rectangle in display space, + taking arguments in revision space.""" + disp_y = cy(y) + disp_h = ch(h) + if disp_h < 0: + disp_y += disp_h + disp_h = -disp_h + + print '<rect id=%s x=%s y=%s' % (qa(revision), qa(cx(x)), qa(disp_y),), + print 'width=%s height=%s' % (qa(cw(w)), qa(disp_h),), + print 'fill="white"', + print 'stroke="rgb(98%%,98%%,88%%)" stroke-width=%s' % qa(line_width), + print 'onmouseover=%s' % qa( + "var event = arguments[0] || window.event;" + " if (event.shiftKey) {" + " highlightRevision('"+str(revision)+"');" + " return false;" + " }"), + print ' />' + + xes = set() + for line in lines.itervalues(): + for point in line: + xes.add(point[0]) + revisions = list(xes) + revisions.sort() + + left = x + current_revision = revisions[0] + for next_revision in revisions[1:]: + width = (((next_revision - current_revision) / 2.0) + + (current_revision - left)) + print_rect(left, y, width, h, current_revision) + left += width + current_revision = next_revision + print_rect(left, y, x+w - left, h, current_revision) + + #output the lines + print """ +<script type="text/javascript">//<![CDATA[ + var previous; + var previousColor; + var previousOpacity; + function highlight(id) { + if (previous == id) return; + + document.getElementById('label').firstChild.nodeValue = id; + + var preGroup = document.getElementById(previous); + if (preGroup) { + var preLine = document.getElementById(previous+'_line'); + preLine.setAttributeNS(null,'stroke', previousColor); + preLine.setAttributeNS(null,'opacity', previousOpacity); + + var preSlope = document.getElementById(previous+'_linear'); + if (preSlope) { + preSlope.setAttributeNS(null,'visibility', 'hidden'); + } + } + + var group = document.getElementById(id); + previous = id; + if (group) { + group.parentNode.appendChild(group); + + var line = document.getElementById(id+'_line'); + previousColor = line.getAttributeNS(null,'stroke'); + previousOpacity = line.getAttributeNS(null,'opacity'); + line.setAttributeNS(null,'stroke', 'blue'); + line.setAttributeNS(null,'opacity', '1'); + + var slope = document.getElementById(id+'_linear'); + if (slope) { + slope.setAttributeNS(null,'visibility', 'visible'); + } + } + } +//]]></script>""" + for label, line in lines.items(): + print '<g id=%s>' % qa(label) + r = 128 + g = 128 + b = 128 + a = .10 + if label in regressions: + regression = regressions[label] + min_slope = regression.find_min_slope() + if min_slope < 0: + d = max(0, (min_slope / min_down_slope)) + g += int(d*128) + a += d*0.9 + elif min_slope > 0: + d = max(0, (min_slope / max_up_slope)) + r += int(d*128) + a += d*0.9 + + slope = regression.slope + intercept = regression.intercept + min_x = regression.min_x + max_x = regression.max_x + print '<polyline id=%s' % qa(str(label)+'_linear'), + print 'fill="none" stroke="yellow"', + print 'stroke-width=%s' % qa(abs(ch(regression.serror*2))), + print 'opacity="0.5" pointer-events="none" visibility="hidden"', + print 'points="', + print '%s,%s' % (str(cx(min_x)), str(cy(slope*min_x + intercept))), + print '%s,%s' % (str(cx(max_x)), str(cy(slope*max_x + intercept))), + print '"/>' + + print '<polyline id=%s' % qa(str(label)+'_line'), + print 'onmouseover=%s' % qa( + "var event = arguments[0] || window.event;" + " if (event.ctrlKey) {" + " highlight('"+str(label).replace("'", "\\'")+"');" + " return false;" + " }"), + print 'fill="none" stroke="rgb(%s,%s,%s)"' % (str(r), str(g), str(b)), + print 'stroke-width=%s' % qa(line_width), + print 'opacity=%s' % qa(a), + print 'points="', + for point in line: + print '%s,%s' % (str(cx(point[0])), str(cy(point[1]))), + print '"/>' + + print '</g>' + + #output the labels + print '<text id="label" x="0" y=%s' % qa(font_size), + print 'font-size=%s> </text>' % qa(font_size) + + print '<a id="rev_link" xlink:href="" target="_top">' + print '<text id="revision" x="0" y=%s style="' % qa(font_size*2) + print 'font-size: %s; ' % qe(font_size) + print 'stroke: #0000dd; text-decoration: underline; ' + print '"> </text></a>' + + print '</svg>' + +if __name__ == "__main__": + main() diff --git a/bench/bench_util.py b/bench/bench_util.py new file mode 100644 index 0000000..e36e6c6 --- /dev/null +++ b/bench/bench_util.py @@ -0,0 +1,178 @@ +''' +Created on May 19, 2011 + +@author: bungeman +''' + +import re +import math + +class BenchDataPoint: + """A single data point produced by bench. + + (str, str, str, float, {str:str})""" + def __init__(self, bench, config, time_type, time, settings): + self.bench = bench + self.config = config + self.time_type = time_type + self.time = time + self.settings = settings + + def __repr__(self): + return "BenchDataPoint(%s, %s, %s, %s, %s)" % ( + str(self.bench), + str(self.config), + str(self.time_type), + str(self.time), + str(self.settings), + ) + +class _ExtremeType(object): + """Instances of this class compare greater or less than other objects.""" + def __init__(self, cmpr, rep): + object.__init__(self) + self._cmpr = cmpr + self._rep = rep + + def __cmp__(self, other): + if isinstance(other, self.__class__) and other._cmpr == self._cmpr: + return 0 + return self._cmpr + + def __repr__(self): + return self._rep + +Max = _ExtremeType(1, "Max") +Min = _ExtremeType(-1, "Min") + +def parse(settings, lines): + """Parses bench output into a useful data structure. + + ({str:str}, __iter__ -> str) -> [BenchDataPoint]""" + + benches = [] + current_bench = None + setting_re = '([^\s=]+)(?:=(\S+))?' + settings_re = 'skia bench:((?:\s+' + setting_re + ')*)' + bench_re = 'running bench (?:\[\d+ \d+\] )?\s*(\S+)' + time_re = '(?:(\w*)msecs = )?\s*(\d+\.\d+)' + config_re = '(\S+): ((?:' + time_re + '\s+)+)' + + for line in lines: + + #see if this line is a settings line + settingsMatch = re.search(settings_re, line) + if (settingsMatch): + settings = dict(settings) + for settingMatch in re.finditer(setting_re, settingsMatch.group(1)): + if (settingMatch.group(2)): + settings[settingMatch.group(1)] = settingMatch.group(2) + else: + settings[settingMatch.group(1)] = True + + #see if this line starts a new bench + new_bench = re.search(bench_re, 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(config_re, line): + current_config = new_config.group(1) + times = new_config.group(2) + for new_time in re.finditer(time_re, times): + current_time_type = new_time.group(1) + current_time = float(new_time.group(2)) + benches.append(BenchDataPoint( + current_bench + , current_config + , current_time_type + , current_time + , settings)) + + return benches + +class LinearRegression: + """Linear regression data based on a set of data points. + + ([(Number,Number)]) + There must be at least two points for this to make sense.""" + def __init__(self, points): + n = len(points) + max_x = Min + min_x = Max + + Sx = 0.0 + Sy = 0.0 + Sxx = 0.0 + Sxy = 0.0 + Syy = 0.0 + for point in points: + x = point[0] + y = point[1] + max_x = max(max_x, x) + min_x = min(min_x, x) + + Sx += x + Sy += y + Sxx += x*x + Sxy += x*y + Syy += y*y + + B = (n*Sxy - Sx*Sy) / (n*Sxx - Sx*Sx) + a = (1.0/n)*(Sy - B*Sx) + + se2 = 0 + sB2 = 0 + sa2 = 0 + if (n >= 3): + se2 = (1.0/(n*(n-2)) * (n*Syy - Sy*Sy - B*B*(n*Sxx - Sx*Sx))) + sB2 = (n*se2) / (n*Sxx - Sx*Sx) + sa2 = sB2 * (1.0/n) * Sxx + + + self.slope = B + self.intercept = a + self.serror = math.sqrt(max(0, se2)) + self.serror_slope = math.sqrt(max(0, sB2)) + self.serror_intercept = math.sqrt(max(0, sa2)) + self.max_x = max_x + self.min_x = min_x + + def __repr__(self): + return "LinearRegression(%s, %s, %s, %s, %s)" % ( + str(self.slope), + str(self.intercept), + str(self.serror), + str(self.serror_slope), + str(self.serror_intercept), + ) + + def find_min_slope(self): + """Finds the minimal slope given one standard deviation.""" + slope = self.slope + intercept = self.intercept + error = self.serror + regr_start = self.min_x + regr_end = self.max_x + regr_width = regr_end - regr_start + + if slope < 0: + lower_left_y = slope*regr_start + intercept - error + upper_right_y = slope*regr_end + intercept + error + return min(0, (upper_right_y - lower_left_y) / regr_width) + + elif slope > 0: + upper_left_y = slope*regr_start + intercept + error + lower_right_y = slope*regr_end + intercept - error + return max(0, (lower_right_y - upper_left_y) / regr_width) + + return 0 + +def CreateRevisionLink(revision_number): + """Returns HTML displaying the given revision number and linking to + that revision's change page at code.google.com, e.g. + http://code.google.com/p/skia/source/detail?r=2056 + """ + return '<a href="http://code.google.com/p/skia/source/detail?r=%s">%s</a>'%( + revision_number, revision_number) diff --git a/bench/benchmain.cpp b/bench/benchmain.cpp index 34f8a1a..024ad0f 100644 --- a/bench/benchmain.cpp +++ b/bench/benchmain.cpp @@ -1,18 +1,30 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#include "BenchTimer.h" + +#include "GrContext.h" +#include "GrRenderTarget.h" + +#include "SkBenchmark.h" #include "SkCanvas.h" #include "SkColorPriv.h" +#include "SkGpuDevice.h" #include "SkGraphics.h" #include "SkImageEncoder.h" +#include "SkNativeGLContext.h" +#include "SkNullGLContext.h" #include "SkNWayCanvas.h" #include "SkPicture.h" #include "SkString.h" -#include "GrContext.h" -#include "SkGpuDevice.h" -#include "SkEGLContext.h" -#include "SkBenchmark.h" -#include "BenchTimer.h" - -#ifdef ANDROID +#ifdef SK_BUILD_FOR_ANDROID static void log_error(const char msg[]) { SkDebugf("%s", msg); } static void log_progress(const char msg[]) { SkDebugf("%s", msg); } #else @@ -161,8 +173,67 @@ enum Backend { kPDF_Backend, }; +class GLHelper { +public: + GLHelper() { + } + + bool init(SkGLContext* glCtx, int width, int height) { + GrContext* grCtx; + GrRenderTarget* rt; + if (glCtx->init(width, height)) { + GrPlatform3DContext ctx = + reinterpret_cast<GrPlatform3DContext>(glCtx->gl()); + grCtx = GrContext::Create(kOpenGL_Shaders_GrEngine, ctx); + if (NULL != grCtx) { + GrPlatformRenderTargetDesc desc; + desc.fConfig = kSkia8888_PM_GrPixelConfig; + desc.fWidth = width; + desc.fHeight = height; + desc.fStencilBits = 8; + desc.fRenderTargetHandle = glCtx->getFBOID(); + rt = grCtx->createPlatformRenderTarget(desc); + if (NULL == rt) { + grCtx->unref(); + return false; + } + } + } else { + return false; + } + glCtx->ref(); + fGLContext.reset(glCtx); + fGrContext.reset(grCtx); + fRenderTarget.reset(rt); + return true; + } + + bool isValid() { + return NULL != fGLContext.get(); + } + + SkGLContext* glContext() { + return fGLContext.get(); + } + + GrRenderTarget* renderTarget() { + return fRenderTarget.get(); + } + + GrContext* grContext() { + return fGrContext.get(); + } +private: + SkAutoTUnref<SkGLContext> fGLContext; + SkAutoTUnref<GrContext> fGrContext; + SkAutoTUnref<GrRenderTarget> fRenderTarget; +}; + +static GLHelper gRealGLHelper; +static GLHelper gNullGLHelper; + static SkDevice* make_device(SkBitmap::Config config, const SkIPoint& size, - Backend backend, GrContext* context) { + Backend backend, GLHelper* glHelper) { SkDevice* device = NULL; SkBitmap bitmap; bitmap.setConfig(config, size.fX, size.fY); @@ -171,11 +242,11 @@ static SkDevice* make_device(SkBitmap::Config config, const SkIPoint& size, case kRaster_Backend: bitmap.allocPixels(); erase(bitmap); - device = new SkDevice(NULL, bitmap, true); + device = new SkDevice(bitmap); break; case kGPU_Backend: - device = new SkGpuDevice(context, bitmap, SkGpuDevice::Current3DApiRenderTarget()); -// device->clear(0xFFFFFFFF); + device = new SkGpuDevice(glHelper->grContext(), + glHelper->renderTarget()); break; case kPDF_Backend: default: @@ -188,10 +259,12 @@ static const struct { SkBitmap::Config fConfig; const char* fName; Backend fBackend; + GLHelper* fGLHelper; } gConfigs[] = { - { SkBitmap::kARGB_8888_Config, "8888", kRaster_Backend }, - { SkBitmap::kRGB_565_Config, "565", kRaster_Backend }, - { SkBitmap::kARGB_8888_Config, "GPU", kGPU_Backend }, + { SkBitmap::kARGB_8888_Config, "8888", kRaster_Backend, NULL }, + { SkBitmap::kRGB_565_Config, "565", kRaster_Backend, NULL }, + { SkBitmap::kARGB_8888_Config, "GPU", kGPU_Backend, &gRealGLHelper }, + { SkBitmap::kARGB_8888_Config, "NULLGPU", kGPU_Backend, &gNullGLHelper }, }; static int findConfig(const char config[]) { @@ -203,6 +276,36 @@ static int findConfig(const char config[]) { return -1; } +static void determine_gpu_context_size(SkTDict<const char*>& defineDict, + int* contextWidth, + int* contextHeight) { + Iter iter(&defineDict); + SkBenchmark* bench; + while ((bench = iter.next()) != NULL) { + SkIPoint dim = bench->getSize(); + if (*contextWidth < dim.fX) { + *contextWidth = dim.fX; + } + if (*contextHeight < dim.fY) { + *contextHeight = dim.fY; + } + } +} + +static bool skip_name(const SkTDArray<const char*> array, const char name[]) { + if (0 == array.count()) { + // no names, so don't skip anything + return false; + } + for (int i = 0; i < array.count(); ++i) { + if (strstr(name, array[i])) { + // found the name, so don't skip + return false; + } + } + return true; +} + int main (int argc, char * const argv[]) { SkAutoGraphics ag; @@ -218,12 +321,13 @@ int main (int argc, char * const argv[]) { bool doScale = false; bool doRotate = false; bool doClip = false; - const char* matchStr = NULL; bool hasStrokeWidth = false; float strokeWidth; + SkTDArray<const char*> fMatches; SkString outDir; SkBitmap::Config outConfig = SkBitmap::kNo_Config; + GLHelper* glHelper = NULL; const char* configName = ""; Backend backend = kRaster_Backend; // for warning int configCount = SK_ARRAY_COUNT(gConfigs); @@ -312,7 +416,7 @@ int main (int argc, char * const argv[]) { } else if (strcmp(*argv, "-match") == 0) { argv++; if (argv < stop) { - matchStr = *argv; + *fMatches.append() = *argv; } else { log_error("missing arg for -match\n"); return -1; @@ -325,6 +429,7 @@ int main (int argc, char * const argv[]) { outConfig = gConfigs[index].fConfig; configName = gConfigs[index].fName; backend = gConfigs[index].fBackend; + glHelper = gConfigs[index].fGLHelper; configCount = 1; } else { SkString str; @@ -355,19 +460,63 @@ int main (int argc, char * const argv[]) { // report our current settings { SkString str; - str.printf("skia bench: alpha=0x%02X antialias=%d filter=%d\n", + str.printf("skia bench: alpha=0x%02X antialias=%d filter=%d", forceAlpha, forceAA, forceFilter); + str.appendf(" rotate=%d scale=%d clip=%d", + doRotate, doScale, doClip); + + const char * ditherName; + switch (forceDither) { + case SkTriState::kDefault: ditherName = "default"; break; + case SkTriState::kTrue: ditherName = "true"; break; + case SkTriState::kFalse: ditherName = "false"; break; + default: ditherName = "<invalid>"; break; + } + str.appendf(" dither=%s", ditherName); + + if (hasStrokeWidth) { + str.appendf(" strokeWidth=%f", strokeWidth); + } else { + str.append(" strokeWidth=none"); + } + +#if defined(SK_SCALAR_IS_FLOAT) + str.append(" scalar=float"); +#elif defined(SK_SCALAR_IS_FIXED) + str.append(" scalar=fixed"); +#endif + +#if defined(SK_BUILD_FOR_WIN32) + str.append(" system=WIN32"); +#elif defined(SK_BUILD_FOR_MAC) + str.append(" system=MAC"); +#elif defined(SK_BUILD_FOR_ANDROID) + str.append(" system=ANDROID"); +#elif defined(SK_BUILD_FOR_UNIX) + str.append(" system=UNIX"); +#else + str.append(" system=other"); +#endif + +#if defined(SK_DEBUG) + str.append(" DEBUG"); +#endif + str.append("\n"); log_progress(str); } - - GrContext* context = NULL; - SkEGLContext eglContext; - if (eglContext.init(1024, 1024)) { - context = GrContext::CreateGLShaderContext(); - } - - BenchTimer timer = BenchTimer(); - + + //Don't do GL when fixed. +#if !defined(SK_SCALAR_IS_FIXED) + int contextWidth = 1024; + int contextHeight = 1024; + determine_gpu_context_size(defineDict, &contextWidth, &contextHeight); + SkAutoTUnref<SkGLContext> realGLCtx(new SkNativeGLContext); + SkAutoTUnref<SkGLContext> nullGLCtx(new SkNullGLContext); + gRealGLHelper.init(realGLCtx.get(), contextWidth, contextHeight); + gNullGLHelper.init(nullGLCtx.get(), contextWidth, contextHeight); +#endif + BenchTimer timer = BenchTimer(gRealGLHelper.glContext()); + Iter iter(&defineDict); SkBenchmark* bench; while ((bench = iter.next()) != NULL) { @@ -385,7 +534,7 @@ int main (int argc, char * const argv[]) { } // only run benchmarks if their name contains matchStr - if (matchStr && strstr(bench->getName(), matchStr) == NULL) { + if (skip_name(fMatches, bench->getName())) { continue; } @@ -401,13 +550,15 @@ int main (int argc, char * const argv[]) { outConfig = gConfigs[configIndex].fConfig; configName = gConfigs[configIndex].fName; backend = gConfigs[configIndex].fBackend; + glHelper = gConfigs[configIndex].fGLHelper; } - - if (kGPU_Backend == backend && NULL == context) { + + if (kGPU_Backend == backend && + (NULL == glHelper || !glHelper->isValid())) { continue; } - SkDevice* device = make_device(outConfig, dim, backend, context); + SkDevice* device = make_device(outConfig, dim, backend, glHelper); SkCanvas canvas(device); device->unref(); @@ -420,15 +571,14 @@ int main (int argc, char * const argv[]) { if (doRotate) { performRotate(&canvas, dim.fX, dim.fY); } - - bool gpu = kGPU_Backend == backend && context; + //warm up caches if needed if (repeatDraw > 1) { SkAutoCanvasRestore acr(&canvas, true); bench->draw(&canvas); - if (gpu) { - context->flush(); - glFinish(); + if (glHelper) { + glHelper->grContext()->flush(); + SK_GL(*glHelper->glContext(), Finish()); } } @@ -436,8 +586,14 @@ int main (int argc, char * const argv[]) { for (int i = 0; i < repeatDraw; i++) { SkAutoCanvasRestore acr(&canvas, true); bench->draw(&canvas); + if (glHelper) { + glHelper->grContext()->flush(); + } } - timer.end(); + if (glHelper) { + SK_GL(*glHelper->glContext(), Finish()); + } + timer.end(); if (repeatDraw > 1) { SkString str; @@ -448,7 +604,7 @@ int main (int argc, char * const argv[]) { if (timerCpu) { str.appendf(" cmsecs = %6.2f", timer.fCpu / repeatDraw); } - if (timerGpu && gpu && timer.fGpu > 0) { + if (timerGpu && glHelper && timer.fGpu > 0) { str.appendf(" gmsecs = %6.2f", timer.fGpu / repeatDraw); } log_progress(str); @@ -460,6 +616,6 @@ int main (int argc, char * const argv[]) { } log_progress("\n"); } - + return 0; } |