]> git.cworth.org Git - apitrace/commitdiff
Merge branch 'master' into d3dretrace
authorJosé Fonseca <jose.r.fonseca@gmail.com>
Fri, 30 Mar 2012 19:41:25 +0000 (20:41 +0100)
committerJosé Fonseca <jose.r.fonseca@gmail.com>
Fri, 30 Mar 2012 19:41:25 +0000 (20:41 +0100)
Conflicts:
retrace.py

45 files changed:
CMakeLists.txt
NEWS.markdown
README.markdown
cli/cli_dump.cpp
common/image.hpp
common/image_pnm.cpp
common/trace_api.hpp
common/trace_parser.cpp
common/trace_parser.hpp
common/trace_parser_flags.cpp
eglimports.hpp
glproc.py
glretrace.py
glstate.cpp
glstate.hpp
glstate.py [deleted file]
glstate_images.cpp [new file with mode: 0644]
glstate_internal.hpp
glstate_params.py [new file with mode: 0644]
glstate_shaders.cpp [new file with mode: 0644]
glws_wgl.cpp
gui/apicalldelegate.cpp
gui/apisurface.cpp
gui/apitrace.cpp
gui/apitrace.h
gui/apitracecall.cpp
gui/apitracecall.h
gui/apitracemodel.cpp
gui/apitracemodel.h
gui/imageviewer.cpp
gui/imageviewer.h
gui/main.cpp
gui/mainwindow.cpp
gui/mainwindow.h
gui/retracer.cpp
gui/retracer.h
gui/thumbnail.h [new file with mode: 0644]
gui/traceloader.cpp
gui/traceloader.h
gui/ui/imageviewer.ui
gui/ui/mainwindow.ui
gui/ui/settings.ui
retrace.hpp
retrace.py
scripts/tracediff2.py

index 35d9a7848560cb8d77d89beca9f3b8fc40224848..03208fbf8ce3b4bea46019019684f71e873424f8 100755 (executable)
@@ -60,10 +60,6 @@ else ()
         include_directories (${X11_INCLUDE_DIR})
         add_definitions (-DHAVE_X11)
     endif ()
-
-    if (ENABLE_EGL)
-        add_definitions (-DHAVE_EGL)
-    endif ()
 endif ()
 
 
@@ -510,12 +506,6 @@ if (ENABLE_EGL AND NOT WIN32 AND NOT APPLE)
 
     add_dependencies (egltrace glproc)
 
-    set_property (
-        TARGET egltrace
-        APPEND
-        PROPERTY COMPILE_DEFINITIONS "TRACE_EGL"
-    )
-
     set_target_properties (egltrace PROPERTIES
         # avoid the default "lib" prefix
         PREFIX ""
@@ -548,11 +538,11 @@ add_custom_command (
 
 add_custom_command (
     OUTPUT glstate_params.cpp
-    COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/glstate.py > ${CMAKE_CURRENT_BINARY_DIR}/glstate_params.cpp
-    DEPENDS glstate.py specs/glparams.py specs/gltypes.py specs/stdapi.py
+    COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/glstate_params.py > ${CMAKE_CURRENT_BINARY_DIR}/glstate_params.cpp
+    DEPENDS glstate_params.py specs/glparams.py specs/gltypes.py specs/stdapi.py
 )
 
-set (retrace_sources
+add_library (retrace_common
     glretrace_gl.cpp
     glretrace_cgl.cpp
     glretrace_glx.cpp
@@ -560,15 +550,22 @@ set (retrace_sources
     glretrace_egl.cpp
     glretrace_main.cpp
     glstate.cpp
+    glstate_images.cpp
     glstate_params.cpp
+    glstate_shaders.cpp
     retrace.cpp
     retrace_stdc.cpp
     glws.cpp
 )
 
+set_property (
+    TARGET retrace_common
+    APPEND
+    PROPERTY COMPILE_DEFINITIONS "RETRACE"
+)
+
 if (WIN32 OR APPLE OR X11_FOUND)
     add_executable (glretrace
-        ${retrace_sources}
         ${glws_os}
         glproc_gl.cpp
     )
@@ -582,6 +579,7 @@ if (WIN32 OR APPLE OR X11_FOUND)
     )
 
     target_link_libraries (glretrace
+        retrace_common
         common
         ${PNG_LIBRARIES}
         ${ZLIB_LIBRARIES}
@@ -619,7 +617,6 @@ endif ()
 
 if (ENABLE_EGL AND X11_FOUND AND NOT WIN32 AND NOT APPLE)
     add_executable (eglretrace
-        ${retrace_sources}
         glws_egl_xlib.cpp
         glproc_egl.cpp
     )
@@ -630,10 +627,10 @@ if (ENABLE_EGL AND X11_FOUND AND NOT WIN32 AND NOT APPLE)
         TARGET eglretrace
         APPEND
         PROPERTY COMPILE_DEFINITIONS "RETRACE"
-        PROPERTY COMPILE_DEFINITIONS "TRACE_EGL"
     )
 
     target_link_libraries (eglretrace
+        retrace_common
         common
         ${PNG_LIBRARIES}
         ${ZLIB_LIBRARIES}
index 562fb740ac68c2c14c47eb1c3fb2bc3e81dda232..658a907dacf864457722d7b9b2fb3a07ba257975 100644 (file)
@@ -2,6 +2,14 @@ This file lists the major user visible improvements.  For a full list of changes
 and their authors see the git history.
 
 
+Development
+===========
+
+* Support to trace in Android
+
+* Show frame thumbnails in the GUI
+
+
 Version 3.0
 ===========
 
index 168a6ea19469c44b6618511450b3e6c1b623bcf9..536729e81fcac8bbc121db5ca71b87d2b82210b5 100644 (file)
@@ -358,16 +358,20 @@ reference software renderer.
 
 This can be achieved with retracediff.py script, which invokes glretrace with
 different environments, allowing to choose the desired GL driver by
-manipulating variables such as `LD_LIBRARY_PATH` or `LIBGL_DRIVERS_DIR`.
+manipulating variables such as `LD_LIBRARY_PATH`, `LIBGL_DRIVERS_DIR`, or
+`TRACE_LIBGL`.
 
-For example:
+For example, on Linux:
 
     ./scripts/retracediff.py \
         --ref-env LD_LIBRARY_PATH=/path/to/reference/GL/implementation \
-        -r ./glretrace \
+        --retrace /path/to/glretrace \
         --diff-prefix=/path/to/output/diffs \
         application.trace
 
+Or on Windows:
+
+    python scripts\retracediff.py --retrace \path\to\glretrace.exe --ref-env TRACE_LIBGL=\path\to\reference\opengl32.dll application.trace
 
 
 Links
index 28c91af92c7c9f7cf6d2f32df27570f64f513632..f52b83c3e0dcbde9cd3b5bb6347fb9a064a8a0a7 100644 (file)
@@ -28,6 +28,9 @@
 #include <string.h>
 #include <limits.h> // for CHAR_MAX
 #include <getopt.h>
+#ifndef _WIN32
+#include <unistd.h> // for isatty()
+#endif
 
 #include "cli.hpp"
 #include "cli_pager.hpp"
index 6316791bba210763dd79d10ed52afd6a20bfef9c..ea3eefb0d589c6e3f342860b8a1bd9720b3011bf 100644 (file)
@@ -108,6 +108,8 @@ bool writePixelsToBuffer(unsigned char *pixels,
 Image *
 readPNG(const char *filename);
 
+const char *
+readPNMHeader(const char *buffer, size_t size, unsigned *channels, unsigned *width, unsigned *height);
 
 } /* namespace image */
 
index c95f640fcc1ee6ff69af318f3bc52ba0d4edf1a8..f9cd05d1fbc7593dafcb6e5bf77d48beb7505a1d 100644 (file)
@@ -28,6 +28,7 @@
 #include <assert.h>
 #include <string.h>
 #include <stdint.h>
+#include <stdio.h>
 
 #include "image.hpp"
 
@@ -108,5 +109,55 @@ Image::writePNM(std::ostream &os, const char *comment) const {
     }
 }
 
+const char *
+readPNMHeader(const char *buffer, size_t bufferSize, unsigned *channels, unsigned *width, unsigned *height)
+{
+    *channels = 0;
+    *width = 0;
+    *height = 0;
+
+    const char *currentBuffer = buffer;
+    const char *nextBuffer;
+
+    // parse number of channels
+    int scannedChannels = sscanf(currentBuffer, "P%d\n", channels);
+    if (scannedChannels != 1) { // validate scanning of channels
+        // invalid channel line
+        return buffer;
+    }
+    // convert channel token to number of channels
+    *channels = (*channels == 5) ? 1 : 3;
+
+    // advance past channel line
+    nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1;
+    bufferSize -= nextBuffer - currentBuffer;
+    currentBuffer = nextBuffer;
+
+    // skip over optional comment
+    if (*currentBuffer == '#') {
+        // advance past comment line
+        nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1;
+        bufferSize -= nextBuffer - currentBuffer;
+        currentBuffer = nextBuffer;
+    }
+
+    // parse dimensions of image
+    int scannedDimensions = sscanf(currentBuffer, "%d %d\n", width, height);
+    if (scannedDimensions != 2) { // validate scanning of dimensions
+        // invalid dimension line
+        return buffer;
+    }
+
+    // advance past dimension line
+    nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1;
+    bufferSize -= nextBuffer - currentBuffer;
+    currentBuffer = nextBuffer;
+
+    // skip over "255\n" at end of header
+    nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1;
+
+    // return start of image data
+    return nextBuffer;
+}
 
 } /* namespace image */
index ed4823f5cab2492a3bdeb2abce2872298d2bc2aa..7619f71d1d5d853c98962fda56ba047bb34809fc 100644 (file)
 namespace trace {
 
 
+/**
+ * Enum to distuinguish the API for tools.
+ *
+ * It should never be embedded in the trace file.
+ */
 enum API {
+    API_UNKNOWN = 0,
     API_GL, // GL + GLX/WGL/CGL
     API_EGL, // GL/GLES1/GLES2/VG + EGL
     API_D3D7,
index dc9634f0d36f823b635d26c7efd7bc1a1082625f..896bfd437fc720e835458abfefbbfc4ad34d863c 100644 (file)
@@ -43,6 +43,7 @@ Parser::Parser() {
     file = NULL;
     next_call_no = 0;
     version = 0;
+    api = API_UNKNOWN;
 
     glGetErrorSig = NULL;
 }
@@ -65,6 +66,7 @@ bool Parser::open(const char *filename) {
         std::cerr << "error: unsupported trace format version " << version << "\n";
         return false;
     }
+    api = API_UNKNOWN;
 
     return true;
 }
@@ -232,6 +234,25 @@ Parser::parse_function_sig(void) {
         sig->offset = file->currentOffset();
         functions[id] = sig;
 
+        /**
+         * Try to autodetect the API.
+         *
+         * XXX: Ideally we would allow to mix multiple APIs in a single trace,
+         * but as it stands today, retrace is done separately for each API.
+         */
+        if (api == API_UNKNOWN) {
+            const char *n = sig->name;
+            if ((n[0] == 'g' && n[1] == 'l' && n[2] == 'X') || // glX
+                (n[0] == 'w' && n[1] == 'g' && n[2] == 'l' && n[3] >= 'A' && n[3] <= 'Z') || // wgl[A-Z]
+                (n[0] == 'C' && n[1] == 'G' && n[2] == 'L')) { // CGL
+                api = trace::API_GL;
+            } else if (n[0] == 'e' && n[1] == 'g' && n[2] == 'l' && n[3] >= 'A' && n[3] <= 'Z') { // egl
+                api = trace::API_EGL;
+            } else {
+                /* TODO */
+            }
+        }
+
         /**
          * Note down the signature of special functions for future reference.
          *
index d8c5915ea3d8e3976c1b2d38e33d8e8f57a21d53..43c13563a639cb59e535dff580be1e39d22ff28f 100644 (file)
@@ -33,6 +33,7 @@
 #include "trace_file.hpp"
 #include "trace_format.hpp"
 #include "trace_model.hpp"
+#include "trace_api.hpp"
 
 
 namespace trace {
@@ -94,6 +95,7 @@ protected:
 
 public:
     unsigned long long version;
+    API api;
 
     Parser();
 
index 936a89145c303a6a09d60fc96477485248b96269..304e2a85bb4221750afd4c84c53fdc949732dcaa 100644 (file)
@@ -64,6 +64,8 @@ callFlagTable[] = {
     { "glBindFramebufferOES",                          CALL_FLAG_SWAP_RENDERTARGET },
     { "glBlitFramebuffer",                             CALL_FLAG_RENDER },
     { "glBlitFramebufferEXT",                          CALL_FLAG_RENDER },
+    { "glCallList",                                    CALL_FLAG_RENDER },
+    { "glCallLists",                                   CALL_FLAG_RENDER },
     { "glClear",                                       CALL_FLAG_RENDER },
     { "glDrawArrays",                                  CALL_FLAG_RENDER },
     { "glDrawArraysEXT",                               CALL_FLAG_RENDER },
index 5443bef61d3bddb633a1362761951be7c992855e..a3e396abf8046d63ffb373b215c8cc03f7030bc6 100644 (file)
 #define _EGLIMPORTS_HPP_
 
 
-#ifdef HAVE_EGL
+#include <KHR/khrplatform.h>
+
+#ifdef _GDI32_
+/* Prevent __declspec(dllimport) attribute */
+#undef KHRONOS_APICALL
+#define KHRONOS_APICALL
+#endif
 
 // EGL
 #include <EGL/egl.h>
 #include <GLES/glplatform.h>
 #include <GLES2/gl2platform.h>
 
-#else // HAVE_EGL
-
-// We always include GLES headers below to have the types and enums defined.
-// For that to work without GLES platform headers, we need to define GL_API,
-// GL_APICALL, and GL_APIENTRY.  It does not matter what they are defined to.
-// When we hit here, EGL/GLES support is disabled and all we need from the
-// headers are the types and enums.
-
-#ifndef GL_API
-#define GL_API GLAPI
-#endif
-
-#ifndef GL_APICALL
-#define GL_APICALL GLAPI
-#endif
-
-#ifndef GL_APIENTRY
-#define GL_APIENTRY APIENTRY
-#endif
-
-#endif //! HAVE_EGL
-
 
 // OpenGL ES 1.1
 typedef int32_t  GLfixed;
index 95ab80f50c27870a6d83dbf04abbcdbea38c6f02..b54f713f02d12bc92b882991d2b51a2f54bac1c7 100644 (file)
--- a/glproc.py
+++ b/glproc.py
@@ -515,20 +515,24 @@ if __name__ == '__main__':
     print '#include "glimports.hpp"'
     print '#include "os.hpp"'
     print
-    print
     dispatcher = GlDispatcher()
+    print
     dispatcher.header()
-    print '#if defined(TRACE_EGL)'
     print
     dispatcher.dispatch_api(eglapi)
-    print '#elif defined(_WIN32)'
+    print
+    print '#if defined(_WIN32)'
     print
     dispatcher.dispatch_api(wglapi)
+    print
     print '#elif defined(__APPLE__)'
+    print
     dispatcher.dispatch_api(cglapi)
-    print '#else'
+    print
+    print '#elif defined(HAVE_X11)'
     print
     dispatcher.dispatch_api(glxapi)
+    print
     print '#endif'
     print
     dispatcher.dispatch_api(glapi)
index e3fca8fd94624c1f72fb105346b80f0d41cf320a..bb88288060f09d968a9303808d92894e17aeef61 100644 (file)
@@ -111,6 +111,8 @@ class GlRetracer(Retracer):
     ])
 
     misc_draw_function_names = set([
+        "glCallList",
+        "glCallLists",
         "glClear",
         "glEnd",
         "glDrawPixels",
@@ -222,6 +224,21 @@ class GlRetracer(Retracer):
         # Infer the drawable size from GL calls
         if function.name == "glViewport":
             print '    glretrace::updateDrawable(x + width, y + height);'
+        if function.name == "glViewportArray":
+            # We are concerned about drawables so only care for the first viewport
+            print '    if (first == 0 && count > 0) {'
+            print '        GLfloat x = v[0], y = v[1], w = v[2], h = v[3];'
+            print '        glretrace::updateDrawable(x + w, y + h);'
+            print '    }'
+        if function.name == "glViewportIndexedf":
+            print '    if (index == 0) {'
+            print '        glretrace::updateDrawable(x + w, y + h);'
+            print '    }'
+        if function.name == "glViewportIndexedfv":
+            print '    if (index == 0) {'
+            print '        GLfloat x = v[0], y = v[1], w = v[2], h = v[3];'
+            print '        glretrace::updateDrawable(x + w, y + h);'
+            print '    }'
         if function.name in ('glBlitFramebuffer', 'glBlitFramebufferEXT'):
             # Some applications do all their rendering in a framebuffer, and
             # then just blit to the drawable without ever calling glViewport.
@@ -338,21 +355,21 @@ class GlRetracer(Retracer):
                 print r'    }'
             print '    }'
 
-            # Query the buffer length for whole buffer mappings
-            if function.name in self.map_function_names:
-                if 'length' in function.argNames():
-                    assert 'BufferRange' in function.name
+        # Query the buffer length for whole buffer mappings
+        if function.name in self.map_function_names:
+            if 'length' in function.argNames():
+                assert 'BufferRange' in function.name
+            else:
+                assert 'BufferRange' not in function.name
+                print r'    GLint length = 0;'
+                if function.name in ('glMapBuffer', 'glMapBufferOES'):
+                    print r'    glGetBufferParameteriv(target, GL_BUFFER_SIZE, &length);'
+                elif function.name == 'glMapBufferARB':
+                    print r'    glGetBufferParameterivARB(target, GL_BUFFER_SIZE_ARB, &length);'
+                elif function.name == 'glMapNamedBufferEXT':
+                    print r'    glGetNamedBufferParameterivEXT(buffer, GL_BUFFER_SIZE, &length);'
                 else:
-                    assert 'BufferRange' not in function.name
-                    print r'    GLint length = 0;'
-                    if function.name in ('glMapBuffer', 'glMapBufferOES'):
-                        print r'    glGetBufferParameteriv(target, GL_BUFFER_SIZE, &length);'
-                    elif function.name == 'glMapBufferARB':
-                        print r'    glGetBufferParameterivARB(target, GL_BUFFER_SIZE_ARB, &length);'
-                    elif function.name == 'glMapNamedBufferEXT':
-                        print r'    glGetNamedBufferParameterivEXT(buffer, GL_BUFFER_SIZE, &length);'
-                    else:
-                        assert False
+                    assert False
 
     def extractArg(self, function, arg, arg_type, lvalue, rvalue):
         if function.name in self.array_pointer_function_names and arg.name == 'pointer':
index 55f3631b945df3ee3cdd34b3eb8a0151bfe09f9d..cac9f4e44a0d96cc9f7909f9b5f4379516133f00 100644 (file)
@@ -28,8 +28,6 @@
 
 #include <algorithm>
 #include <iostream>
-#include <map>
-#include <sstream>
 
 #include "image.hpp"
 #include "json.hpp"
 #include "glstate_internal.hpp"
 
 
-#ifdef __APPLE__
-
-#include <Carbon/Carbon.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-OSStatus CGSGetSurfaceBounds(CGSConnectionID, CGWindowID, CGSSurfaceID, CGRect *);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __APPLE__ */
-
-
 namespace glstate {
 
 
@@ -112,1374 +93,6 @@ Context::restorePixelPackState(void) {
 }
 
 
-// Mapping from shader type to shader source, used to accumulated the sources
-// of different shaders with same type.
-typedef std::map<std::string, std::string> ShaderMap;
-
-
-static void
-getShaderSource(ShaderMap &shaderMap, GLuint shader)
-{
-    if (!shader) {
-        return;
-    }
-
-    GLint shader_type = 0;
-    glGetShaderiv(shader, GL_SHADER_TYPE, &shader_type);
-    if (!shader_type) {
-        return;
-    }
-
-    GLint source_length = 0;
-    glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &source_length);
-    if (!source_length) {
-        return;
-    }
-
-    GLchar *source = new GLchar[source_length];
-    GLsizei length = 0;
-    source[0] = 0;
-    glGetShaderSource(shader, source_length, &length, source);
-
-    shaderMap[enumToString(shader_type)] += source;
-
-    delete [] source;
-}
-
-
-static void
-getShaderObjSource(ShaderMap &shaderMap, GLhandleARB shaderObj)
-{
-    if (!shaderObj) {
-        return;
-    }
-
-    GLint object_type = 0;
-    glGetObjectParameterivARB(shaderObj, GL_OBJECT_TYPE_ARB, &object_type);
-    if (object_type != GL_SHADER_OBJECT_ARB) {
-        return;
-    }
-
-    GLint shader_type = 0;
-    glGetObjectParameterivARB(shaderObj, GL_OBJECT_SUBTYPE_ARB, &shader_type);
-    if (!shader_type) {
-        return;
-    }
-
-    GLint source_length = 0;
-    glGetObjectParameterivARB(shaderObj, GL_OBJECT_SHADER_SOURCE_LENGTH_ARB, &source_length);
-    if (!source_length) {
-        return;
-    }
-
-    GLcharARB *source = new GLcharARB[source_length];
-    GLsizei length = 0;
-    source[0] = 0;
-    glGetShaderSource(shaderObj, source_length, &length, source);
-
-    shaderMap[enumToString(shader_type)] += source;
-
-    delete [] source;
-}
-
-
-static inline void
-dumpProgram(JSONWriter &json, GLint program)
-{
-    GLint attached_shaders = 0;
-    glGetProgramiv(program, GL_ATTACHED_SHADERS, &attached_shaders);
-    if (!attached_shaders) {
-        return;
-    }
-
-    ShaderMap shaderMap;
-
-    GLuint *shaders = new GLuint[attached_shaders];
-    GLsizei count = 0;
-    glGetAttachedShaders(program, attached_shaders, &count, shaders);
-    std::sort(shaders, shaders + count);
-    for (GLsizei i = 0; i < count; ++ i) {
-       getShaderSource(shaderMap, shaders[i]);
-    }
-    delete [] shaders;
-
-    for (ShaderMap::const_iterator it = shaderMap.begin(); it != shaderMap.end(); ++it) {
-        json.beginMember(it->first);
-        json.writeString(it->second);
-        json.endMember();
-    }
-}
-
-
-static inline void
-dumpProgramObj(JSONWriter &json, GLhandleARB programObj)
-{
-    GLint attached_shaders = 0;
-    glGetObjectParameterivARB(programObj, GL_OBJECT_ATTACHED_OBJECTS_ARB, &attached_shaders);
-    if (!attached_shaders) {
-        return;
-    }
-
-    ShaderMap shaderMap;
-
-    GLhandleARB *shaderObjs = new GLhandleARB[attached_shaders];
-    GLsizei count = 0;
-    glGetAttachedObjectsARB(programObj, attached_shaders, &count, shaderObjs);
-    std::sort(shaderObjs, shaderObjs + count);
-    for (GLsizei i = 0; i < count; ++ i) {
-       getShaderObjSource(shaderMap, shaderObjs[i]);
-    }
-    delete [] shaderObjs;
-
-    for (ShaderMap::const_iterator it = shaderMap.begin(); it != shaderMap.end(); ++it) {
-        json.beginMember(it->first);
-        json.writeString(it->second);
-        json.endMember();
-    }
-}
-
-/*
- * When fetching the uniform name of an array we usually get name[0]
- * so we need to cut the trailing "[0]" in order to properly construct
- * array names later. Otherwise we endup with stuff like
- * uniformArray[0][0],
- * uniformArray[0][1],
- * instead of
- * uniformArray[0],
- * uniformArray[1].
- */
-static std::string
-resolveUniformName(const GLchar *name,  GLint size)
-{
-    std::string qualifiedName(name);
-    if (size > 1) {
-        std::string::size_type nameLength = qualifiedName.length();
-        static const char * const arrayStart = "[0]";
-        static const int arrayStartLen = 3;
-        if (qualifiedName.rfind(arrayStart) == (nameLength - arrayStartLen)) {
-            qualifiedName = qualifiedName.substr(0, nameLength - 3);
-        }
-    }
-    return qualifiedName;
-}
-
-static void
-dumpUniform(JSONWriter &json, GLint program, GLint size, GLenum type, const GLchar *name) {
-    GLenum elemType;
-    GLint numElems;
-    __gl_uniform_size(type, elemType, numElems);
-    if (elemType == GL_NONE) {
-        return;
-    }
-
-    GLfloat fvalues[4*4];
-    GLdouble dvalues[4*4];
-    GLint ivalues[4*4];
-    GLuint uivalues[4*4];
-
-    GLint i, j;
-
-    std::string qualifiedName = resolveUniformName(name, size);
-
-    for (i = 0; i < size; ++i) {
-        std::stringstream ss;
-        ss << qualifiedName;
-
-        if (size > 1) {
-            ss << '[' << i << ']';
-        }
-
-        std::string elemName = ss.str();
-
-        json.beginMember(elemName);
-
-        GLint location = glGetUniformLocation(program, elemName.c_str());
-
-        if (numElems > 1) {
-            json.beginArray();
-        }
-
-        switch (elemType) {
-        case GL_FLOAT:
-            glGetUniformfv(program, location, fvalues);
-            for (j = 0; j < numElems; ++j) {
-                json.writeNumber(fvalues[j]);
-            }
-            break;
-        case GL_DOUBLE:
-            glGetUniformdv(program, location, dvalues);
-            for (j = 0; j < numElems; ++j) {
-                json.writeNumber(dvalues[j]);
-            }
-            break;
-        case GL_INT:
-            glGetUniformiv(program, location, ivalues);
-            for (j = 0; j < numElems; ++j) {
-                json.writeNumber(ivalues[j]);
-            }
-            break;
-        case GL_UNSIGNED_INT:
-            glGetUniformuiv(program, location, uivalues);
-            for (j = 0; j < numElems; ++j) {
-                json.writeNumber(uivalues[j]);
-            }
-            break;
-        case GL_BOOL:
-            glGetUniformiv(program, location, ivalues);
-            for (j = 0; j < numElems; ++j) {
-                json.writeBool(ivalues[j]);
-            }
-            break;
-        default:
-            assert(0);
-            break;
-        }
-
-        if (numElems > 1) {
-            json.endArray();
-        }
-
-        json.endMember();
-    }
-}
-
-
-static void
-dumpUniformARB(JSONWriter &json, GLhandleARB programObj, GLint size, GLenum type, const GLchar *name) {
-
-    GLenum elemType;
-    GLint numElems;
-    __gl_uniform_size(type, elemType, numElems);
-    if (elemType == GL_NONE) {
-        return;
-    }
-
-    GLfloat fvalues[4*4];
-    GLint ivalues[4*4];
-
-    GLint i, j;
-
-    std::string qualifiedName = resolveUniformName(name, size);
-
-    for (i = 0; i < size; ++i) {
-        std::stringstream ss;
-        ss << qualifiedName;
-
-        if (size > 1) {
-            ss << '[' << i << ']';
-        }
-
-        std::string elemName = ss.str();
-
-        json.beginMember(elemName);
-
-        GLint location = glGetUniformLocationARB(programObj, elemName.c_str());
-
-        if (numElems > 1) {
-            json.beginArray();
-        }
-
-        switch (elemType) {
-        case GL_DOUBLE:
-            // glGetUniformdvARB does not exists
-        case GL_FLOAT:
-            glGetUniformfvARB(programObj, location, fvalues);
-            for (j = 0; j < numElems; ++j) {
-                json.writeNumber(fvalues[j]);
-            }
-            break;
-        case GL_UNSIGNED_INT:
-            // glGetUniformuivARB does not exists
-        case GL_INT:
-            glGetUniformivARB(programObj, location, ivalues);
-            for (j = 0; j < numElems; ++j) {
-                json.writeNumber(ivalues[j]);
-            }
-            break;
-        case GL_BOOL:
-            glGetUniformivARB(programObj, location, ivalues);
-            for (j = 0; j < numElems; ++j) {
-                json.writeBool(ivalues[j]);
-            }
-            break;
-        default:
-            assert(0);
-            break;
-        }
-
-        if (numElems > 1) {
-            json.endArray();
-        }
-
-        json.endMember();
-    }
-}
-
-
-static inline void
-dumpProgramUniforms(JSONWriter &json, GLint program)
-{
-    GLint active_uniforms = 0;
-    glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniforms);
-    if (!active_uniforms) {
-        return;
-    }
-
-    GLint active_uniform_max_length = 0;
-    glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &active_uniform_max_length);
-    GLchar *name = new GLchar[active_uniform_max_length];
-    if (!name) {
-        return;
-    }
-
-    for (GLint index = 0; index < active_uniforms; ++index) {
-        GLsizei length = 0;
-        GLint size = 0;
-        GLenum type = GL_NONE;
-        glGetActiveUniform(program, index, active_uniform_max_length, &length, &size, &type, name);
-
-        dumpUniform(json, program, size, type, name);
-    }
-
-    delete [] name;
-}
-
-
-static inline void
-dumpProgramObjUniforms(JSONWriter &json, GLhandleARB programObj)
-{
-    GLint active_uniforms = 0;
-    glGetObjectParameterivARB(programObj, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &active_uniforms);
-    if (!active_uniforms) {
-        return;
-    }
-
-    GLint active_uniform_max_length = 0;
-    glGetObjectParameterivARB(programObj, GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB, &active_uniform_max_length);
-    GLchar *name = new GLchar[active_uniform_max_length];
-    if (!name) {
-        return;
-    }
-
-    for (GLint index = 0; index < active_uniforms; ++index) {
-        GLsizei length = 0;
-        GLint size = 0;
-        GLenum type = GL_NONE;
-        glGetActiveUniformARB(programObj, index, active_uniform_max_length, &length, &size, &type, name);
-
-        dumpUniformARB(json, programObj, size, type, name);
-    }
-
-    delete [] name;
-}
-
-
-static inline void
-dumpArbProgram(JSONWriter &json, GLenum target)
-{
-    if (!glIsEnabled(target)) {
-        return;
-    }
-
-    GLint program_length = 0;
-    glGetProgramivARB(target, GL_PROGRAM_LENGTH_ARB, &program_length);
-    if (!program_length) {
-        return;
-    }
-
-    GLchar *source = new GLchar[program_length + 1];
-    source[0] = 0;
-    glGetProgramStringARB(target, GL_PROGRAM_STRING_ARB, source);
-    source[program_length] = 0;
-
-    json.beginMember(enumToString(target));
-    json.writeString(source);
-    json.endMember();
-
-    delete [] source;
-}
-
-
-static inline void
-dumpArbProgramUniforms(JSONWriter &json, GLenum target, const char *prefix)
-{
-    if (!glIsEnabled(target)) {
-        return;
-    }
-
-    GLint program_parameters = 0;
-    glGetProgramivARB(target, GL_PROGRAM_PARAMETERS_ARB, &program_parameters);
-    if (!program_parameters) {
-        return;
-    }
-
-    GLint max_program_local_parameters = 0;
-    glGetProgramivARB(target, GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB, &max_program_local_parameters);
-    for (GLint index = 0; index < max_program_local_parameters; ++index) {
-        GLdouble params[4] = {0, 0, 0, 0};
-        glGetProgramLocalParameterdvARB(target, index, params);
-
-        if (!params[0] && !params[1] && !params[2] && !params[3]) {
-            continue;
-        }
-
-        char name[256];
-        snprintf(name, sizeof name, "%sprogram.local[%i]", prefix, index);
-
-        json.beginMember(name);
-        json.beginArray();
-        json.writeNumber(params[0]);
-        json.writeNumber(params[1]);
-        json.writeNumber(params[2]);
-        json.writeNumber(params[3]);
-        json.endArray();
-        json.endMember();
-    }
-
-    GLint max_program_env_parameters = 0;
-    glGetProgramivARB(target, GL_MAX_PROGRAM_ENV_PARAMETERS_ARB, &max_program_env_parameters);
-    for (GLint index = 0; index < max_program_env_parameters; ++index) {
-        GLdouble params[4] = {0, 0, 0, 0};
-        glGetProgramEnvParameterdvARB(target, index, params);
-
-        if (!params[0] && !params[1] && !params[2] && !params[3]) {
-            continue;
-        }
-
-        char name[256];
-        snprintf(name, sizeof name, "%sprogram.env[%i]", prefix, index);
-
-        json.beginMember(name);
-        json.beginArray();
-        json.writeNumber(params[0]);
-        json.writeNumber(params[1]);
-        json.writeNumber(params[2]);
-        json.writeNumber(params[3]);
-        json.endArray();
-        json.endMember();
-    }
-}
-
-
-static inline void
-dumpShadersUniforms(JSONWriter &json)
-{
-    GLint program = 0;
-    glGetIntegerv(GL_CURRENT_PROGRAM, &program);
-
-    GLhandleARB programObj = glGetHandleARB(GL_PROGRAM_OBJECT_ARB);
-
-    json.beginMember("shaders");
-    json.beginObject();
-    if (program) {
-        dumpProgram(json, program);
-    } else if (programObj) {
-        dumpProgramObj(json, programObj);
-    } else {
-        dumpArbProgram(json, GL_FRAGMENT_PROGRAM_ARB);
-        dumpArbProgram(json, GL_VERTEX_PROGRAM_ARB);
-    }
-    json.endObject();
-    json.endMember(); // shaders
-
-    json.beginMember("uniforms");
-    json.beginObject();
-    if (program) {
-        dumpProgramUniforms(json, program);
-    } else if (programObj) {
-        dumpProgramObjUniforms(json, programObj);
-    } else {
-        dumpArbProgramUniforms(json, GL_FRAGMENT_PROGRAM_ARB, "fp.");
-        dumpArbProgramUniforms(json, GL_VERTEX_PROGRAM_ARB, "vp.");
-    }
-    json.endObject();
-    json.endMember(); // uniforms
-}
-
-
-static inline void
-dumpTextureImage(JSONWriter &json, Context &context, GLenum target, GLint level)
-{
-    GLint width, height = 1, depth = 1;
-    GLint format;
-
-    glGetTexLevelParameteriv(target, level, GL_TEXTURE_INTERNAL_FORMAT, &format);
-
-    width = 0;
-    glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &width);
-
-    if (target != GL_TEXTURE_1D) {
-        height = 0;
-        glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &height);
-        if (target == GL_TEXTURE_3D) {
-            depth = 0;
-            glGetTexLevelParameteriv(target, level, GL_TEXTURE_DEPTH, &depth);
-        }
-    }
-
-    if (width <= 0 || height <= 0 || depth <= 0) {
-        return;
-    } else {
-        char label[512];
-
-        GLint active_texture = GL_TEXTURE0;
-        glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
-        snprintf(label, sizeof label, "%s, %s, level = %d",
-                 enumToString(active_texture), enumToString(target), level);
-
-        json.beginMember(label);
-
-        json.beginObject();
-
-        // Tell the GUI this is no ordinary object, but an image
-        json.writeStringMember("__class__", "image");
-
-        json.writeNumberMember("__width__", width);
-        json.writeNumberMember("__height__", height);
-        json.writeNumberMember("__depth__", depth);
-
-        json.writeStringMember("__format__", enumToString(format));
-
-        // Hardcoded for now, but we could chose types more adequate to the
-        // texture internal format
-        json.writeStringMember("__type__", "uint8");
-        json.writeBoolMember("__normalized__", true);
-        json.writeNumberMember("__channels__", 4);
-
-        GLubyte *pixels = new GLubyte[depth*width*height*4];
-
-        context.resetPixelPackState();
-
-        glGetTexImage(target, level, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
-
-        context.restorePixelPackState();
-
-        json.beginMember("__data__");
-        char *pngBuffer;
-        int pngBufferSize;
-        image::writePixelsToBuffer(pixels, width, height, 4, true, &pngBuffer, &pngBufferSize);
-        json.writeBase64(pngBuffer, pngBufferSize);
-        free(pngBuffer);
-        json.endMember(); // __data__
-
-        delete [] pixels;
-        json.endObject();
-    }
-}
-
-
-static inline void
-dumpTexture(JSONWriter &json, Context &context, GLenum target, GLenum binding)
-{
-    GLint texture_binding = 0;
-    glGetIntegerv(binding, &texture_binding);
-    if (!glIsEnabled(target) && !texture_binding) {
-        return;
-    }
-
-    GLint level = 0;
-    do {
-        GLint width = 0, height = 0;
-        glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &width);
-        glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &height);
-        if (!width || !height) {
-            break;
-        }
-
-        if (target == GL_TEXTURE_CUBE_MAP) {
-            for (int face = 0; face < 6; ++face) {
-                dumpTextureImage(json, context, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level);
-            }
-        } else {
-            dumpTextureImage(json, context, target, level);
-        }
-
-        ++level;
-    } while(true);
-}
-
-
-static inline void
-dumpTextures(JSONWriter &json, Context &context)
-{
-    json.beginMember("textures");
-    json.beginObject();
-    GLint active_texture = GL_TEXTURE0;
-    glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
-    GLint max_texture_coords = 0;
-    glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);
-    GLint max_combined_texture_image_units = 0;
-    glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_combined_texture_image_units);
-    GLint max_units = std::max(max_combined_texture_image_units, max_texture_coords);
-    for (GLint unit = 0; unit < max_units; ++unit) {
-        GLenum texture = GL_TEXTURE0 + unit;
-        glActiveTexture(texture);
-        dumpTexture(json, context, GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D);
-        dumpTexture(json, context, GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D);
-        dumpTexture(json, context, GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D);
-        dumpTexture(json, context, GL_TEXTURE_RECTANGLE, GL_TEXTURE_BINDING_RECTANGLE);
-        dumpTexture(json, context, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP);
-    }
-    glActiveTexture(active_texture);
-    json.endObject();
-    json.endMember(); // textures
-}
-
-
-static bool
-getDrawableBounds(GLint *width, GLint *height) {
-#if defined(TRACE_EGL)
-
-    EGLContext currentContext = eglGetCurrentContext();
-    if (currentContext == EGL_NO_CONTEXT) {
-        return false;
-    }
-
-    EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW);
-    if (currentSurface == EGL_NO_SURFACE) {
-        return false;
-    }
-
-    EGLDisplay currentDisplay = eglGetCurrentDisplay();
-    if (currentDisplay == EGL_NO_DISPLAY) {
-        return false;
-    }
-
-    if (!eglQuerySurface(currentDisplay, currentSurface, EGL_WIDTH, width) ||
-        !eglQuerySurface(currentDisplay, currentSurface, EGL_HEIGHT, height)) {
-        return false;
-    }
-
-    return true;
-
-#elif defined(_WIN32)
-
-    HDC hDC = wglGetCurrentDC();
-    if (!hDC) {
-        return false;
-    }
-
-    HWND hWnd = WindowFromDC(hDC);
-    RECT rect;
-
-    if (!GetClientRect(hWnd, &rect)) {
-       return false;
-    }
-
-    *width  = rect.right  - rect.left;
-    *height = rect.bottom - rect.top;
-    return true;
-
-#elif defined(__APPLE__)
-
-    CGLContextObj ctx = CGLGetCurrentContext();
-    if (ctx == NULL) {
-        return false;
-    }
-
-    CGSConnectionID cid;
-    CGSWindowID wid;
-    CGSSurfaceID sid;
-
-    if (CGLGetSurface(ctx, &cid, &wid, &sid) != kCGLNoError) {
-        return false;
-    }
-
-    CGRect rect;
-
-    if (CGSGetSurfaceBounds(cid, wid, sid, &rect) != 0) {
-        return false;
-    }
-
-    *width = rect.size.width;
-    *height = rect.size.height;
-    return true;
-
-#elif defined(HAVE_X11)
-
-    Display *display;
-    Drawable drawable;
-    Window root;
-    int x, y;
-    unsigned int w, h, bw, depth;
-
-    display = glXGetCurrentDisplay();
-    if (!display) {
-        return false;
-    }
-
-    drawable = glXGetCurrentDrawable();
-    if (drawable == None) {
-        return false;
-    }
-
-    if (!XGetGeometry(display, drawable, &root, &x, &y, &w, &h, &bw, &depth)) {
-        return false;
-    }
-
-    *width = w;
-    *height = h;
-    return true;
-
-#else
-
-    return false;
-
-#endif
-}
-
-
-static const GLenum texture_bindings[][2] = {
-    {GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D},
-    {GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D},
-    {GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D},
-    {GL_TEXTURE_RECTANGLE, GL_TEXTURE_BINDING_RECTANGLE},
-    {GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP}
-};
-
-
-static bool
-bindTexture(GLint texture, GLenum &target, GLint &bound_texture)
-{
-
-    for (unsigned i = 0; i < sizeof(texture_bindings)/sizeof(texture_bindings[0]); ++i) {
-        target  = texture_bindings[i][0];
-
-        GLenum binding = texture_bindings[i][1];
-
-        while (glGetError() != GL_NO_ERROR)
-            ;
-
-        glGetIntegerv(binding, &bound_texture);
-        glBindTexture(target, texture);
-
-        if (glGetError() == GL_NO_ERROR) {
-            return true;
-        }
-
-        glBindTexture(target, bound_texture);
-    }
-
-    target = GL_NONE;
-
-    return false;
-}
-
-
-static bool
-getTextureLevelSize(GLint texture, GLint level, GLint *width, GLint *height)
-{
-    *width = 0;
-    *height = 0;
-
-    GLenum target;
-    GLint bound_texture = 0;
-    if (!bindTexture(texture, target, bound_texture)) {
-        return false;
-    }
-
-    glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, width);
-    glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, height);
-
-    glBindTexture(target, bound_texture);
-
-    return *width > 0 && *height > 0;
-}
-
-
-static GLenum
-getTextureLevelFormat(GLint texture, GLint level)
-{
-    GLenum target;
-    GLint bound_texture = 0;
-    if (!bindTexture(texture, target, bound_texture)) {
-        return GL_NONE;
-    }
-
-    GLint format = GL_NONE;
-    glGetTexLevelParameteriv(target, level, GL_TEXTURE_INTERNAL_FORMAT, &format);
-
-    glBindTexture(target, bound_texture);
-
-    return format;
-}
-
-
-
-static bool
-getRenderbufferSize(GLint renderbuffer, GLint *width, GLint *height)
-{
-    GLint bound_renderbuffer = 0;
-    glGetIntegerv(GL_RENDERBUFFER_BINDING, &bound_renderbuffer);
-    glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
-
-    *width = 0;
-    *height = 0;
-    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, width);
-    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, height);
-
-    glBindRenderbuffer(GL_RENDERBUFFER, bound_renderbuffer);
-    
-    return *width > 0 && *height > 0;
-}
-
-
-static GLenum
-getRenderbufferFormat(GLint renderbuffer)
-{
-    GLint bound_renderbuffer = 0;
-    glGetIntegerv(GL_RENDERBUFFER_BINDING, &bound_renderbuffer);
-    glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
-
-    GLint format = GL_NONE;
-    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_INTERNAL_FORMAT, &format);
-
-    glBindRenderbuffer(GL_RENDERBUFFER, bound_renderbuffer);
-    
-    return format;
-}
-
-
-static bool
-getFramebufferAttachmentSize(GLenum target, GLenum attachment, GLint *width, GLint *height)
-{
-    GLint object_type = GL_NONE;
-    glGetFramebufferAttachmentParameteriv(target, attachment,
-                                          GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
-                                          &object_type);
-    if (object_type == GL_NONE) {
-        return false;
-    }
-
-    GLint object_name = 0;
-    glGetFramebufferAttachmentParameteriv(target, attachment,
-                                          GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
-                                          &object_name);
-    if (object_name == 0) {
-        return false;
-    }
-
-    if (object_type == GL_RENDERBUFFER) {
-        return getRenderbufferSize(object_name, width, height);
-    } else if (object_type == GL_TEXTURE) {
-        GLint texture_level = 0;
-        glGetFramebufferAttachmentParameteriv(target, attachment,
-                                              GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL,
-                                              &texture_level);
-        return getTextureLevelSize(object_name, texture_level, width, height);
-    } else {
-        std::cerr << "warning: unexpected GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = " << object_type << "\n";
-        return false;
-    }
-}
-
-
-
-static GLint
-getFramebufferAttachmentFormat(GLenum target, GLenum attachment)
-{
-    GLint object_type = GL_NONE;
-    glGetFramebufferAttachmentParameteriv(target, attachment,
-                                          GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
-                                          &object_type);
-    if (object_type == GL_NONE) {
-        return GL_NONE;
-    }
-
-    GLint object_name = 0;
-    glGetFramebufferAttachmentParameteriv(target, attachment,
-                                          GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
-                                          &object_name);
-    if (object_name == 0) {
-        return GL_NONE;
-    }
-
-    if (object_type == GL_RENDERBUFFER) {
-        return getRenderbufferFormat(object_name);
-    } else if (object_type == GL_TEXTURE) {
-        GLint texture_level = 0;
-        glGetFramebufferAttachmentParameteriv(target, attachment,
-                                              GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL,
-                                              &texture_level);
-        return getTextureLevelFormat(object_name, texture_level);
-    } else {
-        std::cerr << "warning: unexpected GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = " << object_type << "\n";
-        return GL_NONE;
-    }
-}
-
-
-
-image::Image *
-getDrawBufferImage() {
-    GLenum format = GL_RGB;
-    GLint channels = __gl_format_channels(format);
-    if (channels > 4) {
-        return NULL;
-    }
-
-    Context context;
-
-    GLenum framebuffer_binding;
-    GLenum framebuffer_target;
-    if (context.ES) {
-        framebuffer_binding = GL_FRAMEBUFFER_BINDING;
-        framebuffer_target = GL_FRAMEBUFFER;
-    } else {
-        framebuffer_binding = GL_DRAW_FRAMEBUFFER_BINDING;
-        framebuffer_target = GL_DRAW_FRAMEBUFFER;
-    }
-
-    GLint draw_framebuffer = 0;
-    glGetIntegerv(framebuffer_binding, &draw_framebuffer);
-
-    GLint draw_buffer = GL_NONE;
-    GLint width, height;
-    if (draw_framebuffer) {
-        if (context.ARB_draw_buffers) {
-            glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer);
-            if (draw_buffer == GL_NONE) {
-                return NULL;
-            }
-        }
-
-        if (!getFramebufferAttachmentSize(framebuffer_target, draw_buffer, &width, &height)) {
-            return NULL;
-        }
-    } else {
-        if (!context.ES) {
-            glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
-            if (draw_buffer == GL_NONE) {
-                return NULL;
-            }
-        }
-
-        if (!getDrawableBounds(&width, &height)) {
-            return NULL;
-        }
-    }
-
-    image::Image *image = new image::Image(width, height, channels, true);
-    if (!image) {
-        return NULL;
-    }
-
-    while (glGetError() != GL_NO_ERROR) {}
-
-    GLint read_framebuffer = 0;
-    GLint read_buffer = 0;
-    if (!context.ES) {
-        glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
-        glBindFramebuffer(GL_READ_FRAMEBUFFER, draw_framebuffer);
-
-        glGetIntegerv(GL_READ_BUFFER, &read_buffer);
-        glReadBuffer(draw_buffer);
-    }
-
-    // TODO: reset imaging state too
-    context.resetPixelPackState();
-
-    glReadPixels(0, 0, width, height, format, GL_UNSIGNED_BYTE, image->pixels);
-
-    context.restorePixelPackState();
-
-    if (!context.ES) {
-        glReadBuffer(read_buffer);
-        glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
-    }
-
-    GLenum error = glGetError();
-    if (error != GL_NO_ERROR) {
-        do {
-            std::cerr << "warning: " << enumToString(error) << " while getting snapshot\n";
-            error = glGetError();
-        } while(error != GL_NO_ERROR);
-        delete image;
-        return NULL;
-    }
-     
-    return image;
-}
-
-
-/**
- * Dump the image of the currently bound read buffer.
- */
-static inline void
-dumpReadBufferImage(JSONWriter &json, GLint width, GLint height, GLenum format,
-                    GLint internalFormat = GL_NONE)
-{
-    GLint channels = __gl_format_channels(format);
-
-    Context context;
-
-    json.beginObject();
-
-    // Tell the GUI this is no ordinary object, but an image
-    json.writeStringMember("__class__", "image");
-
-    json.writeNumberMember("__width__", width);
-    json.writeNumberMember("__height__", height);
-    json.writeNumberMember("__depth__", 1);
-
-    json.writeStringMember("__format__", enumToString(internalFormat));
-
-    // Hardcoded for now, but we could chose types more adequate to the
-    // texture internal format
-    json.writeStringMember("__type__", "uint8");
-    json.writeBoolMember("__normalized__", true);
-    json.writeNumberMember("__channels__", channels);
-
-    GLubyte *pixels = new GLubyte[width*height*channels];
-
-    // TODO: reset imaging state too
-    context.resetPixelPackState();
-
-    glReadPixels(0, 0, width, height, format, GL_UNSIGNED_BYTE, pixels);
-
-    context.restorePixelPackState();
-
-    json.beginMember("__data__");
-    char *pngBuffer;
-    int pngBufferSize;
-    image::writePixelsToBuffer(pixels, width, height, channels, true, &pngBuffer, &pngBufferSize);
-    //std::cerr <<" Before = "<<(width * height * channels * sizeof *pixels)
-    //          <<", after = "<<pngBufferSize << ", ratio = " << double(width * height * channels * sizeof *pixels)/pngBufferSize;
-    json.writeBase64(pngBuffer, pngBufferSize);
-    free(pngBuffer);
-    json.endMember(); // __data__
-
-    delete [] pixels;
-    json.endObject();
-}
-
-
-static inline GLuint
-downsampledFramebuffer(GLuint oldFbo, GLint drawbuffer,
-                       GLint colorRb, GLint depthRb, GLint stencilRb,
-                       GLuint *rbs, GLint *numRbs)
-{
-    GLuint fbo;
-    GLint format;
-    GLint w, h;
-
-    *numRbs = 0;
-
-    glGenFramebuffers(1, &fbo);
-    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
-
-    glBindRenderbuffer(GL_RENDERBUFFER, colorRb);
-    glGetRenderbufferParameteriv(GL_RENDERBUFFER,
-                                 GL_RENDERBUFFER_WIDTH, &w);
-    glGetRenderbufferParameteriv(GL_RENDERBUFFER,
-                                 GL_RENDERBUFFER_HEIGHT, &h);
-    glGetRenderbufferParameteriv(GL_RENDERBUFFER,
-                                 GL_RENDERBUFFER_INTERNAL_FORMAT, &format);
-
-    glGenRenderbuffers(1, &rbs[*numRbs]);
-    glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
-    glRenderbufferStorage(GL_RENDERBUFFER, format, w, h);
-    glFramebufferRenderbuffer(GL_FRAMEBUFFER, drawbuffer,
-                              GL_RENDERBUFFER, rbs[*numRbs]);
-
-    glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
-    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
-    glDrawBuffer(drawbuffer);
-    glReadBuffer(drawbuffer);
-    glBlitFramebuffer(0, 0, w, h, 0, 0, w, h,
-                      GL_COLOR_BUFFER_BIT, GL_NEAREST);
-    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
-    ++*numRbs;
-
-    if (stencilRb == depthRb && stencilRb) {
-        //combined depth and stencil buffer
-        glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
-        glGetRenderbufferParameteriv(GL_RENDERBUFFER,
-                                     GL_RENDERBUFFER_WIDTH, &w);
-        glGetRenderbufferParameteriv(GL_RENDERBUFFER,
-                                     GL_RENDERBUFFER_HEIGHT, &h);
-        glGetRenderbufferParameteriv(GL_RENDERBUFFER,
-                                     GL_RENDERBUFFER_INTERNAL_FORMAT, &format);
-
-        glGenRenderbuffers(1, &rbs[*numRbs]);
-        glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
-        glRenderbufferStorage(GL_RENDERBUFFER, format, w, h);
-        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
-                                  GL_RENDERBUFFER, rbs[*numRbs]);
-        glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
-        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
-        glBlitFramebuffer(0, 0, w, h, 0, 0, w, h,
-                          GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
-        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
-        ++*numRbs;
-    } else {
-        if (depthRb) {
-            glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
-            glGetRenderbufferParameteriv(GL_RENDERBUFFER,
-                                         GL_RENDERBUFFER_WIDTH, &w);
-            glGetRenderbufferParameteriv(GL_RENDERBUFFER,
-                                         GL_RENDERBUFFER_HEIGHT, &h);
-            glGetRenderbufferParameteriv(GL_RENDERBUFFER,
-                                         GL_RENDERBUFFER_INTERNAL_FORMAT, &format);
-
-            glGenRenderbuffers(1, &rbs[*numRbs]);
-            glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
-            glRenderbufferStorage(GL_RENDERBUFFER, format, w, h);
-            glFramebufferRenderbuffer(GL_FRAMEBUFFER,
-                                      GL_DEPTH_ATTACHMENT,
-                                      GL_RENDERBUFFER, rbs[*numRbs]);
-            glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
-            glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
-            glDrawBuffer(GL_DEPTH_ATTACHMENT);
-            glReadBuffer(GL_DEPTH_ATTACHMENT);
-            glBlitFramebuffer(0, 0, w, h, 0, 0, w, h,
-                              GL_DEPTH_BUFFER_BIT, GL_NEAREST);
-            ++*numRbs;
-        }
-        if (stencilRb) {
-            glBindRenderbuffer(GL_RENDERBUFFER, stencilRb);
-            glGetRenderbufferParameteriv(GL_RENDERBUFFER,
-                                         GL_RENDERBUFFER_WIDTH, &w);
-            glGetRenderbufferParameteriv(GL_RENDERBUFFER,
-                                         GL_RENDERBUFFER_HEIGHT, &h);
-            glGetRenderbufferParameteriv(GL_RENDERBUFFER,
-                                     GL_RENDERBUFFER_INTERNAL_FORMAT, &format);
-
-            glGenRenderbuffers(1, &rbs[*numRbs]);
-            glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
-            glRenderbufferStorage(GL_RENDERBUFFER, format, w, h);
-            glFramebufferRenderbuffer(GL_FRAMEBUFFER,
-                                      GL_STENCIL_ATTACHMENT,
-                                      GL_RENDERBUFFER, rbs[*numRbs]);
-            glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
-            glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
-            glDrawBuffer(GL_STENCIL_ATTACHMENT);
-            glReadBuffer(GL_STENCIL_ATTACHMENT);
-            glBlitFramebuffer(0, 0, w, h, 0, 0, w, h,
-                              GL_STENCIL_BUFFER_BIT, GL_NEAREST);
-            ++*numRbs;
-        }
-    }
-
-    return fbo;
-}
-
-
-/**
- * Dump images of current draw drawable/window.
- */
-static void
-dumpDrawableImages(JSONWriter &json, Context &context)
-{
-    GLint width, height;
-
-    if (!getDrawableBounds(&width, &height)) {
-        return;
-    }
-
-    GLint draw_buffer = GL_NONE;
-    if (context.ES) {
-        draw_buffer = GL_BACK;
-    } else {
-        glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
-        glReadBuffer(draw_buffer);
-    }
-
-    if (draw_buffer != GL_NONE) {
-        GLint read_buffer = GL_NONE;
-        if (!context.ES) {
-            glGetIntegerv(GL_READ_BUFFER, &read_buffer);
-        }
-
-        GLint alpha_bits = 0;
-#if 0
-        // XXX: Ignore alpha until we are able to match the traced visual
-        glGetIntegerv(GL_ALPHA_BITS, &alpha_bits);
-#endif
-        GLenum format = alpha_bits ? GL_RGBA : GL_RGB;
-        json.beginMember(enumToString(draw_buffer));
-        dumpReadBufferImage(json, width, height, format);
-        json.endMember();
-
-        if (!context.ES) {
-            glReadBuffer(read_buffer);
-        }
-    }
-
-    if (!context.ES) {
-        GLint depth_bits = 0;
-        glGetIntegerv(GL_DEPTH_BITS, &depth_bits);
-        if (depth_bits) {
-            json.beginMember("GL_DEPTH_COMPONENT");
-            dumpReadBufferImage(json, width, height, GL_DEPTH_COMPONENT);
-            json.endMember();
-        }
-
-        GLint stencil_bits = 0;
-        glGetIntegerv(GL_STENCIL_BITS, &stencil_bits);
-        if (stencil_bits) {
-            json.beginMember("GL_STENCIL_INDEX");
-            dumpReadBufferImage(json, width, height, GL_STENCIL_INDEX);
-            json.endMember();
-        }
-    }
-}
-
-
-/**
- * Dump the specified framebuffer attachment.
- *
- * In the case of a color attachment, it assumes it is already bound for read.
- */
-static void
-dumpFramebufferAttachment(JSONWriter &json, GLenum target, GLenum attachment, GLenum format)
-{
-    GLint width = 0, height = 0;
-    if (!getFramebufferAttachmentSize(target, attachment, &width, &height)) {
-        return;
-    }
-
-    GLint internalFormat = getFramebufferAttachmentFormat(target, attachment);
-
-    json.beginMember(enumToString(attachment));
-    dumpReadBufferImage(json, width, height, format, internalFormat);
-    json.endMember();
-}
-
-
-static void
-dumpFramebufferAttachments(JSONWriter &json, Context &context, GLenum target)
-{
-    GLint read_framebuffer = 0;
-    glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
-
-    GLint read_buffer = GL_NONE;
-    glGetIntegerv(GL_READ_BUFFER, &read_buffer);
-
-    GLint max_draw_buffers = 1;
-    glGetIntegerv(GL_MAX_DRAW_BUFFERS, &max_draw_buffers);
-    GLint max_color_attachments = 0;
-    glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments);
-
-    for (GLint i = 0; i < max_draw_buffers; ++i) {
-        GLint draw_buffer = GL_NONE;
-        glGetIntegerv(GL_DRAW_BUFFER0 + i, &draw_buffer);
-        if (draw_buffer != GL_NONE) {
-            glReadBuffer(draw_buffer);
-            GLint attachment;
-            if (draw_buffer >= GL_COLOR_ATTACHMENT0 && draw_buffer < GL_COLOR_ATTACHMENT0 + max_color_attachments) {
-                attachment = draw_buffer;
-            } else {
-                std::cerr << "warning: unexpected GL_DRAW_BUFFER" << i << " = " << draw_buffer << "\n";
-                attachment = GL_COLOR_ATTACHMENT0;
-            }
-            GLint alpha_size = 0;
-            glGetFramebufferAttachmentParameteriv(target, attachment,
-                                                  GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE,
-                                                  &alpha_size);
-            GLenum format = alpha_size ? GL_RGBA : GL_RGB;
-            dumpFramebufferAttachment(json, target, attachment, format);
-        }
-    }
-
-    glReadBuffer(read_buffer);
-
-    if (!context.ES) {
-        dumpFramebufferAttachment(json, target, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT);
-        dumpFramebufferAttachment(json, target, GL_STENCIL_ATTACHMENT, GL_STENCIL_INDEX);
-    }
-
-    glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
-}
-
-
-static void
-dumpFramebuffer(JSONWriter &json, Context &context)
-{
-    json.beginMember("framebuffer");
-    json.beginObject();
-
-    GLint boundDrawFbo = 0, boundReadFbo = 0;
-    glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &boundDrawFbo);
-    glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &boundReadFbo);
-    if (!boundDrawFbo) {
-        dumpDrawableImages(json, context);
-    } else if (context.ES) {
-        dumpFramebufferAttachments(json, context, GL_FRAMEBUFFER);
-    } else {
-        GLint colorRb = 0, stencilRb = 0, depthRb = 0;
-        GLint draw_buffer0 = GL_NONE;
-        glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer0);
-        bool multisample = false;
-
-        GLint boundRb = 0;
-        glGetIntegerv(GL_RENDERBUFFER_BINDING, &boundRb);
-
-        GLint object_type;
-        glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, draw_buffer0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
-        if (object_type == GL_RENDERBUFFER) {
-            glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, draw_buffer0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &colorRb);
-            glBindRenderbuffer(GL_RENDERBUFFER, colorRb);
-            GLint samples = 0;
-            glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
-            if (samples) {
-                multisample = true;
-            }
-        }
-
-        glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
-        if (object_type == GL_RENDERBUFFER) {
-            glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthRb);
-            glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
-            GLint samples = 0;
-            glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
-            if (samples) {
-                multisample = true;
-            }
-        }
-
-        glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
-        if (object_type == GL_RENDERBUFFER) {
-            glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &stencilRb);
-            glBindRenderbuffer(GL_RENDERBUFFER, stencilRb);
-            GLint samples = 0;
-            glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
-            if (samples) {
-                multisample = true;
-            }
-        }
-
-        glBindRenderbuffer(GL_RENDERBUFFER, boundRb);
-
-        GLuint rbs[3];
-        GLint numRbs = 0;
-        GLuint fboCopy = 0;
-
-        if (multisample) {
-            // glReadPixels doesnt support multisampled buffers so we need
-            // to blit the fbo to a temporary one
-            fboCopy = downsampledFramebuffer(boundDrawFbo, draw_buffer0,
-                                             colorRb, depthRb, stencilRb,
-                                             rbs, &numRbs);
-        }
-
-        dumpFramebufferAttachments(json, context, GL_DRAW_FRAMEBUFFER);
-
-        if (multisample) {
-            glBindRenderbuffer(GL_RENDERBUFFER_BINDING, boundRb);
-            glDeleteRenderbuffers(numRbs, rbs);
-            glDeleteFramebuffers(1, &fboCopy);
-        }
-
-        glBindFramebuffer(GL_READ_FRAMEBUFFER, boundReadFbo);
-        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, boundDrawFbo);
-    }
-
-    json.endObject();
-    json.endMember(); // framebuffer
-}
-
-
 static const GLenum bindings[] = {
     GL_DRAW_BUFFER,
     GL_READ_BUFFER,
index dc73e58082bb2cbab1bb7a46e1a27a327d47364c..6fb615fae91ab6332bdde801d7c66b2655dd3d3e 100644 (file)
@@ -32,9 +32,6 @@
 #include "glimports.hpp"
 
 
-class JSONWriter;
-
-
 namespace image {
     class Image;
 }
diff --git a/glstate.py b/glstate.py
deleted file mode 100644 (file)
index ad8e258..0000000
+++ /dev/null
@@ -1,503 +0,0 @@
-##########################################################################
-#
-# Copyright 2011 Jose Fonseca
-# All Rights Reserved.
-#
-# 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.
-#
-##########################################################################/
-
-
-'''Generate code to dump most GL state into JSON.'''
-
-
-from specs.stdapi import *
-
-from specs.gltypes import *
-from specs.glparams import *
-
-
-texture_targets = [
-    ('GL_TEXTURE_1D', 'GL_TEXTURE_BINDING_1D'),
-    ('GL_TEXTURE_2D', 'GL_TEXTURE_BINDING_2D'),
-    ('GL_TEXTURE_3D', 'GL_TEXTURE_BINDING_3D'),
-    ('GL_TEXTURE_RECTANGLE', 'GL_TEXTURE_BINDING_RECTANGLE'),
-    ('GL_TEXTURE_CUBE_MAP', 'GL_TEXTURE_BINDING_CUBE_MAP')
-]
-
-framebuffer_targets = [
-    ('GL_DRAW_FRAMEBUFFER', 'GL_DRAW_FRAMEBUFFER_BINDING'),
-    ('GL_READ_FRAMEBUFFER', 'GL_READ_FRAMEBUFFER_BINDING'),
-]
-
-class GetInflector:
-    '''Objects that describes how to inflect.'''
-
-    reduced_types = {
-        B: I,
-        E: I,
-        I: F,
-    }
-
-    def __init__(self, radical, inflections, suffix = ''):
-        self.radical = radical
-        self.inflections = inflections
-        self.suffix = suffix
-
-    def reduced_type(self, type):
-        if type in self.inflections:
-            return type
-        if type in self.reduced_types:
-            return self.reduced_type(self.reduced_types[type])
-        raise NotImplementedError
-
-    def inflect(self, type):
-        return self.radical + self.inflection(type) + self.suffix
-
-    def inflection(self, type):
-        type = self.reduced_type(type)
-        assert type in self.inflections
-        return self.inflections[type]
-
-    def __str__(self):
-        return self.radical + self.suffix
-
-
-class StateGetter(Visitor):
-    '''Type visitor that is able to extract the state via one of the glGet*
-    functions.
-
-    It will declare any temporary variable
-    '''
-
-    def __init__(self, radical, inflections, suffix=''):
-        self.inflector = GetInflector(radical, inflections)
-        self.suffix = suffix
-
-    def iter(self):
-        for function, type, count, name in parameters:
-            inflection = self.inflector.radical + self.suffix
-            if inflection not in function.split(','):
-                continue
-            if type is X:
-                continue
-            yield type, count, name
-
-    def __call__(self, *args):
-        pname = args[-1]
-
-        for type, count, name in self.iter():
-            if name == pname:
-                if count != 1:
-                    type = Array(type, str(count))
-
-                return type, self.visit(type, args)
-
-        raise NotImplementedError
-
-    def temp_name(self, args):
-        '''Return the name of a temporary variable to hold the state.'''
-        pname = args[-1]
-
-        return pname[3:].lower()
-
-    def visitConst(self, const, args):
-        return self.visit(const.type, args)
-
-    def visitScalar(self, type, args):
-        temp_name = self.temp_name(args)
-        elem_type = self.inflector.reduced_type(type)
-        inflection = self.inflector.inflect(type)
-        if inflection.endswith('v'):
-            print '    %s %s = 0;' % (elem_type, temp_name)
-            print '    %s(%s, &%s);' % (inflection + self.suffix, ', '.join(args), temp_name)
-        else:
-            print '    %s %s = %s(%s);' % (elem_type, temp_name, inflection + self.suffix, ', '.join(args))
-        return temp_name
-
-    def visitString(self, string, args):
-        temp_name = self.temp_name(args)
-        inflection = self.inflector.inflect(string)
-        assert not inflection.endswith('v')
-        print '    %s %s = (%s)%s(%s);' % (string, temp_name, string, inflection + self.suffix, ', '.join(args))
-        return temp_name
-
-    def visitAlias(self, alias, args):
-        return self.visitScalar(alias, args)
-
-    def visitEnum(self, enum, args):
-        return self.visit(GLint, args)
-
-    def visitBitmask(self, bitmask, args):
-        return self.visit(GLint, args)
-
-    def visitArray(self, array, args):
-        temp_name = self.temp_name(args)
-        if array.length == '1':
-            return self.visit(array.type)
-        elem_type = self.inflector.reduced_type(array.type)
-        inflection = self.inflector.inflect(array.type)
-        assert inflection.endswith('v')
-        print '    %s %s[%s + 1];' % (elem_type, temp_name, array.length)
-        print '    memset(%s, 0, %s * sizeof *%s);' % (temp_name, array.length, temp_name)
-        print '    %s[%s] = (%s)0xdeadc0de;' % (temp_name, array.length, elem_type)
-        print '    %s(%s, %s);' % (inflection + self.suffix, ', '.join(args), temp_name)
-        # Simple buffer overflow detection
-        print '    assert(%s[%s] == (%s)0xdeadc0de);' % (temp_name, array.length, elem_type)
-        return temp_name
-
-    def visitOpaque(self, pointer, args):
-        temp_name = self.temp_name(args)
-        inflection = self.inflector.inflect(pointer)
-        assert inflection.endswith('v')
-        print '    GLvoid *%s;' % temp_name
-        print '    %s(%s, &%s);' % (inflection + self.suffix, ', '.join(args), temp_name)
-        return temp_name
-
-
-glGet = StateGetter('glGet', {
-    B: 'Booleanv',
-    I: 'Integerv',
-    F: 'Floatv',
-    D: 'Doublev',
-    S: 'String',
-    P: 'Pointerv',
-})
-
-glGetMaterial = StateGetter('glGetMaterial', {I: 'iv', F: 'fv'})
-glGetLight = StateGetter('glGetLight', {I: 'iv', F: 'fv'})
-glGetVertexAttrib = StateGetter('glGetVertexAttrib', {I: 'iv', F: 'fv', D: 'dv', P: 'Pointerv'})
-glGetTexParameter = StateGetter('glGetTexParameter', {I: 'iv', F: 'fv'})
-glGetTexEnv = StateGetter('glGetTexEnv', {I: 'iv', F: 'fv'})
-glGetTexLevelParameter = StateGetter('glGetTexLevelParameter', {I: 'iv', F: 'fv'})
-glGetShader = StateGetter('glGetShaderiv', {I: 'iv'})
-glGetProgram = StateGetter('glGetProgram', {I: 'iv'})
-glGetProgramARB = StateGetter('glGetProgram', {I: 'iv', F: 'fv', S: 'Stringv'}, 'ARB')
-glGetFramebufferAttachmentParameter = StateGetter('glGetFramebufferAttachmentParameter', {I: 'iv'})
-
-
-class JsonWriter(Visitor):
-    '''Type visitor that will dump a value of the specified type through the
-    JSON writer.
-    
-    It expects a previously declared JSONWriter instance named "json".'''
-
-    def visitLiteral(self, literal, instance):
-        if literal.kind == 'Bool':
-            print '    json.writeBool(%s);' % instance
-        elif literal.kind in ('SInt', 'Uint', 'Float', 'Double'):
-            print '    json.writeNumber(%s);' % instance
-        else:
-            raise NotImplementedError
-
-    def visitString(self, string, instance):
-        assert string.length is None
-        print '    json.writeString((const char *)%s);' % instance
-
-    def visitEnum(self, enum, instance):
-        if enum.expr == 'GLenum':
-            print '    dumpEnum(json, %s);' % instance
-        else:
-            print '    json.writeNumber(%s);' % instance
-
-    def visitBitmask(self, bitmask, instance):
-        raise NotImplementedError
-
-    def visitAlias(self, alias, instance):
-        self.visit(alias.type, instance)
-
-    def visitOpaque(self, opaque, instance):
-        print '    json.writeNumber((size_t)%s);' % instance
-
-    __index = 0
-
-    def visitArray(self, array, instance):
-        index = '__i%u' % JsonWriter.__index
-        JsonWriter.__index += 1
-        print '    json.beginArray();'
-        print '    for (unsigned %s = 0; %s < %s; ++%s) {' % (index, index, array.length, index)
-        self.visit(array.type, '%s[%s]' % (instance, index))
-        print '    }'
-        print '    json.endArray();'
-
-
-
-class StateDumper:
-    '''Class to generate code to dump all GL state in JSON format via
-    stdout.'''
-
-    def __init__(self):
-        pass
-
-    def dump(self):
-        print '#include <string.h>'
-        print
-        print '#include "json.hpp"'
-        print '#include "glproc.hpp"'
-        print '#include "glsize.hpp"'
-        print '#include "glstate.hpp"'
-        print '#include "glstate_internal.hpp"'
-        print
-        print 'namespace glstate {'
-        print
-
-        print 'const char *'
-        print 'enumToString(GLenum pname)'
-        print '{'
-        print '    switch (pname) {'
-        for name in GLenum.values:
-            print '    case %s:' % name
-            print '        return "%s";' % name
-        print '    default:'
-        print '        return NULL;'
-        print '    }'
-        print '}'
-        print
-
-        print 'static void'
-        print 'dumpFramebufferAttachementParameters(JSONWriter &json, GLenum target, GLenum attachment)'
-        print '{'
-        self.dump_attachment_parameters('target', 'attachment')
-        print '}'
-        print
-
-        print 'void'
-        print 'dumpEnum(JSONWriter &json, GLenum pname)'
-        print '{'
-        print '    const char *s = enumToString(pname);'
-        print '    if (s) {'
-        print '        json.writeString(s);'
-        print '    } else {'
-        print '        json.writeNumber(pname);'
-        print '    }'
-        print '}'
-        print
-
-        print 'void dumpParameters(JSONWriter &json, Context &context)'
-        print '{'
-        print '    json.beginMember("parameters");'
-        print '    json.beginObject();'
-        
-        self.dump_atoms(glGet)
-        
-        self.dump_material_params()
-        self.dump_light_params()
-        self.dump_vertex_attribs()
-        self.dump_program_params()
-        self.dump_texture_parameters()
-        self.dump_framebuffer_parameters()
-
-        print '    json.endObject();'
-        print '    json.endMember(); // parameters'
-        print '}'
-        print
-        
-        print '} /*namespace glstate */'
-
-    def dump_material_params(self):
-        print '    if (!context.ES) {'
-        for face in ['GL_FRONT', 'GL_BACK']:
-            print '    json.beginMember("%s");' % face
-            print '    json.beginObject();'
-            self.dump_atoms(glGetMaterial, face)
-            print '    json.endObject();'
-        print '    }'
-        print
-
-    def dump_light_params(self):
-        print '    GLint max_lights = 0;'
-        print '    __glGetIntegerv(GL_MAX_LIGHTS, &max_lights);'
-        print '    for (GLint index = 0; index < max_lights; ++index) {'
-        print '        GLenum light = GL_LIGHT0 + index;'
-        print '        if (glIsEnabled(light)) {'
-        print '            char name[32];'
-        print '            snprintf(name, sizeof name, "GL_LIGHT%i", index);'
-        print '            json.beginMember(name);'
-        print '            json.beginObject();'
-        self.dump_atoms(glGetLight, '    GL_LIGHT0 + index')
-        print '            json.endObject();'
-        print '            json.endMember(); // GL_LIGHTi'
-        print '        }'
-        print '    }'
-        print
-
-    def texenv_param_target(self, name):
-        if name == 'GL_TEXTURE_LOD_BIAS':
-           return 'GL_TEXTURE_FILTER_CONTROL'
-        elif name == 'GL_COORD_REPLACE':
-           return 'GL_POINT_SPRITE'
-        else:
-           return 'GL_TEXTURE_ENV'
-
-    def dump_texenv_params(self):
-        for target in ['GL_TEXTURE_ENV', 'GL_TEXTURE_FILTER_CONTROL', 'GL_POINT_SPRITE']:
-            print '    if (!context.ES) {'
-            print '        json.beginMember("%s");' % target
-            print '        json.beginObject();'
-            for _, _, name in glGetTexEnv.iter():
-                if self.texenv_param_target(name) == target:
-                    self.dump_atom(glGetTexEnv, target, name) 
-            print '        json.endObject();'
-            print '    }'
-
-    def dump_vertex_attribs(self):
-        print '    GLint max_vertex_attribs = 0;'
-        print '    __glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_vertex_attribs);'
-        print '    for (GLint index = 0; index < max_vertex_attribs; ++index) {'
-        print '        char name[32];'
-        print '        snprintf(name, sizeof name, "GL_VERTEX_ATTRIB_ARRAY%i", index);'
-        print '        json.beginMember(name);'
-        print '        json.beginObject();'
-        self.dump_atoms(glGetVertexAttrib, 'index')
-        print '        json.endObject();'
-        print '        json.endMember(); // GL_VERTEX_ATTRIB_ARRAYi'
-        print '    }'
-        print
-
-    program_targets = [
-        'GL_FRAGMENT_PROGRAM_ARB',
-        'GL_VERTEX_PROGRAM_ARB',
-    ]
-
-    def dump_program_params(self):
-        for target in self.program_targets:
-            print '    if (glIsEnabled(%s)) {' % target
-            print '        json.beginMember("%s");' % target
-            print '        json.beginObject();'
-            self.dump_atoms(glGetProgramARB, target)
-            print '        json.endObject();'
-            print '    }'
-
-    def dump_texture_parameters(self):
-        print '    {'
-        print '        GLint active_texture = GL_TEXTURE0;'
-        print '        glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);'
-        print '        GLint max_texture_coords = 0;'
-        print '        glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);'
-        print '        GLint max_combined_texture_image_units = 0;'
-        print '        glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_combined_texture_image_units);'
-        print '        GLint max_units = std::max(max_combined_texture_image_units, max_texture_coords);'
-        print '        for (GLint unit = 0; unit < max_units; ++unit) {'
-        print '            char name[32];'
-        print '            snprintf(name, sizeof name, "GL_TEXTURE%i", unit);'
-        print '            json.beginMember(name);'
-        print '            glActiveTexture(GL_TEXTURE0 + unit);'
-        print '            json.beginObject();'
-        print '            GLboolean enabled;'
-        print '            GLint binding;'
-        print
-        for target, binding in texture_targets:
-            print '            // %s' % target
-            print '            enabled = GL_FALSE;'
-            print '            glGetBooleanv(%s, &enabled);' % target
-            print '            json.writeBoolMember("%s", enabled);' % target
-            print '            binding = 0;'
-            print '            glGetIntegerv(%s, &binding);' % binding
-            print '            json.writeNumberMember("%s", binding);' % binding
-            print '            if (enabled || binding) {'
-            print '                json.beginMember("%s");' % target
-            print '                json.beginObject();'
-            self.dump_atoms(glGetTexParameter, target)
-            print '                if (!context.ES) {'
-            # We only dump the first level parameters
-            self.dump_atoms(glGetTexLevelParameter, target, "0")
-            print '                }'
-            print '                json.endObject();'
-            print '                json.endMember(); // %s' % target
-            print '            }'
-            print
-        print '            if (unit < max_texture_coords) {'
-        self.dump_texenv_params()
-        print '            }'
-        print '            json.endObject();'
-        print '            json.endMember(); // GL_TEXTUREi'
-        print '        }'
-        print '        glActiveTexture(active_texture);'
-        print '    }'
-        print
-
-    def dump_framebuffer_parameters(self):
-        print '    {'
-        print '        GLint max_color_attachments = 0;'
-        print '        glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments);'
-        print '        GLint framebuffer;'
-        for target, binding in framebuffer_targets:
-            print '            // %s' % target
-            print '            framebuffer = 0;'
-            print '            glGetIntegerv(%s, &framebuffer);' % binding
-            print '            if (framebuffer) {'
-            print '                json.beginMember("%s");' % target
-            print '                json.beginObject();'
-            print '                for (GLint i = 0; i < max_color_attachments; ++i) {'
-            print '                    GLint color_attachment = GL_COLOR_ATTACHMENT0 + i;'
-            print '                    dumpFramebufferAttachementParameters(json, %s, color_attachment);' % target
-            print '                }'
-            print '                dumpFramebufferAttachementParameters(json, %s, GL_DEPTH_ATTACHMENT);' % target
-            print '                dumpFramebufferAttachementParameters(json, %s, GL_STENCIL_ATTACHMENT);' % target
-            print '                json.endObject();'
-            print '                json.endMember(); // %s' % target
-            print '            }'
-            print
-        print '    }'
-        print
-
-    def dump_attachment_parameters(self, target, attachment):
-        print '            {'
-        print '                GLint object_type = GL_NONE;'
-        print '                glGetFramebufferAttachmentParameteriv(%s, %s, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);' % (target, attachment)
-        print '                if (object_type != GL_NONE) {'
-        print '                    json.beginMember(enumToString(%s));' % attachment
-        print '                    json.beginObject();'
-        self.dump_atoms(glGetFramebufferAttachmentParameter, target, attachment)
-        print '                    json.endObject();'
-        print '                    json.endMember(); // GL_x_ATTACHMENT'
-        print '                }'
-        print '            }'
-
-    def dump_atoms(self, getter, *args):
-        for _, _, name in getter.iter():
-            self.dump_atom(getter, *(args + (name,))) 
-
-    def dump_atom(self, getter, *args):
-        name = args[-1]
-
-        # Avoid crash on MacOSX
-        # XXX: The right fix would be to look at the support extensions..
-        import platform
-        if name == 'GL_SAMPLER_BINDING' and platform.system() == 'Darwin':
-            return
-
-        print '        // %s' % name
-        print '        {'
-        #print '            assert(glGetError() == GL_NO_ERROR);'
-        type, value = getter(*args)
-        print '            if (glGetError() != GL_NO_ERROR) {'
-        #print '                std::cerr << "warning: %s(%s) failed\\n";' % (inflection, name)
-        print '                while (glGetError() != GL_NO_ERROR) {}'
-        print '            } else {'
-        print '                json.beginMember("%s");' % name
-        JsonWriter().visit(type, value)
-        print '                json.endMember();'
-        print '            }'
-        print '        }'
-        print
-
-
-if __name__ == '__main__':
-    StateDumper().dump()
diff --git a/glstate_images.cpp b/glstate_images.cpp
new file mode 100644 (file)
index 0000000..2c7c66b
--- /dev/null
@@ -0,0 +1,1146 @@
+/**************************************************************************
+ *
+ * Copyright 2011 Jose Fonseca
+ * All Rights Reserved.
+ *
+ * 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 <string.h>
+
+#include <algorithm>
+#include <iostream>
+
+#include "image.hpp"
+#include "json.hpp"
+#include "glproc.hpp"
+#include "glsize.hpp"
+#include "glstate.hpp"
+#include "glstate_internal.hpp"
+
+
+#ifdef __linux__
+#include <dlfcn.h>
+#endif
+
+#ifdef __APPLE__
+
+#include <Carbon/Carbon.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+OSStatus CGSGetSurfaceBounds(CGSConnectionID, CGWindowID, CGSSurfaceID, CGRect *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __APPLE__ */
+
+
+/* Change thi to one to force interpreting depth buffers as RGBA, which enables
+ * visualizing full dynamic range, until we can transmit HDR images to the GUI */
+#define DEPTH_AS_RGBA 0
+
+
+namespace glstate {
+
+
+struct ImageDesc
+{
+    GLint width;
+    GLint height;
+    GLint depth;
+    GLint internalFormat;
+
+    inline
+    ImageDesc() :
+        width(0),
+        height(0),
+        depth(0),
+        internalFormat(GL_NONE)
+    {}
+
+    inline bool
+    valid(void) const {
+        return width > 0 && height > 0 && depth > 0;
+    }
+};
+
+
+/**
+ * OpenGL ES does not support glGetTexLevelParameteriv, but it is possible to
+ * probe whether a texture has a given size by crafting a dummy glTexSubImage()
+ * call.
+ */
+static bool
+probeTextureLevelSizeOES(GLenum target, GLint level, const GLint size[3]) {
+    while (glGetError() != GL_NO_ERROR)
+        ;
+
+    GLenum internalFormat = GL_RGBA;
+    GLenum type = GL_UNSIGNED_BYTE;
+    GLint dummy = 0;
+
+    switch (target) {
+    case GL_TEXTURE_2D:
+    case GL_TEXTURE_CUBE_MAP:
+    case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+    case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+    case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+    case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+    case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+    case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+        glTexSubImage2D(target, level, size[0], size[1], 0, 0, internalFormat, type, &dummy);
+        break;
+    case GL_TEXTURE_3D_OES:
+        glTexSubImage3DOES(target, level, size[0], size[1], size[2], 0, 0, 0, internalFormat, type, &dummy);
+    default:
+        assert(0);
+        return false;
+    }
+
+    GLenum error = glGetError();
+
+    if (0) {
+        std::cerr << "(" << size[0] << ", " << size[1] << ", " << size[2] << ") = " << enumToString(error) << "\n";
+    }
+
+    if (error == GL_NO_ERROR) {
+        return true;
+    }
+
+    while (glGetError() != GL_NO_ERROR)
+        ;
+
+    return false;
+}
+
+
+/**
+ * Bisect the texture size along an axis.
+ *
+ * It is assumed that the texture exists.
+ */
+static GLint
+bisectTextureLevelSizeOES(GLenum target, GLint level, GLint axis, GLint max) {
+    GLint size[3] = {0, 0, 0};
+
+    assert(axis < 3);
+    assert(max >= 0);
+
+    GLint min = 0;
+    while (true) {
+        GLint test = (min + max) / 2;
+        if (test == min) {
+            return min;
+        }
+
+        size[axis] = test;
+
+        if (probeTextureLevelSizeOES(target, level, size)) {
+            min = test;
+        } else {
+            max = test;
+        }
+    }
+}
+
+
+/**
+ * Special path to obtain texture size on OpenGL ES, that does not rely on
+ * glGetTexLevelParameteriv
+ */
+static bool
+getActiveTextureLevelDescOES(Context &context, GLenum target, GLint level, ImageDesc &desc)
+{
+    if (target == GL_TEXTURE_1D) {
+        // OpenGL ES does not support 1D textures
+        return false;
+    }
+
+    const GLint size[3] = {1, 1, 1}; 
+    if (!probeTextureLevelSizeOES(target, level, size)) {
+        return false;
+    }
+
+    // XXX: mere guess
+    desc.internalFormat = GL_RGBA;
+
+    GLint maxSize = 0;
+    switch (target) {
+    case GL_TEXTURE_2D:
+        glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
+        desc.width = bisectTextureLevelSizeOES(target, level, 0, maxSize);
+        desc.height = bisectTextureLevelSizeOES(target, level, 1, maxSize);
+        desc.depth = 1;
+        break;
+    case GL_TEXTURE_CUBE_MAP:
+    case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+    case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+    case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+    case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+    case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+    case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+        glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &maxSize);
+        desc.width = bisectTextureLevelSizeOES(target, level, 0, maxSize);
+        desc.height = desc.width;
+        desc.depth = 1;
+        break;
+    case GL_TEXTURE_3D_OES:
+        glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE_OES, &maxSize);
+        desc.width = bisectTextureLevelSizeOES(target, level, 0, maxSize);
+        desc.width = bisectTextureLevelSizeOES(target, level, 1, maxSize);
+        desc.depth = bisectTextureLevelSizeOES(target, level, 2, maxSize);
+        break;
+    default:
+        return false;
+    }
+
+    if (0) {
+        std::cerr
+            << enumToString(target) << " "
+            << level << " "
+            << desc.width << "x" << desc.height << "x" << desc.depth
+            << "\n";
+    }
+
+    return desc.valid();
+}
+
+
+static inline bool
+getActiveTextureLevelDesc(Context &context, GLenum target, GLint level, ImageDesc &desc)
+{
+    if (context.ES) {
+        return getActiveTextureLevelDescOES(context, target, level, desc);
+    }
+
+    glGetTexLevelParameteriv(target, level, GL_TEXTURE_INTERNAL_FORMAT, &desc.internalFormat);
+
+    desc.width = 0;
+    glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &desc.width);
+
+    if (target == GL_TEXTURE_1D) {
+        desc.height = 1;
+        desc.depth = 1;
+    } else {
+        desc.height = 0;
+        glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &desc.height);
+        if (target != GL_TEXTURE_3D) {
+            desc.depth = 1;
+        } else {
+            desc.depth = 0;
+            glGetTexLevelParameteriv(target, level, GL_TEXTURE_DEPTH, &desc.depth);
+        }
+    }
+
+    return desc.valid();
+}
+
+
+/**
+ * OpenGL ES does not support glGetTexImage. Obtain the pixels by attaching the
+ * texture to a framebuffer.
+ */
+static inline void
+getTexImageOES(GLenum target, GLint level, ImageDesc &desc, GLubyte *pixels)
+{
+    memset(pixels, 0x80, desc.height * desc.width * 4);
+
+    GLenum texture_binding = GL_NONE;
+    switch (target) {
+    case GL_TEXTURE_2D:
+        texture_binding = GL_TEXTURE_BINDING_2D;
+        break;
+    case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+    case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+    case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+    case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+    case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+    case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+        texture_binding = GL_TEXTURE_BINDING_CUBE_MAP;
+        break;
+    case GL_TEXTURE_3D_OES:
+        texture_binding = GL_TEXTURE_BINDING_3D_OES;
+    default:
+        return;
+    }
+
+    GLint texture = 0;
+    glGetIntegerv(texture_binding, &texture);
+    if (!texture) {
+        return;
+    }
+
+    GLint prev_fbo = 0;
+    GLuint fbo = 0;
+    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prev_fbo);
+    glGenFramebuffers(1, &fbo);
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+    GLenum status;
+
+    switch (target) {
+    case GL_TEXTURE_2D:
+    case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+    case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+    case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+    case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+    case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+    case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, level);
+        status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+        if (status != GL_FRAMEBUFFER_COMPLETE) {
+            std::cerr << __FUNCTION__ << ": " << enumToString(status) << "\n";
+        }
+        glReadPixels(0, 0, desc.width, desc.height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+        break;
+    case GL_TEXTURE_3D_OES:
+        for (int i = 0; i < desc.depth; i++) {
+            glFramebufferTexture3D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_3D, texture, level, i);
+            glReadPixels(0, 0, desc.width, desc.height, GL_RGBA, GL_UNSIGNED_BYTE, pixels + 4 * i * desc.width * desc.height);
+        }
+        break;
+    }
+
+    glBindFramebuffer(GL_FRAMEBUFFER, prev_fbo);
+
+    glDeleteFramebuffers(1, &fbo);
+}
+
+
+static inline void
+dumpActiveTextureLevel(JSONWriter &json, Context &context, GLenum target, GLint level)
+{
+    ImageDesc desc;
+    if (!getActiveTextureLevelDesc(context, target, level, desc)) {
+        return;
+    }
+
+    char label[512];
+
+    GLint active_texture = GL_TEXTURE0;
+    glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
+    snprintf(label, sizeof label, "%s, %s, level = %d",
+             enumToString(active_texture), enumToString(target), level);
+
+    json.beginMember(label);
+
+    json.beginObject();
+
+    // Tell the GUI this is no ordinary object, but an image
+    json.writeStringMember("__class__", "image");
+
+    json.writeNumberMember("__width__", desc.width);
+    json.writeNumberMember("__height__", desc.height);
+    json.writeNumberMember("__depth__", desc.depth);
+
+    json.writeStringMember("__format__", enumToString(desc.internalFormat));
+
+    // Hardcoded for now, but we could chose types more adequate to the
+    // texture internal format
+    json.writeStringMember("__type__", "uint8");
+    json.writeBoolMember("__normalized__", true);
+    json.writeNumberMember("__channels__", 4);
+
+    GLubyte *pixels = new GLubyte[desc.depth*desc.width*desc.height*4];
+
+    context.resetPixelPackState();
+
+    if (context.ES) {
+        getTexImageOES(target, level, desc, pixels);
+    } else {
+        glGetTexImage(target, level, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+    }
+
+    context.restorePixelPackState();
+
+    json.beginMember("__data__");
+    char *pngBuffer;
+    int pngBufferSize;
+    image::writePixelsToBuffer(pixels, desc.width, desc.height, 4, true, &pngBuffer, &pngBufferSize);
+    json.writeBase64(pngBuffer, pngBufferSize);
+    free(pngBuffer);
+    json.endMember(); // __data__
+
+    delete [] pixels;
+    json.endObject();
+}
+
+
+static inline void
+dumpTexture(JSONWriter &json, Context &context, GLenum target, GLenum binding)
+{
+    GLint texture_binding = 0;
+    glGetIntegerv(binding, &texture_binding);
+    if (!glIsEnabled(target) && !texture_binding) {
+        return;
+    }
+
+    GLint level = 0;
+    do {
+        ImageDesc desc;
+        if (!getActiveTextureLevelDesc(context, target, level, desc)) {
+            break;
+        }
+
+        if (target == GL_TEXTURE_CUBE_MAP) {
+            for (int face = 0; face < 6; ++face) {
+                dumpActiveTextureLevel(json, context, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level);
+            }
+        } else {
+            dumpActiveTextureLevel(json, context, target, level);
+        }
+
+        ++level;
+    } while(true);
+}
+
+
+void
+dumpTextures(JSONWriter &json, Context &context)
+{
+    json.beginMember("textures");
+    json.beginObject();
+    GLint active_texture = GL_TEXTURE0;
+    glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
+    GLint max_texture_coords = 0;
+    glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);
+    GLint max_combined_texture_image_units = 0;
+    glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_combined_texture_image_units);
+    GLint max_units = std::max(max_combined_texture_image_units, max_texture_coords);
+    for (GLint unit = 0; unit < max_units; ++unit) {
+        GLenum texture = GL_TEXTURE0 + unit;
+        glActiveTexture(texture);
+        dumpTexture(json, context, GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D);
+        dumpTexture(json, context, GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D);
+        dumpTexture(json, context, GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D);
+        dumpTexture(json, context, GL_TEXTURE_RECTANGLE, GL_TEXTURE_BINDING_RECTANGLE);
+        dumpTexture(json, context, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP);
+    }
+    glActiveTexture(active_texture);
+    json.endObject();
+    json.endMember(); // textures
+}
+
+
+static bool
+getDrawableBounds(GLint *width, GLint *height) {
+#if defined(__linux__)
+    if (dlsym(RTLD_DEFAULT, "eglGetCurrentContext")) {
+        EGLContext currentContext = eglGetCurrentContext();
+        if (currentContext == EGL_NO_CONTEXT) {
+            return false;
+        }
+
+        EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW);
+        if (currentSurface == EGL_NO_SURFACE) {
+            return false;
+        }
+
+        EGLDisplay currentDisplay = eglGetCurrentDisplay();
+        if (currentDisplay == EGL_NO_DISPLAY) {
+            return false;
+        }
+
+        if (!eglQuerySurface(currentDisplay, currentSurface, EGL_WIDTH, width) ||
+            !eglQuerySurface(currentDisplay, currentSurface, EGL_HEIGHT, height)) {
+            return false;
+        }
+
+        return true;
+    }
+#endif
+
+#if defined(_WIN32)
+
+    HDC hDC = wglGetCurrentDC();
+    if (!hDC) {
+        return false;
+    }
+
+    HWND hWnd = WindowFromDC(hDC);
+    RECT rect;
+
+    if (!GetClientRect(hWnd, &rect)) {
+       return false;
+    }
+
+    *width  = rect.right  - rect.left;
+    *height = rect.bottom - rect.top;
+    return true;
+
+#elif defined(__APPLE__)
+
+    CGLContextObj ctx = CGLGetCurrentContext();
+    if (ctx == NULL) {
+        return false;
+    }
+
+    CGSConnectionID cid;
+    CGSWindowID wid;
+    CGSSurfaceID sid;
+
+    if (CGLGetSurface(ctx, &cid, &wid, &sid) != kCGLNoError) {
+        return false;
+    }
+
+    CGRect rect;
+
+    if (CGSGetSurfaceBounds(cid, wid, sid, &rect) != 0) {
+        return false;
+    }
+
+    *width = rect.size.width;
+    *height = rect.size.height;
+    return true;
+
+#elif defined(HAVE_X11)
+
+    Display *display;
+    Drawable drawable;
+    Window root;
+    int x, y;
+    unsigned int w, h, bw, depth;
+
+    display = glXGetCurrentDisplay();
+    if (!display) {
+        return false;
+    }
+
+    drawable = glXGetCurrentDrawable();
+    if (drawable == None) {
+        return false;
+    }
+
+    if (!XGetGeometry(display, drawable, &root, &x, &y, &w, &h, &bw, &depth)) {
+        return false;
+    }
+
+    *width = w;
+    *height = h;
+    return true;
+
+#else
+
+    return false;
+
+#endif
+}
+
+
+static const GLenum texture_bindings[][2] = {
+    {GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D},
+    {GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D},
+    {GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D},
+    {GL_TEXTURE_RECTANGLE, GL_TEXTURE_BINDING_RECTANGLE},
+    {GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP}
+};
+
+
+static bool
+bindTexture(GLint texture, GLenum &target, GLint &bound_texture)
+{
+
+    for (unsigned i = 0; i < sizeof(texture_bindings)/sizeof(texture_bindings[0]); ++i) {
+        target  = texture_bindings[i][0];
+
+        GLenum binding = texture_bindings[i][1];
+
+        while (glGetError() != GL_NO_ERROR)
+            ;
+
+        glGetIntegerv(binding, &bound_texture);
+        glBindTexture(target, texture);
+
+        if (glGetError() == GL_NO_ERROR) {
+            return true;
+        }
+
+        glBindTexture(target, bound_texture);
+    }
+
+    target = GL_NONE;
+
+    return false;
+}
+
+
+static bool
+getTextureLevelDesc(Context &context, GLint texture, GLint level, ImageDesc &desc)
+{
+    GLenum target;
+    GLint bound_texture = 0;
+    if (!bindTexture(texture, target, bound_texture)) {
+        return false;
+    }
+
+    getActiveTextureLevelDesc(context, target, level, desc);
+
+    glBindTexture(target, bound_texture);
+
+    return desc.valid();
+}
+
+
+static bool
+getBoundRenderbufferDesc(Context &context, ImageDesc &desc)
+{
+    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &desc.width);
+    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &desc.height);
+    desc.depth = 1;
+    
+    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_INTERNAL_FORMAT, &desc.internalFormat);
+    
+    return desc.valid();
+}
+
+
+static bool
+getRenderbufferDesc(Context &context, GLint renderbuffer, ImageDesc &desc)
+{
+    GLint bound_renderbuffer = 0;
+    glGetIntegerv(GL_RENDERBUFFER_BINDING, &bound_renderbuffer);
+    glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
+
+    getBoundRenderbufferDesc(context, desc);
+
+    glBindRenderbuffer(GL_RENDERBUFFER, bound_renderbuffer);
+    
+    return desc.valid();
+}
+
+
+static bool
+getFramebufferAttachmentDesc(Context &context, GLenum target, GLenum attachment, ImageDesc &desc)
+{
+    GLint object_type = GL_NONE;
+    glGetFramebufferAttachmentParameteriv(target, attachment,
+                                          GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
+                                          &object_type);
+    if (object_type == GL_NONE) {
+        return false;
+    }
+
+    GLint object_name = 0;
+    glGetFramebufferAttachmentParameteriv(target, attachment,
+                                          GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
+                                          &object_name);
+    if (object_name == 0) {
+        return false;
+    }
+
+    if (object_type == GL_RENDERBUFFER) {
+        return getRenderbufferDesc(context, object_name, desc);
+    } else if (object_type == GL_TEXTURE) {
+        GLint texture_level = 0;
+        glGetFramebufferAttachmentParameteriv(target, attachment,
+                                              GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL,
+                                              &texture_level);
+        return getTextureLevelDesc(context, object_name, texture_level, desc);
+    } else {
+        std::cerr << "warning: unexpected GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = " << object_type << "\n";
+        return false;
+    }
+}
+
+
+
+image::Image *
+getDrawBufferImage() {
+    GLenum format = GL_RGB;
+    GLint channels = __gl_format_channels(format);
+    if (channels > 4) {
+        return NULL;
+    }
+
+    Context context;
+
+    GLenum framebuffer_binding;
+    GLenum framebuffer_target;
+    if (context.ES) {
+        framebuffer_binding = GL_FRAMEBUFFER_BINDING;
+        framebuffer_target = GL_FRAMEBUFFER;
+    } else {
+        framebuffer_binding = GL_DRAW_FRAMEBUFFER_BINDING;
+        framebuffer_target = GL_DRAW_FRAMEBUFFER;
+    }
+
+    GLint draw_framebuffer = 0;
+    glGetIntegerv(framebuffer_binding, &draw_framebuffer);
+
+    GLint draw_buffer = GL_NONE;
+    ImageDesc desc;
+    if (draw_framebuffer) {
+        if (context.ARB_draw_buffers) {
+            glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer);
+            if (draw_buffer == GL_NONE) {
+                return NULL;
+            }
+        }
+
+        if (!getFramebufferAttachmentDesc(context, framebuffer_target, draw_buffer, desc)) {
+            return NULL;
+        }
+    } else {
+        if (!context.ES) {
+            glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
+            if (draw_buffer == GL_NONE) {
+                return NULL;
+            }
+        }
+
+        if (!getDrawableBounds(&desc.width, &desc.height)) {
+            return NULL;
+        }
+
+        desc.depth = 1;
+    }
+
+    GLenum type = GL_UNSIGNED_BYTE;
+
+#if DEPTH_AS_RGBA
+    if (format == GL_DEPTH_COMPONENT) {
+        type = GL_UNSIGNED_INT;
+        channels = 4;
+    }
+#endif
+
+    image::Image *image = new image::Image(desc.width, desc.height, channels, true);
+    if (!image) {
+        return NULL;
+    }
+
+    while (glGetError() != GL_NO_ERROR) {}
+
+    GLint read_framebuffer = 0;
+    GLint read_buffer = GL_NONE;
+    if (!context.ES) {
+        glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
+        glBindFramebuffer(GL_READ_FRAMEBUFFER, draw_framebuffer);
+
+        glGetIntegerv(GL_READ_BUFFER, &read_buffer);
+        glReadBuffer(draw_buffer);
+    }
+
+    // TODO: reset imaging state too
+    context.resetPixelPackState();
+
+    glReadPixels(0, 0, desc.width, desc.height, format, type, image->pixels);
+
+    context.restorePixelPackState();
+
+    if (!context.ES) {
+        glReadBuffer(read_buffer);
+        glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
+    }
+
+    GLenum error = glGetError();
+    if (error != GL_NO_ERROR) {
+        do {
+            std::cerr << "warning: " << enumToString(error) << " while getting snapshot\n";
+            error = glGetError();
+        } while(error != GL_NO_ERROR);
+        delete image;
+        return NULL;
+    }
+     
+    return image;
+}
+
+
+/**
+ * Dump the image of the currently bound read buffer.
+ */
+static inline void
+dumpReadBufferImage(JSONWriter &json, GLint width, GLint height, GLenum format,
+                    GLint internalFormat = GL_NONE)
+{
+    GLint channels = __gl_format_channels(format);
+
+    Context context;
+
+    json.beginObject();
+
+    // Tell the GUI this is no ordinary object, but an image
+    json.writeStringMember("__class__", "image");
+
+    json.writeNumberMember("__width__", width);
+    json.writeNumberMember("__height__", height);
+    json.writeNumberMember("__depth__", 1);
+
+    json.writeStringMember("__format__", enumToString(internalFormat));
+
+    // Hardcoded for now, but we could chose types more adequate to the
+    // texture internal format
+    json.writeStringMember("__type__", "uint8");
+    json.writeBoolMember("__normalized__", true);
+    json.writeNumberMember("__channels__", channels);
+
+    GLenum type = GL_UNSIGNED_BYTE;
+
+#if DEPTH_AS_RGBA
+    if (format == GL_DEPTH_COMPONENT) {
+        type = GL_UNSIGNED_INT;
+        channels = 4;
+    }
+#endif
+
+    GLubyte *pixels = new GLubyte[width*height*channels];
+
+    // TODO: reset imaging state too
+    context.resetPixelPackState();
+
+    glReadPixels(0, 0, width, height, format, type, pixels);
+
+    context.restorePixelPackState();
+
+    json.beginMember("__data__");
+    char *pngBuffer;
+    int pngBufferSize;
+    image::writePixelsToBuffer(pixels, width, height, channels, true, &pngBuffer, &pngBufferSize);
+    //std::cerr <<" Before = "<<(width * height * channels * sizeof *pixels)
+    //          <<", after = "<<pngBufferSize << ", ratio = " << double(width * height * channels * sizeof *pixels)/pngBufferSize;
+    json.writeBase64(pngBuffer, pngBufferSize);
+    free(pngBuffer);
+    json.endMember(); // __data__
+
+    delete [] pixels;
+    json.endObject();
+}
+
+
+static inline GLuint
+downsampledFramebuffer(Context &context,
+                       GLuint oldFbo, GLint drawbuffer,
+                       GLint colorRb, GLint depthRb, GLint stencilRb,
+                       GLuint *rbs, GLint *numRbs)
+{
+    GLuint fbo;
+
+
+    *numRbs = 0;
+
+    glGenFramebuffers(1, &fbo);
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+    {
+        // color buffer
+        ImageDesc desc;
+        glBindRenderbuffer(GL_RENDERBUFFER, colorRb);
+        getBoundRenderbufferDesc(context, desc);
+
+        glGenRenderbuffers(1, &rbs[*numRbs]);
+        glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
+        glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
+        glFramebufferRenderbuffer(GL_FRAMEBUFFER, drawbuffer,
+                                  GL_RENDERBUFFER, rbs[*numRbs]);
+
+        glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
+        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
+        glDrawBuffer(drawbuffer);
+        glReadBuffer(drawbuffer);
+        glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
+                          GL_COLOR_BUFFER_BIT, GL_NEAREST);
+        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+        ++*numRbs;
+    }
+
+    if (stencilRb == depthRb && stencilRb) {
+        //combined depth and stencil buffer
+        ImageDesc desc;
+        glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
+        getBoundRenderbufferDesc(context, desc);
+
+        glGenRenderbuffers(1, &rbs[*numRbs]);
+        glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
+        glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
+        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
+                                  GL_RENDERBUFFER, rbs[*numRbs]);
+        glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
+        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
+        glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
+                          GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
+        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+        ++*numRbs;
+    } else {
+        if (depthRb) {
+            ImageDesc desc;
+            glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
+            getBoundRenderbufferDesc(context, desc);
+
+            glGenRenderbuffers(1, &rbs[*numRbs]);
+            glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
+            glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
+            glFramebufferRenderbuffer(GL_FRAMEBUFFER,
+                                      GL_DEPTH_ATTACHMENT,
+                                      GL_RENDERBUFFER, rbs[*numRbs]);
+            glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
+            glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
+            glDrawBuffer(GL_DEPTH_ATTACHMENT);
+            glReadBuffer(GL_DEPTH_ATTACHMENT);
+            glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
+                              GL_DEPTH_BUFFER_BIT, GL_NEAREST);
+            ++*numRbs;
+        }
+        if (stencilRb) {
+            ImageDesc desc;
+            glBindRenderbuffer(GL_RENDERBUFFER, stencilRb);
+            getBoundRenderbufferDesc(context, desc);
+
+            glGenRenderbuffers(1, &rbs[*numRbs]);
+            glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
+            glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
+            glFramebufferRenderbuffer(GL_FRAMEBUFFER,
+                                      GL_STENCIL_ATTACHMENT,
+                                      GL_RENDERBUFFER, rbs[*numRbs]);
+            glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
+            glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
+            glDrawBuffer(GL_STENCIL_ATTACHMENT);
+            glReadBuffer(GL_STENCIL_ATTACHMENT);
+            glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
+                              GL_STENCIL_BUFFER_BIT, GL_NEAREST);
+            ++*numRbs;
+        }
+    }
+
+    return fbo;
+}
+
+
+/**
+ * Dump images of current draw drawable/window.
+ */
+static void
+dumpDrawableImages(JSONWriter &json, Context &context)
+{
+    GLint width, height;
+
+    if (!getDrawableBounds(&width, &height)) {
+        return;
+    }
+
+    GLint draw_buffer = GL_NONE;
+    if (context.ES) {
+        draw_buffer = GL_BACK;
+    } else {
+        glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
+        glReadBuffer(draw_buffer);
+    }
+
+    if (draw_buffer != GL_NONE) {
+        GLint read_buffer = GL_NONE;
+        if (!context.ES) {
+            glGetIntegerv(GL_READ_BUFFER, &read_buffer);
+        }
+
+        GLint alpha_bits = 0;
+#if 0
+        // XXX: Ignore alpha until we are able to match the traced visual
+        glGetIntegerv(GL_ALPHA_BITS, &alpha_bits);
+#endif
+        GLenum format = alpha_bits ? GL_RGBA : GL_RGB;
+        json.beginMember(enumToString(draw_buffer));
+        dumpReadBufferImage(json, width, height, format);
+        json.endMember();
+
+        if (!context.ES) {
+            glReadBuffer(read_buffer);
+        }
+    }
+
+    if (!context.ES) {
+        GLint depth_bits = 0;
+        glGetIntegerv(GL_DEPTH_BITS, &depth_bits);
+        if (depth_bits) {
+            json.beginMember("GL_DEPTH_COMPONENT");
+            dumpReadBufferImage(json, width, height, GL_DEPTH_COMPONENT);
+            json.endMember();
+        }
+
+        GLint stencil_bits = 0;
+        glGetIntegerv(GL_STENCIL_BITS, &stencil_bits);
+        if (stencil_bits) {
+            json.beginMember("GL_STENCIL_INDEX");
+            dumpReadBufferImage(json, width, height, GL_STENCIL_INDEX);
+            json.endMember();
+        }
+    }
+}
+
+
+/**
+ * Dump the specified framebuffer attachment.
+ *
+ * In the case of a color attachment, it assumes it is already bound for read.
+ */
+static void
+dumpFramebufferAttachment(JSONWriter &json, Context &context, GLenum target, GLenum attachment, GLenum format)
+{
+    ImageDesc desc;
+    if (!getFramebufferAttachmentDesc(context, target, attachment, desc)) {
+        return;
+    }
+
+    json.beginMember(enumToString(attachment));
+    dumpReadBufferImage(json, desc.width, desc.height, format, desc.internalFormat);
+    json.endMember();
+}
+
+
+static void
+dumpFramebufferAttachments(JSONWriter &json, Context &context, GLenum target)
+{
+    GLint read_framebuffer = 0;
+    glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
+
+    GLint read_buffer = GL_NONE;
+    glGetIntegerv(GL_READ_BUFFER, &read_buffer);
+
+    GLint max_draw_buffers = 1;
+    glGetIntegerv(GL_MAX_DRAW_BUFFERS, &max_draw_buffers);
+    GLint max_color_attachments = 0;
+    glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments);
+
+    for (GLint i = 0; i < max_draw_buffers; ++i) {
+        GLint draw_buffer = GL_NONE;
+        glGetIntegerv(GL_DRAW_BUFFER0 + i, &draw_buffer);
+        if (draw_buffer != GL_NONE) {
+            glReadBuffer(draw_buffer);
+            GLint attachment;
+            if (draw_buffer >= GL_COLOR_ATTACHMENT0 && draw_buffer < GL_COLOR_ATTACHMENT0 + max_color_attachments) {
+                attachment = draw_buffer;
+            } else {
+                std::cerr << "warning: unexpected GL_DRAW_BUFFER" << i << " = " << draw_buffer << "\n";
+                attachment = GL_COLOR_ATTACHMENT0;
+            }
+            GLint alpha_size = 0;
+            glGetFramebufferAttachmentParameteriv(target, attachment,
+                                                  GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE,
+                                                  &alpha_size);
+            GLenum format = alpha_size ? GL_RGBA : GL_RGB;
+            dumpFramebufferAttachment(json, context, target, attachment, format);
+        }
+    }
+
+    glReadBuffer(read_buffer);
+
+    if (!context.ES) {
+        dumpFramebufferAttachment(json, context, target, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT);
+        dumpFramebufferAttachment(json, context, target, GL_STENCIL_ATTACHMENT, GL_STENCIL_INDEX);
+    }
+
+    glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
+}
+
+
+void
+dumpFramebuffer(JSONWriter &json, Context &context)
+{
+    json.beginMember("framebuffer");
+    json.beginObject();
+
+    GLint boundDrawFbo = 0, boundReadFbo = 0;
+    glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &boundDrawFbo);
+    glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &boundReadFbo);
+    if (!boundDrawFbo) {
+        dumpDrawableImages(json, context);
+    } else if (context.ES) {
+        dumpFramebufferAttachments(json, context, GL_FRAMEBUFFER);
+    } else {
+        GLint colorRb = 0, stencilRb = 0, depthRb = 0;
+        GLint draw_buffer0 = GL_NONE;
+        glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer0);
+        bool multisample = false;
+
+        GLint boundRb = 0;
+        glGetIntegerv(GL_RENDERBUFFER_BINDING, &boundRb);
+
+        GLint object_type;
+        glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, draw_buffer0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
+        if (object_type == GL_RENDERBUFFER) {
+            glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, draw_buffer0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &colorRb);
+            glBindRenderbuffer(GL_RENDERBUFFER, colorRb);
+            GLint samples = 0;
+            glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
+            if (samples) {
+                multisample = true;
+            }
+        }
+
+        glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
+        if (object_type == GL_RENDERBUFFER) {
+            glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthRb);
+            glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
+            GLint samples = 0;
+            glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
+            if (samples) {
+                multisample = true;
+            }
+        }
+
+        glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
+        if (object_type == GL_RENDERBUFFER) {
+            glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &stencilRb);
+            glBindRenderbuffer(GL_RENDERBUFFER, stencilRb);
+            GLint samples = 0;
+            glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
+            if (samples) {
+                multisample = true;
+            }
+        }
+
+        glBindRenderbuffer(GL_RENDERBUFFER, boundRb);
+
+        GLuint rbs[3];
+        GLint numRbs = 0;
+        GLuint fboCopy = 0;
+
+        if (multisample) {
+            // glReadPixels doesnt support multisampled buffers so we need
+            // to blit the fbo to a temporary one
+            fboCopy = downsampledFramebuffer(context,
+                                             boundDrawFbo, draw_buffer0,
+                                             colorRb, depthRb, stencilRb,
+                                             rbs, &numRbs);
+        }
+
+        dumpFramebufferAttachments(json, context, GL_DRAW_FRAMEBUFFER);
+
+        if (multisample) {
+            glBindRenderbuffer(GL_RENDERBUFFER_BINDING, boundRb);
+            glDeleteRenderbuffers(numRbs, rbs);
+            glDeleteFramebuffers(1, &fboCopy);
+        }
+
+        glBindFramebuffer(GL_READ_FRAMEBUFFER, boundReadFbo);
+        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, boundDrawFbo);
+    }
+
+    json.endObject();
+    json.endMember(); // framebuffer
+}
+
+
+} /* namespace glstate */
index 0398decb339f020f59abdc96d055289793bd1138..ddc802ef26be996b7e48685e8af12c1d055e4fd9 100644 (file)
@@ -42,7 +42,6 @@ struct Context
 
     bool ARB_draw_buffers;
 
-    inline
     Context(void);
 
     GLint packAlignment;
@@ -59,7 +58,11 @@ void dumpEnum(JSONWriter &json, GLenum pname);
 
 void dumpParameters(JSONWriter &json, Context &context);
 
-void dumpCurrentContext(std::ostream &os);
+void dumpShadersUniforms(JSONWriter &json);
+
+void dumpTextures(JSONWriter &json, Context &context);
+
+void dumpFramebuffer(JSONWriter &json, Context &context);
 
 
 } /* namespace glstate */
diff --git a/glstate_params.py b/glstate_params.py
new file mode 100644 (file)
index 0000000..ad8e258
--- /dev/null
@@ -0,0 +1,503 @@
+##########################################################################
+#
+# Copyright 2011 Jose Fonseca
+# All Rights Reserved.
+#
+# 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.
+#
+##########################################################################/
+
+
+'''Generate code to dump most GL state into JSON.'''
+
+
+from specs.stdapi import *
+
+from specs.gltypes import *
+from specs.glparams import *
+
+
+texture_targets = [
+    ('GL_TEXTURE_1D', 'GL_TEXTURE_BINDING_1D'),
+    ('GL_TEXTURE_2D', 'GL_TEXTURE_BINDING_2D'),
+    ('GL_TEXTURE_3D', 'GL_TEXTURE_BINDING_3D'),
+    ('GL_TEXTURE_RECTANGLE', 'GL_TEXTURE_BINDING_RECTANGLE'),
+    ('GL_TEXTURE_CUBE_MAP', 'GL_TEXTURE_BINDING_CUBE_MAP')
+]
+
+framebuffer_targets = [
+    ('GL_DRAW_FRAMEBUFFER', 'GL_DRAW_FRAMEBUFFER_BINDING'),
+    ('GL_READ_FRAMEBUFFER', 'GL_READ_FRAMEBUFFER_BINDING'),
+]
+
+class GetInflector:
+    '''Objects that describes how to inflect.'''
+
+    reduced_types = {
+        B: I,
+        E: I,
+        I: F,
+    }
+
+    def __init__(self, radical, inflections, suffix = ''):
+        self.radical = radical
+        self.inflections = inflections
+        self.suffix = suffix
+
+    def reduced_type(self, type):
+        if type in self.inflections:
+            return type
+        if type in self.reduced_types:
+            return self.reduced_type(self.reduced_types[type])
+        raise NotImplementedError
+
+    def inflect(self, type):
+        return self.radical + self.inflection(type) + self.suffix
+
+    def inflection(self, type):
+        type = self.reduced_type(type)
+        assert type in self.inflections
+        return self.inflections[type]
+
+    def __str__(self):
+        return self.radical + self.suffix
+
+
+class StateGetter(Visitor):
+    '''Type visitor that is able to extract the state via one of the glGet*
+    functions.
+
+    It will declare any temporary variable
+    '''
+
+    def __init__(self, radical, inflections, suffix=''):
+        self.inflector = GetInflector(radical, inflections)
+        self.suffix = suffix
+
+    def iter(self):
+        for function, type, count, name in parameters:
+            inflection = self.inflector.radical + self.suffix
+            if inflection not in function.split(','):
+                continue
+            if type is X:
+                continue
+            yield type, count, name
+
+    def __call__(self, *args):
+        pname = args[-1]
+
+        for type, count, name in self.iter():
+            if name == pname:
+                if count != 1:
+                    type = Array(type, str(count))
+
+                return type, self.visit(type, args)
+
+        raise NotImplementedError
+
+    def temp_name(self, args):
+        '''Return the name of a temporary variable to hold the state.'''
+        pname = args[-1]
+
+        return pname[3:].lower()
+
+    def visitConst(self, const, args):
+        return self.visit(const.type, args)
+
+    def visitScalar(self, type, args):
+        temp_name = self.temp_name(args)
+        elem_type = self.inflector.reduced_type(type)
+        inflection = self.inflector.inflect(type)
+        if inflection.endswith('v'):
+            print '    %s %s = 0;' % (elem_type, temp_name)
+            print '    %s(%s, &%s);' % (inflection + self.suffix, ', '.join(args), temp_name)
+        else:
+            print '    %s %s = %s(%s);' % (elem_type, temp_name, inflection + self.suffix, ', '.join(args))
+        return temp_name
+
+    def visitString(self, string, args):
+        temp_name = self.temp_name(args)
+        inflection = self.inflector.inflect(string)
+        assert not inflection.endswith('v')
+        print '    %s %s = (%s)%s(%s);' % (string, temp_name, string, inflection + self.suffix, ', '.join(args))
+        return temp_name
+
+    def visitAlias(self, alias, args):
+        return self.visitScalar(alias, args)
+
+    def visitEnum(self, enum, args):
+        return self.visit(GLint, args)
+
+    def visitBitmask(self, bitmask, args):
+        return self.visit(GLint, args)
+
+    def visitArray(self, array, args):
+        temp_name = self.temp_name(args)
+        if array.length == '1':
+            return self.visit(array.type)
+        elem_type = self.inflector.reduced_type(array.type)
+        inflection = self.inflector.inflect(array.type)
+        assert inflection.endswith('v')
+        print '    %s %s[%s + 1];' % (elem_type, temp_name, array.length)
+        print '    memset(%s, 0, %s * sizeof *%s);' % (temp_name, array.length, temp_name)
+        print '    %s[%s] = (%s)0xdeadc0de;' % (temp_name, array.length, elem_type)
+        print '    %s(%s, %s);' % (inflection + self.suffix, ', '.join(args), temp_name)
+        # Simple buffer overflow detection
+        print '    assert(%s[%s] == (%s)0xdeadc0de);' % (temp_name, array.length, elem_type)
+        return temp_name
+
+    def visitOpaque(self, pointer, args):
+        temp_name = self.temp_name(args)
+        inflection = self.inflector.inflect(pointer)
+        assert inflection.endswith('v')
+        print '    GLvoid *%s;' % temp_name
+        print '    %s(%s, &%s);' % (inflection + self.suffix, ', '.join(args), temp_name)
+        return temp_name
+
+
+glGet = StateGetter('glGet', {
+    B: 'Booleanv',
+    I: 'Integerv',
+    F: 'Floatv',
+    D: 'Doublev',
+    S: 'String',
+    P: 'Pointerv',
+})
+
+glGetMaterial = StateGetter('glGetMaterial', {I: 'iv', F: 'fv'})
+glGetLight = StateGetter('glGetLight', {I: 'iv', F: 'fv'})
+glGetVertexAttrib = StateGetter('glGetVertexAttrib', {I: 'iv', F: 'fv', D: 'dv', P: 'Pointerv'})
+glGetTexParameter = StateGetter('glGetTexParameter', {I: 'iv', F: 'fv'})
+glGetTexEnv = StateGetter('glGetTexEnv', {I: 'iv', F: 'fv'})
+glGetTexLevelParameter = StateGetter('glGetTexLevelParameter', {I: 'iv', F: 'fv'})
+glGetShader = StateGetter('glGetShaderiv', {I: 'iv'})
+glGetProgram = StateGetter('glGetProgram', {I: 'iv'})
+glGetProgramARB = StateGetter('glGetProgram', {I: 'iv', F: 'fv', S: 'Stringv'}, 'ARB')
+glGetFramebufferAttachmentParameter = StateGetter('glGetFramebufferAttachmentParameter', {I: 'iv'})
+
+
+class JsonWriter(Visitor):
+    '''Type visitor that will dump a value of the specified type through the
+    JSON writer.
+    
+    It expects a previously declared JSONWriter instance named "json".'''
+
+    def visitLiteral(self, literal, instance):
+        if literal.kind == 'Bool':
+            print '    json.writeBool(%s);' % instance
+        elif literal.kind in ('SInt', 'Uint', 'Float', 'Double'):
+            print '    json.writeNumber(%s);' % instance
+        else:
+            raise NotImplementedError
+
+    def visitString(self, string, instance):
+        assert string.length is None
+        print '    json.writeString((const char *)%s);' % instance
+
+    def visitEnum(self, enum, instance):
+        if enum.expr == 'GLenum':
+            print '    dumpEnum(json, %s);' % instance
+        else:
+            print '    json.writeNumber(%s);' % instance
+
+    def visitBitmask(self, bitmask, instance):
+        raise NotImplementedError
+
+    def visitAlias(self, alias, instance):
+        self.visit(alias.type, instance)
+
+    def visitOpaque(self, opaque, instance):
+        print '    json.writeNumber((size_t)%s);' % instance
+
+    __index = 0
+
+    def visitArray(self, array, instance):
+        index = '__i%u' % JsonWriter.__index
+        JsonWriter.__index += 1
+        print '    json.beginArray();'
+        print '    for (unsigned %s = 0; %s < %s; ++%s) {' % (index, index, array.length, index)
+        self.visit(array.type, '%s[%s]' % (instance, index))
+        print '    }'
+        print '    json.endArray();'
+
+
+
+class StateDumper:
+    '''Class to generate code to dump all GL state in JSON format via
+    stdout.'''
+
+    def __init__(self):
+        pass
+
+    def dump(self):
+        print '#include <string.h>'
+        print
+        print '#include "json.hpp"'
+        print '#include "glproc.hpp"'
+        print '#include "glsize.hpp"'
+        print '#include "glstate.hpp"'
+        print '#include "glstate_internal.hpp"'
+        print
+        print 'namespace glstate {'
+        print
+
+        print 'const char *'
+        print 'enumToString(GLenum pname)'
+        print '{'
+        print '    switch (pname) {'
+        for name in GLenum.values:
+            print '    case %s:' % name
+            print '        return "%s";' % name
+        print '    default:'
+        print '        return NULL;'
+        print '    }'
+        print '}'
+        print
+
+        print 'static void'
+        print 'dumpFramebufferAttachementParameters(JSONWriter &json, GLenum target, GLenum attachment)'
+        print '{'
+        self.dump_attachment_parameters('target', 'attachment')
+        print '}'
+        print
+
+        print 'void'
+        print 'dumpEnum(JSONWriter &json, GLenum pname)'
+        print '{'
+        print '    const char *s = enumToString(pname);'
+        print '    if (s) {'
+        print '        json.writeString(s);'
+        print '    } else {'
+        print '        json.writeNumber(pname);'
+        print '    }'
+        print '}'
+        print
+
+        print 'void dumpParameters(JSONWriter &json, Context &context)'
+        print '{'
+        print '    json.beginMember("parameters");'
+        print '    json.beginObject();'
+        
+        self.dump_atoms(glGet)
+        
+        self.dump_material_params()
+        self.dump_light_params()
+        self.dump_vertex_attribs()
+        self.dump_program_params()
+        self.dump_texture_parameters()
+        self.dump_framebuffer_parameters()
+
+        print '    json.endObject();'
+        print '    json.endMember(); // parameters'
+        print '}'
+        print
+        
+        print '} /*namespace glstate */'
+
+    def dump_material_params(self):
+        print '    if (!context.ES) {'
+        for face in ['GL_FRONT', 'GL_BACK']:
+            print '    json.beginMember("%s");' % face
+            print '    json.beginObject();'
+            self.dump_atoms(glGetMaterial, face)
+            print '    json.endObject();'
+        print '    }'
+        print
+
+    def dump_light_params(self):
+        print '    GLint max_lights = 0;'
+        print '    __glGetIntegerv(GL_MAX_LIGHTS, &max_lights);'
+        print '    for (GLint index = 0; index < max_lights; ++index) {'
+        print '        GLenum light = GL_LIGHT0 + index;'
+        print '        if (glIsEnabled(light)) {'
+        print '            char name[32];'
+        print '            snprintf(name, sizeof name, "GL_LIGHT%i", index);'
+        print '            json.beginMember(name);'
+        print '            json.beginObject();'
+        self.dump_atoms(glGetLight, '    GL_LIGHT0 + index')
+        print '            json.endObject();'
+        print '            json.endMember(); // GL_LIGHTi'
+        print '        }'
+        print '    }'
+        print
+
+    def texenv_param_target(self, name):
+        if name == 'GL_TEXTURE_LOD_BIAS':
+           return 'GL_TEXTURE_FILTER_CONTROL'
+        elif name == 'GL_COORD_REPLACE':
+           return 'GL_POINT_SPRITE'
+        else:
+           return 'GL_TEXTURE_ENV'
+
+    def dump_texenv_params(self):
+        for target in ['GL_TEXTURE_ENV', 'GL_TEXTURE_FILTER_CONTROL', 'GL_POINT_SPRITE']:
+            print '    if (!context.ES) {'
+            print '        json.beginMember("%s");' % target
+            print '        json.beginObject();'
+            for _, _, name in glGetTexEnv.iter():
+                if self.texenv_param_target(name) == target:
+                    self.dump_atom(glGetTexEnv, target, name) 
+            print '        json.endObject();'
+            print '    }'
+
+    def dump_vertex_attribs(self):
+        print '    GLint max_vertex_attribs = 0;'
+        print '    __glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_vertex_attribs);'
+        print '    for (GLint index = 0; index < max_vertex_attribs; ++index) {'
+        print '        char name[32];'
+        print '        snprintf(name, sizeof name, "GL_VERTEX_ATTRIB_ARRAY%i", index);'
+        print '        json.beginMember(name);'
+        print '        json.beginObject();'
+        self.dump_atoms(glGetVertexAttrib, 'index')
+        print '        json.endObject();'
+        print '        json.endMember(); // GL_VERTEX_ATTRIB_ARRAYi'
+        print '    }'
+        print
+
+    program_targets = [
+        'GL_FRAGMENT_PROGRAM_ARB',
+        'GL_VERTEX_PROGRAM_ARB',
+    ]
+
+    def dump_program_params(self):
+        for target in self.program_targets:
+            print '    if (glIsEnabled(%s)) {' % target
+            print '        json.beginMember("%s");' % target
+            print '        json.beginObject();'
+            self.dump_atoms(glGetProgramARB, target)
+            print '        json.endObject();'
+            print '    }'
+
+    def dump_texture_parameters(self):
+        print '    {'
+        print '        GLint active_texture = GL_TEXTURE0;'
+        print '        glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);'
+        print '        GLint max_texture_coords = 0;'
+        print '        glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);'
+        print '        GLint max_combined_texture_image_units = 0;'
+        print '        glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_combined_texture_image_units);'
+        print '        GLint max_units = std::max(max_combined_texture_image_units, max_texture_coords);'
+        print '        for (GLint unit = 0; unit < max_units; ++unit) {'
+        print '            char name[32];'
+        print '            snprintf(name, sizeof name, "GL_TEXTURE%i", unit);'
+        print '            json.beginMember(name);'
+        print '            glActiveTexture(GL_TEXTURE0 + unit);'
+        print '            json.beginObject();'
+        print '            GLboolean enabled;'
+        print '            GLint binding;'
+        print
+        for target, binding in texture_targets:
+            print '            // %s' % target
+            print '            enabled = GL_FALSE;'
+            print '            glGetBooleanv(%s, &enabled);' % target
+            print '            json.writeBoolMember("%s", enabled);' % target
+            print '            binding = 0;'
+            print '            glGetIntegerv(%s, &binding);' % binding
+            print '            json.writeNumberMember("%s", binding);' % binding
+            print '            if (enabled || binding) {'
+            print '                json.beginMember("%s");' % target
+            print '                json.beginObject();'
+            self.dump_atoms(glGetTexParameter, target)
+            print '                if (!context.ES) {'
+            # We only dump the first level parameters
+            self.dump_atoms(glGetTexLevelParameter, target, "0")
+            print '                }'
+            print '                json.endObject();'
+            print '                json.endMember(); // %s' % target
+            print '            }'
+            print
+        print '            if (unit < max_texture_coords) {'
+        self.dump_texenv_params()
+        print '            }'
+        print '            json.endObject();'
+        print '            json.endMember(); // GL_TEXTUREi'
+        print '        }'
+        print '        glActiveTexture(active_texture);'
+        print '    }'
+        print
+
+    def dump_framebuffer_parameters(self):
+        print '    {'
+        print '        GLint max_color_attachments = 0;'
+        print '        glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments);'
+        print '        GLint framebuffer;'
+        for target, binding in framebuffer_targets:
+            print '            // %s' % target
+            print '            framebuffer = 0;'
+            print '            glGetIntegerv(%s, &framebuffer);' % binding
+            print '            if (framebuffer) {'
+            print '                json.beginMember("%s");' % target
+            print '                json.beginObject();'
+            print '                for (GLint i = 0; i < max_color_attachments; ++i) {'
+            print '                    GLint color_attachment = GL_COLOR_ATTACHMENT0 + i;'
+            print '                    dumpFramebufferAttachementParameters(json, %s, color_attachment);' % target
+            print '                }'
+            print '                dumpFramebufferAttachementParameters(json, %s, GL_DEPTH_ATTACHMENT);' % target
+            print '                dumpFramebufferAttachementParameters(json, %s, GL_STENCIL_ATTACHMENT);' % target
+            print '                json.endObject();'
+            print '                json.endMember(); // %s' % target
+            print '            }'
+            print
+        print '    }'
+        print
+
+    def dump_attachment_parameters(self, target, attachment):
+        print '            {'
+        print '                GLint object_type = GL_NONE;'
+        print '                glGetFramebufferAttachmentParameteriv(%s, %s, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);' % (target, attachment)
+        print '                if (object_type != GL_NONE) {'
+        print '                    json.beginMember(enumToString(%s));' % attachment
+        print '                    json.beginObject();'
+        self.dump_atoms(glGetFramebufferAttachmentParameter, target, attachment)
+        print '                    json.endObject();'
+        print '                    json.endMember(); // GL_x_ATTACHMENT'
+        print '                }'
+        print '            }'
+
+    def dump_atoms(self, getter, *args):
+        for _, _, name in getter.iter():
+            self.dump_atom(getter, *(args + (name,))) 
+
+    def dump_atom(self, getter, *args):
+        name = args[-1]
+
+        # Avoid crash on MacOSX
+        # XXX: The right fix would be to look at the support extensions..
+        import platform
+        if name == 'GL_SAMPLER_BINDING' and platform.system() == 'Darwin':
+            return
+
+        print '        // %s' % name
+        print '        {'
+        #print '            assert(glGetError() == GL_NO_ERROR);'
+        type, value = getter(*args)
+        print '            if (glGetError() != GL_NO_ERROR) {'
+        #print '                std::cerr << "warning: %s(%s) failed\\n";' % (inflection, name)
+        print '                while (glGetError() != GL_NO_ERROR) {}'
+        print '            } else {'
+        print '                json.beginMember("%s");' % name
+        JsonWriter().visit(type, value)
+        print '                json.endMember();'
+        print '            }'
+        print '        }'
+        print
+
+
+if __name__ == '__main__':
+    StateDumper().dump()
diff --git a/glstate_shaders.cpp b/glstate_shaders.cpp
new file mode 100644 (file)
index 0000000..90e5d0a
--- /dev/null
@@ -0,0 +1,529 @@
+/**************************************************************************
+ *
+ * Copyright 2011 Jose Fonseca
+ * All Rights Reserved.
+ *
+ * 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 <string.h>
+
+#include <algorithm>
+#include <iostream>
+#include <map>
+#include <sstream>
+
+#include "json.hpp"
+#include "glproc.hpp"
+#include "glsize.hpp"
+#include "glstate.hpp"
+#include "glstate_internal.hpp"
+
+
+namespace glstate {
+
+
+// Mapping from shader type to shader source, used to accumulated the sources
+// of different shaders with same type.
+typedef std::map<std::string, std::string> ShaderMap;
+
+
+static void
+getShaderSource(ShaderMap &shaderMap, GLuint shader)
+{
+    if (!shader) {
+        return;
+    }
+
+    GLint shader_type = 0;
+    glGetShaderiv(shader, GL_SHADER_TYPE, &shader_type);
+    if (!shader_type) {
+        return;
+    }
+
+    GLint source_length = 0;
+    glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &source_length);
+    if (!source_length) {
+        return;
+    }
+
+    GLchar *source = new GLchar[source_length];
+    GLsizei length = 0;
+    source[0] = 0;
+    glGetShaderSource(shader, source_length, &length, source);
+
+    shaderMap[enumToString(shader_type)] += source;
+
+    delete [] source;
+}
+
+
+static void
+getShaderObjSource(ShaderMap &shaderMap, GLhandleARB shaderObj)
+{
+    if (!shaderObj) {
+        return;
+    }
+
+    GLint object_type = 0;
+    glGetObjectParameterivARB(shaderObj, GL_OBJECT_TYPE_ARB, &object_type);
+    if (object_type != GL_SHADER_OBJECT_ARB) {
+        return;
+    }
+
+    GLint shader_type = 0;
+    glGetObjectParameterivARB(shaderObj, GL_OBJECT_SUBTYPE_ARB, &shader_type);
+    if (!shader_type) {
+        return;
+    }
+
+    GLint source_length = 0;
+    glGetObjectParameterivARB(shaderObj, GL_OBJECT_SHADER_SOURCE_LENGTH_ARB, &source_length);
+    if (!source_length) {
+        return;
+    }
+
+    GLcharARB *source = new GLcharARB[source_length];
+    GLsizei length = 0;
+    source[0] = 0;
+    glGetShaderSource(shaderObj, source_length, &length, source);
+
+    shaderMap[enumToString(shader_type)] += source;
+
+    delete [] source;
+}
+
+
+static inline void
+dumpProgram(JSONWriter &json, GLint program)
+{
+    GLint attached_shaders = 0;
+    glGetProgramiv(program, GL_ATTACHED_SHADERS, &attached_shaders);
+    if (!attached_shaders) {
+        return;
+    }
+
+    ShaderMap shaderMap;
+
+    GLuint *shaders = new GLuint[attached_shaders];
+    GLsizei count = 0;
+    glGetAttachedShaders(program, attached_shaders, &count, shaders);
+    std::sort(shaders, shaders + count);
+    for (GLsizei i = 0; i < count; ++ i) {
+       getShaderSource(shaderMap, shaders[i]);
+    }
+    delete [] shaders;
+
+    for (ShaderMap::const_iterator it = shaderMap.begin(); it != shaderMap.end(); ++it) {
+        json.beginMember(it->first);
+        json.writeString(it->second);
+        json.endMember();
+    }
+}
+
+
+static inline void
+dumpProgramObj(JSONWriter &json, GLhandleARB programObj)
+{
+    GLint attached_shaders = 0;
+    glGetObjectParameterivARB(programObj, GL_OBJECT_ATTACHED_OBJECTS_ARB, &attached_shaders);
+    if (!attached_shaders) {
+        return;
+    }
+
+    ShaderMap shaderMap;
+
+    GLhandleARB *shaderObjs = new GLhandleARB[attached_shaders];
+    GLsizei count = 0;
+    glGetAttachedObjectsARB(programObj, attached_shaders, &count, shaderObjs);
+    std::sort(shaderObjs, shaderObjs + count);
+    for (GLsizei i = 0; i < count; ++ i) {
+       getShaderObjSource(shaderMap, shaderObjs[i]);
+    }
+    delete [] shaderObjs;
+
+    for (ShaderMap::const_iterator it = shaderMap.begin(); it != shaderMap.end(); ++it) {
+        json.beginMember(it->first);
+        json.writeString(it->second);
+        json.endMember();
+    }
+}
+
+/*
+ * When fetching the uniform name of an array we usually get name[0]
+ * so we need to cut the trailing "[0]" in order to properly construct
+ * array names later. Otherwise we endup with stuff like
+ * uniformArray[0][0],
+ * uniformArray[0][1],
+ * instead of
+ * uniformArray[0],
+ * uniformArray[1].
+ */
+static std::string
+resolveUniformName(const GLchar *name,  GLint size)
+{
+    std::string qualifiedName(name);
+    if (size > 1) {
+        std::string::size_type nameLength = qualifiedName.length();
+        static const char * const arrayStart = "[0]";
+        static const int arrayStartLen = 3;
+        if (qualifiedName.rfind(arrayStart) == (nameLength - arrayStartLen)) {
+            qualifiedName = qualifiedName.substr(0, nameLength - 3);
+        }
+    }
+    return qualifiedName;
+}
+
+static void
+dumpUniform(JSONWriter &json, GLint program, GLint size, GLenum type, const GLchar *name) {
+    GLenum elemType;
+    GLint numElems;
+    __gl_uniform_size(type, elemType, numElems);
+    if (elemType == GL_NONE) {
+        return;
+    }
+
+    GLfloat fvalues[4*4];
+    GLdouble dvalues[4*4];
+    GLint ivalues[4*4];
+    GLuint uivalues[4*4];
+
+    GLint i, j;
+
+    std::string qualifiedName = resolveUniformName(name, size);
+
+    for (i = 0; i < size; ++i) {
+        std::stringstream ss;
+        ss << qualifiedName;
+
+        if (size > 1) {
+            ss << '[' << i << ']';
+        }
+
+        std::string elemName = ss.str();
+
+        json.beginMember(elemName);
+
+        GLint location = glGetUniformLocation(program, elemName.c_str());
+
+        if (numElems > 1) {
+            json.beginArray();
+        }
+
+        switch (elemType) {
+        case GL_FLOAT:
+            glGetUniformfv(program, location, fvalues);
+            for (j = 0; j < numElems; ++j) {
+                json.writeNumber(fvalues[j]);
+            }
+            break;
+        case GL_DOUBLE:
+            glGetUniformdv(program, location, dvalues);
+            for (j = 0; j < numElems; ++j) {
+                json.writeNumber(dvalues[j]);
+            }
+            break;
+        case GL_INT:
+            glGetUniformiv(program, location, ivalues);
+            for (j = 0; j < numElems; ++j) {
+                json.writeNumber(ivalues[j]);
+            }
+            break;
+        case GL_UNSIGNED_INT:
+            glGetUniformuiv(program, location, uivalues);
+            for (j = 0; j < numElems; ++j) {
+                json.writeNumber(uivalues[j]);
+            }
+            break;
+        case GL_BOOL:
+            glGetUniformiv(program, location, ivalues);
+            for (j = 0; j < numElems; ++j) {
+                json.writeBool(ivalues[j]);
+            }
+            break;
+        default:
+            assert(0);
+            break;
+        }
+
+        if (numElems > 1) {
+            json.endArray();
+        }
+
+        json.endMember();
+    }
+}
+
+
+static void
+dumpUniformARB(JSONWriter &json, GLhandleARB programObj, GLint size, GLenum type, const GLchar *name) {
+
+    GLenum elemType;
+    GLint numElems;
+    __gl_uniform_size(type, elemType, numElems);
+    if (elemType == GL_NONE) {
+        return;
+    }
+
+    GLfloat fvalues[4*4];
+    GLint ivalues[4*4];
+
+    GLint i, j;
+
+    std::string qualifiedName = resolveUniformName(name, size);
+
+    for (i = 0; i < size; ++i) {
+        std::stringstream ss;
+        ss << qualifiedName;
+
+        if (size > 1) {
+            ss << '[' << i << ']';
+        }
+
+        std::string elemName = ss.str();
+
+        json.beginMember(elemName);
+
+        GLint location = glGetUniformLocationARB(programObj, elemName.c_str());
+
+        if (numElems > 1) {
+            json.beginArray();
+        }
+
+        switch (elemType) {
+        case GL_DOUBLE:
+            // glGetUniformdvARB does not exists
+        case GL_FLOAT:
+            glGetUniformfvARB(programObj, location, fvalues);
+            for (j = 0; j < numElems; ++j) {
+                json.writeNumber(fvalues[j]);
+            }
+            break;
+        case GL_UNSIGNED_INT:
+            // glGetUniformuivARB does not exists
+        case GL_INT:
+            glGetUniformivARB(programObj, location, ivalues);
+            for (j = 0; j < numElems; ++j) {
+                json.writeNumber(ivalues[j]);
+            }
+            break;
+        case GL_BOOL:
+            glGetUniformivARB(programObj, location, ivalues);
+            for (j = 0; j < numElems; ++j) {
+                json.writeBool(ivalues[j]);
+            }
+            break;
+        default:
+            assert(0);
+            break;
+        }
+
+        if (numElems > 1) {
+            json.endArray();
+        }
+
+        json.endMember();
+    }
+}
+
+
+static inline void
+dumpProgramUniforms(JSONWriter &json, GLint program)
+{
+    GLint active_uniforms = 0;
+    glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniforms);
+    if (!active_uniforms) {
+        return;
+    }
+
+    GLint active_uniform_max_length = 0;
+    glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &active_uniform_max_length);
+    GLchar *name = new GLchar[active_uniform_max_length];
+    if (!name) {
+        return;
+    }
+
+    for (GLint index = 0; index < active_uniforms; ++index) {
+        GLsizei length = 0;
+        GLint size = 0;
+        GLenum type = GL_NONE;
+        glGetActiveUniform(program, index, active_uniform_max_length, &length, &size, &type, name);
+
+        dumpUniform(json, program, size, type, name);
+    }
+
+    delete [] name;
+}
+
+
+static inline void
+dumpProgramObjUniforms(JSONWriter &json, GLhandleARB programObj)
+{
+    GLint active_uniforms = 0;
+    glGetObjectParameterivARB(programObj, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &active_uniforms);
+    if (!active_uniforms) {
+        return;
+    }
+
+    GLint active_uniform_max_length = 0;
+    glGetObjectParameterivARB(programObj, GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB, &active_uniform_max_length);
+    GLchar *name = new GLchar[active_uniform_max_length];
+    if (!name) {
+        return;
+    }
+
+    for (GLint index = 0; index < active_uniforms; ++index) {
+        GLsizei length = 0;
+        GLint size = 0;
+        GLenum type = GL_NONE;
+        glGetActiveUniformARB(programObj, index, active_uniform_max_length, &length, &size, &type, name);
+
+        dumpUniformARB(json, programObj, size, type, name);
+    }
+
+    delete [] name;
+}
+
+
+static inline void
+dumpArbProgram(JSONWriter &json, GLenum target)
+{
+    if (!glIsEnabled(target)) {
+        return;
+    }
+
+    GLint program_length = 0;
+    glGetProgramivARB(target, GL_PROGRAM_LENGTH_ARB, &program_length);
+    if (!program_length) {
+        return;
+    }
+
+    GLchar *source = new GLchar[program_length + 1];
+    source[0] = 0;
+    glGetProgramStringARB(target, GL_PROGRAM_STRING_ARB, source);
+    source[program_length] = 0;
+
+    json.beginMember(enumToString(target));
+    json.writeString(source);
+    json.endMember();
+
+    delete [] source;
+}
+
+
+static inline void
+dumpArbProgramUniforms(JSONWriter &json, GLenum target, const char *prefix)
+{
+    if (!glIsEnabled(target)) {
+        return;
+    }
+
+    GLint program_parameters = 0;
+    glGetProgramivARB(target, GL_PROGRAM_PARAMETERS_ARB, &program_parameters);
+    if (!program_parameters) {
+        return;
+    }
+
+    GLint max_program_local_parameters = 0;
+    glGetProgramivARB(target, GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB, &max_program_local_parameters);
+    for (GLint index = 0; index < max_program_local_parameters; ++index) {
+        GLdouble params[4] = {0, 0, 0, 0};
+        glGetProgramLocalParameterdvARB(target, index, params);
+
+        if (!params[0] && !params[1] && !params[2] && !params[3]) {
+            continue;
+        }
+
+        char name[256];
+        snprintf(name, sizeof name, "%sprogram.local[%i]", prefix, index);
+
+        json.beginMember(name);
+        json.beginArray();
+        json.writeNumber(params[0]);
+        json.writeNumber(params[1]);
+        json.writeNumber(params[2]);
+        json.writeNumber(params[3]);
+        json.endArray();
+        json.endMember();
+    }
+
+    GLint max_program_env_parameters = 0;
+    glGetProgramivARB(target, GL_MAX_PROGRAM_ENV_PARAMETERS_ARB, &max_program_env_parameters);
+    for (GLint index = 0; index < max_program_env_parameters; ++index) {
+        GLdouble params[4] = {0, 0, 0, 0};
+        glGetProgramEnvParameterdvARB(target, index, params);
+
+        if (!params[0] && !params[1] && !params[2] && !params[3]) {
+            continue;
+        }
+
+        char name[256];
+        snprintf(name, sizeof name, "%sprogram.env[%i]", prefix, index);
+
+        json.beginMember(name);
+        json.beginArray();
+        json.writeNumber(params[0]);
+        json.writeNumber(params[1]);
+        json.writeNumber(params[2]);
+        json.writeNumber(params[3]);
+        json.endArray();
+        json.endMember();
+    }
+}
+
+
+void
+dumpShadersUniforms(JSONWriter &json)
+{
+    GLint program = 0;
+    glGetIntegerv(GL_CURRENT_PROGRAM, &program);
+
+    GLhandleARB programObj = glGetHandleARB(GL_PROGRAM_OBJECT_ARB);
+
+    json.beginMember("shaders");
+    json.beginObject();
+    if (program) {
+        dumpProgram(json, program);
+    } else if (programObj) {
+        dumpProgramObj(json, programObj);
+    } else {
+        dumpArbProgram(json, GL_FRAGMENT_PROGRAM_ARB);
+        dumpArbProgram(json, GL_VERTEX_PROGRAM_ARB);
+    }
+    json.endObject();
+    json.endMember(); // shaders
+
+    json.beginMember("uniforms");
+    json.beginObject();
+    if (program) {
+        dumpProgramUniforms(json, program);
+    } else if (programObj) {
+        dumpProgramObjUniforms(json, programObj);
+    } else {
+        dumpArbProgramUniforms(json, GL_FRAGMENT_PROGRAM_ARB, "fp.");
+        dumpArbProgramUniforms(json, GL_VERTEX_PROGRAM_ARB, "vp.");
+    }
+    json.endObject();
+    json.endMember(); // uniforms
+}
+
+
+} /* namespace glstate */
index 5fddb7932a5ef49cb3c4c7fab080d4270fe00deb..efefff0e1e784faef84ba4928f7f9e0d64bcf068 100644 (file)
  *
  **************************************************************************/
 
+
+/*
+ * WGL bindings.
+ */
+
+
 #include <iostream>
 
 #include "glproc.hpp"
 namespace glws {
 
 
+/*
+ * Several WGL functions come in two flavors:
+ * - GDI (ChoosePixelFormat, SetPixelFormat, SwapBuffers, etc)
+ * - WGL (wglChoosePixelFormat, wglSetPixelFormat, wglSwapBuffers, etc)
+ *
+ * The GDI entrypoints will inevitably dispatch to the first module named
+ * "OPENGL32", loading "C:\Windows\System32\opengl32.dll" if none was loaded so
+ * far.
+ *
+ * In order to use a implementation other than the one installed in the system
+ * (when specified via the TRACE_LIBGL environment variable), we need to use
+ * WGL entrypoints.
+ *
+ * See also:
+ * - http://www.opengl.org/archives/resources/faq/technical/mswindows.htm
+ */
+static __PFNWGLCHOOSEPIXELFORMAT pfnChoosePixelFormat = &ChoosePixelFormat;
+static __PFNWGLSETPIXELFORMAT pfnSetPixelFormat = &SetPixelFormat;
+static __PFNWGLSWAPBUFFERS pfnSwapBuffers = &SwapBuffers;
+
+
 static LRESULT CALLBACK
 WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
@@ -68,6 +95,7 @@ public:
     {
         static bool first = TRUE;
         RECT rect;
+        BOOL bRet;
 
         if (first) {
             WNDCLASS wc;
@@ -126,9 +154,17 @@ public:
            pfd.dwFlags |= PFD_DOUBLEBUFFER;
         }
 
-        iPixelFormat = ChoosePixelFormat(hDC, &pfd);
+        iPixelFormat = pfnChoosePixelFormat(hDC, &pfd);
+        if (iPixelFormat <= 0) {
+            std::cerr << "error: ChoosePixelFormat failed\n";
+            exit(1);
+        }
 
-        SetPixelFormat(hDC, iPixelFormat, &pfd);
+        bRet = pfnSetPixelFormat(hDC, iPixelFormat, &pfd);
+        if (!bRet) {
+            std::cerr << "error: SetPixelFormat failed\n";
+            exit(1);
+        }
     }
 
     ~WglDrawable() {
@@ -163,7 +199,11 @@ public:
     }
 
     void swapBuffers(void) {
-        SwapBuffers(hDC);
+        BOOL bRet;
+        bRet = pfnSwapBuffers(hDC);
+        if (!bRet) {
+            std::cerr << "warning: SwapBuffers failed\n";
+        }
 
         // Drain message queue to prevent window from being considered
         // non-responsive
@@ -204,7 +244,11 @@ init(void) {
 
     const char * libgl_filename = getenv("TRACE_LIBGL");
 
-    if (!libgl_filename) {
+    if (libgl_filename) {
+        pfnChoosePixelFormat = &wglChoosePixelFormat;
+        pfnSetPixelFormat = &wglSetPixelFormat;
+        pfnSwapBuffers = &wglSwapBuffers;
+    } else {
         libgl_filename = "OPENGL32";
     }
 
@@ -260,11 +304,17 @@ makeCurrent(Drawable *drawable, Context *context)
         if (!wglContext->hglrc) {
             wglContext->hglrc = wglCreateContext(wglDrawable->hDC);
             if (!wglContext->hglrc) {
+                std::cerr << "error: wglCreateContext failed\n";
+                exit(1);
                 return false;
             }
             if (wglContext->shareContext) {
-                wglShareLists(wglContext->shareContext->hglrc,
-                              wglContext->hglrc);
+                BOOL bRet;
+                bRet = wglShareLists(wglContext->shareContext->hglrc,
+                                     wglContext->hglrc);
+                if (!bRet) {
+                    std::cerr << "warning: wglShareLists failed\n";
+                }
             }
         }
 
index 11ed3a5ca03d5c59c96b8c7bc05c67e2e5583bf8..9ad9f24ad535fe729cb478164b30410727014401 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "apitracecall.h"
 #include "apitracemodel.h"
+#include "thumbnail.h"
 
 #include <QApplication>
 #include <QDebug>
@@ -27,35 +28,47 @@ void ApiCallDelegate::paint(QPainter *painter,
     Q_ASSERT(index.column() == 0);
 
     if (event) {
-        QPoint offset;
+        QPoint offset = option.rect.topLeft();
         QStaticText text = event->staticText();
+        QSize textSize = text.size().toSize();
         //text.setTextWidth(option.rect.width());
         //QStyledItemDelegate::paint(painter, option, index);
         QStyle *style = QApplication::style();
         style->drawControl(QStyle::CE_ItemViewItem, &option, painter, 0);
+
+        // draw thumbnail of frame
+        if (event->type() == ApiTraceEvent::Frame) {
+            ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
+            const QImage & thumbnail = frame->thumbnail();
+            if (!thumbnail.isNull()) {
+                painter->drawImage(offset, thumbnail);
+                offset += QPoint(textSize.height() + thumbnail.width(), option.rect.height()/2 - textSize.height()/2);
+            }
+        }
+
         if (event->hasState()) {
-            QPixmap px = m_stateEmblem.pixmap(option.rect.height(),
-                                              option.rect.height());
+            QPixmap px = m_stateEmblem.pixmap(textSize.height(),
+                                              textSize.height());
             painter->drawPixmap(option.rect.topLeft(), px);
-            offset = QPoint(option.rect.height() + 5, 0);
+            offset += QPoint(textSize.height() + 5, 0);
         }
         if (event->type() == ApiTraceEvent::Call) {
             ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
             if (call->hasError()) {
-                QPixmap px = m_errorEmblem.pixmap(option.rect.height(),
-                                                  option.rect.height());
-                painter->drawPixmap(option.rect.topLeft() + offset, px);
-                offset += QPoint(option.rect.height() + 5, 0);
+                QPixmap px = m_errorEmblem.pixmap(textSize.height(),
+                                                  textSize.height());
+                painter->drawPixmap(offset, px);
+                offset += QPoint(textSize.height() + 5, 0);
             }
             if (call->edited()) {
-                QPixmap px = m_editEmblem.pixmap(option.rect.height(),
-                                                 option.rect.height());
-                painter->drawPixmap(option.rect.topLeft() + offset, px);
-                offset += QPoint(option.rect.height() + 5, 0);
+                QPixmap px = m_editEmblem.pixmap(textSize.height(),
+                                                 textSize.height());
+                painter->drawPixmap(offset, px);
+                offset += QPoint(textSize.height() + 5, 0);
             }
         }
 
-        painter->drawStaticText(option.rect.topLeft() + offset, text);
+        painter->drawStaticText(offset, text);
     } else {
         QStyledItemDelegate::paint(painter, option, index);
     }
@@ -64,8 +77,8 @@ void ApiCallDelegate::paint(QPainter *painter,
 QSize ApiCallDelegate::sizeHint(const QStyleOptionViewItem &option,
                                 const QModelIndex &index) const
 {
-    ApiTraceEvent *event =
-        index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
+    QVariant var = index.data(ApiTraceModel::EventRole);
+    ApiTraceEvent *event = var.value<ApiTraceEvent*>();
 
 #ifndef __APPLE__
     /* XXX: This fails on MacOSX, but seems safe to ignore */
@@ -76,7 +89,21 @@ QSize ApiCallDelegate::sizeHint(const QStyleOptionViewItem &option,
         QStaticText text = event->staticText();
         //text.setTextWidth(option.rect.width());
         //qDebug()<<"text size = "<<text.size();
-        return text.size().toSize();
+        QSize size = text.size().toSize();
+
+        // Adjust for thumbnail
+        if (event->type() == ApiTraceEvent::Frame) {
+            ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
+            const QImage & thumbnail = frame->thumbnail();
+            if (!thumbnail.isNull()) {
+                size.rwidth() += thumbnail.width();
+                if (size.height() < thumbnail.height()) {
+                    size.setHeight(thumbnail.height());
+                }
+            }
+        }
+
+        return size;
     }
     return QStyledItemDelegate::sizeHint(option, index);
 }
index 7bf3c8fe5f20c8531d27a4a4feacbfc320c5ad6c..bfc6cfdf128038d6e6b18a6afb057ebeb53e50e2 100644 (file)
@@ -1,4 +1,5 @@
 #include "apisurface.h"
+#include "thumbnail.h"
 
 #include <QDebug>
 #include <QSysInfo>
@@ -31,7 +32,7 @@ void ApiSurface::contentsFromBase64(const QByteArray &base64)
 {
     QByteArray dataArray = QByteArray::fromBase64(base64);
     m_image.loadFromData(dataArray, "png");
-    m_thumb = m_image.scaled(64, 64, Qt::KeepAspectRatio);
+    m_thumb = thumbnail(m_image);
 }
 
 QImage ApiSurface::image() const
index 5758b0750a3acb0ca743af3908bafe6a21d59796..b11c17c6740e33520dd366b50fed219ce3283cd4 100644 (file)
@@ -8,8 +8,7 @@
 #include <QThread>
 
 ApiTrace::ApiTrace()
-    : m_frameMarker(ApiTrace::FrameMarker_SwapBuffers),
-      m_needsSaving(false)
+    : m_needsSaving(false)
 {
     m_loader = new TraceLoader();
 
@@ -23,6 +22,8 @@ ApiTrace::ApiTrace()
             SIGNAL(frameContentsLoaded(ApiTraceFrame*,QVector<ApiTraceCall*>,quint64)),
             this,
             SLOT(loaderFrameLoaded(ApiTraceFrame*,QVector<ApiTraceCall*>,quint64)));
+    connect(m_loader, SIGNAL(guessedApi(int)),
+            this, SLOT(guessedApi(int)));
     connect(m_loader, SIGNAL(finishedParsing()),
             this, SLOT(finishedParsing()));
     connect(this, SIGNAL(loaderSearch(ApiTrace::SearchRequest)),
@@ -73,29 +74,6 @@ ApiTrace::~ApiTrace()
     delete m_saver;
 }
 
-bool ApiTrace::isCallAFrameMarker(const ApiTraceCall *call,
-                                  ApiTrace::FrameMarker marker)
-{
-    if (!call) {
-        return false;
-    }
-
-    switch (marker) {
-    case FrameMarker_SwapBuffers:
-        return call->flags() & trace::CALL_FLAG_END_FRAME;
-    case FrameMarker_Flush:
-        return call->name() == QLatin1String("glFlush");
-    case FrameMarker_Finish:
-        return call->name() == QLatin1String("glFinish");
-    case FrameMarker_Clear:
-        return call->name() == QLatin1String("glClear");
-    }
-
-    Q_ASSERT(!"unknown frame marker");
-
-    return false;
-}
-
 bool ApiTrace::isEmpty() const
 {
     return m_frames.isEmpty();
@@ -110,12 +88,7 @@ QString ApiTrace::fileName() const
     return m_fileName;
 }
 
-ApiTrace::FrameMarker ApiTrace::frameMarker() const
-{
-    return m_frameMarker;
-}
-
-QList<ApiTraceFrame*> ApiTrace::frames() const
+const QList<ApiTraceFrame*> & ApiTrace::frames() const
 {
     return m_frames;
 }
@@ -269,11 +242,23 @@ void ApiTrace::loadFrame(ApiTraceFrame *frame)
     }
 }
 
+void ApiTrace::guessedApi(int api)
+{
+    m_api = static_cast<trace::API>(api);
+}
+
+trace::API ApiTrace::api() const
+{
+    return m_api;
+}
+
 void ApiTrace::finishedParsing()
 {
-    ApiTraceFrame *firstFrame = m_frames[0];
-    if (firstFrame && !firstFrame->isLoaded()) {
-        loadFrame(firstFrame);
+    if (!m_frames.isEmpty()) {
+        ApiTraceFrame *firstFrame = m_frames[0];
+        if (firstFrame && !firstFrame->isLoaded()) {
+            loadFrame(firstFrame);
+        }
     }
 }
 
@@ -450,7 +435,7 @@ int ApiTrace::callInFrame(int callIdx) const
 {
     unsigned numCalls = 0;
 
-    for (int frameIdx = 0; frameIdx <= m_frames.size(); ++frameIdx) {
+    for (int frameIdx = 0; frameIdx < m_frames.size(); ++frameIdx) {
         const ApiTraceFrame *frame = m_frames[frameIdx];
         unsigned numCallsInFrame =  frame->isLoaded()
                 ? frame->numChildren()
@@ -496,4 +481,21 @@ bool ApiTrace::isFrameLoading(ApiTraceFrame *frame) const
     return m_loadingFrames.contains(frame);
 }
 
+void ApiTrace::bindThumbnailsToFrames(const QList<QImage> &thumbnails)
+{
+    QList<ApiTraceFrame *> frames = m_frames;
+
+    QList<QImage>::const_iterator thumbnail = thumbnails.begin();
+
+    foreach (ApiTraceFrame *frame, frames) {
+        if (thumbnail != thumbnails.end()) {
+            frame->setThumbnail(*thumbnail);
+
+            ++thumbnail;
+
+            emit changed(frame);
+        }
+    }
+}
+
 #include "apitrace.moc"
index 2833f60cddf90d5063cf7b40d8f951d5b1e0181c..04e295cd7e39470476990d834b5085be7bf77997 100644 (file)
@@ -3,6 +3,8 @@
 
 #include "apitracecall.h"
 
+#include "trace_api.hpp"
+
 #include <QObject>
 #include <QSet>
 
@@ -14,12 +16,6 @@ class ApiTrace : public QObject
 {
     Q_OBJECT
 public:
-    enum FrameMarker {
-        FrameMarker_SwapBuffers,
-        FrameMarker_Flush,
-        FrameMarker_Finish,
-        FrameMarker_Clear
-    };
     enum SearchResult {
         SearchResult_NotFound,
         SearchResult_Found,
@@ -51,8 +47,6 @@ public:
         Qt::CaseSensitivity cs;
     };
 
-    static bool isCallAFrameMarker(const ApiTraceCall *call,
-                                   FrameMarker marker);
 public:
     ApiTrace();
     ~ApiTrace();
@@ -61,13 +55,11 @@ public:
 
     QString fileName() const;
 
-    FrameMarker frameMarker() const;
-
     ApiTraceState defaultState() const;
 
     ApiTraceCall *callWithIndex(int idx) const;
 
-    QList<ApiTraceFrame*> frames() const;
+    const QList<ApiTraceFrame*> & frames() const;
     ApiTraceFrame *frameAt(int idx) const;
     int numFrames() const;
     int numCallsInFrame(int idx) const;
@@ -83,6 +75,8 @@ public:
 
     bool hasErrors() const;
 
+    trace::API api() const;
+
 public slots:
     void setFileName(const QString &name);
     void save();
@@ -100,6 +94,7 @@ public slots:
     void findCallIndex(int index);
     void setCallError(const ApiTraceError &error);
 
+    void bindThumbnailsToFrames(const QList<QImage> &thumbnails);
 
 signals:
     void loadTrace(const QString &name);
@@ -109,7 +104,7 @@ signals:
     void finishedLoadingTrace();
     void invalidated();
     void framesInvalidated();
-    void changed(ApiTraceCall *call);
+    void changed(ApiTraceEvent *event);
     void startedSaving();
     void saved();
     void findResult(const ApiTrace::SearchRequest &request,
@@ -133,6 +128,7 @@ signals:
 private slots:
     void addFrames(const QList<ApiTraceFrame*> &frames);
     void slotSaved();
+    void guessedApi(int api);
     void finishedParsing();
     void loaderFrameLoaded(ApiTraceFrame *frame,
                            const QVector<ApiTraceCall*> &calls,
@@ -149,8 +145,7 @@ private:
     QString m_tempFileName;
 
     QList<ApiTraceFrame*> m_frames;
-
-    FrameMarker m_frameMarker;
+    trace::API m_api;
 
     TraceLoader *m_loader;
     QThread     *m_loaderThread;
index 267dc0980edeaaa7ea02c26de9e981d25e96b6fa..dfaaeef0e478a6a7b7a9fba76b02ec510a67bcba 100644 (file)
@@ -1197,3 +1197,13 @@ unsigned ApiTraceFrame::lastCallIndex() const
         return m_lastCallIndex;
     }
 }
+
+void ApiTraceFrame::setThumbnail(const QImage & thumbnail)
+{
+    m_thumbnail = thumbnail;
+}
+
+const QImage & ApiTraceFrame::thumbnail() const
+{
+    return m_thumbnail;
+}
index 3a9faafd495e851c281609d5c7c94e9431020c7d..945ab0cb8eedbff9cf1c29389c1a06bc839ea2e9 100644 (file)
@@ -335,6 +335,10 @@ public:
 
     void setLastCallIndex(unsigned index);
     unsigned lastCallIndex() const;
+
+    void setThumbnail(const QImage & thumbnail);
+    const QImage & thumbnail() const;
+
 private:
     ApiTrace *m_parentTrace;
     quint64 m_binaryDataSize;
@@ -342,6 +346,7 @@ private:
     bool m_loaded;
     unsigned m_callsToLoad;
     unsigned m_lastCallIndex;
+    QImage m_thumbnail;
 };
 Q_DECLARE_METATYPE(ApiTraceFrame*);
 
index 7303ae1c31e836bb2e507fe9b5fd34af657fa574..0863c1b5bc0eb19ad1b649b9d83442483a2fc5ad 100644 (file)
@@ -248,8 +248,8 @@ void ApiTraceModel::setApiTrace(ApiTrace *trace)
             this, SLOT(beginAddingFrames(int, int)));
     connect(m_trace, SIGNAL(endAddingFrames()),
             this, SLOT(endAddingFrames()));
-    connect(m_trace, SIGNAL(changed(ApiTraceCall*)),
-            this, SLOT(callChanged(ApiTraceCall*)));
+    connect(m_trace, SIGNAL(changed(ApiTraceEvent*)),
+            this, SLOT(changed(ApiTraceEvent*)));
     connect(m_trace, SIGNAL(beginLoadingFrame(ApiTraceFrame*,int)),
             this, SLOT(beginLoadingFrame(ApiTraceFrame*,int)));
     connect(m_trace, SIGNAL(endLoadingFrame(ApiTraceFrame*)),
@@ -294,7 +294,7 @@ void ApiTraceModel::stateSetOnEvent(ApiTraceEvent *event)
         emit dataChanged(index, index);
     } else if (event->type() == ApiTraceEvent::Frame) {
         ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
-        const QList<ApiTraceFrame*> frames = m_trace->frames();
+        const QList<ApiTraceFrame*> frames = m_trace->frames();
         int row = frames.indexOf(frame);
         QModelIndex index = createIndex(row, 0, frame);
         emit dataChanged(index, index);
@@ -318,6 +318,15 @@ QModelIndex ApiTraceModel::indexForCall(ApiTraceCall *call) const
     return createIndex(row, 0, call);
 }
 
+void ApiTraceModel::changed(ApiTraceEvent *event)
+{
+    if (event->type() == ApiTraceEvent::Call) {
+        callChanged(static_cast<ApiTraceCall*>(event));
+    } else if (event->type() == ApiTraceEvent::Frame) {
+        frameChanged(static_cast<ApiTraceFrame*>(event));
+    }
+}
+
 void ApiTraceModel::callChanged(ApiTraceCall *call)
 {
     ApiTrace *trace = call->parentFrame()->parentTrace();
@@ -339,6 +348,14 @@ void ApiTraceModel::callChanged(ApiTraceCall *call)
     emit dataChanged(index, index);
 }
 
+void ApiTraceModel::frameChanged(ApiTraceFrame *frame)
+{
+    const QList<ApiTraceFrame*> & frames = m_trace->frames();
+    int row = frames.indexOf(frame);
+    QModelIndex index = createIndex(row, 0, frame);
+    emit dataChanged(index, index);
+}
+
 void ApiTraceModel::endAddingFrames()
 {
     endInsertRows();
index fe6b5cea69deb5f0702c8af239066b1db5b806bb..e7354aa6350191ad55aee5a8abe9d675079dfeeb 100644 (file)
@@ -54,7 +54,9 @@ private slots:
     void invalidateFrames();
     void beginAddingFrames(int oldCount, int numAdded);
     void endAddingFrames();
+    void changed(ApiTraceEvent *event);
     void callChanged(ApiTraceCall *call);
+    void frameChanged(ApiTraceFrame *frame);
     void beginLoadingFrame(ApiTraceFrame *frame, int numAdded);
     void endLoadingFrame(ApiTraceFrame *frame);
 
index 4a11da5308d4c786d347cfc8472edc88e84115a7..7b1e425f3e64ef2438d7d9c8e43ea5fbb6189e75 100644 (file)
@@ -1,5 +1,6 @@
 #include "imageviewer.h"
 
+#include <QDebug>
 #include <QDesktopWidget>
 #include <QPainter>
 #include <QPixmap>
@@ -10,6 +11,15 @@ ImageViewer::ImageViewer(QWidget *parent)
 {
     setupUi(this);
 
+    connect(lowerSpinBox, SIGNAL(valueChanged(double)),
+            SLOT(slotUpdate()));
+    connect(upperSpinBox, SIGNAL(valueChanged(double)),
+            SLOT(slotUpdate()));
+    connect(flipCheckBox, SIGNAL(stateChanged(int)),
+            SLOT(slotUpdate()));
+    connect(opaqueCheckBox, SIGNAL(stateChanged(int)),
+            SLOT(slotUpdate()));
+
     QPixmap px(32, 32);
     QPainter p(&px);
     p.fillRect(0, 0, 32, 32, Qt::white);
@@ -27,8 +37,76 @@ ImageViewer::ImageViewer(QWidget *parent)
 void ImageViewer::setImage(const QImage &image)
 {
     m_image = image;
-    QPixmap px = QPixmap::fromImage(image);
+    m_temp = m_image;
+    QPixmap px = QPixmap::fromImage(m_temp);
+    imageLabel->setPixmap(px);
+    updateGeometry();
+}
+
+static inline int clamp(int x)
+{
+    if (x <= 0) {
+        return 0;
+    }
+    if (x > 255) {
+        return 255;
+    }
+    return x;
+}
+
+void ImageViewer::slotUpdate()
+{
+    m_temp = m_image.mirrored(false, flipCheckBox->isChecked());
+
+    double lowerValue = lowerSpinBox->value();
+    double upperValue = upperSpinBox->value();
+
+    bool opaque = opaqueCheckBox->isChecked();
+
+    if (lowerValue != 0.0 || upperValue != 1.0 || opaque) {
+        /*
+         * Rescale the image.
+         *
+         * XXX: This would be much more useful if done with the full precision
+         * of the original image
+         */
+
+        int offset = - lowerValue * 255;
+        int scale = 256 / (upperValue - lowerValue);
+
+        m_temp = m_temp.convertToFormat(QImage::Format_ARGB32);
+
+        if (0) {
+            qDebug()
+                << "offset = " << offset << "\n"
+                << "scale = " << scale << "\n";
+        }
+
+        int width = m_temp.width();
+        int height = m_temp.height();
+
+        int aMask = opaque ? 0xff : 0;
+
+        for (int y = 0; y < height; ++y) {
+            QRgb *scanline = (QRgb *)m_temp.scanLine(y);
+            for (int x = 0; x < width; ++x) {
+                QRgb pixel = scanline[x];
+                int r = qRed(pixel);
+                int g = qGreen(pixel);
+                int b = qBlue(pixel);
+                int a = qAlpha(pixel);
+                r = clamp(((r + offset) * scale) >> 8);
+                g = clamp(((g + offset) * scale) >> 8);
+                b = clamp(((b + offset) * scale) >> 8);
+                a |= aMask;
+                scanline[x] = qRgba(r, g, b, a);
+            }
+        }
+    }
+
+    QPixmap px = QPixmap::fromImage(m_temp);
     imageLabel->setPixmap(px);
+
     updateGeometry();
 }
 
index 16bc7f2ab0b5c0f6e0e10b6955b5cb5a26fc9549..e8781182ebf8ae88343f4f6d0110934f469f97ef 100644 (file)
@@ -13,8 +13,13 @@ public:
     void setImage(const QImage &image);
 
     QSize sizeHint() const;
+
+private slots:
+    void slotUpdate();
+
 private:
     QImage m_image;
+    QImage m_temp;
 };
 
 
index d7af53dde789c219e76dae1428254f3b58337dfd..18e07f1ffcb87275aaa777184a22fb839c31f2fd 100644 (file)
@@ -6,12 +6,14 @@
 #include <QApplication>
 #include <QMetaType>
 #include <QVariant>
+#include <QImage>
 
 Q_DECLARE_METATYPE(QList<ApiTraceFrame*>);
 Q_DECLARE_METATYPE(QVector<ApiTraceCall*>);
 Q_DECLARE_METATYPE(Qt::CaseSensitivity);
 Q_DECLARE_METATYPE(ApiTrace::SearchResult);
 Q_DECLARE_METATYPE(ApiTrace::SearchRequest);
+Q_DECLARE_METATYPE(QList<QImage>);
 
 static void usage(void)
 {
@@ -28,6 +30,7 @@ int main(int argc, char **argv)
     qRegisterMetaType<Qt::CaseSensitivity>();
     qRegisterMetaType<ApiTrace::SearchResult>();
     qRegisterMetaType<ApiTrace::SearchRequest>();
+    qRegisterMetaType<QList<QImage> >();
     QStringList args = app.arguments();
 
     int i = 1;
index 039889ca1aa5bd0a9987cb35d2cc38eb808654cc..5e2a55db7fc7af4e4e3df59dfc010b0394ff0e35 100644 (file)
@@ -14,6 +14,7 @@
 #include "shaderssourcewidget.h"
 #include "tracedialog.h"
 #include "traceprocess.h"
+#include "thumbnail.h"
 #include "ui_retracerdialog.h"
 #include "vertexdatainterpreter.h"
 
@@ -176,7 +177,7 @@ void MainWindow::replayStart()
             dlgUi.doubleBufferingCB->isChecked());
         m_retracer->setBenchmarking(
             !dlgUi.errorCheckCB->isChecked());
-        replayTrace(false);
+        replayTrace(false, true);
     }
 }
 
@@ -186,6 +187,7 @@ void MainWindow::replayStop()
     m_ui.actionStop->setEnabled(false);
     m_ui.actionReplay->setEnabled(true);
     m_ui.actionLookupState->setEnabled(true);
+    m_ui.actionShowThumbnails->setEnabled(true);
 }
 
 void MainWindow::newTraceFile(const QString &fileName)
@@ -198,35 +200,33 @@ void MainWindow::newTraceFile(const QString &fileName)
     if (fileName.isEmpty()) {
         m_ui.actionReplay->setEnabled(false);
         m_ui.actionLookupState->setEnabled(false);
+        m_ui.actionShowThumbnails->setEnabled(false);
         setWindowTitle(tr("QApiTrace"));
     } else {
         QFileInfo info(fileName);
         m_ui.actionReplay->setEnabled(true);
         m_ui.actionLookupState->setEnabled(true);
+        m_ui.actionShowThumbnails->setEnabled(true);
         setWindowTitle(
             tr("QApiTrace - %1").arg(info.fileName()));
     }
 }
 
-void MainWindow::replayFinished(const QString &output)
+void MainWindow::replayFinished(const QString &message)
 {
     m_ui.actionStop->setEnabled(false);
     m_ui.actionReplay->setEnabled(true);
     m_ui.actionLookupState->setEnabled(true);
+    m_ui.actionShowThumbnails->setEnabled(true);
 
     m_progressBar->hide();
-    if (output.length() < 80) {
-        statusBar()->showMessage(output);
-    }
+    statusBar()->showMessage(message, 2000);
     m_stateEvent = 0;
     m_ui.actionShowErrorsDock->setEnabled(m_trace->hasErrors());
     m_ui.errorsDock->setVisible(m_trace->hasErrors());
     if (!m_trace->hasErrors()) {
         m_ui.errorsTreeWidget->clear();
     }
-
-    statusBar()->showMessage(
-        tr("Replaying finished!"), 2000);
 }
 
 void MainWindow::replayError(const QString &message)
@@ -234,6 +234,7 @@ void MainWindow::replayError(const QString &message)
     m_ui.actionStop->setEnabled(false);
     m_ui.actionReplay->setEnabled(true);
     m_ui.actionLookupState->setEnabled(true);
+    m_ui.actionShowThumbnails->setEnabled(true);
     m_stateEvent = 0;
     m_nonDefaultsLookupEvent = 0;
 
@@ -259,6 +260,7 @@ void MainWindow::finishedLoadingTrace()
     if (!m_trace) {
         return;
     }
+    m_api = m_trace->api();
     QFileInfo info(m_trace->fileName());
     statusBar()->showMessage(
         tr("Loaded %1").arg(info.fileName()), 3000);
@@ -268,7 +270,7 @@ void MainWindow::finishedLoadingTrace()
     }
 }
 
-void MainWindow::replayTrace(bool dumpState)
+void MainWindow::replayTrace(bool dumpState, bool dumpThumbnails)
 {
     if (m_trace->fileName().isEmpty()) {
         return;
@@ -277,6 +279,7 @@ void MainWindow::replayTrace(bool dumpState)
     m_retracer->setFileName(m_trace->fileName());
     m_retracer->setAPI(m_api);
     m_retracer->setCaptureState(dumpState);
+    m_retracer->setCaptureThumbnails(dumpThumbnails);
     if (m_retracer->captureState() && m_selectedEvent) {
         int index = 0;
         if (m_selectedEvent->type() == ApiTraceEvent::Call) {
@@ -300,9 +303,17 @@ void MainWindow::replayTrace(bool dumpState)
 
     m_ui.actionStop->setEnabled(true);
     m_progressBar->show();
-    if (dumpState) {
-        statusBar()->showMessage(
-            tr("Looking up the state..."));
+    if (dumpState || dumpThumbnails) {
+        if (dumpState && dumpThumbnails) {
+            statusBar()->showMessage(
+                tr("Looking up the state and capturing thumbnails..."));
+        } else if (dumpState) {
+            statusBar()->showMessage(
+                tr("Looking up the state..."));
+        } else if (dumpThumbnails) {
+            statusBar()->showMessage(
+                tr("Capturing thumbnails..."));
+        }
     } else {
         statusBar()->showMessage(
             tr("Replaying the trace file..."));
@@ -326,7 +337,12 @@ void MainWindow::lookupState()
         return;
     }
     m_stateEvent = m_selectedEvent;
-    replayTrace(true);
+    replayTrace(true, false);
+}
+
+void MainWindow::showThumbnails()
+{
+    replayTrace(false, true);
 }
 
 MainWindow::~MainWindow()
@@ -535,7 +551,7 @@ void MainWindow::fillStateForFrame()
     if (textures.isEmpty() && fbos.isEmpty()) {
         m_ui.surfacesTab->setDisabled(false);
     } else {
-        m_ui.surfacesTreeWidget->setIconSize(QSize(64, 64));
+        m_ui.surfacesTreeWidget->setIconSize(QSize(THUMBNAIL_SIZE, THUMBNAIL_SIZE));
         if (!textures.isEmpty()) {
             QTreeWidgetItem *textureItem =
                 new QTreeWidgetItem(m_ui.surfacesTreeWidget);
@@ -724,8 +740,8 @@ void MainWindow::initConnections()
             this, SLOT(slotStartedSaving()));
     connect(m_trace, SIGNAL(saved()),
             this, SLOT(slotSaved()));
-    connect(m_trace, SIGNAL(changed(ApiTraceCall*)),
-            this, SLOT(slotTraceChanged(ApiTraceCall*)));
+    connect(m_trace, SIGNAL(changed(ApiTraceEvent*)),
+            this, SLOT(slotTraceChanged(ApiTraceEvent*)));
     connect(m_trace, SIGNAL(findResult(ApiTrace::SearchRequest,ApiTrace::SearchResult,ApiTraceCall*)),
             this, SLOT(slotSearchResult(ApiTrace::SearchRequest,ApiTrace::SearchResult,ApiTraceCall*)));
     connect(m_trace, SIGNAL(foundFrameStart(ApiTraceFrame*)),
@@ -741,6 +757,8 @@ void MainWindow::initConnections()
             this, SLOT(replayError(const QString&)));
     connect(m_retracer, SIGNAL(foundState(ApiTraceState*)),
             this, SLOT(replayStateFound(ApiTraceState*)));
+    connect(m_retracer, SIGNAL(foundThumbnails(const QList<QImage>&)),
+            this, SLOT(replayThumbnailsFound(const QList<QImage>&)));
     connect(m_retracer, SIGNAL(retraceErrors(const QList<ApiTraceError>&)),
             this, SLOT(slotRetraceErrors(const QList<ApiTraceError>&)));
 
@@ -778,6 +796,8 @@ void MainWindow::initConnections()
             this, SLOT(replayStop()));
     connect(m_ui.actionLookupState, SIGNAL(triggered()),
             this, SLOT(lookupState()));
+    connect(m_ui.actionShowThumbnails, SIGNAL(triggered()),
+            this, SLOT(showThumbnails()));
     connect(m_ui.actionOptions, SIGNAL(triggered()),
             this, SLOT(showSettings()));
 
@@ -838,6 +858,12 @@ void MainWindow::replayStateFound(ApiTraceState *state)
     m_nonDefaultsLookupEvent = 0;
 }
 
+void MainWindow::replayThumbnailsFound(const QList<QImage> &thumbnails)
+{
+    m_ui.callView->setUniformRowHeights(false);
+    m_trace->bindThumbnailsToFrames(thumbnails);
+}
+
 void MainWindow::slotGoTo()
 {
     m_searchWidget->hide();
@@ -1013,11 +1039,14 @@ ApiTraceFrame * MainWindow::selectedFrame() const
     return NULL;
 }
 
-void MainWindow::slotTraceChanged(ApiTraceCall *call)
+void MainWindow::slotTraceChanged(ApiTraceEvent *event)
 {
-    Q_ASSERT(call);
-    if (call == m_selectedEvent) {
-        m_ui.detailsWebView->setHtml(call->toHtml());
+    Q_ASSERT(event);
+    if (event == m_selectedEvent) {
+        if (event->type() == ApiTraceEvent::Call) {
+            ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
+            m_ui.detailsWebView->setHtml(call->toHtml());
+        }
     }
 }
 
index 66be2e20d88692802fe31c5952947077db237747..a8f8c1d4658f3ccfc36f45223a7164e3991cbe38 100644 (file)
@@ -8,6 +8,8 @@
 
 #include <QMainWindow>
 #include <QProcess>
+#include <QList>
+#include <QImage>
 
 class ApiTrace;
 class ApiTraceCall;
@@ -46,13 +48,15 @@ private slots:
     void openTrace();
     void replayStart();
     void replayStop();
-    void replayFinished(const QString &output);
+    void replayFinished(const QString &message);
     void replayStateFound(ApiTraceState *state);
+    void replayThumbnailsFound(const QList<QImage> &thumbnails);
     void replayError(const QString &msg);
     void startedLoadingTrace();
     void loadProgess(int percent);
     void finishedLoadingTrace();
     void lookupState();
+    void showThumbnails();
     void showSettings();
     void openHelp(const QUrl &url);
     void showSurfacesMenu(const QPoint &pos);
@@ -72,7 +76,7 @@ private slots:
     void slotSaved();
     void slotGoFrameStart();
     void slotGoFrameEnd();
-    void slotTraceChanged(ApiTraceCall *call);
+    void slotTraceChanged(ApiTraceEvent *event);
     void slotRetraceErrors(const QList<ApiTraceError> &errors);
     void slotErrorSelected(QTreeWidgetItem *current);
     void slotSearchResult(const ApiTrace::SearchRequest &request,
@@ -86,7 +90,7 @@ private:
     void initObjects();
     void initConnections();
     void newTraceFile(const QString &fileName);
-    void replayTrace(bool dumpState);
+    void replayTrace(bool dumpState, bool dumpThumbnails);
     void fillStateForFrame();
 
     /* there's a difference between selected frame/call and
index 17ac14d7d8d13a97da4f2e1a61455770e9b1104d..84f974244943b45a551fd561a1f01ce8d8e108ac 100644 (file)
 #include "retracer.h"
 
 #include "apitracecall.h"
+#include "thumbnail.h"
+
+#include "image.hpp"
 
 #include <QDebug>
 #include <QVariant>
+#include <QList>
+#include <QImage>
 
 #include <qjson/parser.h>
 
+/**
+ * Wrapper around a QProcess which enforces IO to block .
+ *
+ * Several QIODevice users (notably QJSON) expect blocking semantics, e.g.,
+ * they expect that QIODevice::read() will blocked until the requested ammount
+ * of bytes is read or end of file is reached. But by default QProcess, does
+ * not block.  And passing QIODevice::Unbuffered mitigates but does not fully
+ * address the problem either.
+ *
+ * This class wraps around QProcess, providing QIODevice interface, while
+ * ensuring that all reads block.
+ *
+ * This class also works around a bug in QProcess::atEnd() implementation.
+ *
+ * See also:
+ * - http://qt-project.org/wiki/Simple_Crypt_IO_Device
+ * - http://qt-project.org/wiki/Custom_IO_Device
+ */
+class BlockingIODevice : public QIODevice
+{
+    /* We don't use the Q_OBJECT in this class given we don't declare any
+     * signals and slots or use any other services provided by Qt's meta-object
+     * system. */
+public:
+    BlockingIODevice(QProcess * io);
+    bool isSequential() const;
+    bool atEnd() const;
+    bool waitForReadyRead(int msecs = -1);
+
+protected:
+    qint64 readData(char * data, qint64 maxSize);
+    qint64 writeData(const char * data, qint64 maxSize);
+
+private:
+    QProcess *m_device;
+};
+
+BlockingIODevice::BlockingIODevice(QProcess * io) :
+    m_device(io)
+{
+    /*
+     * We pass QIODevice::Unbuffered to prevent the base QIODevice class to do
+     * its own buffering on top of the overridden readData() method.
+     *
+     * The only buffering used will be to satisfy QIODevice::peek() and
+     * QIODevice::ungetChar().
+     */
+    setOpenMode(ReadOnly | Unbuffered);
+}
+
+bool BlockingIODevice::isSequential() const
+{
+    return true;
+}
+
+bool BlockingIODevice::atEnd() const
+{
+    /*
+     * XXX: QProcess::atEnd() documentation is wrong -- it will return true
+     * even when the process is running --, so we try to workaround that here.
+     */
+    if (m_device->atEnd()) {
+        if (m_device->state() == QProcess::Running) {
+            if (!m_device->waitForReadyRead(-1)) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+bool BlockingIODevice::waitForReadyRead(int msecs)
+{
+    Q_UNUSED(msecs);
+    return true;
+}
+
+qint64 BlockingIODevice::readData(char * data, qint64 maxSize)
+{
+    qint64 bytesToRead = maxSize;
+    qint64 readSoFar = 0;
+    do {
+        qint64 chunkSize = m_device->read(data + readSoFar, bytesToRead);
+        if (chunkSize < 0) {
+            if (readSoFar) {
+                return readSoFar;
+            } else {
+                return chunkSize;
+            }
+        }
+        Q_ASSERT(chunkSize <= bytesToRead);
+        bytesToRead -= chunkSize;
+        readSoFar += chunkSize;
+        if (bytesToRead) {
+            if (!m_device->waitForReadyRead(-1)) {
+                qDebug() << "waitForReadyRead failed\n";
+                break;
+            }
+        }
+    } while(bytesToRead);
+
+    return readSoFar;
+}
+
+qint64 BlockingIODevice::writeData(const char * data, qint64 maxSize)
+{
+    Q_ASSERT(false);
+    return -1;
+}
+
+Q_DECLARE_METATYPE(QList<ApiTraceError>);
+
 Retracer::Retracer(QObject *parent)
     : QThread(parent),
       m_benchmarking(false),
@@ -14,6 +131,8 @@ Retracer::Retracer(QObject *parent)
       m_captureState(false),
       m_captureCall(0)
 {
+    qRegisterMetaType<QList<ApiTraceError> >();
+
 #ifdef Q_OS_WIN
     QString format = QLatin1String("%1;");
 #else
@@ -83,47 +202,30 @@ void Retracer::setCaptureState(bool enable)
     m_captureState = enable;
 }
 
-
-void Retracer::run()
+bool Retracer::captureThumbnails() const
 {
-    RetraceProcess *retrace = new RetraceProcess();
-    retrace->process()->setProcessEnvironment(m_processEnvironment);
-
-    retrace->setFileName(m_fileName);
-    retrace->setAPI(m_api);
-    retrace->setBenchmarking(m_benchmarking);
-    retrace->setDoubleBuffered(m_doubleBuffered);
-    retrace->setCaptureState(m_captureState);
-    retrace->setCaptureAtCallNumber(m_captureCall);
-
-    connect(retrace, SIGNAL(finished(const QString&)),
-            this, SLOT(cleanup()));
-    connect(retrace, SIGNAL(error(const QString&)),
-            this, SLOT(cleanup()));
-    connect(retrace, SIGNAL(finished(const QString&)),
-            this, SIGNAL(finished(const QString&)));
-    connect(retrace, SIGNAL(error(const QString&)),
-            this, SIGNAL(error(const QString&)));
-    connect(retrace, SIGNAL(foundState(ApiTraceState*)),
-            this, SIGNAL(foundState(ApiTraceState*)));
-    connect(retrace, SIGNAL(retraceErrors(const QList<ApiTraceError>&)),
-            this, SIGNAL(retraceErrors(const QList<ApiTraceError>&)));
-
-    retrace->start();
-
-    exec();
-
-    /* means we need to kill the process */
-    if (retrace->process()->state() != QProcess::NotRunning) {
-        retrace->terminate();
-    }
+    return m_captureThumbnails;
+}
 
-    delete retrace;
+void Retracer::setCaptureThumbnails(bool enable)
+{
+    m_captureThumbnails = enable;
 }
 
 
-void RetraceProcess::start()
+/**
+ * Starting point for the retracing thread.
+ *
+ * Overrides QThread::run().
+ */
+void Retracer::run()
 {
+    QString msg = QLatin1String("Replay finished!");
+
+    /*
+     * Construct command line
+     */
+
     QString prog;
     QStringList arguments;
 
@@ -132,7 +234,7 @@ void RetraceProcess::start()
     } else if (m_api == trace::API_EGL) {
         prog = QLatin1String("eglretrace");
     } else {
-        assert(0);
+        emit finished(QLatin1String("Unsupported API"));
         return;
     }
 
@@ -145,172 +247,163 @@ void RetraceProcess::start()
     if (m_captureState) {
         arguments << QLatin1String("-D");
         arguments << QString::number(m_captureCall);
-    } else {
-        if (m_benchmarking) {
-            arguments << QLatin1String("-b");
-        }
+    } else if (m_captureThumbnails) {
+        arguments << QLatin1String("-s"); // emit snapshots
+        arguments << QLatin1String("-"); // emit to stdout
+    } else if (m_benchmarking) {
+        arguments << QLatin1String("-b");
     }
 
     arguments << m_fileName;
 
-    m_process->start(prog, arguments);
-}
+    /*
+     * Start the process.
+     */
 
+    QProcess process;
 
-void RetraceProcess::replayFinished(int exitCode, QProcess::ExitStatus exitStatus)
-{
-    QByteArray output = m_process->readAllStandardOutput();
-    QString msg;
-    QString errStr = m_process->readAllStandardError();
-
-#if 0
-    qDebug()<<"Process finished = ";
-    qDebug()<<"\terr = "<<errStr;
-    qDebug()<<"\tout = "<<output;
-#endif
+    process.start(prog, arguments, QIODevice::ReadOnly);
+    if (!process.waitForStarted(-1)) {
+        emit finished(QLatin1String("Could not start process"));
+        return;
+    }
+
+    /*
+     * Process standard output
+     */
+
+    QList<QImage> thumbnails;
+    QVariantMap parsedJson;
+
+    process.setReadChannel(QProcess::StandardOutput);
+    if (process.waitForReadyRead(-1)) {
+        BlockingIODevice io(&process);
 
-    if (exitStatus != QProcess::NormalExit) {
-        msg = QLatin1String("Process crashed");
-    } else if (exitCode != 0) {
-        msg = QLatin1String("Process exited with non zero exit code");
-    } else {
         if (m_captureState) {
+            /*
+             * Parse JSON from the output.
+             *
+             * XXX: QJSON expects blocking IO.
+             *
+             * XXX: QJSON's scanner is inneficient as it abuses single
+             * character QIODevice::peek (not cheap), instead of maintaining a
+             * lookahead character on its own.
+             */
+
             bool ok = false;
-            QVariantMap parsedJson = m_jsonParser->parse(output, &ok).toMap();
-            ApiTraceState *state = new ApiTraceState(parsedJson);
-            emit foundState(state);
-            msg = tr("State fetched.");
-        } else {
-            msg = QString::fromUtf8(output);
-        }
-    }
+            QJson::Parser jsonParser;
+            parsedJson = jsonParser.parse(&io, &ok).toMap();
+            if (!ok) {
+                msg = QLatin1String("failed to parse JSON");
+            }
+        } else if (m_captureThumbnails) {
+            /*
+             * Parse concatenated PNM images from output.
+             */
 
-    QStringList errorLines = errStr.split('\n');
-    QList<ApiTraceError> errors;
-    QRegExp regexp("(^\\d+): +(\\b\\w+\\b): (.+$)");
-    foreach(QString line, errorLines) {
-        if (regexp.indexIn(line) != -1) {
-            ApiTraceError error;
-            error.callIndex = regexp.cap(1).toInt();
-            error.type = regexp.cap(2);
-            error.message = regexp.cap(3);
-            errors.append(error);
-        }
-    }
-    if (!errors.isEmpty()) {
-        emit retraceErrors(errors);
-    }
-    emit finished(msg);
-}
+            while (!io.atEnd()) {
+                unsigned channels = 0;
+                unsigned width = 0;
+                unsigned height = 0;
 
-void RetraceProcess::replayError(QProcess::ProcessError err)
-{
-    /*
-     * XXX: this function is likely unnecessary and should be eliminated given
-     * that replayFinished is always called, even on errors.
-     */
+                char header[512];
+                qint64 headerSize = 0;
+                int headerLines = 3; // assume no optional comment line
 
-#if 0
-    qDebug()<<"Process error = "<<err;
-    qDebug()<<"\terr = "<<m_process->readAllStandardError();
-    qDebug()<<"\tout = "<<m_process->readAllStandardOutput();
-#endif
+                for (int headerLine = 0; headerLine < headerLines; ++headerLine) {
+                    qint64 headerRead = io.readLine(&header[headerSize], sizeof(header) - headerSize);
 
-    emit error(
-        tr("Couldn't execute the replay file '%1'").arg(m_fileName));
-}
+                    // if header actually contains optional comment line, ...
+                    if (headerLine == 1 && header[headerSize] == '#') {
+                        ++headerLines;
+                    }
 
-Q_DECLARE_METATYPE(QList<ApiTraceError>);
-RetraceProcess::RetraceProcess(QObject *parent)
-    : QObject(parent)
-{
-    m_process = new QProcess(this);
-    m_jsonParser = new QJson::Parser();
+                    headerSize += headerRead;
+                }
 
-    qRegisterMetaType<QList<ApiTraceError> >();
+                const char *headerEnd = image::readPNMHeader(header, headerSize, &channels, &width, &height);
 
-    connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)),
-            this, SLOT(replayFinished(int, QProcess::ExitStatus)));
-    connect(m_process, SIGNAL(error(QProcess::ProcessError)),
-            this, SLOT(replayError(QProcess::ProcessError)));
-}
+                // if invalid PNM header was encountered, ...
+                if (header == headerEnd) {
+                    qDebug() << "error: invalid snapshot stream encountered";
+                    break;
+                }
 
-QProcess * RetraceProcess::process() const
-{
-    return m_process;
-}
+                // qDebug() << "channels: " << channels << ", width: " << width << ", height: " << height";
 
-QString RetraceProcess::fileName() const
-{
-    return m_fileName;
-}
+                QImage snapshot = QImage(width, height, channels == 1 ? QImage::Format_Mono : QImage::Format_RGB888);
 
-void RetraceProcess::setFileName(const QString &name)
-{
-    m_fileName = name;
-}
+                int rowBytes = channels * width;
+                for (int y = 0; y < height; ++y) {
+                    unsigned char *scanLine = snapshot.scanLine(y);
+                    qint64 readBytes = io.read((char *) scanLine, rowBytes);
+                    Q_ASSERT(readBytes == rowBytes);
+                }
 
-void RetraceProcess::setAPI(trace::API api)
-{
-    m_api = api;
-}
+                QImage thumb = thumbnail(snapshot);
+                thumbnails.append(thumb);
+            }
 
-bool RetraceProcess::isBenchmarking() const
-{
-    return m_benchmarking;
-}
+            Q_ASSERT(process.state() != QProcess::Running);
 
-void RetraceProcess::setBenchmarking(bool bench)
-{
-    m_benchmarking = bench;
-}
+        } else {
+            QByteArray output;
+            output = process.readAllStandardOutput();
+            if (output.length() < 80) {
+                msg = QString::fromUtf8(output);
+            }
+        }
+    }
 
-bool RetraceProcess::isDoubleBuffered() const
-{
-    return m_doubleBuffered;
-}
+    /*
+     * Wait for process termination
+     */
 
-void RetraceProcess::setDoubleBuffered(bool db)
-{
-    m_doubleBuffered = db;
-}
+    process.waitForFinished(-1);
 
-void RetraceProcess::setCaptureAtCallNumber(qlonglong num)
-{
-    m_captureCall = num;
-}
+    if (process.exitStatus() != QProcess::NormalExit) {
+        msg = QLatin1String("Process crashed");
+    } else if (process.exitCode() != 0) {
+        msg = QLatin1String("Process exited with non zero exit code");
+    }
 
-qlonglong RetraceProcess::captureAtCallNumber() const
-{
-    return m_captureCall;
-}
+    /*
+     * Parse errors.
+     */
 
-bool RetraceProcess::captureState() const
-{
-    return m_captureState;
-}
+    QList<ApiTraceError> errors;
+    process.setReadChannel(QProcess::StandardError);
+    QRegExp regexp("(^\\d+): +(\\b\\w+\\b): ([^\\r\\n]+)[\\r\\n]*$");
+    while (!process.atEnd()) {
+        QString line = process.readLine();
+        if (regexp.indexIn(line) != -1) {
+            ApiTraceError error;
+            error.callIndex = regexp.cap(1).toInt();
+            error.type = regexp.cap(2);
+            error.message = regexp.cap(3);
+            errors.append(error);
+        }
+    }
 
-void RetraceProcess::setCaptureState(bool enable)
-{
-    m_captureState = enable;
-}
+    /*
+     * Emit signals
+     */
 
-void RetraceProcess::terminate()
-{
-    if (m_process) {
-        m_process->terminate();
-        emit finished(tr("Process terminated."));
+    if (m_captureState) {
+        ApiTraceState *state = new ApiTraceState(parsedJson);
+        emit foundState(state);
+        msg = QLatin1String("State fetched.");
     }
-}
 
-void Retracer::cleanup()
-{
-    quit();
-}
+    if (m_captureThumbnails && !thumbnails.isEmpty()) {
+        emit foundThumbnails(thumbnails);
+    }
 
-RetraceProcess::~RetraceProcess()
-{
-    delete m_jsonParser;
+    if (!errors.isEmpty()) {
+        emit retraceErrors(errors);
+    }
+
+    emit finished(msg);
 }
 
 #include "retracer.moc"
index e5c391bcb23e91a7b5169601910589dac3d54770..d6da7ac5fd5e385cfca96bcab85553122a958b86 100644 (file)
@@ -8,63 +8,6 @@
 #include <QProcess>
 
 class ApiTraceState;
-namespace QJson {
-    class Parser;
-}
-
-/* internal class used by the retracer to run
- * in the thread */
-class RetraceProcess : public QObject
-{
-    Q_OBJECT
-public:
-    RetraceProcess(QObject *parent=0);
-    ~RetraceProcess();
-
-    QProcess *process() const;
-
-    QString fileName() const;
-    void setFileName(const QString &name);
-
-    void setAPI(trace::API api);
-
-    bool isBenchmarking() const;
-    void setBenchmarking(bool bench);
-
-    bool isDoubleBuffered() const;
-    void setDoubleBuffered(bool db);
-
-    void setCaptureAtCallNumber(qlonglong num);
-    qlonglong captureAtCallNumber() const;
-
-    bool captureState() const;
-    void setCaptureState(bool enable);
-
-public slots:
-    void start();
-    void terminate();
-
-signals:
-    void finished(const QString &output);
-    void error(const QString &msg);
-    void foundState(ApiTraceState *state);
-    void retraceErrors(const QList<ApiTraceError> &errors);
-
-private slots:
-    void replayFinished(int exitCode, QProcess::ExitStatus exitStatus);
-    void replayError(QProcess::ProcessError err);
-
-private:
-    QString m_fileName;
-    trace::API m_api;
-    bool m_benchmarking;
-    bool m_doubleBuffered;
-    bool m_captureState;
-    qlonglong m_captureCall;
-
-    QProcess *m_process;
-    QJson::Parser *m_jsonParser;
-};
 
 class Retracer : public QThread
 {
@@ -89,23 +32,26 @@ public:
     bool captureState() const;
     void setCaptureState(bool enable);
 
+    bool captureThumbnails() const;
+    void setCaptureThumbnails(bool enable);
+
 signals:
     void finished(const QString &output);
     void foundState(ApiTraceState *state);
+    void foundThumbnails(const QList<QImage> &thumbnails);
     void error(const QString &msg);
     void retraceErrors(const QList<ApiTraceError> &errors);
 
 protected:
     virtual void run();
 
-private slots:
-    void cleanup();
 private:
     QString m_fileName;
     trace::API m_api;
     bool m_benchmarking;
     bool m_doubleBuffered;
     bool m_captureState;
+    bool m_captureThumbnails;
     qlonglong m_captureCall;
 
     QProcessEnvironment m_processEnvironment;
diff --git a/gui/thumbnail.h b/gui/thumbnail.h
new file mode 100644 (file)
index 0000000..2315564
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef THUMBNAIL_H
+#define THUMBNAIL_H
+
+#define THUMBNAIL_SIZE 64
+
+#include <QImage>
+
+inline QImage
+thumbnail(const QImage &image, Qt::TransformationMode transformationMode = Qt::FastTransformation) {
+    return image.scaled(THUMBNAIL_SIZE, THUMBNAIL_SIZE, Qt::KeepAspectRatio, transformationMode);
+}
+
+#endif
index 7cb07f00aa1331e814c6d7025deffe76498a36fc..2ad32b1fb4d73523dc4219650ee5ac6cd93a85d6 100644 (file)
@@ -20,8 +20,7 @@ apiCallFromTraceCall(const trace::Call *call,
 }
 
 TraceLoader::TraceLoader(QObject *parent)
-    : QObject(parent),
-      m_frameMarker(ApiTrace::FrameMarker_SwapBuffers)
+    : QObject(parent)
 {
 }
 
@@ -61,6 +60,7 @@ void TraceLoader::loadTrace(const QString &filename)
         //Load the entire file into memory
         parseTrace();
     }
+    emit guessedApi(static_cast<int>(m_parser.api));
     emit finishedParsing();
 }
 
@@ -69,34 +69,6 @@ void TraceLoader::loadFrame(ApiTraceFrame *currentFrame)
     fetchFrameContents(currentFrame);
 }
 
-void TraceLoader::setFrameMarker(ApiTrace::FrameMarker marker)
-{
-    m_frameMarker = marker;
-}
-
-bool TraceLoader::isCallAFrameMarker(const trace::Call *call) const
-{
-    std::string name = call->name();
-
-    switch (m_frameMarker) {
-    case ApiTrace::FrameMarker_SwapBuffers:
-        return  name.find("SwapBuffers") != std::string::npos ||
-                name == "CGLFlushDrawable" ||
-                name == "glFrameTerminatorGREMEDY";
-        break;
-    case ApiTrace::FrameMarker_Flush:
-        return name == "glFlush";
-        break;
-    case ApiTrace::FrameMarker_Finish:
-        return name == "glFinish";
-        break;
-    case ApiTrace::FrameMarker_Clear:
-        return name == "glClear";
-        break;
-    }
-    return false;
-}
-
 int TraceLoader::numberOfFrames() const
 {
     return m_frameBookmarks.size();
@@ -104,7 +76,7 @@ int TraceLoader::numberOfFrames() const
 
 int TraceLoader::numberOfCallsInFrame(int frameIdx) const
 {
-    if (frameIdx > m_frameBookmarks.size()) {
+    if (frameIdx >= m_frameBookmarks.size()) {
         return 0;
     }
     FrameBookmarks::const_iterator itr =
@@ -147,7 +119,7 @@ void TraceLoader::scanTrace()
     while ((call = m_parser.scan_call())) {
         ++numOfCalls;
 
-        if (isCallAFrameMarker(call)) {
+        if (call->flags & trace::CALL_FLAG_END_FRAME) {
             FrameBookmark frameBookmark(startBookmark);
             frameBookmark.numberOfCalls = numOfCalls;
 
@@ -217,8 +189,7 @@ void TraceLoader::parseTrace()
                     apiCall->arguments()[apiCall->binaryDataIndex()].toByteArray();
             binaryDataSize += data.size();
         }
-        if (ApiTrace::isCallAFrameMarker(apiCall,
-                                         m_frameMarker)) {
+        if (call->flags & trace::CALL_FLAG_END_FRAME) {
             calls.squeeze();
             currentFrame->setCalls(calls, binaryDataSize);
             calls.clear();
@@ -386,7 +357,7 @@ int TraceLoader::callInFrame(int callIdx) const
 {
     unsigned numCalls = 0;
 
-    for (int frameIdx = 0; frameIdx <= m_frameBookmarks.size(); ++frameIdx) {
+    for (int frameIdx = 0; frameIdx < m_frameBookmarks.size(); ++frameIdx) {
         const FrameBookmark &frameBookmark = m_frameBookmarks[frameIdx];
         unsigned firstCall = numCalls;
         unsigned endCall = numCalls + frameBookmark.numberOfCalls;
@@ -452,7 +423,7 @@ TraceLoader::fetchFrameContents(ApiTraceFrame *currentFrame)
 
                 delete call;
 
-                if (ApiTrace::isCallAFrameMarker(apiCall, m_frameMarker)) {
+                if (apiCall->flags() & trace::CALL_FLAG_END_FRAME) {
                     break;
                 }
 
index 3a310bad260489aed17b665616c8ebcc43943932..0954078219fba68bd3e188b72d56027cd3b01d7b 100644 (file)
@@ -27,7 +27,6 @@ public:
 public slots:
     void loadTrace(const QString &filename);
     void loadFrame(ApiTraceFrame *frame);
-    void setFrameMarker(ApiTrace::FrameMarker marker);
     void findFrameStart(ApiTraceFrame *frame);
     void findFrameEnd(ApiTraceFrame *frame);
     void findCallIndex(int index);
@@ -36,6 +35,7 @@ public slots:
 signals:
     void startedParsing();
     void parsed(int percent);
+    void guessedApi(int api);
     void finishedParsing();
 
     void framesLoaded(const QList<ApiTraceFrame*> &frames);
@@ -62,11 +62,11 @@ private:
         trace::ParseBookmark start;
         int numberOfCalls;
     };
-    bool isCallAFrameMarker(const trace::Call *call) const;
     int numberOfFrames() const;
     int numberOfCallsInFrame(int frameIdx) const;
 
     void loadHelpFile();
+    void guessApi(const trace::Call *call);
     void scanTrace();
     void parseTrace();
 
@@ -84,7 +84,6 @@ private:
 
 private:
     trace::Parser m_parser;
-    ApiTrace::FrameMarker m_frameMarker;
 
     typedef QMap<int, FrameBookmark> FrameBookmarks;
     FrameBookmarks m_frameBookmarks;
index 3ae2dadcb7d6377631d622411932f87d61cb0bac..c6ab84600f1a6c72eaf30f83483a210618694877 100644 (file)
      </widget>
     </widget>
    </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QLabel" name="lowerLabel">
+       <property name="text">
+        <string>Lower</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QDoubleSpinBox" name="lowerSpinBox">
+       <property name="singleStep">
+        <double>0.050000000000000</double>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="upperLabel">
+       <property name="text">
+        <string>Upper</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QDoubleSpinBox" name="upperSpinBox">
+       <property name="singleStep">
+        <double>0.050000000000000</double>
+       </property>
+       <property name="value">
+        <double>1.000000000000000</double>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QCheckBox" name="flipCheckBox">
+       <property name="text">
+        <string>Flip</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QCheckBox" name="opaqueCheckBox">
+       <property name="text">
+        <string>Opaque</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
   </layout>
  </widget>
  <resources/>
index 5b48dc28d54c872845d0f57bb3ddaf611b71f25f..52cf49ed85040f7fe2bb4c6eb550667589c24914 100644 (file)
@@ -75,6 +75,7 @@
     <addaction name="actionReplay"/>
     <addaction name="actionStop"/>
     <addaction name="actionLookupState"/>
+    <addaction name="actionShowThumbnails"/>
     <addaction name="separator"/>
     <addaction name="actionOptions"/>
    </widget>
     <string>Ctrl+L</string>
    </property>
   </action>
+  <action name="actionShowThumbnails">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Show &amp;Thumbnails</string>
+   </property>
+   <property name="shortcut">
+    <string>Ctrl+T</string>
+   </property>
+  </action>
   <action name="actionOptions">
    <property name="text">
     <string>Options</string>
index 683557d62e826e6305ecb59d7b4d1105d71d48ef..dac4bc25f9cdeab746db2ea2a8e6b13f7efb214b 100644 (file)
      <layout class="QVBoxLayout" name="verticalLayout_4">
       <item>
        <widget class="QComboBox" name="apiComboBox">
+        <item>
+         <property name="text">
+          <string>Unknown</string>
+         </property>
+        </item>
         <item>
          <property name="text">
           <string>GL</string>
index a4559c730f8baf49d7e8661315315f0b9cdbd61d..1b5fdd23cdff7ba01ee17d3164d8697e332f2229 100644 (file)
@@ -1,6 +1,6 @@
 /**************************************************************************
  *
- * Copyright 2011 Jose Fonseca
+ * Copyright 2011-2012 Jose Fonseca
  * All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -84,6 +84,53 @@ public:
 };
 
 
+/**
+ * Similar to alloca(), but implemented with malloc.
+ */
+class ScopedAllocator
+{
+private:
+    void *next;
+
+public:
+    ScopedAllocator() :
+        next(NULL) {
+    }
+
+    inline void *
+    alloc(size_t size) {
+        if (!size) {
+            return NULL;
+        }
+
+        void * * buf = static_cast<void **>(malloc(sizeof(void *) + size));
+        if (!buf) {
+            return NULL;
+        }
+
+        *buf = next;
+        next = buf;
+
+        return &buf[1];
+    }
+
+    template< class T >
+    inline T *
+    alloc(size_t n = 1) {
+        return static_cast<T *>(alloc(sizeof(T) * n));
+    }
+
+    inline
+    ~ScopedAllocator() {
+        while (next) {
+            void *temp = *static_cast<void **>(next);
+            free(next);
+            next = temp;
+        }
+    }
+};
+
+
 void
 addRegion(unsigned long long address, void *buffer, unsigned long long size);
 
index 15cdaf5038e328d05e8646c564670d9ffe8c13fa..d6e838755c39a02cd6f517af2866f9d0736c6074 100644 (file)
@@ -72,7 +72,7 @@ class ValueDeserializer(stdapi.Visitor):
         print '    const trace::Array *__a%s = dynamic_cast<const trace::Array *>(&%s);' % (array.tag, rvalue)
         print '    if (__a%s) {' % (array.tag)
         length = '__a%s->values.size()' % array.tag
-        print '        %s = new %s[%s];' % (lvalue, array.type, length)
+        print '        %s = _allocator.alloc<%s>(%s);' % (lvalue, array.type, length)
         index = '__j' + array.tag
         print '        for (size_t {i} = 0; {i} < {length}; ++{i}) {{'.format(i = index, length = length)
         try:
@@ -86,7 +86,7 @@ class ValueDeserializer(stdapi.Visitor):
     def visitPointer(self, pointer, lvalue, rvalue):
         print '    const trace::Array *__a%s = dynamic_cast<const trace::Array *>(&%s);' % (pointer.tag, rvalue)
         print '    if (__a%s) {' % (pointer.tag)
-        print '        %s = new %s;' % (lvalue, pointer.type)
+        print '        %s = _allocator.alloc<%s>();' % (lvalue, pointer.type)
         try:
             self.visit(pointer.type, '%s[0]' % (lvalue,), '*__a%s->values[0]' % (pointer.tag,))
         finally:
@@ -242,6 +242,8 @@ class Retracer:
         # FIXME
 
     def deserializeArgs(self, function):
+        print '    retrace::ScopedAllocator _allocator;'
+        print '    (void)_allocator;'
         success = True
         for arg in function.args:
             arg_type = ConstRemover().visit(arg.type)
index d6fd39261228b5adb1a391263e7b495f4d086e95..38720718c7534d92bf18342c20fa2678371f37c1 100755 (executable)
@@ -40,6 +40,7 @@ ignoredFunctionNames = set([
     'glGetString',
     'glXGetClientString',
     'glXGetCurrentDisplay',
+    'glXGetCurrentContext',
     'glXGetProcAddress',
     'glXGetProcAddressARB',
     'wglGetProcAddress',
@@ -99,7 +100,6 @@ def readtrace(trace, calls):
         stdout = subprocess.PIPE,
     )
 
-    calls = []
     parser = Loader(p.stdout)
     parser.parse()
     return parser.calls
@@ -186,15 +186,11 @@ class SDiffer:
     def replace_dissimilar(self, alo, ahi, blo, bhi):
         assert alo < ahi and blo < bhi
         if bhi - blo < ahi - alo:
-            first  = self.insert(blo, bhi)
-            second = self.delete(alo, ahi)
+            self.insert(alo, alo, blo, bhi)
+            self.delete(alo, ahi, bhi, bhi)
         else:
-            first  = self.delete(alo, ahi)
-            second = self.insert(blo, bhi)
-
-        for g in first, second:
-            for line in g:
-                yield line
+            self.delete(alo, ahi, blo, blo)
+            self.insert(ahi, ahi, blo, bhi)
 
     def replace_value(self, a, b):
         if b == a:
@@ -212,6 +208,8 @@ class SDiffer:
     escape = "\33["
 
     def delete(self, alo, ahi, blo, bhi):
+        assert alo < ahi
+        assert blo == bhi
         for i in xrange(alo, ahi):
             call = self.a[i]
             self.highlighter.write('- ')
@@ -221,6 +219,8 @@ class SDiffer:
             self.dumpCall(call)
 
     def insert(self, alo, ahi, blo, bhi):
+        assert alo == ahi
+        assert blo < bhi
         for i in xrange(blo, bhi):
             call = self.b[i]
             self.highlighter.write('+ ')
@@ -229,6 +229,8 @@ class SDiffer:
             self.dumpCall(call)
 
     def equal(self, alo, ahi, blo, bhi):
+        assert alo < ahi and blo < bhi
+        assert ahi - alo == bhi - blo
         for i in xrange(0, bhi - blo):
             self.highlighter.write('  ')
             a_call = self.a[alo + i]
@@ -245,21 +247,21 @@ class SDiffer:
         if aNo is None:
             self.highlighter.write(' '*self.aSpace)
         else:
-            aStr = str(aNo)
+            aNoStr = str(aNo)
             self.highlighter.strike()
             self.highlighter.color(self.delete_color)
-            self.highlighter.write(str(aNo))
+            self.highlighter.write(aNoStr)
             self.highlighter.normal()
-            self.aSpace = len(aStr)
+            self.aSpace = len(aNoStr)
         self.highlighter.write(' ')
         if bNo is None:
-            self.highlighter.write(' '*self.aSpace)
+            self.highlighter.write(' '*self.bSpace)
         else:
-            bStr = str(bNo)
+            bNoStr = str(bNo)
             self.highlighter.color(self.insert_color)
-            self.highlighter.write(str(bNo))
+            self.highlighter.write(bNoStr)
             self.highlighter.normal()
-            self.bSpace = len(bStr)
+            self.bSpace = len(bNoStr)
         self.highlighter.write(' ')
 
     def dumpCall(self, call):