diff --git a/example/demo.c b/example/demo.c index 145165c..63d8a86 100644 --- a/example/demo.c +++ b/example/demo.c @@ -849,102 +849,3 @@ void renderDemo(struct NVGcontext* vg, float mx, float my, float width, float he nvgRestore(vg); } -void initFPS(struct FPScounter* fps) -{ - memset(fps, 0, sizeof(struct FPScounter)); -} - -void updateFPS(struct FPScounter* fps, float frameTime) -{ - fps->head = (fps->head+1) % FPS_HISTORY_COUNT; - fps->values[fps->head] = frameTime; -} - -#define AVG_SIZE 20 - -void renderFPS(struct NVGcontext* vg, float x, float y, struct FPScounter* fps) -{ - renderFPSEx(vg,x,y,fps,RENDER_FPS,NULL); -} - -void renderFPSEx(struct NVGcontext* vg, float x, float y, struct FPScounter* fps, enum FPSRenderStyle style, const char* name) -{ - int i, head; - float avg, w, h; - char str[64]; - - avg = 0; - head = fps->head; - for (i = 0; i < AVG_SIZE; i++) { - avg += fps->values[head]; - head = (head+FPS_HISTORY_COUNT-1) % FPS_HISTORY_COUNT; - } - avg /= (float)AVG_SIZE; - - w = 200; - h = 30; - - nvgBeginPath(vg); - nvgRect(vg, x,y, w,h); - nvgFillColor(vg, nvgRGBA(0,0,0,128)); - nvgFill(vg); - - nvgBeginPath(vg); - nvgMoveTo(vg, x, y+h); - if( RENDER_FPS == style ) - { - for (i = 0; i < FPS_HISTORY_COUNT; i++) { - float v = 1.0f / (0.00001f + fps->values[(fps->head+i) % FPS_HISTORY_COUNT]); - if (v > 80.0f) v = 80.0f; - float vx = x + ((float)i/(FPS_HISTORY_COUNT-1)) * w; - float vy = y + h - ((v / 80.0f) * h); - nvgLineTo(vg, vx, vy); - } - } - else - { - for (i = 0; i < FPS_HISTORY_COUNT; i++) { - float v = fps->values[(fps->head+i) % FPS_HISTORY_COUNT] * 1000.0f; - if (v > 20.0f) v = 20.0f; - float vx = x + ((float)i/(FPS_HISTORY_COUNT-1)) * w; - float vy = y + h - ((v / 20.0f) * h); - nvgLineTo(vg, vx, vy); - } - } - nvgLineTo(vg, x+w, y+h); - nvgFillColor(vg, nvgRGBA(255,192,0,128)); - nvgFill(vg); - - nvgFontFace(vg, "sans"); - if( RENDER_FPS == style ) { - nvgFontSize(vg, 18.0f); - nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE); - nvgFillColor(vg, nvgRGBA(240,240,240,255)); - sprintf(str, "%.2f FPS", 1.0f / avg); - nvgText(vg, x+w-5,y+h/2, str, NULL); - - nvgFontSize(vg, 15.0f); - nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE); - nvgFillColor(vg, nvgRGBA(240,240,240,160)); - sprintf(str, "%.2f ms", avg * 1000.0f); - nvgText(vg, x+5,y+h/2, str, NULL); - } - else { - nvgFontSize(vg, 18.0f); - nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE); - nvgFillColor(vg, nvgRGBA(240,240,240,255)); - sprintf(str, "%.2f ms", avg * 1000.0f); - nvgText(vg, x+w-5,y+h/2, str, NULL); - - if( name ) - { - nvgFontSize(vg, 20.0f); - nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE); - nvgFillColor(vg, nvgRGBA(240,240,240,160)); - nvgText(vg, x+5,y+h/2, name, NULL); - } - } - - -} - diff --git a/example/demo.h b/example/demo.h index 0134e12..5162716 100644 --- a/example/demo.h +++ b/example/demo.h @@ -1,5 +1,5 @@ -#ifndef WIDGETS_H -#define WIDGETS_H +#ifndef DEMO_H +#define DEMO_H #include "nanovg.h" @@ -16,24 +16,8 @@ int loadDemoData(struct NVGcontext* vg, struct DemoData* data); void freeDemoData(struct NVGcontext* vg, struct DemoData* data); void renderDemo(struct NVGcontext* vg, float mx, float my, float width, float height, float t, int blowup, struct DemoData* data); -#define FPS_HISTORY_COUNT 100 -enum FPSRenderStyle { - RENDER_FPS, - RENDER_MS, -}; -struct FPScounter -{ - float values[FPS_HISTORY_COUNT]; - int head; -}; - -void initFPS(struct FPScounter* fps); -void updateFPS(struct FPScounter* fps, float frameTime); -void renderFPSEx(struct NVGcontext* vg, float x, float y, struct FPScounter* fps, enum FPSRenderStyle style, const char* name ); -void renderFPS(struct NVGcontext* vg, float x, float y, struct FPScounter* fps); - #ifdef __cplusplus } #endif -#endif // WIDGETS_H \ No newline at end of file +#endif // DEMO_H \ No newline at end of file diff --git a/example/example_gl2.c b/example/example_gl2.c index 26f7cb4..35dbc8b 100644 --- a/example/example_gl2.c +++ b/example/example_gl2.c @@ -25,6 +25,7 @@ #define NANOVG_GL2_IMPLEMENTATION #include "nanovg_gl2.h" #include "demo.h" +#include "perf.h" void errorcb(int error, const char* desc) @@ -57,7 +58,7 @@ int main() return -1; } - initFPS(&fps); + initFPS(&fps, FPS_RENDER_FPS, "Frame Time"); glfwSetErrorCallback(errorcb); diff --git a/example/example_gl3.c b/example/example_gl3.c index 129eb00..c7d60f9 100644 --- a/example/example_gl3.c +++ b/example/example_gl3.c @@ -30,14 +30,7 @@ //#include "nanovg_gl3.h" #include "nanovg_gl3buf.h" #include "demo.h" - - -// timer query support -#ifndef GL_ARB_timer_query -#define GL_TIME_ELAPSED 0x88BF -typedef void (APIENTRY *pfnGLGETQUERYOBJECTUI64V)(GLuint id, GLenum pname, GLuint64* params); -pfnGLGETQUERYOBJECTUI64V glGetQueryObjectui64v = 0; -#endif +#include "perf.h" void errorcb(int error, const char* desc) { @@ -56,35 +49,30 @@ static void key(GLFWwindow* window, int key, int scancode, int action, int mods) blowup = !blowup; } -enum numqueries { - NUM_QUERIES = 5 -}; - int main() { GLFWwindow* window; struct DemoData data; struct NVGcontext* vg = NULL; - struct FPScounter fps, cpuTimes, gpuTimes; + struct GPUtimer gpuTimer; + struct FPScounter fps, cpuGraph, gpuGraph; double prevt = 0, cpuTime = 0; - int timerquery = GL_FALSE, currquery = 0, retquery = 0; - GLuint timerqueryid[NUM_QUERIES]; if (!glfwInit()) { printf("Failed to init GLFW."); return -1; } - initFPS(&fps); - initFPS(&cpuTimes); - initFPS(&gpuTimes); + initFPS(&fps, FPS_RENDER_FPS, "Frame Time"); + initFPS(&cpuGraph, FPS_RENDER_MS, "CPU Time"); + initFPS(&gpuGraph, FPS_RENDER_MS, "GPU Time"); glfwSetErrorCallback(errorcb); #ifndef _WIN32 // don't require this on win32, and works with more cards glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); #endif #ifdef DEMO_MSAA @@ -92,7 +80,7 @@ int main() #endif window = glfwCreateWindow(1000, 600, "NanoVG", NULL, NULL); // window = glfwCreateWindow(1000, 600, "NanoVG", glfwGetPrimaryMonitor(), NULL); - if (!window) { + if (!window) { glfwTerminate(); return -1; } @@ -102,7 +90,7 @@ int main() glfwMakeContextCurrent(window); #ifdef NANOVG_GLEW glewExperimental = GL_TRUE; - if(glewInit() != GLEW_OK) { + if(glewInit() != GLEW_OK) { printf("Could not init glew.\n"); return -1; } @@ -123,17 +111,7 @@ int main() glfwSwapInterval(0); - timerquery = glfwExtensionSupported("GL_ARB_timer_query"); - if( timerquery ) { -#ifndef GL_ARB_timer_query - glGetQueryObjectui64v = (pfnGLGETQUERYOBJECTUI64V)glfwGetProcAddress("glGetQueryObjectui64v"); - if( !glGetQueryObjectui64v ) - { - timerquery = GL_FALSE; - } -#endif - glGenQueries(NUM_QUERIES, timerqueryid); - } + initGPUTimer(&gpuTimer); glfwSetTime(0); prevt = glfwGetTime(); @@ -144,16 +122,14 @@ int main() int winWidth, winHeight; int fbWidth, fbHeight; float pxRatio; + float gpuTimes[3]; + int i, n; t = glfwGetTime(); dt = t - prevt; prevt = t; - updateFPS(&fps, dt); - updateFPS(&cpuTimes, cpuTime); - if( timerquery ) { - glBeginQuery(GL_TIME_ELAPSED, timerqueryid[currquery % NUM_QUERIES] ); - currquery = ++currquery; - } + + startGPUTimer(&gpuTimer); glfwGetCursorPos(window, &mx, &my); glfwGetWindowSize(window, &winWidth, &winHeight); @@ -174,34 +150,27 @@ int main() nvgBeginFrame(vg, winWidth, winHeight, pxRatio); renderDemo(vg, mx,my, winWidth,winHeight, t, blowup, &data); + renderFPS(vg, 5,5, &fps); - renderFPSEx(vg, 310,5, &cpuTimes, RENDER_MS, "CPU Time"); - renderFPSEx(vg, 615,5, &gpuTimes, RENDER_MS, "GPU Time"); + renderFPS(vg, 5+200+5,5, &cpuGraph); + if (gpuTimer.supported) + renderFPS(vg, 5+200+5+200+5,5, &gpuGraph); nvgEndFrame(vg); glEnable(GL_DEPTH_TEST); - // Measure the CPU time taken excluding swap buffers (as the swap may wait for GPU) - cpuTime = glfwGetTime() - t; - - if( timerquery ) { - GLint available = 1; - glEndQuery(GL_TIME_ELAPSED); - while( available && retquery <= currquery ) { - // check for results if there are any - glGetQueryObjectiv(timerqueryid[retquery % NUM_QUERIES], GL_QUERY_RESULT_AVAILABLE, &available); - if( available ) { - GLuint64 timeElapsed = 0; - double gpuTime; - glGetQueryObjectui64v(timerqueryid[retquery % NUM_QUERIES], GL_QUERY_RESULT, &timeElapsed); - retquery = ++retquery ; - gpuTime = (double)timeElapsed * 1e-9; - updateFPS(&gpuTimes, (float)gpuTime); - } - } - - } + // Measure the CPU time taken excluding swap buffers (as the swap may wait for GPU) + cpuTime = glfwGetTime() - t; + + updateFPS(&fps, dt); + updateFPS(&cpuGraph, cpuTime); + + // We may get multiple results. + n = stopGPUTimer(&gpuTimer, gpuTimes, 3); + for (i = 0; i < n; i++) + updateFPS(&gpuGraph, gpuTimes[i]); + glfwSwapBuffers(window); glfwPollEvents(); } diff --git a/example/example_gles2.c b/example/example_gles2.c index d4664cc..0710b06 100644 --- a/example/example_gles2.c +++ b/example/example_gles2.c @@ -23,6 +23,7 @@ #define NANOVG_GLES2_IMPLEMENTATION #include "nanovg_gl2.h" #include "demo.h" +#include "perf.h" void errorcb(int error, const char* desc) @@ -55,7 +56,7 @@ int main() return -1; } - initFPS(&fps); + initFPS(&fps, FPS_RENDER_FPS, "Frame Time"); glfwSetErrorCallback(errorcb); diff --git a/example/example_gles3.c b/example/example_gles3.c index a717252..34c57d7 100644 --- a/example/example_gles3.c +++ b/example/example_gles3.c @@ -23,6 +23,7 @@ #define NANOVG_GLES3_IMPLEMENTATION #include "nanovg_gl3.h" #include "demo.h" +#include "perf.h" void errorcb(int error, const char* desc) @@ -55,7 +56,7 @@ int main() return -1; } - initFPS(&fps); + initFPS(&fps, FPS_RENDER_FPS, "Frame Time"); glfwSetErrorCallback(errorcb); diff --git a/example/perf.c b/example/perf.c new file mode 100644 index 0000000..7d108e8 --- /dev/null +++ b/example/perf.c @@ -0,0 +1,168 @@ +#include "perf.h" +#include +#include +#include +#ifdef NANOVG_GLEW +# include +#endif +#include +#include "nanovg.h" + +#ifdef _MSC_VER +#define snprintf _snprintf +#else +#include +#endif + +// timer query support +#ifndef GL_ARB_timer_query +#define GL_TIME_ELAPSED 0x88BF +typedef void (APIENTRY *pfnGLGETQUERYOBJECTUI64V)(GLuint id, GLenum pname, GLuint64* params); +pfnGLGETQUERYOBJECTUI64V glGetQueryObjectui64v = 0; +#endif + +void initGPUTimer(struct GPUtimer* timer) +{ + memset(timer, 0, sizeof(*timer)); + + timer->supported = glfwExtensionSupported("GL_ARB_timer_query"); + if (timer->supported) { +#ifndef GL_ARB_timer_query + glGetQueryObjectui64v = (pfnGLGETQUERYOBJECTUI64V)glfwGetProcAddress("glGetQueryObjectui64v"); + if (!glGetQueryObjectui64v) { + timer->supported = GL_FALSE; + return; + } +#endif + glGenQueries(GPU_QUERY_COUNT, timer->queries); + } +} + +void startGPUTimer(struct GPUtimer* timer) +{ + if (!timer->supported) + return; + glBeginQuery(GL_TIME_ELAPSED, timer->queries[timer->cur % GPU_QUERY_COUNT] ); + timer->cur++; +} + +int stopGPUTimer(struct GPUtimer* timer, float* times, int maxTimes) +{ + GLint available = 1; + int n = 0; + if (!timer->supported) + return 0; + + glEndQuery(GL_TIME_ELAPSED); + while (available && timer->ret <= timer->cur) { + // check for results if there are any + glGetQueryObjectiv(timer->queries[timer->ret % GPU_QUERY_COUNT], GL_QUERY_RESULT_AVAILABLE, &available); + if (available) { + GLuint64 timeElapsed = 0; + glGetQueryObjectui64v(timer->queries[timer->ret % GPU_QUERY_COUNT], GL_QUERY_RESULT, &timeElapsed); + timer->ret++; + if (n < maxTimes) { + times[n] = (float)((double)timeElapsed * 1e-9); + n++; + } + } + } + return n; +} + + +void initFPS(struct FPScounter* fps, int style, const char* name) +{ + memset(fps, 0, sizeof(struct FPScounter)); + fps->style = style; + strncpy(fps->name, name, sizeof(fps->name)); + fps->name[sizeof(fps->name)-1] = '\0'; +} + +void updateFPS(struct FPScounter* fps, float frameTime) +{ + fps->head = (fps->head+1) % FPS_HISTORY_COUNT; + fps->values[fps->head] = frameTime; +} + +#define AVG_SIZE 20 + +static float getAvg(struct FPScounter* fps) +{ + int i, head = fps->head; + float avg = 0; + for (i = 0; i < AVG_SIZE; i++) { + avg += fps->values[head]; + head = (head+FPS_HISTORY_COUNT-1) % FPS_HISTORY_COUNT; + } + return avg / (float)AVG_SIZE; +} + +void renderFPS(struct NVGcontext* vg, float x, float y, struct FPScounter* fps) +{ + int i; + float avg, w, h; + char str[64]; + + avg = getAvg(fps); + + w = 200; + h = 35; + + nvgBeginPath(vg); + nvgRect(vg, x,y, w,h); + nvgFillColor(vg, nvgRGBA(0,0,0,128)); + nvgFill(vg); + + nvgBeginPath(vg); + nvgMoveTo(vg, x, y+h); + if (fps->style == FPS_RENDER_FPS) { + for (i = 0; i < FPS_HISTORY_COUNT; i++) { + float v = 1.0f / (0.00001f + fps->values[(fps->head+i) % FPS_HISTORY_COUNT]); + if (v > 80.0f) v = 80.0f; + float vx = x + ((float)i/(FPS_HISTORY_COUNT-1)) * w; + float vy = y + h - ((v / 80.0f) * h); + nvgLineTo(vg, vx, vy); + } + } else { + for (i = 0; i < FPS_HISTORY_COUNT; i++) { + float v = fps->values[(fps->head+i) % FPS_HISTORY_COUNT] * 1000.0f; + if (v > 20.0f) v = 20.0f; + float vx = x + ((float)i/(FPS_HISTORY_COUNT-1)) * w; + float vy = y + h - ((v / 20.0f) * h); + nvgLineTo(vg, vx, vy); + } + } + nvgLineTo(vg, x+w, y+h); + nvgFillColor(vg, nvgRGBA(255,192,0,128)); + nvgFill(vg); + + nvgFontFace(vg, "sans"); + + if (fps->name[0] != '\0') { + nvgFontSize(vg, 14.0f); + nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_TOP); + nvgFillColor(vg, nvgRGBA(240,240,240,192)); + nvgText(vg, x+3,y+1, fps->name, NULL); + } + + if (fps->style == FPS_RENDER_FPS) { + nvgFontSize(vg, 18.0f); + nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_TOP); + nvgFillColor(vg, nvgRGBA(240,240,240,255)); + sprintf(str, "%.2f FPS", 1.0f / avg); + nvgText(vg, x+w-3,y+1, str, NULL); + + nvgFontSize(vg, 15.0f); + nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_BOTTOM); + nvgFillColor(vg, nvgRGBA(240,240,240,160)); + sprintf(str, "%.2f ms", avg * 1000.0f); + nvgText(vg, x+w-3,y+h-1, str, NULL); + } else { + nvgFontSize(vg, 18.0f); + nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_TOP); + nvgFillColor(vg, nvgRGBA(240,240,240,255)); + sprintf(str, "%.2f ms", avg * 1000.0f); + nvgText(vg, x+w-3,y+1, str, NULL); + } +} diff --git a/example/perf.h b/example/perf.h new file mode 100644 index 0000000..358ccc7 --- /dev/null +++ b/example/perf.h @@ -0,0 +1,42 @@ +#ifndef PERF_H +#define PERF_H + +#include "nanovg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum FPSrenderStyle { + FPS_RENDER_FPS, + FPS_RENDER_MS, +}; + +#define FPS_HISTORY_COUNT 100 +struct FPScounter { + int style; + char name[32]; + float values[FPS_HISTORY_COUNT]; + int head; +}; + +void initFPS(struct FPScounter* fps, int style, const char* name); +void updateFPS(struct FPScounter* fps, float frameTime); +void renderFPS(struct NVGcontext* vg, float x, float y, struct FPScounter* fps); + +#define GPU_QUERY_COUNT 5 +struct GPUtimer { + int supported; + int cur, ret; + unsigned int queries[GPU_QUERY_COUNT]; +}; + +void initGPUTimer(struct GPUtimer* timer); +void startGPUTimer(struct GPUtimer* timer); +int stopGPUTimer(struct GPUtimer* timer, float* times, int maxTimes); + +#ifdef __cplusplus +} +#endif + +#endif // PERF_H \ No newline at end of file diff --git a/premake4.lua b/premake4.lua index 9960fdc..f5dfd98 100644 --- a/premake4.lua +++ b/premake4.lua @@ -16,7 +16,7 @@ solution "nanovg" project "example_gl2" kind "ConsoleApp" language "C" - files { "example/example_gl2.c", "example/demo.c" } + files { "example/example_gl2.c", "example/demo.c", "example/perf.c" } includedirs { "src", "example" } targetdir("build") links { "nanovg" } @@ -43,7 +43,7 @@ solution "nanovg" project "example_gl3" kind "ConsoleApp" language "C" - files { "example/example_gl3.c", "example/demo.c" } + files { "example/example_gl3.c", "example/demo.c", "example/perf.c" } includedirs { "src", "example" } targetdir("build") links { "nanovg" } @@ -71,7 +71,7 @@ solution "nanovg" kind "ConsoleApp" language "C" defines { "DEMO_MSAA" } - files { "example/example_gl2.c", "example/demo.c" } + files { "example/example_gl2.c", "example/demo.c", "example/perf.c" } includedirs { "src", "example" } targetdir("build") links { "nanovg" } @@ -99,7 +99,7 @@ solution "nanovg" kind "ConsoleApp" language "C" defines { "DEMO_MSAA" } - files { "example/example_gl3.c", "example/demo.c" } + files { "example/example_gl3.c", "example/demo.c", "example/perf.c" } includedirs { "src", "example" } targetdir("build") links { "nanovg" } @@ -126,7 +126,7 @@ solution "nanovg" project "example_gles2" kind "ConsoleApp" language "C" - files { "example/example_gles2.c", "example/demo.c" } + files { "example/example_gles2.c", "example/demo.c", "example/perf.c" } includedirs { "src", "example" } targetdir("build") links { "nanovg" } @@ -152,7 +152,7 @@ solution "nanovg" project "example_gles3" kind "ConsoleApp" language "C" - files { "example/example_gles3.c", "example/demo.c" } + files { "example/example_gles3.c", "example/demo.c", "example/perf.c" } includedirs { "src", "example" } targetdir("build") links { "nanovg" }