[PATCH 8/9] optionally track the GL state manually

Imre Deak imre.deak at intel.com
Wed Apr 11 08:19:38 PDT 2012


At the moment apitrace uses GL/GLES calls to querry the current GL status.
This might not work if the underlying implementation doesn't support
these calls. This is the case at least with the Android software GL
implementation, where the following calls/arguments are not implemented:

- glGetIntegerv with the following pname parameters, with for example
  GL_VERTEX_ARRAY_BUFFER_BINDING and in general any GL_*_ARRAY_BUFFER_BINDING
  pname parameter.

- glEnabled with several cap parameters.

Also glGetBufferSubData is not supported by GLES, so we need to track
buffer contents, at least in some cases.

This patch won't provide tracking for the full state, only for those
parts that are essential for tracing GLES applications on Android. For
the rest fall back to calling the the platform's GL function as it's
used to be.

Signed-off-by: Imre Deak <imre.deak at intel.com>
---
 glsize.hpp  |   14 +++-
 gltrace.hpp |   13 +++-
 gltrace.py  |  258 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 272 insertions(+), 13 deletions(-)

diff --git a/glsize.hpp b/glsize.hpp
index d81cdf1..9c2f0c7 100644
--- a/glsize.hpp
+++ b/glsize.hpp
@@ -309,6 +309,14 @@ __glDrawArrays_maxindex(GLint first, GLsizei count)
 
 #define __glDrawArraysEXT_maxindex __glDrawArrays_maxindex
 
+/* Forward declarations for definitions in gltrace.py */
+void
+__apitrace_glGetIntegerv(GLenum pname, GLint *params);
+
+void
+__apitrace_glGetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size,
+                              GLvoid *data);
+
 static inline GLuint
 __glDrawElementsBaseVertex_maxindex(GLsizei count, GLenum type, const GLvoid *indices, GLint basevertex)
 {
@@ -319,7 +327,8 @@ __glDrawElementsBaseVertex_maxindex(GLsizei count, GLenum type, const GLvoid *in
         return 0;
     }
 
-    __glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &__element_array_buffer);
+    __apitrace_glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING,
+                                      &__element_array_buffer);
     if (__element_array_buffer) {
         // Read indices from index buffer object
         GLintptr offset = (GLintptr)indices;
@@ -329,7 +338,8 @@ __glDrawElementsBaseVertex_maxindex(GLsizei count, GLenum type, const GLvoid *in
             return 0;
         }
         memset(temp, 0, size);
-        __glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, size, temp);
+        __apitrace_glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset,
+                                               size, temp);
         indices = temp;
     } else {
         if (!indices) {
diff --git a/gltrace.hpp b/gltrace.hpp
index e4fefc3..992b2d6 100644
--- a/gltrace.hpp
+++ b/gltrace.hpp
@@ -26,7 +26,8 @@
 #ifndef _GLTRACE_HPP_
 #define _GLTRACE_HPP_
 
-
+#include <map>
+#include <set>
 #include "glimports.hpp"
 
 
@@ -39,11 +40,21 @@ enum Profile {
     PROFILE_ES2,
 };
 
+struct gl_buffer {
+	size_t size;
+	void *data;
+};
+
 struct Context {
     enum Profile profile;
     bool user_arrays;
     bool user_arrays_arb;
     bool user_arrays_nv;
+    std::map <GLuint, GLuint> bindings;
+    std::map <GLuint, struct gl_buffer *> buffers;
+    std::set <GLenum> enabled_caps;
+    std::map <GLenum, const GLvoid *> pointers;
+    std::map <GLenum, GLuint> integers;
 };
     
 Context *
diff --git a/gltrace.py b/gltrace.py
index 6fc456c..24b560f 100644
--- a/gltrace.py
+++ b/gltrace.py
@@ -89,6 +89,23 @@ class TypeGetter(stdapi.Visitor):
     def visitOpaque(self, pointer):
         return self.prefix + 'Pointerv' + self.ext_suffix, 'GLvoid *'
 
+class TypeGetterManualTracking(TypeGetter):
+    def manualTrackingFunctionName(self, function_name):
+        return 'apitrace_' + function_name
+
+    def visitAlias(self, alias):
+        function_name, arg_type = TypeGetter.visitAlias(self, alias)
+        # We don't support yet the rest of the types
+        if alias.expr in ('GLint', 'GLuint', 'GLsizei'):
+            function_name = self.manualTrackingFunctionName(function_name)
+
+        return function_name, arg_type
+
+    def visitOpaque(self, pointer):
+        function_name, arg_type = TypeGetter.visitOpaque(self, pointer)
+        function_name = self.manualTrackingFunctionName(function_name)
+
+        return function_name, arg_type
 
 class GlTracer(Tracer):
 
@@ -124,7 +141,7 @@ class GlTracer(Tracer):
         print 'gltrace::getContext(void)'
         print '{'
         print '    // TODO return the context set by other APIs (GLX, EGL, and etc.)'
-        print '    static gltrace::Context __ctx = { gltrace::PROFILE_COMPAT, false, false, false };'
+        print '    static gltrace::Context __ctx = { gltrace::PROFILE_COMPAT, };'
         print '    return &__ctx;'
         print '}'
         print
@@ -148,6 +165,8 @@ class GlTracer(Tracer):
         print '}'
         print
 
+        self.defineManualTrackingHelpers()
+
         # Whether we need user arrays
         print 'static inline bool __need_user_arrays(void)'
         print '{'
@@ -169,9 +188,9 @@ class GlTracer(Tracer):
             print '    // %s' % function_name
             print '  if (%s) {' % profile_check
             self.array_prolog(api, uppercase_name)
-            print '    if (__glIsEnabled(%s)) {' % enable_name
+            print '    if (__apitrace_glIsEnabled(%s)) {' % enable_name
             print '        GLint __binding = 0;'
-            print '        __glGetIntegerv(%s, &__binding);' % binding_name
+            print '        __apitrace_glGetIntegerv(%s, &__binding);' % binding_name
             print '        if (!__binding) {'
             self.array_cleanup(api, uppercase_name)
             print '            return true;'
@@ -324,6 +343,224 @@ class GlTracer(Tracer):
         print '}'
         print
 
+    def manualTracking(self):
+        return False
+
+    def defineManualTrackingHelpers(self):
+        print 'void __apitrace_glGetIntegerv(GLenum pname, GLint *params)'
+        print '{'
+        if self.manualTracking():
+            print '    gltrace::Context *ctx = gltrace::getContext();'
+            print
+            print '    if (ctx->integers.find(pname) != ctx->integers.end())'
+            print '        *params = ctx->integers[pname];'
+            print '    else if (pname == GL_CLIENT_ACTIVE_TEXTURE)'
+            print '        *params = GL_TEXTURE0;'
+            print '    else'
+            print '        __glGetIntegerv(pname, params);'
+        else:
+            print '    __glGetIntegerv(pname, params);'
+        print '}'
+        print
+
+        print 'static GLboolean __apitrace_glIsEnabled(GLenum cap)'
+        print '{'
+        if self.manualTracking():
+            print '    gltrace::Context *ctx = gltrace::getContext();'
+            print '    std::set <GLenum> *caps = &ctx->enabled_caps;'
+            print
+            print '    if (caps->find(cap) != caps->end())'
+            print '        return true;'
+            print '    else'
+            print '        return __glIsEnabled(cap);'
+        else:
+            print '    return __glIsEnabled(cap);'
+        print '}'
+        print
+
+        print 'static void __apitrace_glGetPointerv(GLenum pname, GLvoid **params)'
+        print '{'
+        print '    gltrace::Context *ctx = gltrace::getContext();'
+        print '    *params = (void *)ctx->pointers[pname];'
+        print '}'
+        print
+
+        print 'void __apitrace_glGetBufferSubData(GLenum target,'
+        print '                                          GLintptr offset,'
+        print '                                          GLsizeiptr size,'
+        print '                                          GLvoid *data)'
+        print '{'
+        if self.manualTracking():
+            print '    if (target != GL_ELEMENT_ARRAY_BUFFER_BINDING) {'
+            print '        glGetBufferSubData(target, offset, size, data);'
+            print '        return;'
+            print '    }'
+            print '    struct gltrace::Context *ctx = gltrace::getContext();'
+            print '    struct gltrace::gl_buffer *buf;'
+            print '    GLint buf_id;'
+            print
+            print '    __apitrace_glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING,'
+            print '                             &buf_id);'
+            print '    assert(buf_id == ctx->bindings[GL_ELEMENT_ARRAY_BUFFER]);'
+            print '    buf = ctx->buffers[buf_id];'
+            print '    assert(size + offset <= buf->size);'
+            print '    memcpy(data, (uint8_t *)buf->data + offset, size);'
+        else:
+            print '    glGetBufferSubData(target, offset, size, data);'
+        print '}'
+        print
+
+    def manualTrackingProlog(self, function):
+        if function.name == 'glBindBuffer':
+            print '    gltrace::Context *ctx = gltrace::getContext();'
+            print '    ctx->bindings[target] = buffer;'
+            print '    switch (target) {'
+            print '        case GL_ARRAY_BUFFER:'
+            print '            ctx->integers[GL_ARRAY_BUFFER_BINDING] = buffer;'
+            print '            break;'
+            print '        case GL_ELEMENT_ARRAY_BUFFER:'
+            print '            ctx->integers[GL_ELEMENT_ARRAY_BUFFER_BINDING] = buffer;'
+            print '            break;'
+            print '        case GL_PIXEL_UNPACK_BUFFER:'
+            print '            ctx->integers[GL_PIXEL_UNPACK_BUFFER_BINDING] = buffer;'
+            print '            break;'
+            print '    }'
+            print
+
+        if function.name == 'glBufferData':
+            print '    if (target == GL_ELEMENT_ARRAY_BUFFER) {'
+            print '        gltrace::Context *ctx = gltrace::getContext();'
+            print '        unsigned long buf_id = ctx->bindings[target];'
+            print '        struct gltrace::gl_buffer *buf;'
+            print '        if (ctx->buffers.find(buf_id) != ctx->buffers.end()) {'
+            print '            buf = ctx->buffers[buf_id];'
+            print '        } else {'
+            print '            buf = (struct gltrace::gl_buffer *)calloc(1, sizeof(*buf));'
+            print '            ctx->buffers[buf_id] = buf;'
+            print '        }'
+            print '        if (data) {'
+            print '            buf->data = realloc(buf->data, size);'
+            print '            buf->size = size;'
+            print '            memcpy(buf->data, data, size);'
+            print '        } else {'
+            print '            buf->size = 0;'
+            print '            free(buf->data);'
+            print '            buf->data = NULL;'
+            print '        }'
+            print '    }'
+            print
+
+        if function.name == 'glBufferSubData':
+            print '    if (target == GL_ELEMENT_ARRAY_BUFFER) {'
+            print '        gltrace::Context *ctx = gltrace::getContext();'
+            print '        struct gltrace::gl_buffer *buf;'
+            print '        unsigned long buf_id = ctx->bindings[target];'
+            print '        buf = ctx->buffers[buf_id];'
+            print '        memcpy((uint8_t *)buf->data + offset, data, size);'
+            print '    }'
+            print
+
+        if function.name == 'glDeleteBuffers':
+            print '    gltrace::Context *ctx = gltrace::getContext();'
+            print
+            print '    while (n) {'
+            print '        unsigned long buf_id;'
+            print '        struct gltrace::gl_buffer *buf;'
+            print '        n--;'
+            print '        buf_id = buffer[n];'
+            print '        buf = ctx->buffers[buf_id];'
+            print '        if (buf) {'
+            print '            free(buf->data);'
+            print '            free(buf);'
+            print '            ctx->buffers.erase(buf_id);'
+            print '            std::map <GLuint, GLuint>::iterator b;'
+            print
+            print '            b = ctx->bindings.begin();'
+            print '            while (b != ctx->bindings.end()) {'
+            print '                if (b->second != buf_id) {'
+            print '                    b++;'
+            print '                    continue;'
+            print '                }'
+            print '                ctx->bindings.erase(b++);'
+            print '                std::map <GLenum, GLuint>::iterator i;'
+            print '                i = ctx->integers.begin();'
+            print '                while (i != ctx->integers.end()) {'
+            print '                    if (i->second != buf_id) {'
+            print '                        i++;'
+            print '                        continue;'
+            print '                    }'
+            print
+            print '                    switch (i->first) {'
+            for i in ['', 'ELEMENT_', 'VERTEX_', 'NORMAL_', 'COLOR_', 'INDEX_', 'TEXTURE_COORD_', 'EDGE_FLAG_', 'FOG_COORD_', 'SECONDARY_COLOR_']:
+                print '                    case GL_%sARRAY_BUFFER_BINDING:' % i
+            print '                        ctx->integers.erase(i++);'
+            print '                        break;'
+            print '                     }'
+            print '                }'
+            print '            }'
+            print '        }'
+            print '    }'
+
+        if function.name == 'glEnable':
+            print '    gltrace::Context *ctx = gltrace::getContext();'
+            print '    std::set <GLenum> *caps = &ctx->enabled_caps;'
+            print
+            # We don't care if the cap is valid here. We anyway use our own
+            # set of enabled caps for known-good values only. The traced
+            # application itself will use the native glIsEnabled instead (if
+            # it's supported.
+            print '    caps->insert(cap);'
+
+        if function.name == 'glEnableClientState':
+            print '    gltrace::Context *ctx = gltrace::getContext();'
+            print '    std::set <GLenum> *caps = &ctx->enabled_caps;'
+            print
+            # We don't care if the cap is valid here. We anyway use our own
+            # set of enabled caps for known-good values only. The traced
+            # application itself will use the native glIsEnabled instead (if
+            # it's supported.
+            print '    caps->insert(array);'
+
+        if function.name == 'glDisable':
+            print '    gltrace::Context *ctx = gltrace::getContext();'
+            print '    std::set <GLenum> *caps = &ctx->enabled_caps;'
+            print
+            print '    caps->erase(cap);'
+
+        if function.name == 'glDisableClientState':
+            print '    gltrace::Context *ctx = gltrace::getContext();'
+            print '    std::set <GLenum> *caps = &ctx->enabled_caps;'
+            print
+            print '    caps->erase(array);'
+
+        if function.name == 'glClientActiveTexture':
+            print '    gltrace::Context *ctx = gltrace::getContext();'
+            print
+            print '    ctx->integers[GL_CLIENT_ACTIVE_TEXTURE] = texture;'
+
+        pointer_functions = {
+                'glVertexPointer': 'GL_VERTEX_ARRAY',
+                'glTexCoordPointer': 'GL_TEXTURE_COORD_ARRAY',
+                'glNormalPointer': 'GL_NORMAL_ARRAY',
+                'glColorPointer': 'GL_COLOR_ARRAY',
+        }
+
+        if function.name in pointer_functions:
+            print '    gltrace::Context *ctx = gltrace::getContext();'
+            print '    GLuint binding;'
+            array_name = pointer_functions[function.name]
+            if function.name != 'glNormalPointer':
+                print '    ctx->integers[%s_SIZE] = size;' % array_name
+            print '    ctx->integers[%s_STRIDE] = stride;' % array_name
+            print '    ctx->integers[%s_TYPE] = type;' % array_name
+            print '    ctx->pointers[%s_POINTER] = pointer;' % array_name
+            print '    if (ctx->bindings.find(GL_ARRAY_BUFFER) != '
+            print '        ctx->bindings.end())'
+            print '        binding = ctx->bindings[GL_ARRAY_BUFFER];'
+            print '    else'
+            print '        binding = 0;'
+            print '    ctx->integers[%s_BUFFER_BINDING] = binding;' % array_name
+
     array_pointer_function_names = set((
         "glVertexPointer",
         "glNormalPointer",
@@ -407,7 +644,7 @@ class GlTracer(Tracer):
         # Defer tracing of user array pointers...
         if function.name in self.array_pointer_function_names:
             print '    GLint __array_buffer = 0;'
-            print '    __glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &__array_buffer);'
+            print '    __apitrace_glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &__array_buffer);'
             print '    if (!__array_buffer) {'
             print '        gltrace::Context *ctx = gltrace::getContext();'
             print '        ctx->user_arrays = true;'
@@ -607,6 +844,7 @@ class GlTracer(Tracer):
             print '        }'
             print '    }'
 
+        self.manualTrackingProlog(function)
         Tracer.traceFunctionImplBody(self, function)
 
     marker_functions = [
@@ -746,7 +984,7 @@ class GlTracer(Tracer):
     def serializeArgValue(self, function, arg):
         if function.name in self.draw_function_names and arg.name == 'indices':
             print '    GLint __element_array_buffer = 0;'
-            print '    __glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &__element_array_buffer);'
+            print '    __apitrace_glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &__element_array_buffer);'
             print '    if (!__element_array_buffer) {'
             if isinstance(arg.type, stdapi.Array):
                 print '        trace::localWriter.beginArray(%s);' % arg.type.length
@@ -772,7 +1010,7 @@ class GlTracer(Tracer):
             print '        gltrace::Context *ctx = gltrace::getContext();'
             print '        GLint __unpack_buffer = 0;'
             print '        if (ctx->profile == gltrace::PROFILE_COMPAT)'
-            print '            __glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &__unpack_buffer);'
+            print '            __apitrace_glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &__unpack_buffer);'
             print '        if (__unpack_buffer) {'
             print '            trace::localWriter.writeOpaque(%s);' % arg.name
             print '        } else {'
@@ -822,15 +1060,15 @@ class GlTracer(Tracer):
             print '  if (%s) {' % profile_check
             self.array_trace_prolog(api, uppercase_name)
             self.array_prolog(api, uppercase_name)
-            print '    if (__glIsEnabled(%s)) {' % enable_name
+            print '    if (__apitrace_glIsEnabled(%s)) {' % enable_name
             print '        GLint __binding = 0;'
-            print '        __glGetIntegerv(%s, &__binding);' % binding_name
+            print '        __apitrace_glGetIntegerv(%s, &__binding);' % binding_name
             print '        if (!__binding) {'
 
             # Get the arguments via glGet*
             for arg in function.args:
                 arg_get_enum = 'GL_%s_ARRAY_%s' % (uppercase_name, arg.name.upper())
-                arg_get_function, arg_type = TypeGetter().visit(arg.type)
+                arg_get_function, arg_type = TypeGetterManualTracking().visit(arg.type)
                 print '            %s %s = 0;' % (arg_type, arg.name)
                 print '            __%s(%s, &%s);' % (arg_get_function, arg_get_enum, arg.name)
             
@@ -947,7 +1185,7 @@ class GlTracer(Tracer):
     def array_prolog(self, api, uppercase_name):
         if uppercase_name == 'TEXTURE_COORD':
             print '    GLint client_active_texture = 0;'
-            print '    __glGetIntegerv(GL_CLIENT_ACTIVE_TEXTURE, &client_active_texture);'
+            print '    __apitrace_glGetIntegerv(GL_CLIENT_ACTIVE_TEXTURE, &client_active_texture);'
             print '    GLint max_texture_coords = 0;'
             print '    if (ctx->profile == gltrace::PROFILE_COMPAT)'
             print '        __glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);'
-- 
1.7.5.4



More information about the apitrace mailing list