]> git.cworth.org Git - apitrace/blobdiff - glstate_images.cpp
Move tracers to wrappers subdirectory.
[apitrace] / glstate_images.cpp
index 4995500e97d15183caa9d340c809f668faee7256..f16cba41194a3c5b58ff219b1ec8fcc70d6c762a 100644 (file)
 #include "glstate_internal.hpp"
 
 
+#ifdef __linux__
+#include <dlfcn.h>
+#endif
+
 #ifdef __APPLE__
 
 #include <Carbon/Carbon.h>
@@ -62,74 +66,356 @@ OSStatus CGSGetSurfaceBounds(CGSConnectionID, CGWindowID, CGSSurfaceID, CGRect *
 namespace glstate {
 
 
-static inline void
-dumpTextureImage(JSONWriter &json, Context &context, GLenum target, GLint level)
+struct ImageDesc
 {
-    GLint width, height = 1, depth = 1;
-    GLint format;
+    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};
 
-    glGetTexLevelParameteriv(target, level, GL_TEXTURE_INTERNAL_FORMAT, &format);
+    assert(axis < 3);
+    assert(max >= 0);
 
-    width = 0;
-    glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &width);
+    GLint min = 0;
+    while (true) {
+        GLint test = (min + max) / 2;
+        if (test == min) {
+            return min;
+        }
+
+        size[axis] = test;
 
-    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 (probeTextureLevelSizeOES(target, level, size)) {
+            min = test;
+        } else {
+            max = test;
         }
     }
+}
 
-    if (width <= 0 || height <= 0 || depth <= 0) {
-        return;
+
+/**
+ * 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 {
-        char label[512];
+        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);
+        }
+    }
 
-        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);
+    return desc.valid();
+}
 
-        json.beginMember(label);
 
-        json.beginObject();
+/**
+ * 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;
+    }
 
-        // Tell the GUI this is no ordinary object, but an image
-        json.writeStringMember("__class__", "image");
+    GLint texture = 0;
+    glGetIntegerv(texture_binding, &texture);
+    if (!texture) {
+        return;
+    }
 
-        json.writeNumberMember("__width__", width);
-        json.writeNumberMember("__height__", height);
-        json.writeNumberMember("__depth__", depth);
+    GLint prev_fbo = 0;
+    GLuint fbo = 0;
+    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prev_fbo);
+    glGenFramebuffers(1, &fbo);
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
 
-        json.writeStringMember("__format__", enumToString(format));
+    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;
+    }
 
-        // 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);
+    glBindFramebuffer(GL_FRAMEBUFFER, prev_fbo);
 
-        GLubyte *pixels = new GLubyte[depth*width*height*4];
+    glDeleteFramebuffers(1, &fbo);
+}
 
-        context.resetPixelPackState();
 
-        glGetTexImage(target, level, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+static inline GLboolean
+isDepthFormat(GLenum internalFormat)
+{
+   switch (internalFormat) {
+   case GL_DEPTH_COMPONENT:
+   case GL_DEPTH_COMPONENT16:
+   case GL_DEPTH_COMPONENT24:
+   case GL_DEPTH_COMPONENT32:
+   case GL_DEPTH_COMPONENT32F:
+   case GL_DEPTH_COMPONENT32F_NV:
+   case GL_DEPTH_STENCIL:
+   case GL_DEPTH24_STENCIL8:
+   case GL_DEPTH32F_STENCIL8:
+   case GL_DEPTH32F_STENCIL8_NV:
+      return GL_TRUE;
+   }
+   return GL_FALSE;
+}
 
-        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__
+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();
 
-        delete [] pixels;
-        json.endObject();
+    GLuint channels;
+    GLenum format;
+    if (!context.ES && isDepthFormat(desc.internalFormat)) {
+       format = GL_DEPTH_COMPONENT;
+       channels = 1;
+    } else {
+       format = GL_RGBA;
+       channels = 4;
     }
+
+    // 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__", channels);
+
+    GLubyte *pixels = new GLubyte[desc.depth*desc.width*desc.height*channels];
+
+    context.resetPixelPackState();
+
+    if (context.ES) {
+        getTexImageOES(target, level, desc, pixels);
+    } else {
+        glGetTexImage(target, level, format, GL_UNSIGNED_BYTE, pixels);
+    }
+
+    context.restorePixelPackState();
+
+    json.beginMember("__data__");
+    char *pngBuffer;
+    int pngBufferSize;
+    image::writePixelsToBuffer(pixels, desc.width, desc.height, channels, true, &pngBuffer, &pngBufferSize);
+    json.writeBase64(pngBuffer, pngBufferSize);
+    free(pngBuffer);
+    json.endMember(); // __data__
+
+    delete [] pixels;
+    json.endObject();
 }
 
 
@@ -144,19 +430,17 @@ dumpTexture(JSONWriter &json, Context &context, GLenum target, GLenum binding)
 
     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) {
+        ImageDesc desc;
+        if (!getActiveTextureLevelDesc(context, target, level, desc)) {
             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);
+                dumpActiveTextureLevel(json, context, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level);
             }
         } else {
-            dumpTextureImage(json, context, target, level);
+            dumpActiveTextureLevel(json, context, target, level);
         }
 
         ++level;
@@ -171,11 +455,20 @@ dumpTextures(JSONWriter &json, Context &context)
     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);
+
+    /*
+     * At least the Android software GL implementation doesn't return the
+     * proper value for this, but rather returns 0. The GL(ES) specification
+     * mandates a minimum value of 2, so use this as a fall-back value.
+     */
+    max_units = std::min(max_units, 2);
+
     for (GLint unit = 0; unit < max_units; ++unit) {
         GLenum texture = GL_TEXTURE0 + unit;
         glActiveTexture(texture);
@@ -193,31 +486,33 @@ dumpTextures(JSONWriter &json, Context &context)
 
 static bool
 getDrawableBounds(GLint *width, GLint *height) {
-#if defined(TRACE_EGL)
+#if defined(__linux__)
+    if (dlsym(RTLD_DEFAULT, "eglGetCurrentContext")) {
+        EGLContext currentContext = eglGetCurrentContext();
+        if (currentContext == EGL_NO_CONTEXT) {
+            return false;
+        }
 
-    EGLContext currentContext = eglGetCurrentContext();
-    if (currentContext == EGL_NO_CONTEXT) {
-        return false;
-    }
+        EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW);
+        if (currentSurface == EGL_NO_SURFACE) {
+            return false;
+        }
 
-    EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW);
-    if (currentSurface == EGL_NO_SURFACE) {
-        return false;
-    }
+        EGLDisplay currentDisplay = eglGetCurrentDisplay();
+        if (currentDisplay == EGL_NO_DISPLAY) {
+            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;
+        }
 
-    if (!eglQuerySurface(currentDisplay, currentSurface, EGL_WIDTH, width) ||
-        !eglQuerySurface(currentDisplay, currentSurface, EGL_HEIGHT, height)) {
-        return false;
+        return true;
     }
+#endif
 
-    return true;
-
-#elif defined(_WIN32)
+#if defined(_WIN32)
 
     HDC hDC = wglGetCurrentDC();
     if (!hDC) {
@@ -332,81 +627,52 @@ bindTexture(GLint texture, GLenum &target, GLint &bound_texture)
 
 
 static bool
-getTextureLevelSize(GLint texture, GLint level, GLint *width, GLint *height)
+getTextureLevelDesc(Context &context, GLint texture, GLint level, ImageDesc &desc)
 {
-    *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);
+    getActiveTextureLevelDesc(context, target, level, desc);
 
     glBindTexture(target, bound_texture);
 
-    return *width > 0 && *height > 0;
+    return desc.valid();
 }
 
 
-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)
+getBoundRenderbufferDesc(Context &context, ImageDesc &desc)
 {
-    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);
+    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 *width > 0 && *height > 0;
+    return desc.valid();
 }
 
 
-static GLenum
-getRenderbufferFormat(GLint renderbuffer)
+static bool
+getRenderbufferDesc(Context &context, GLint renderbuffer, ImageDesc &desc)
 {
     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);
+    getBoundRenderbufferDesc(context, desc);
 
     glBindRenderbuffer(GL_RENDERBUFFER, bound_renderbuffer);
     
-    return format;
+    return desc.valid();
 }
 
 
 static bool
-getFramebufferAttachmentSize(GLenum target, GLenum attachment, GLint *width, GLint *height)
+getFramebufferAttachmentDesc(Context &context, GLenum target, GLenum attachment, ImageDesc &desc)
 {
     GLint object_type = GL_NONE;
     glGetFramebufferAttachmentParameteriv(target, attachment,
@@ -425,13 +691,13 @@ getFramebufferAttachmentSize(GLenum target, GLenum attachment, GLint *width, GLi
     }
 
     if (object_type == GL_RENDERBUFFER) {
-        return getRenderbufferSize(object_name, width, height);
+        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 getTextureLevelSize(object_name, texture_level, width, height);
+        return getTextureLevelDesc(context, object_name, texture_level, desc);
     } else {
         std::cerr << "warning: unexpected GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = " << object_type << "\n";
         return false;
@@ -440,41 +706,6 @@ getFramebufferAttachmentSize(GLenum target, GLenum attachment, GLint *width, GLi
 
 
 
-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;
@@ -499,7 +730,7 @@ getDrawBufferImage() {
     glGetIntegerv(framebuffer_binding, &draw_framebuffer);
 
     GLint draw_buffer = GL_NONE;
-    GLint width, height;
+    ImageDesc desc;
     if (draw_framebuffer) {
         if (context.ARB_draw_buffers) {
             glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer);
@@ -508,7 +739,7 @@ getDrawBufferImage() {
             }
         }
 
-        if (!getFramebufferAttachmentSize(framebuffer_target, draw_buffer, &width, &height)) {
+        if (!getFramebufferAttachmentDesc(context, framebuffer_target, draw_buffer, desc)) {
             return NULL;
         }
     } else {
@@ -519,9 +750,11 @@ getDrawBufferImage() {
             }
         }
 
-        if (!getDrawableBounds(&width, &height)) {
+        if (!getDrawableBounds(&desc.width, &desc.height)) {
             return NULL;
         }
+
+        desc.depth = 1;
     }
 
     GLenum type = GL_UNSIGNED_BYTE;
@@ -533,7 +766,7 @@ getDrawBufferImage() {
     }
 #endif
 
-    image::Image *image = new image::Image(width, height, channels, true);
+    image::Image *image = new image::Image(desc.width, desc.height, channels, true);
     if (!image) {
         return NULL;
     }
@@ -553,7 +786,7 @@ getDrawBufferImage() {
     // TODO: reset imaging state too
     context.resetPixelPackState();
 
-    glReadPixels(0, 0, width, height, format, type, image->pixels);
+    glReadPixels(0, 0, desc.width, desc.height, format, type, image->pixels);
 
     context.restorePixelPackState();
 
@@ -638,76 +871,67 @@ dumpReadBufferImage(JSONWriter &json, GLint width, GLint height, GLenum format,
 
 
 static inline GLuint
-downsampledFramebuffer(GLuint oldFbo, GLint drawbuffer,
+downsampledFramebuffer(Context &context,
+                       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;
+    {
+        // 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);
-        glGetRenderbufferParameteriv(GL_RENDERBUFFER,
-                                     GL_RENDERBUFFER_WIDTH, &w);
-        glGetRenderbufferParameteriv(GL_RENDERBUFFER,
-                                     GL_RENDERBUFFER_HEIGHT, &h);
-        glGetRenderbufferParameteriv(GL_RENDERBUFFER,
-                                     GL_RENDERBUFFER_INTERNAL_FORMAT, &format);
+        getBoundRenderbufferDesc(context, desc);
 
         glGenRenderbuffers(1, &rbs[*numRbs]);
         glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
-        glRenderbufferStorage(GL_RENDERBUFFER, format, w, h);
+        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, w, h, 0, 0, w, h,
+        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);
-            glGetRenderbufferParameteriv(GL_RENDERBUFFER,
-                                         GL_RENDERBUFFER_WIDTH, &w);
-            glGetRenderbufferParameteriv(GL_RENDERBUFFER,
-                                         GL_RENDERBUFFER_HEIGHT, &h);
-            glGetRenderbufferParameteriv(GL_RENDERBUFFER,
-                                         GL_RENDERBUFFER_INTERNAL_FORMAT, &format);
+            getBoundRenderbufferDesc(context, desc);
 
             glGenRenderbuffers(1, &rbs[*numRbs]);
             glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
-            glRenderbufferStorage(GL_RENDERBUFFER, format, w, h);
+            glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
             glFramebufferRenderbuffer(GL_FRAMEBUFFER,
                                       GL_DEPTH_ATTACHMENT,
                                       GL_RENDERBUFFER, rbs[*numRbs]);
@@ -715,22 +939,18 @@ downsampledFramebuffer(GLuint oldFbo, GLint drawbuffer,
             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
             glDrawBuffer(GL_DEPTH_ATTACHMENT);
             glReadBuffer(GL_DEPTH_ATTACHMENT);
-            glBlitFramebuffer(0, 0, w, h, 0, 0, w, h,
+            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);
-            glGetRenderbufferParameteriv(GL_RENDERBUFFER,
-                                         GL_RENDERBUFFER_WIDTH, &w);
-            glGetRenderbufferParameteriv(GL_RENDERBUFFER,
-                                         GL_RENDERBUFFER_HEIGHT, &h);
-            glGetRenderbufferParameteriv(GL_RENDERBUFFER,
-                                     GL_RENDERBUFFER_INTERNAL_FORMAT, &format);
+            getBoundRenderbufferDesc(context, desc);
 
             glGenRenderbuffers(1, &rbs[*numRbs]);
             glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
-            glRenderbufferStorage(GL_RENDERBUFFER, format, w, h);
+            glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
             glFramebufferRenderbuffer(GL_FRAMEBUFFER,
                                       GL_STENCIL_ATTACHMENT,
                                       GL_RENDERBUFFER, rbs[*numRbs]);
@@ -738,7 +958,7 @@ downsampledFramebuffer(GLuint oldFbo, GLint drawbuffer,
             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
             glDrawBuffer(GL_STENCIL_ATTACHMENT);
             glReadBuffer(GL_STENCIL_ATTACHMENT);
-            glBlitFramebuffer(0, 0, w, h, 0, 0, w, h,
+            glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
                               GL_STENCIL_BUFFER_BIT, GL_NEAREST);
             ++*numRbs;
         }
@@ -815,17 +1035,15 @@ dumpDrawableImages(JSONWriter &json, Context &context)
  * 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)
+dumpFramebufferAttachment(JSONWriter &json, Context &context, GLenum target, GLenum attachment, GLenum format)
 {
-    GLint width = 0, height = 0;
-    if (!getFramebufferAttachmentSize(target, attachment, &width, &height)) {
+    ImageDesc desc;
+    if (!getFramebufferAttachmentDesc(context, target, attachment, desc)) {
         return;
     }
 
-    GLint internalFormat = getFramebufferAttachmentFormat(target, attachment);
-
     json.beginMember(enumToString(attachment));
-    dumpReadBufferImage(json, width, height, format, internalFormat);
+    dumpReadBufferImage(json, desc.width, desc.height, format, desc.internalFormat);
     json.endMember();
 }
 
@@ -861,15 +1079,15 @@ dumpFramebufferAttachments(JSONWriter &json, Context &context, GLenum target)
                                                   GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE,
                                                   &alpha_size);
             GLenum format = alpha_size ? GL_RGBA : GL_RGB;
-            dumpFramebufferAttachment(json, target, attachment, format);
+            dumpFramebufferAttachment(json, context, 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);
+        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);
@@ -941,7 +1159,8 @@ dumpFramebuffer(JSONWriter &json, Context &context)
         if (multisample) {
             // glReadPixels doesnt support multisampled buffers so we need
             // to blit the fbo to a temporary one
-            fboCopy = downsampledFramebuffer(boundDrawFbo, draw_buffer0,
+            fboCopy = downsampledFramebuffer(context,
+                                             boundDrawFbo, draw_buffer0,
                                              colorRb, depthRb, stencilRb,
                                              rbs, &numRbs);
         }