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