]> git.cworth.org Git - apitrace/blob - retrace/glstate_images.cpp
glretrace: Dump depth buffers as floating point images.
[apitrace] / retrace / glstate_images.cpp
1 /**************************************************************************
2  *
3  * Copyright 2011 Jose Fonseca
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26
27 #include <assert.h>
28 #include <string.h>
29
30 #include <algorithm>
31 #include <iostream>
32
33 #include "image.hpp"
34 #include "json.hpp"
35 #include "glproc.hpp"
36 #include "glsize.hpp"
37 #include "glstate.hpp"
38 #include "glstate_internal.hpp"
39
40
41 #ifdef __linux__
42 #include <dlfcn.h>
43 #endif
44
45 #ifdef __APPLE__
46
47 #include <Carbon/Carbon.h>
48
49 #ifdef __cplusplus
50 extern "C" {
51 #endif
52
53 OSStatus CGSGetSurfaceBounds(CGSConnectionID, CGWindowID, CGSSurfaceID, CGRect *);
54
55 #ifdef __cplusplus
56 }
57 #endif
58
59 #endif /* __APPLE__ */
60
61
62 namespace glstate {
63
64
65 struct ImageDesc
66 {
67     GLint width;
68     GLint height;
69     GLint depth;
70     GLint samples;
71     GLint internalFormat;
72
73     inline
74     ImageDesc() :
75         width(0),
76         height(0),
77         depth(0),
78         samples(0),
79         internalFormat(GL_NONE)
80     {}
81
82     inline bool
83     operator == (const ImageDesc &other) const {
84         return width == other.width &&
85                height == other.height &&
86                depth == other.depth &&
87                samples == other.samples &&
88                internalFormat == other.internalFormat;
89     }
90
91     inline bool
92     valid(void) const {
93         return width > 0 && height > 0 && depth > 0;
94     }
95 };
96
97
98 /**
99  * Sames as enumToString, but with special provision to handle formatsLUMINANCE_ALPHA.
100  *
101  * OpenGL 2.1 specification states that "internalFormat may (for backwards
102  * compatibility with the 1.0 version of the GL) also take on the integer
103  * values 1, 2, 3, and 4, which are equivalent to symbolic constants LUMINANCE,
104  * LUMINANCE ALPHA, RGB, and RGBA respectively". 
105  */
106 const char *
107 formatToString(GLenum internalFormat) {
108     switch (internalFormat) {
109     case 1:
110         return "GL_LUMINANCE";
111     case 2:
112         return "GL_LUMINANCE_ALPHA";
113     case 3:
114         return "GL_RGB";
115     case 4:
116         return "GL_RGBA";
117     default:
118         return enumToString(internalFormat);
119     }
120 }
121
122
123 /**
124  * OpenGL ES does not support glGetTexLevelParameteriv, but it is possible to
125  * probe whether a texture has a given size by crafting a dummy glTexSubImage()
126  * call.
127  */
128 static bool
129 probeTextureLevelSizeOES(GLenum target, GLint level, const GLint size[3]) {
130     while (glGetError() != GL_NO_ERROR)
131         ;
132
133     GLenum internalFormat = GL_RGBA;
134     GLenum type = GL_UNSIGNED_BYTE;
135     GLint dummy = 0;
136
137     switch (target) {
138     case GL_TEXTURE_2D:
139     case GL_TEXTURE_CUBE_MAP:
140     case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
141     case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
142     case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
143     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
144     case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
145     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
146         glTexSubImage2D(target, level, size[0], size[1], 0, 0, internalFormat, type, &dummy);
147         break;
148     case GL_TEXTURE_3D_OES:
149         glTexSubImage3DOES(target, level, size[0], size[1], size[2], 0, 0, 0, internalFormat, type, &dummy);
150     default:
151         assert(0);
152         return false;
153     }
154
155     GLenum error = glGetError();
156
157     if (0) {
158         std::cerr << "(" << size[0] << ", " << size[1] << ", " << size[2] << ") = " << enumToString(error) << "\n";
159     }
160
161     if (error == GL_NO_ERROR) {
162         return true;
163     }
164
165     while (glGetError() != GL_NO_ERROR)
166         ;
167
168     return false;
169 }
170
171
172 /**
173  * Bisect the texture size along an axis.
174  *
175  * It is assumed that the texture exists.
176  */
177 static GLint
178 bisectTextureLevelSizeOES(GLenum target, GLint level, GLint axis, GLint max) {
179     GLint size[3] = {0, 0, 0};
180
181     assert(axis < 3);
182     assert(max >= 0);
183
184     GLint min = 0;
185     while (true) {
186         GLint test = (min + max) / 2;
187         if (test == min) {
188             return min;
189         }
190
191         size[axis] = test;
192
193         if (probeTextureLevelSizeOES(target, level, size)) {
194             min = test;
195         } else {
196             max = test;
197         }
198     }
199 }
200
201
202 /**
203  * Special path to obtain texture size on OpenGL ES, that does not rely on
204  * glGetTexLevelParameteriv
205  */
206 static bool
207 getActiveTextureLevelDescOES(Context &context, GLenum target, GLint level, ImageDesc &desc)
208 {
209     if (target == GL_TEXTURE_1D) {
210         // OpenGL ES does not support 1D textures
211         return false;
212     }
213
214     const GLint size[3] = {1, 1, 1}; 
215     if (!probeTextureLevelSizeOES(target, level, size)) {
216         return false;
217     }
218
219     // XXX: mere guess
220     desc.internalFormat = GL_RGBA;
221
222     GLint maxSize = 0;
223     switch (target) {
224     case GL_TEXTURE_2D:
225         glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
226         desc.width = bisectTextureLevelSizeOES(target, level, 0, maxSize);
227         desc.height = bisectTextureLevelSizeOES(target, level, 1, maxSize);
228         desc.depth = 1;
229         break;
230     case GL_TEXTURE_CUBE_MAP:
231     case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
232     case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
233     case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
234     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
235     case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
236     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
237         glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &maxSize);
238         desc.width = bisectTextureLevelSizeOES(target, level, 0, maxSize);
239         desc.height = desc.width;
240         desc.depth = 1;
241         break;
242     case GL_TEXTURE_3D_OES:
243         glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE_OES, &maxSize);
244         desc.width = bisectTextureLevelSizeOES(target, level, 0, maxSize);
245         desc.width = bisectTextureLevelSizeOES(target, level, 1, maxSize);
246         desc.depth = bisectTextureLevelSizeOES(target, level, 2, maxSize);
247         break;
248     default:
249         return false;
250     }
251
252     if (0) {
253         std::cerr
254             << enumToString(target) << " "
255             << level << " "
256             << desc.width << "x" << desc.height << "x" << desc.depth
257             << "\n";
258     }
259
260     return desc.valid();
261 }
262
263
264 static inline bool
265 getActiveTextureLevelDesc(Context &context, GLenum target, GLint level, ImageDesc &desc)
266 {
267     assert(target != GL_TEXTURE_CUBE_MAP);
268
269     if (context.ES) {
270         return getActiveTextureLevelDescOES(context, target, level, desc);
271     }
272
273     glGetTexLevelParameteriv(target, level, GL_TEXTURE_INTERNAL_FORMAT, &desc.internalFormat);
274
275     desc.width = 0;
276     glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &desc.width);
277
278     if (target == GL_TEXTURE_1D) {
279         desc.height = 1;
280         desc.depth = 1;
281     } else {
282         desc.height = 0;
283         glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &desc.height);
284         if (target != GL_TEXTURE_3D) {
285             desc.depth = 1;
286         } else {
287             desc.depth = 0;
288             glGetTexLevelParameteriv(target, level, GL_TEXTURE_DEPTH, &desc.depth);
289         }
290     }
291
292     glGetTexLevelParameteriv(target, level, GL_TEXTURE_SAMPLES, &desc.samples);
293
294     return desc.valid();
295 }
296
297
298 static GLenum
299 getTextureBinding(GLenum target)
300 {
301     switch (target) {
302     case GL_TEXTURE_1D:
303         return GL_TEXTURE_BINDING_1D;
304     case GL_TEXTURE_1D_ARRAY:
305         return GL_TEXTURE_BINDING_1D_ARRAY;
306     case GL_TEXTURE_2D:
307         return GL_TEXTURE_BINDING_2D;
308     case GL_TEXTURE_2D_MULTISAMPLE:
309         return GL_TEXTURE_BINDING_2D_MULTISAMPLE;
310     case GL_TEXTURE_2D_ARRAY:
311         return GL_TEXTURE_BINDING_2D_ARRAY;
312     case GL_TEXTURE_RECTANGLE:
313         return GL_TEXTURE_BINDING_RECTANGLE;
314     case GL_TEXTURE_CUBE_MAP:
315     case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
316     case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
317     case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
318     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
319     case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
320     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
321         return GL_TEXTURE_BINDING_CUBE_MAP;
322     case GL_TEXTURE_CUBE_MAP_ARRAY:
323         return GL_TEXTURE_BINDING_CUBE_MAP_ARRAY;
324     case GL_TEXTURE_3D:
325         return GL_TEXTURE_BINDING_3D;
326     default:
327         assert(false);
328         return GL_NONE;
329     }
330 }
331
332
333 /**
334  * OpenGL ES does not support glGetTexImage. Obtain the pixels by attaching the
335  * texture to a framebuffer.
336  */
337 static inline void
338 getTexImageOES(GLenum target, GLint level, ImageDesc &desc, GLubyte *pixels)
339 {
340     memset(pixels, 0x80, desc.height * desc.width * 4);
341
342     GLenum texture_binding = getTextureBinding(target);
343     if (texture_binding == GL_NONE) {
344         return;
345     }
346
347     GLint texture = 0;
348     glGetIntegerv(texture_binding, &texture);
349     if (!texture) {
350         return;
351     }
352
353     GLint prev_fbo = 0;
354     GLuint fbo = 0;
355     glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prev_fbo);
356     glGenFramebuffers(1, &fbo);
357     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
358
359     GLenum status;
360
361     switch (target) {
362     case GL_TEXTURE_2D:
363     case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
364     case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
365     case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
366     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
367     case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
368     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
369         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, level);
370         status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
371         if (status != GL_FRAMEBUFFER_COMPLETE) {
372             std::cerr << __FUNCTION__ << ": " << enumToString(status) << "\n";
373         }
374         glReadPixels(0, 0, desc.width, desc.height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
375         break;
376     case GL_TEXTURE_3D_OES:
377         for (int i = 0; i < desc.depth; i++) {
378             glFramebufferTexture3D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_3D, texture, level, i);
379             glReadPixels(0, 0, desc.width, desc.height, GL_RGBA, GL_UNSIGNED_BYTE, pixels + 4 * i * desc.width * desc.height);
380         }
381         break;
382     }
383
384     glBindFramebuffer(GL_FRAMEBUFFER, prev_fbo);
385
386     glDeleteFramebuffers(1, &fbo);
387 }
388
389
390 struct InternalFormatDesc
391 {
392     GLenum internalFormat;
393     GLenum format;
394 };
395
396
397 static const InternalFormatDesc
398 internalFormatDescs[] = {
399
400     {1, GL_RED},
401     {2, GL_RG},
402     {3, GL_RGB},
403     {4, GL_RGBA},
404
405     {GL_RED,    GL_RED},
406     {GL_GREEN,  GL_GREEN},
407     {GL_BLUE,   GL_BLUE},
408     {GL_ALPHA,  GL_ALPHA},
409     {GL_RG,     GL_RG},
410     {GL_RGB,    GL_RGB},
411     {GL_BGR,    GL_RGB},
412     {GL_RGBA,   GL_RGBA},
413     {GL_BGRA,   GL_RGBA},
414     {GL_LUMINANCE,      GL_LUMINANCE},
415     {GL_LUMINANCE_ALPHA,        GL_LUMINANCE_ALPHA},
416     {GL_INTENSITY,      GL_INTENSITY},
417  
418     {GL_RG8,    GL_RG},
419     {GL_RG16,   GL_RG},
420     {GL_RGB8,   GL_RGB},
421     {GL_RGB16,  GL_RGB},
422     {GL_RGBA8,  GL_RGBA},
423     {GL_RGBA16, GL_RGBA},
424     {GL_RGB10_A2,       GL_RGBA},
425     {GL_LUMINANCE8,     GL_LUMINANCE},
426     {GL_LUMINANCE16,    GL_LUMINANCE},
427     {GL_ALPHA8, GL_ALPHA},
428     {GL_ALPHA16,        GL_ALPHA},
429     {GL_LUMINANCE8_ALPHA8,      GL_LUMINANCE_ALPHA},
430     {GL_LUMINANCE16_ALPHA16,    GL_LUMINANCE_ALPHA},
431     {GL_INTENSITY8,     GL_INTENSITY},
432     {GL_INTENSITY16,    GL_INTENSITY},
433     
434     {GL_RED_INTEGER,    GL_RED_INTEGER},
435     {GL_GREEN_INTEGER,  GL_GREEN_INTEGER},
436     {GL_BLUE_INTEGER,   GL_BLUE_INTEGER},
437     {GL_ALPHA_INTEGER,  GL_ALPHA_INTEGER},
438     {GL_RG_INTEGER,     GL_RG_INTEGER},
439     {GL_RGB_INTEGER,    GL_RGB_INTEGER},
440     {GL_BGR_INTEGER,    GL_RGB_INTEGER},
441     {GL_RGBA_INTEGER,   GL_RGBA_INTEGER},
442     {GL_BGRA_INTEGER,   GL_RGBA_INTEGER},
443     {GL_LUMINANCE_INTEGER_EXT,  GL_LUMINANCE_INTEGER_EXT},
444     {GL_LUMINANCE_ALPHA_INTEGER_EXT,    GL_LUMINANCE_ALPHA_INTEGER_EXT},
445  
446     {GL_R8I,    GL_RED_INTEGER},
447     {GL_R8UI,   GL_RED_INTEGER},
448     {GL_R16I,   GL_RED_INTEGER},
449     {GL_R16UI,  GL_RED_INTEGER},
450     {GL_R32I,   GL_RED_INTEGER},
451     {GL_R32UI,  GL_RED_INTEGER},
452     {GL_RG8I,   GL_RG_INTEGER},
453     {GL_RG8UI,  GL_RG_INTEGER},
454     {GL_RG16I,  GL_RG_INTEGER},
455     {GL_RG16UI, GL_RG_INTEGER},
456     {GL_RG32I,  GL_RG_INTEGER},
457     {GL_RG32UI, GL_RG_INTEGER},
458     {GL_RGB8I,  GL_RGB_INTEGER},
459     {GL_RGB8UI, GL_RGB_INTEGER},
460     {GL_RGB16I, GL_RGB_INTEGER},
461     {GL_RGB16UI,        GL_RGB_INTEGER},
462     {GL_RGB32I, GL_RGB_INTEGER},
463     {GL_RGB32UI,        GL_RGB_INTEGER},
464     {GL_RGBA8I, GL_RGBA_INTEGER},
465     {GL_RGBA8UI,        GL_RGBA_INTEGER},
466     {GL_RGBA16I,        GL_RGBA_INTEGER},
467     {GL_RGBA16UI,       GL_RGBA_INTEGER},
468     {GL_RGBA32I,        GL_RGBA_INTEGER},
469     {GL_RGBA32UI,       GL_RGBA_INTEGER},
470     {GL_RGB10_A2UI,     GL_RGBA_INTEGER},
471     {GL_LUMINANCE8I_EXT,        GL_LUMINANCE_INTEGER_EXT},
472     {GL_LUMINANCE8UI_EXT,       GL_LUMINANCE_INTEGER_EXT},
473     {GL_LUMINANCE16I_EXT,       GL_LUMINANCE_INTEGER_EXT},
474     {GL_LUMINANCE16UI_EXT,      GL_LUMINANCE_INTEGER_EXT},
475     {GL_LUMINANCE32I_EXT,       GL_LUMINANCE_INTEGER_EXT},
476     {GL_LUMINANCE32UI_EXT,      GL_LUMINANCE_INTEGER_EXT},
477     {GL_ALPHA8I_EXT,    GL_ALPHA_INTEGER_EXT},
478     {GL_ALPHA8UI_EXT,   GL_ALPHA_INTEGER_EXT},
479     {GL_ALPHA16I_EXT,   GL_ALPHA_INTEGER_EXT},
480     {GL_ALPHA16UI_EXT,  GL_ALPHA_INTEGER_EXT},
481     {GL_ALPHA32I_EXT,   GL_ALPHA_INTEGER_EXT},
482     {GL_ALPHA32UI_EXT,  GL_ALPHA_INTEGER_EXT},
483     {GL_LUMINANCE_ALPHA8I_EXT,  GL_LUMINANCE_ALPHA_INTEGER_EXT},
484     {GL_LUMINANCE_ALPHA8UI_EXT, GL_LUMINANCE_ALPHA_INTEGER_EXT},
485     {GL_LUMINANCE_ALPHA16I_EXT, GL_LUMINANCE_ALPHA_INTEGER_EXT},
486     {GL_LUMINANCE_ALPHA16UI_EXT,        GL_LUMINANCE_ALPHA_INTEGER_EXT},
487     {GL_LUMINANCE_ALPHA32I_EXT, GL_LUMINANCE_ALPHA_INTEGER_EXT},
488     {GL_LUMINANCE_ALPHA32UI_EXT,        GL_LUMINANCE_ALPHA_INTEGER_EXT},
489     {GL_INTENSITY8I_EXT,        GL_RED_INTEGER},
490     {GL_INTENSITY8UI_EXT,       GL_RED_INTEGER},
491     {GL_INTENSITY16I_EXT,       GL_RED_INTEGER},
492     {GL_INTENSITY16UI_EXT,      GL_RED_INTEGER},
493     {GL_INTENSITY32I_EXT,       GL_RED_INTEGER},
494     {GL_INTENSITY32UI_EXT,      GL_RED_INTEGER},
495     
496     {GL_DEPTH_COMPONENT,        GL_DEPTH_COMPONENT},
497     {GL_DEPTH_COMPONENT16,      GL_DEPTH_COMPONENT},
498     {GL_DEPTH_COMPONENT24,      GL_DEPTH_COMPONENT},
499     {GL_DEPTH_COMPONENT32,      GL_DEPTH_COMPONENT},
500     {GL_DEPTH_COMPONENT32F,     GL_DEPTH_COMPONENT},
501     {GL_DEPTH_COMPONENT32F_NV,  GL_DEPTH_COMPONENT},
502     {GL_DEPTH_STENCIL,          GL_DEPTH_COMPONENT},
503     {GL_DEPTH24_STENCIL8,       GL_DEPTH_COMPONENT},
504     {GL_DEPTH32F_STENCIL8,      GL_DEPTH_COMPONENT},
505     {GL_DEPTH32F_STENCIL8_NV,   GL_DEPTH_COMPONENT},
506 };
507
508
509 static GLenum
510 getFormat(GLenum internalFormat)
511 {
512     for (unsigned i = 0; i < sizeof internalFormatDescs / sizeof internalFormatDescs[0]; ++i) {
513         if (internalFormatDescs[i].internalFormat == internalFormat) {
514             return internalFormatDescs[i].format;
515         }
516     }
517     return GL_RGBA;
518 }
519
520
521 static inline void
522 dumpActiveTextureLevel(JSONWriter &json, Context &context, GLenum target, GLint level)
523 {
524     ImageDesc desc;
525     if (!getActiveTextureLevelDesc(context, target, level, desc)) {
526         return;
527     }
528
529     char label[512];
530     GLint active_texture = GL_TEXTURE0;
531     glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
532     snprintf(label, sizeof label, "%s, %s, level = %d",
533              enumToString(active_texture), enumToString(target), level);
534
535     json.beginMember(label);
536
537     GLenum format = getFormat(desc.internalFormat);;
538     if (context.ES && format == GL_DEPTH_COMPONENT) {
539         format = GL_RED;
540     }
541     GLuint channels = _gl_format_channels(format);;
542
543     image::Image *image = new image::Image(desc.width, desc.height*desc.depth, channels, true);
544
545     context.resetPixelPackState();
546
547     if (context.ES) {
548         getTexImageOES(target, level, desc, image->pixels);
549     } else {
550         glGetTexImage(target, level, format, GL_UNSIGNED_BYTE, image->pixels);
551     }
552
553     context.restorePixelPackState();
554
555     json.writeImage(image, formatToString(desc.internalFormat), desc.depth);
556
557     delete image;
558
559     json.endMember(); // label
560 }
561
562
563 static inline void
564 dumpTexture(JSONWriter &json, Context &context, GLenum target, GLenum binding)
565 {
566     GLint texture_binding = 0;
567     glGetIntegerv(binding, &texture_binding);
568     if (!glIsEnabled(target) && !texture_binding) {
569         return;
570     }
571
572     GLint level = 0;
573     do {
574         ImageDesc desc;
575
576         if (target == GL_TEXTURE_CUBE_MAP) {
577             for (int face = 0; face < 6; ++face) {
578                 if (!getActiveTextureLevelDesc(context, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level, desc)) {
579                     return;
580                 }
581                 dumpActiveTextureLevel(json, context, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level);
582             }
583         } else {
584             if (!getActiveTextureLevelDesc(context, target, level, desc)) {
585                 return;
586             }
587             dumpActiveTextureLevel(json, context, target, level);
588         }
589
590         ++level;
591     } while(true);
592 }
593
594
595 void
596 dumpTextures(JSONWriter &json, Context &context)
597 {
598     json.beginMember("textures");
599     json.beginObject();
600     GLint active_texture = GL_TEXTURE0;
601     glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
602
603     GLint max_texture_coords = 0;
604     glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);
605     GLint max_combined_texture_image_units = 0;
606     glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_combined_texture_image_units);
607     GLint max_units = std::max(max_combined_texture_image_units, max_texture_coords);
608
609     /*
610      * At least the Android software GL implementation doesn't return the
611      * proper value for this, but rather returns 0. The GL(ES) specification
612      * mandates a minimum value of 2, so use this as a fall-back value.
613      */
614     max_units = std::max(max_units, 2);
615
616     for (GLint unit = 0; unit < max_units; ++unit) {
617         GLenum texture = GL_TEXTURE0 + unit;
618         glActiveTexture(texture);
619         dumpTexture(json, context, GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D);
620         dumpTexture(json, context, GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D);
621         dumpTexture(json, context, GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D);
622         dumpTexture(json, context, GL_TEXTURE_RECTANGLE, GL_TEXTURE_BINDING_RECTANGLE);
623         dumpTexture(json, context, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP);
624     }
625     glActiveTexture(active_texture);
626     json.endObject();
627     json.endMember(); // textures
628 }
629
630
631 static bool
632 getDrawableBounds(GLint *width, GLint *height) {
633 #if defined(__linux__)
634     if (dlsym(RTLD_DEFAULT, "eglGetCurrentContext")) {
635         EGLContext currentContext = eglGetCurrentContext();
636         if (currentContext != EGL_NO_CONTEXT) {
637             EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW);
638             if (currentSurface == EGL_NO_SURFACE) {
639                 return false;
640             }
641
642             EGLDisplay currentDisplay = eglGetCurrentDisplay();
643             if (currentDisplay == EGL_NO_DISPLAY) {
644                 return false;
645             }
646
647             if (!eglQuerySurface(currentDisplay, currentSurface, EGL_WIDTH, width) ||
648                 !eglQuerySurface(currentDisplay, currentSurface, EGL_HEIGHT, height)) {
649                 return false;
650             }
651
652             return true;
653         }
654     }
655 #endif
656
657 #if defined(_WIN32)
658
659     HDC hDC = wglGetCurrentDC();
660     if (!hDC) {
661         return false;
662     }
663
664     HWND hWnd = WindowFromDC(hDC);
665     RECT rect;
666
667     if (!GetClientRect(hWnd, &rect)) {
668        return false;
669     }
670
671     *width  = rect.right  - rect.left;
672     *height = rect.bottom - rect.top;
673     return true;
674
675 #elif defined(__APPLE__)
676
677     CGLContextObj ctx = CGLGetCurrentContext();
678     if (ctx == NULL) {
679         return false;
680     }
681
682     CGSConnectionID cid;
683     CGSWindowID wid;
684     CGSSurfaceID sid;
685
686     if (CGLGetSurface(ctx, &cid, &wid, &sid) != kCGLNoError) {
687         return false;
688     }
689
690     CGRect rect;
691
692     if (CGSGetSurfaceBounds(cid, wid, sid, &rect) != 0) {
693         return false;
694     }
695
696     *width = rect.size.width;
697     *height = rect.size.height;
698     return true;
699
700 #elif defined(HAVE_X11)
701
702     Display *display;
703     Drawable drawable;
704     Window root;
705     int x, y;
706     unsigned int w, h, bw, depth;
707
708     display = glXGetCurrentDisplay();
709     if (!display) {
710         return false;
711     }
712
713     drawable = glXGetCurrentDrawable();
714     if (drawable == None) {
715         return false;
716     }
717
718     if (!XGetGeometry(display, drawable, &root, &x, &y, &w, &h, &bw, &depth)) {
719         return false;
720     }
721
722     *width = w;
723     *height = h;
724     return true;
725
726 #else
727
728     return false;
729
730 #endif
731 }
732
733
734 struct TextureTargetBinding
735 {
736     GLenum target;
737     GLenum binding;
738 };
739
740
741 static const GLenum
742 textureTargets[] = {
743     GL_TEXTURE_1D,
744     GL_TEXTURE_2D,
745     GL_TEXTURE_RECTANGLE,
746     GL_TEXTURE_CUBE_MAP,
747     GL_TEXTURE_3D,
748     GL_TEXTURE_2D_MULTISAMPLE,
749     GL_TEXTURE_1D_ARRAY,
750     GL_TEXTURE_2D_ARRAY,
751     GL_TEXTURE_CUBE_MAP_ARRAY,
752 };
753
754
755 static GLenum
756 getTextureTarget(GLint texture)
757 {
758     if (!glIsTexture(texture)) {
759         return GL_NONE;
760     }
761
762     for (unsigned i = 0; i < sizeof(textureTargets)/sizeof(textureTargets[0]); ++i) {
763         GLenum target = textureTargets[i];
764         GLenum binding = getTextureBinding(target);
765
766         while (glGetError() != GL_NO_ERROR)
767             ;
768
769         GLint bound_texture = 0;
770         glGetIntegerv(binding, &bound_texture);
771         glBindTexture(target, texture);
772
773         bool succeeded = glGetError() == GL_NO_ERROR;
774
775         glBindTexture(target, bound_texture);
776
777         if (succeeded) {
778             return target;
779         }
780     }
781
782     return GL_NONE;
783 }
784
785
786 static bool
787 getBoundRenderbufferDesc(Context &context, ImageDesc &desc)
788 {
789     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &desc.width);
790     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &desc.height);
791     desc.depth = 1;
792     
793     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &desc.samples);
794
795     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_INTERNAL_FORMAT, &desc.internalFormat);
796     
797     return desc.valid();
798 }
799
800
801 static bool
802 getRenderbufferDesc(Context &context, GLint renderbuffer, ImageDesc &desc)
803 {
804     GLint bound_renderbuffer = 0;
805     glGetIntegerv(GL_RENDERBUFFER_BINDING, &bound_renderbuffer);
806     glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
807
808     getBoundRenderbufferDesc(context, desc);
809
810     glBindRenderbuffer(GL_RENDERBUFFER, bound_renderbuffer);
811     
812     return desc.valid();
813 }
814
815
816 static bool
817 getFramebufferAttachmentDesc(Context &context, GLenum target, GLenum attachment, ImageDesc &desc)
818 {
819     GLint object_type = GL_NONE;
820     glGetFramebufferAttachmentParameteriv(target, attachment,
821                                           GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
822                                           &object_type);
823     if (object_type == GL_NONE) {
824         return false;
825     }
826
827     GLint object_name = 0;
828     glGetFramebufferAttachmentParameteriv(target, attachment,
829                                           GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
830                                           &object_name);
831     if (object_name == 0) {
832         return false;
833     }
834
835     if (object_type == GL_RENDERBUFFER) {
836         return getRenderbufferDesc(context, object_name, desc);
837     } else if (object_type == GL_TEXTURE) {
838         GLint texture_face = 0;
839         glGetFramebufferAttachmentParameteriv(target, attachment,
840                                               GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE,
841                                               &texture_face);
842
843         GLint texture_level = 0;
844         glGetFramebufferAttachmentParameteriv(target, attachment,
845                                               GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL,
846                                               &texture_level);
847
848         GLint bound_texture = 0;
849         if (texture_face != 0) {
850             glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &bound_texture);
851             glBindTexture(GL_TEXTURE_CUBE_MAP, object_name);
852             getActiveTextureLevelDesc(context, texture_face, texture_level, desc);
853             glBindTexture(GL_TEXTURE_CUBE_MAP, bound_texture);
854         } else {
855             GLenum texture_target = getTextureTarget(object_name);
856             GLenum texture_binding = getTextureBinding(texture_target);
857             glGetIntegerv(texture_binding, &bound_texture);
858             glBindTexture(texture_target, object_name);
859             getActiveTextureLevelDesc(context, texture_target, texture_level, desc);
860             glBindTexture(texture_target, bound_texture);
861         }
862
863         return desc.valid();
864     } else {
865         std::cerr << "warning: unexpected GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = " << object_type << "\n";
866         return false;
867     }
868 }
869
870
871
872 image::Image *
873 getDrawBufferImage() {
874     GLenum format = GL_RGB;
875     GLint channels = _gl_format_channels(format);
876     if (channels > 4) {
877         return NULL;
878     }
879
880     Context context;
881
882     GLenum framebuffer_binding;
883     GLenum framebuffer_target;
884     if (context.ES) {
885         framebuffer_binding = GL_FRAMEBUFFER_BINDING;
886         framebuffer_target = GL_FRAMEBUFFER;
887     } else {
888         framebuffer_binding = GL_DRAW_FRAMEBUFFER_BINDING;
889         framebuffer_target = GL_DRAW_FRAMEBUFFER;
890     }
891
892     GLint draw_framebuffer = 0;
893     glGetIntegerv(framebuffer_binding, &draw_framebuffer);
894
895     GLint draw_buffer = GL_NONE;
896     ImageDesc desc;
897     if (draw_framebuffer) {
898         if (context.ARB_draw_buffers) {
899             glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer);
900             if (draw_buffer == GL_NONE) {
901                 return NULL;
902             }
903         } else {
904             // GL_COLOR_ATTACHMENT0 is implied
905             draw_buffer = GL_COLOR_ATTACHMENT0;
906         }
907
908         if (!getFramebufferAttachmentDesc(context, framebuffer_target, draw_buffer, desc)) {
909             return NULL;
910         }
911     } else {
912         if (context.ES) {
913             // XXX: Draw buffer is always FRONT for single buffer context, BACK
914             // for double buffered contexts. There is no way to know which (as
915             // GL_DOUBLEBUFFER state is also unavailable), so always assume
916             // double-buffering.
917             draw_buffer = GL_BACK;
918         } else {
919             glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
920             if (draw_buffer == GL_NONE) {
921                 return NULL;
922             }
923         }
924
925         if (!getDrawableBounds(&desc.width, &desc.height)) {
926             return NULL;
927         }
928
929         desc.depth = 1;
930     }
931
932     GLenum type = GL_UNSIGNED_BYTE;
933     image::ChannelType channelType = image::TYPE_UNORM8;
934
935     if (format == GL_DEPTH_COMPONENT) {
936         type = GL_FLOAT;
937         channels = 1;
938         channelType = image::TYPE_FLOAT;
939     }
940
941     image::Image *image = new image::Image(desc.width, desc.height, channels, true, channelType);
942     if (!image) {
943         return NULL;
944     }
945
946     while (glGetError() != GL_NO_ERROR) {}
947
948     GLint read_framebuffer = 0;
949     GLint read_buffer = GL_NONE;
950     if (!context.ES) {
951         glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
952         glBindFramebuffer(GL_READ_FRAMEBUFFER, draw_framebuffer);
953
954         glGetIntegerv(GL_READ_BUFFER, &read_buffer);
955         glReadBuffer(draw_buffer);
956     }
957
958     // TODO: reset imaging state too
959     context.resetPixelPackState();
960
961     glReadPixels(0, 0, desc.width, desc.height, format, type, image->pixels);
962
963     context.restorePixelPackState();
964
965     if (!context.ES) {
966         glReadBuffer(read_buffer);
967         glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
968     }
969
970     GLenum error = glGetError();
971     if (error != GL_NO_ERROR) {
972         do {
973             std::cerr << "warning: " << enumToString(error) << " while getting snapshot\n";
974             error = glGetError();
975         } while(error != GL_NO_ERROR);
976         delete image;
977         return NULL;
978     }
979      
980     return image;
981 }
982
983
984 /**
985  * Dump the image of the currently bound read buffer.
986  */
987 static inline void
988 dumpReadBufferImage(JSONWriter &json, GLint width, GLint height, GLenum format,
989                     GLint internalFormat = GL_NONE)
990 {
991     GLint channels = _gl_format_channels(format);
992
993     if (internalFormat == GL_NONE) {
994         internalFormat = format;
995     }
996
997     Context context;
998
999     GLenum type = GL_UNSIGNED_BYTE;
1000     image::ChannelType channelType = image::TYPE_UNORM8;
1001
1002     if (format == GL_DEPTH_COMPONENT) {
1003         type = GL_FLOAT;
1004         channels = 1;
1005         channelType = image::TYPE_FLOAT;
1006     }
1007
1008     image::Image *image = new image::Image(width, height, channels, true, channelType);
1009
1010     while (glGetError() != GL_NO_ERROR) {}
1011
1012     // TODO: reset imaging state too
1013     context.resetPixelPackState();
1014
1015     glReadPixels(0, 0, width, height, format, type, image->pixels);
1016
1017     context.restorePixelPackState();
1018
1019     GLenum error = glGetError();
1020     if (error != GL_NO_ERROR) {
1021         do {
1022             std::cerr << "warning: " << enumToString(error) << " while reading framebuffer\n";
1023             error = glGetError();
1024         } while(error != GL_NO_ERROR);
1025         json.writeNull();
1026     } else {
1027         json.writeImage(image, formatToString(internalFormat));
1028     }
1029
1030     delete image;
1031 }
1032
1033
1034 static inline GLuint
1035 downsampledFramebuffer(Context &context,
1036                        GLuint oldFbo, GLint drawbuffer,
1037                        const ImageDesc &colorDesc,
1038                        const ImageDesc &depthDesc,
1039                        const ImageDesc &stencilDesc,
1040                        GLuint *rbs, GLint *numRbs)
1041 {
1042     GLuint fbo;
1043
1044
1045     *numRbs = 0;
1046
1047     glGenFramebuffers(1, &fbo);
1048     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1049
1050     {
1051         // color buffer
1052         glGenRenderbuffers(1, &rbs[*numRbs]);
1053         glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
1054         glRenderbufferStorage(GL_RENDERBUFFER, colorDesc.internalFormat, colorDesc.width, colorDesc.height);
1055         glFramebufferRenderbuffer(GL_FRAMEBUFFER, drawbuffer,
1056                                   GL_RENDERBUFFER, rbs[*numRbs]);
1057
1058         glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
1059         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
1060         glDrawBuffer(drawbuffer);
1061         glReadBuffer(drawbuffer);
1062         glBlitFramebuffer(0, 0, colorDesc.width, colorDesc.height, 0, 0, colorDesc.width, colorDesc.height,
1063                           GL_COLOR_BUFFER_BIT, GL_NEAREST);
1064         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1065         ++*numRbs;
1066     }
1067
1068     if (stencilDesc == depthDesc &&
1069         depthDesc.valid()) {
1070         //combined depth and stencil buffer
1071         glGenRenderbuffers(1, &rbs[*numRbs]);
1072         glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
1073         glRenderbufferStorage(GL_RENDERBUFFER, depthDesc.internalFormat, depthDesc.width, depthDesc.height);
1074         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
1075                                   GL_RENDERBUFFER, rbs[*numRbs]);
1076         glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
1077         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
1078         glBlitFramebuffer(0, 0, depthDesc.width, depthDesc.height, 0, 0, depthDesc.width, depthDesc.height,
1079                           GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
1080         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1081         ++*numRbs;
1082     } else {
1083         if (depthDesc.valid()) {
1084             glGenRenderbuffers(1, &rbs[*numRbs]);
1085             glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
1086             glRenderbufferStorage(GL_RENDERBUFFER, depthDesc.internalFormat, depthDesc.width, depthDesc.height);
1087             glFramebufferRenderbuffer(GL_FRAMEBUFFER,
1088                                       GL_DEPTH_ATTACHMENT,
1089                                       GL_RENDERBUFFER, rbs[*numRbs]);
1090             glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
1091             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
1092             glDrawBuffer(GL_DEPTH_ATTACHMENT);
1093             glReadBuffer(GL_DEPTH_ATTACHMENT);
1094             glBlitFramebuffer(0, 0, depthDesc.width, depthDesc.height, 0, 0, depthDesc.width, depthDesc.height,
1095                               GL_DEPTH_BUFFER_BIT, GL_NEAREST);
1096             ++*numRbs;
1097         }
1098         if (stencilDesc.valid()) {
1099             glGenRenderbuffers(1, &rbs[*numRbs]);
1100             glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
1101             glRenderbufferStorage(GL_RENDERBUFFER, stencilDesc.internalFormat, stencilDesc.width, stencilDesc.height);
1102             glFramebufferRenderbuffer(GL_FRAMEBUFFER,
1103                                       GL_STENCIL_ATTACHMENT,
1104                                       GL_RENDERBUFFER, rbs[*numRbs]);
1105             glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
1106             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
1107             glDrawBuffer(GL_STENCIL_ATTACHMENT);
1108             glReadBuffer(GL_STENCIL_ATTACHMENT);
1109             glBlitFramebuffer(0, 0, stencilDesc.width, stencilDesc.height, 0, 0, stencilDesc.width, stencilDesc.height,
1110                               GL_STENCIL_BUFFER_BIT, GL_NEAREST);
1111             ++*numRbs;
1112         }
1113     }
1114
1115     return fbo;
1116 }
1117
1118
1119 /**
1120  * Dump images of current draw drawable/window.
1121  */
1122 static void
1123 dumpDrawableImages(JSONWriter &json, Context &context)
1124 {
1125     GLint width, height;
1126
1127     if (!getDrawableBounds(&width, &height)) {
1128         return;
1129     }
1130
1131     GLint draw_buffer = GL_NONE;
1132     if (context.ES) {
1133         // XXX: Draw buffer is always FRONT for single buffer context, BACK for
1134         // double buffered contexts. There is no way to know which (as
1135         // GL_DOUBLEBUFFER state is also unavailable), so always assume
1136         // double-buffering.
1137         draw_buffer = GL_BACK;
1138     } else {
1139         glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
1140     }
1141
1142     if (draw_buffer != GL_NONE) {
1143         // Read from current draw buffer
1144         GLint read_buffer = GL_NONE;
1145         if (!context.ES) {
1146             glGetIntegerv(GL_READ_BUFFER, &read_buffer);
1147             glReadBuffer(draw_buffer);
1148         }
1149
1150         GLint alpha_bits = 0;
1151 #if 0
1152         // XXX: Ignore alpha until we are able to match the traced visual
1153         glGetIntegerv(GL_ALPHA_BITS, &alpha_bits);
1154 #endif
1155         GLenum format = alpha_bits ? GL_RGBA : GL_RGB;
1156         json.beginMember(enumToString(draw_buffer));
1157         dumpReadBufferImage(json, width, height, format);
1158         json.endMember();
1159
1160         // Restore original read buffer
1161         if (!context.ES) {
1162             glReadBuffer(read_buffer);
1163         }
1164     }
1165
1166     if (!context.ES) {
1167         GLint depth_bits = 0;
1168         glGetIntegerv(GL_DEPTH_BITS, &depth_bits);
1169         if (depth_bits) {
1170             json.beginMember("GL_DEPTH_COMPONENT");
1171             dumpReadBufferImage(json, width, height, GL_DEPTH_COMPONENT);
1172             json.endMember();
1173         }
1174
1175         GLint stencil_bits = 0;
1176         glGetIntegerv(GL_STENCIL_BITS, &stencil_bits);
1177         if (stencil_bits) {
1178             json.beginMember("GL_STENCIL_INDEX");
1179             dumpReadBufferImage(json, width, height, GL_STENCIL_INDEX);
1180             json.endMember();
1181         }
1182     }
1183 }
1184
1185
1186 /**
1187  * Dump the specified framebuffer attachment.
1188  *
1189  * In the case of a color attachment, it assumes it is already bound for read.
1190  */
1191 static void
1192 dumpFramebufferAttachment(JSONWriter &json, Context &context, GLenum target, GLenum attachment, GLenum format)
1193 {
1194     ImageDesc desc;
1195     if (!getFramebufferAttachmentDesc(context, target, attachment, desc)) {
1196         return;
1197     }
1198
1199     assert(desc.samples == 0);
1200
1201     json.beginMember(enumToString(attachment));
1202     dumpReadBufferImage(json, desc.width, desc.height, format, desc.internalFormat);
1203     json.endMember();
1204 }
1205
1206
1207 static void
1208 dumpFramebufferAttachments(JSONWriter &json, Context &context, GLenum target)
1209 {
1210     GLint read_framebuffer = 0;
1211     glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
1212
1213     GLint read_buffer = GL_NONE;
1214     glGetIntegerv(GL_READ_BUFFER, &read_buffer);
1215
1216     GLint max_draw_buffers = 1;
1217     glGetIntegerv(GL_MAX_DRAW_BUFFERS, &max_draw_buffers);
1218     GLint max_color_attachments = 0;
1219     glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments);
1220
1221     for (GLint i = 0; i < max_draw_buffers; ++i) {
1222         GLint draw_buffer = GL_NONE;
1223         glGetIntegerv(GL_DRAW_BUFFER0 + i, &draw_buffer);
1224         if (draw_buffer != GL_NONE) {
1225             glReadBuffer(draw_buffer);
1226             GLint attachment;
1227             if (draw_buffer >= GL_COLOR_ATTACHMENT0 && draw_buffer < GL_COLOR_ATTACHMENT0 + max_color_attachments) {
1228                 attachment = draw_buffer;
1229             } else {
1230                 std::cerr << "warning: unexpected GL_DRAW_BUFFER" << i << " = " << draw_buffer << "\n";
1231                 attachment = GL_COLOR_ATTACHMENT0;
1232             }
1233             GLint alpha_size = 0;
1234             glGetFramebufferAttachmentParameteriv(target, attachment,
1235                                                   GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE,
1236                                                   &alpha_size);
1237             GLenum format = alpha_size ? GL_RGBA : GL_RGB;
1238             dumpFramebufferAttachment(json, context, target, attachment, format);
1239         }
1240     }
1241
1242     glReadBuffer(read_buffer);
1243
1244     if (!context.ES) {
1245         dumpFramebufferAttachment(json, context, target, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT);
1246         dumpFramebufferAttachment(json, context, target, GL_STENCIL_ATTACHMENT, GL_STENCIL_INDEX);
1247     }
1248
1249     glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
1250 }
1251
1252
1253 void
1254 dumpFramebuffer(JSONWriter &json, Context &context)
1255 {
1256     json.beginMember("framebuffer");
1257     json.beginObject();
1258
1259     GLint boundDrawFbo = 0, boundReadFbo = 0;
1260     glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &boundDrawFbo);
1261     glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &boundReadFbo);
1262     if (!boundDrawFbo) {
1263         dumpDrawableImages(json, context);
1264     } else if (context.ES) {
1265         dumpFramebufferAttachments(json, context, GL_FRAMEBUFFER);
1266     } else {
1267         GLint draw_buffer0 = GL_NONE;
1268         glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer0);
1269         bool multisample = false;
1270
1271         GLint boundRb = 0;
1272         glGetIntegerv(GL_RENDERBUFFER_BINDING, &boundRb);
1273
1274         ImageDesc colorDesc;
1275         if (getFramebufferAttachmentDesc(context, GL_DRAW_FRAMEBUFFER, draw_buffer0, colorDesc)) {
1276             if (colorDesc.samples) {
1277                 multisample = true;
1278             }
1279         }
1280
1281         ImageDesc depthDesc;
1282         if (getFramebufferAttachmentDesc(context, GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthDesc)) {
1283             if (depthDesc.samples) {
1284                 multisample = true;
1285             }
1286         }
1287
1288         ImageDesc stencilDesc;
1289         if (getFramebufferAttachmentDesc(context, GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, stencilDesc)) {
1290             if (stencilDesc.samples) {
1291                 multisample = true;
1292             }
1293         }
1294
1295         GLuint rbs[3];
1296         GLint numRbs = 0;
1297         GLuint fboCopy = 0;
1298
1299         if (multisample) {
1300             // glReadPixels doesnt support multisampled buffers so we need
1301             // to blit the fbo to a temporary one
1302             fboCopy = downsampledFramebuffer(context,
1303                                              boundDrawFbo, draw_buffer0,
1304                                              colorDesc, depthDesc, stencilDesc,
1305                                              rbs, &numRbs);
1306         } else {
1307             glBindFramebuffer(GL_READ_FRAMEBUFFER, boundDrawFbo);
1308         }
1309
1310         dumpFramebufferAttachments(json, context, GL_READ_FRAMEBUFFER);
1311
1312         if (multisample) {
1313             glBindRenderbuffer(GL_RENDERBUFFER, boundRb);
1314             glDeleteRenderbuffers(numRbs, rbs);
1315             glDeleteFramebuffers(1, &fboCopy);
1316         }
1317
1318         glBindFramebuffer(GL_READ_FRAMEBUFFER, boundReadFbo);
1319         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, boundDrawFbo);
1320     }
1321
1322     json.endObject();
1323     json.endMember(); // framebuffer
1324 }
1325
1326
1327 } /* namespace glstate */