]> git.cworth.org Git - fips/commitdiff
Wrap all OpenGL drawing calls, measure GPU time of each, and account to shader.
authorCarl Worth <cworth@cworth.org>
Thu, 25 Apr 2013 05:57:50 +0000 (22:57 -0700)
committerCarl Worth <cworth@cworth.org>
Thu, 25 Apr 2013 05:57:50 +0000 (22:57 -0700)
Thanks to José Fonseca, there's a nice list of OpenGL functions identified
as "drawing operations" in the apitrace source. (It's mostly glDraw* but
there are a few oddball as well.) With that list we implement a wrapper
for each draw call.

And in each wrapper we fire of a glBeginQuery/glEndQuery measurement around
the call to measure how much GPU time is consumed by the call. At the end
of each frame, we capture all available query results and accumulate those
into per-shader-program counters based on the currently active program,
(which we track by wrapping glUseProgram and glUseProgramObjectARB).

Finally, every 60 frames, we print out a simple report showing the
accumulated time for each shader program.

The report could very easily become more sophisticated. Here are some
obvious ideas:

  1. Sort the report so that the most active shaders are reported first

  2. Come up with some real units for the report values rather than mega-ticks

  3. Report relative execution time as percentages

  4. Clear the screen for each report, (with ncurses)

  5. Dump the source for the shaders themselves to a file for easy inspection

Without any of the above, things are fairly raw for now, but are
perhaps still useful.

Makefile.local
glwrap.c [new file with mode: 0644]
glwrap.h [new file with mode: 0644]
glxwrap.c
libfips.sym

index 6ee50ab1148e458af9ec731875d5e8613e093d23..c82590c14b478ad582c57927b3e9be765b003d04 100644 (file)
@@ -75,12 +75,13 @@ fips: $(fips_modules)
        $(call quiet,$(FINAL_FIPS_LINKER) $(CFLAGS)) $(FINAL_CFLAGS) $^ $(FINAL_FIPS_LDFLAGS) -o $@
 
 # GL-wrapper library, libfips
-LIBRARY_LINK_FLAGS = -shared -Wl,--version-script=libfips.sym,--no-undefined
+LIBRARY_LINK_FLAGS = -shared -Wl,--version-script=libfips.sym
 
 extra_cflags += -I$(srcdir) -fPIC
 
 libfips_srcs = \
        dlwrap.c \
+       glwrap.c \
        glxwrap.c
 
 libfips_32_modules = $(libfips_srcs:.c=-32.o)
diff --git a/glwrap.c b/glwrap.c
new file mode 100644 (file)
index 0000000..3a789fc
--- /dev/null
+++ b/glwrap.c
@@ -0,0 +1,521 @@
+/* Copyright © 2013, Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "fips.h"
+
+#include "glwrap.h"
+
+#define GL_GLEXT_PROTOTYPES
+#include <GL/gl.h>
+
+#include <sys/time.h>
+
+#include "dlwrap.h"
+
+typedef struct counter
+{
+       unsigned id;
+       unsigned program;
+       struct counter *next;
+} counter_t;
+
+typedef struct program_metrics
+{
+       unsigned id;
+       double ticks;
+} program_metrics_t;
+
+typedef struct context
+{
+       unsigned int program;
+
+       counter_t *counter_head;
+       counter_t *counter_tail;
+
+       unsigned num_program_metrics;
+       program_metrics_t *program_metrics;
+} context_t;
+
+/* FIXME: Need a map from integers to context objects and track the
+ * current context with glXMakeContextCurrent, eglMakeCurrent, etc. */
+
+context_t current_context;
+
+static unsigned
+add_counter (void)
+{
+       counter_t *counter;
+
+       counter = malloc (sizeof(counter_t));
+       if (counter == NULL) {
+               fprintf (stderr, "Out of memory\n");
+               exit (1);
+       }
+
+       glGenQueries (1, &counter->id);
+
+       counter->program = current_context.program;
+       counter->next = NULL;
+
+       if (current_context.counter_tail) {
+               current_context.counter_tail->next = counter;
+               current_context.counter_tail = counter;
+       } else {
+               current_context.counter_tail = counter;
+               current_context.counter_head = counter;
+       }
+
+       return counter->id;
+}
+
+static void *
+lookup (const char *name)
+{
+       const char *libgl_filename = "libGL.so.1";
+       static void *libgl_handle = NULL;
+
+       if (! libgl_handle) {
+               libgl_handle = dlwrap_real_dlopen (libgl_filename, RTLD_NOW | RTLD_DEEPBIND);
+               if (! libgl_handle) {
+                       fprintf (stderr, "Error: Failed to dlopen %s\n",
+                                libgl_filename);
+                       exit (1);
+               }
+       }
+
+       return dlwrap_real_dlsym (libgl_handle, name);
+}
+
+/* Defer to the underlying, ''real'' function to do the real work. */
+#define DEFER(function,...) do {                               \
+       static typeof(&function) real_ ## function;             \
+       if (! real_ ## function)                                \
+               real_ ## function = lookup (#function);         \
+       real_ ## function(__VA_ARGS__);                         \
+} while (0);
+
+/* Execute a glBegineQuery/glEndQuery pair around an OpenGL call. */
+#define TIMED_DEFER(function,...) do {                 \
+       unsigned counter;                               \
+       counter = add_counter ();                       \
+       glBeginQuery (GL_TIME_ELAPSED, counter);        \
+       DEFER(function, __VA_ARGS__);                   \
+       glEndQuery (GL_TIME_ELAPSED);                   \
+} while (0);
+
+/* Thanks to apitrace source code for the list of OpenGL draw calls. */
+void
+glDrawArrays (GLenum mode, GLint first, GLsizei count)
+{
+       TIMED_DEFER (glDrawArrays, mode, first, count);
+}
+
+void
+glDrawArraysEXT (GLenum mode, GLint first, GLsizei count)
+{
+       TIMED_DEFER (glDrawArraysEXT, mode, first, count);
+}
+
+void
+glDrawArraysIndirect (GLenum mode, const GLvoid *indirect)
+{
+       TIMED_DEFER (glDrawArraysIndirect, mode, indirect);
+}
+
+void
+glDrawArraysInstanced (GLenum mode, GLint first, GLsizei count,
+                      GLsizei primcount)
+{
+       TIMED_DEFER (glDrawArraysInstanced, mode, first, count, primcount);
+}
+
+void
+glDrawArraysInstancedARB (GLenum mode, GLint first, GLsizei count,
+                         GLsizei primcount)
+{
+       TIMED_DEFER (glDrawArraysInstancedARB, mode, first, count, primcount);
+}
+
+void
+glDrawArraysInstancedEXT (GLenum mode, GLint start, GLsizei count,
+                         GLsizei primcount)
+{
+       TIMED_DEFER (glDrawArraysInstancedEXT, mode, start, count, primcount);
+}
+
+void
+glDrawArraysInstancedBaseInstance (GLenum mode, GLint first, GLsizei count,
+                                  GLsizei primcount, GLuint baseinstance)
+{
+       TIMED_DEFER (glDrawArraysInstancedBaseInstance, mode,
+                    first, count, primcount, baseinstance);
+}
+
+void
+glDrawMeshArraysSUN (GLenum mode, GLint first, GLsizei count, GLsizei width)
+{
+       TIMED_DEFER (glDrawMeshArraysSUN, mode, first, count, width);
+}
+
+void
+glMultiDrawArrays (GLenum mode, const GLint *first,
+                  const GLsizei *count, GLsizei primcount)
+{
+       TIMED_DEFER (glMultiDrawArrays, mode, first, count, primcount);
+}
+
+void
+glMultiDrawArraysEXT (GLenum mode, const GLint *first,
+                     const GLsizei *count, GLsizei primcount)
+{
+       TIMED_DEFER (glMultiDrawArraysEXT, mode, first, count, primcount);
+}
+
+void
+glMultiModeDrawArraysIBM (const GLenum *mode, const GLint *first,
+                         const GLsizei *count, GLsizei primcount,
+                         GLint modestride)
+{
+       TIMED_DEFER (glMultiModeDrawArraysIBM, mode,
+                    first, count, primcount, modestride);
+}
+
+/* FIXME?
+void
+glMultiDrawArraysIndirect (...)
+{
+       TIMED_DEFER (glMultiDrawArraysIndirect, ...);
+}
+*/
+
+void
+glMultiDrawArraysIndirectAMD (GLenum mode, const GLvoid *indirect,
+                             GLsizei primcount, GLsizei stride)
+{
+       TIMED_DEFER (glMultiDrawArraysIndirectAMD, mode,
+                    indirect, primcount, stride);
+}
+
+void
+glDrawElements (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices)
+{
+       TIMED_DEFER (glDrawElements, mode, count, type, indices);
+}
+
+void
+glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum type,
+                         const GLvoid *indices, GLint basevertex)
+{
+       TIMED_DEFER (glDrawElementsBaseVertex, mode, count,
+                    type, indices, basevertex);
+}
+
+void
+glDrawElementsIndirect (GLenum mode, GLenum type, const GLvoid *indirect)
+{
+       TIMED_DEFER (glDrawElementsIndirect, mode, type, indirect);
+}
+
+void
+glDrawElementsInstanced (GLenum mode, GLsizei count, GLenum type,
+                        const GLvoid *indices, GLsizei primcount)
+{
+       TIMED_DEFER (glDrawElementsInstanced, mode, count,
+                    type, indices, primcount);
+}
+
+void
+glDrawElementsInstancedARB (GLenum mode, GLsizei count, GLenum type,
+                           const GLvoid *indices, GLsizei primcount)
+{
+       TIMED_DEFER (glDrawElementsInstancedARB, mode, count,
+                    type, indices, primcount);
+}
+
+void
+glDrawElementsInstancedEXT (GLenum mode, GLsizei count, GLenum type,
+                           const GLvoid *indices, GLsizei primcount)
+{
+       TIMED_DEFER (glDrawElementsInstancedEXT, mode, count,
+                    type, indices, primcount);
+}
+
+void
+glDrawElementsInstancedBaseVertex (GLenum mode, GLsizei count, GLenum type,
+                                  const GLvoid *indices, GLsizei primcount,
+                                  GLint basevertex)
+{
+       TIMED_DEFER (glDrawElementsInstancedBaseVertex, mode, count,
+                    type, indices, primcount, basevertex);
+}
+
+void
+glDrawElementsInstancedBaseInstance (GLenum mode, GLsizei count, GLenum type,
+                                    const void *indices, GLsizei primcount,
+                                    GLuint baseinstance)
+{
+       TIMED_DEFER (glDrawElementsInstancedBaseInstance, mode, count, type,
+                    indices, primcount, baseinstance);
+}
+
+void
+glDrawElementsInstancedBaseVertexBaseInstance (GLenum mode, GLsizei count,
+                     GLenum type, const void *indices, GLsizei primcount,
+                     GLint basevertex, GLuint baseinstance)
+{
+       TIMED_DEFER (glDrawElementsInstancedBaseVertexBaseInstance, mode,
+                    count, type, indices, primcount, basevertex, baseinstance);
+}
+
+void
+glDrawRangeElements (GLenum mode, GLuint start, GLuint end, GLsizei count,
+                    GLenum type, const GLvoid *indices)
+{
+       TIMED_DEFER (glDrawRangeElements, mode, start, end,
+                    count, type, indices);
+}
+
+void
+glDrawRangeElementsEXT (GLenum mode, GLuint start, GLuint end, GLsizei count,
+                       GLenum type, const GLvoid *indices)
+{
+       TIMED_DEFER (glDrawRangeElementsEXT, mode, start, end,
+                    count, type, indices);
+}
+
+void
+glDrawRangeElementsBaseVertex (GLenum mode, GLuint start, GLuint end,
+                              GLsizei count, GLenum type,
+                              const GLvoid *indices, GLint basevertex)
+{
+       TIMED_DEFER (glDrawRangeElementsBaseVertex, mode, start, end,
+                    count, type, indices, basevertex);
+}
+
+void
+glMultiDrawElements (GLenum mode, const GLsizei *count, GLenum type,
+                    const GLvoid* *indices, GLsizei primcount)
+{
+       TIMED_DEFER (glMultiDrawElements, mode, count, type,
+                    indices, primcount);
+}
+
+void
+glMultiDrawElementsBaseVertex (GLenum mode, const GLsizei *count,
+                              GLenum type, const GLvoid* *indices,
+                              GLsizei primcount, const GLint *basevertex)
+{
+       TIMED_DEFER (glMultiDrawElementsBaseVertex, mode, count,
+                    type, indices, primcount, basevertex);
+}
+
+void
+glMultiDrawElementsEXT (GLenum mode, const GLsizei *count, GLenum type,
+                       const GLvoid* *indices, GLsizei primcount)
+{
+       TIMED_DEFER (glMultiDrawElementsEXT, mode, count,
+                    type, indices, primcount);
+}
+
+void
+glMultiModeDrawElementsIBM (const GLenum *mode, const GLsizei *count,
+                           GLenum type, const GLvoid* const *indices,
+                           GLsizei primcount, GLint modestride)
+{
+       TIMED_DEFER (glMultiModeDrawElementsIBM, mode, count,
+                    type, indices, primcount, modestride);
+}
+
+/* FIXME?
+void
+glMultiDrawElementsIndirect (...)
+{
+       TIMED_DEFER (glMultiDrawElementsIndirect, ...);
+}
+*/
+
+void
+glMultiDrawElementsIndirectAMD (GLenum mode, GLenum type,
+                               const GLvoid *indirect,
+                               GLsizei primcount, GLsizei stride)
+{
+       TIMED_DEFER (glMultiDrawElementsIndirectAMD, mode, type,
+                    indirect, primcount, stride);
+}
+
+void
+glCallList (GLuint list)
+{
+       TIMED_DEFER (glCallList, list);
+}
+
+void
+glCallLists (GLsizei n, GLenum type, const GLvoid *lists)
+{
+       TIMED_DEFER (glCallLists, n, type, lists);
+}
+
+void
+glClear (GLbitfield mask)
+{
+       TIMED_DEFER (glClear, mask);
+}
+
+void
+glEnd (void)
+{
+       TIMED_DEFER (glEnd,);
+}
+
+void
+glDrawPixels (GLsizei width, GLsizei height, GLenum format,
+             GLenum type, const GLvoid *pixels)
+{
+       TIMED_DEFER (glDrawPixels, width, height, format, type, pixels);
+}
+
+void
+glBlitFramebuffer (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
+                  GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
+                  GLbitfield mask, GLenum filter)
+{
+       TIMED_DEFER (glBlitFramebuffer,
+                    srcX0, srcY0, srcX1, srcY1,
+                    dstX0, dstY0, dstX1, dstY1,
+                    mask, filter);
+}
+
+void
+glBlitFramebufferEXT (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
+                     GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
+                     GLbitfield mask, GLenum filter)
+{
+       TIMED_DEFER (glBlitFramebufferEXT,
+                    srcX0, srcY0, srcX1, srcY1,
+                    dstX0, dstY0, dstX1, dstY1,
+                    mask, filter);
+}
+
+void
+glUseProgram (GLuint program)
+{
+       current_context.program = program;
+
+       DEFER(glUseProgram, program);
+}
+
+void
+glUseProgramObjectARB (GLhandleARB programObj)
+{
+       current_context.program = programObj;
+
+       DEFER(glUseProgramObjectARB, programObj);
+}
+
+static void
+accumulate_program_ticks (unsigned program_id, unsigned ticks)
+{
+       context_t *ctx = &current_context;
+       unsigned i;
+
+       if (program_id >= ctx->num_program_metrics) {
+               ctx->program_metrics = realloc (ctx->program_metrics,
+                                               (program_id + 1) * sizeof (program_metrics_t));
+               for (i = ctx->num_program_metrics; i < program_id + 1; i++) {
+                       ctx->program_metrics[i].id = i;
+                       ctx->program_metrics[i].ticks = 0.0;
+               }
+
+               ctx->num_program_metrics = program_id + 1;
+       }
+
+       ctx->program_metrics[program_id].ticks += ticks;
+}
+
+/* FIXME: Should sort the metrics, print out percentages, etc. */
+static void
+print_program_metrics (void)
+{
+       context_t *ctx = &current_context;
+       unsigned i;
+
+       for (i = 0; i < ctx->num_program_metrics; i++) {
+               if (ctx->program_metrics[i].ticks == 0.0)
+                       continue;
+               printf ("Program %d:\t%7.2f mega-ticks\n",
+                       i, ctx->program_metrics[i].ticks / 1e6);
+       }
+}
+
+void
+glwrap_end_frame (void)
+{
+       static int initialized = 0;
+       static int frames;
+       static struct timeval tv_start, tv_now;
+
+       if (! initialized) {
+               frames = 0;
+               gettimeofday (&tv_start, NULL);
+               initialized = 1;
+       }
+
+
+       frames++;
+
+       if (frames % 60 == 0) {
+               double fps;
+               gettimeofday (&tv_now, NULL);
+
+               fps = (double) frames / (tv_now.tv_sec - tv_start.tv_sec +
+                                        (tv_now.tv_usec - tv_start.tv_usec) / 1.0e6);
+
+               printf("FPS: %.3f\n", fps);
+
+               print_program_metrics ();
+       }
+
+       /* Consume all counters that are ready. */
+       counter_t *counter = current_context.counter_head;
+
+       while (counter) {
+               GLint available;
+               GLuint elapsed;
+
+               glGetQueryObjectiv (counter->id, GL_QUERY_RESULT_AVAILABLE,
+                                   &available);
+               if (! available)
+                       break;
+
+               glGetQueryObjectuiv (counter->id, GL_QUERY_RESULT, &elapsed);
+
+               accumulate_program_ticks (counter->program, elapsed);
+
+               current_context.counter_head = counter->next;
+               if (current_context.counter_head == NULL)
+                       current_context.counter_tail = NULL;
+
+               glDeleteQueries (1, &counter->id);
+
+               free (counter);
+               counter = current_context.counter_head;
+       }
+}
diff --git a/glwrap.h b/glwrap.h
new file mode 100644 (file)
index 0000000..81e1e34
--- /dev/null
+++ b/glwrap.h
@@ -0,0 +1,31 @@
+/* Copyright © 2013, Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GLWRAP_H
+#define GLWRAP_H
+
+/* Should be called at the end of ever function wrapper for an OpenGL
+ * function that ends a frame, (glXSwapBuffers and similar).
+ */
+void
+glwrap_end_frame (void);
+
+#endif
index 4d16e4d6a1b86c093bbad6f4d8b1f35ff337e0f8..78627d42005a223eca6128dd12177d06e8278257 100644 (file)
--- a/glxwrap.c
+++ b/glxwrap.c
 #include <GL/gl.h>
 #include <GL/glx.h>
 
-#include <sys/time.h>
-
 #include "dlwrap.h"
 
+#include "glwrap.h"
+
 typedef void (* fips_glXSwapBuffers_t)(Display *dpy, GLXDrawable drawable);
 
 static void *
@@ -69,28 +69,9 @@ call_glXSwapBuffers (Display *dpy, GLXDrawable drawable)
 void
 glXSwapBuffers (Display *dpy, GLXDrawable drawable)
 {
-       static int initialized = 0;
-       static int frames;
-       static struct timeval tv_start, tv_now;
-
-       if (! initialized) {
-               frames = 0;
-               gettimeofday (&tv_start, NULL);
-               initialized = 1;
-       }
-
        call_glXSwapBuffers (dpy, drawable);
 
-       frames++;
-       if (frames % 60 == 0) {
-               double fps;
-               gettimeofday (&tv_now, NULL);
-
-               fps = (double) frames / (tv_now.tv_sec - tv_start.tv_sec +
-                                        (tv_now.tv_usec - tv_start.tv_usec) / 1.0e6);
-
-               printf("FPS: %.3f\n", fps);
-       }
+       glwrap_end_frame ();
 }
 
 
index a387ddad14495a7f65ce6d637467b436d05e5c69..65567cfa4c22291c50cc3c094c1c0d796f176c19 100644 (file)
@@ -2,6 +2,37 @@
 global:
        dlopen;
        dlsym;
+       glDrawArrays;
+       glDrawArraysEXT;
+       glDrawArraysIndirect;
+       glDrawArraysInstanced;
+       glDrawArraysInstancedARB;
+       glDrawArraysInstancedEXT;
+       glDrawArraysInstancedBaseInstance;
+       glDrawMeshArraysSUN;
+       glMultiDrawArrays;
+       glMultiDrawArraysEXT;
+       glMultiModeDrawArraysIBM;
+       glMultiDrawArraysIndirectAMD;
+       glDrawElements;
+       glDrawElementsBaseVertex;
+       glDrawElementsIndirect;
+       glDrawElementsInstanced;
+       glDrawElementsInstancedARB;
+       glDrawElementsInstancedEXT;
+       glDrawElementsInstancedBaseVertex;
+       glDrawElementsInstancedBaseInstance;
+       glDrawElementsInstancedBaseVertexBaseInstance;
+       glDrawRangeElements;
+       glDrawRangeElementsEXT;
+       glDrawRangeElementsBaseVertex;
+       glMultiDrawElements;
+       glMultiDrawElementsBaseVertex;
+       glMultiDrawElementsEXT;
+       glMultiModeDrawElementsIBM;
+       glMultiDrawElementsIndirectAMD;
+       glUseProgram;
+       glUseProgramObjectARB;
        glXSwapBuffers;
        glXGetProcAddressARB;
 local: