]> git.cworth.org Git - apitrace/blob - retrace/glstate_images.cpp
549dec709fbac4df6b3cead455a83ad3b4d6f89e
[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 /**
510  * Choose the glReadPixels/glGetTexImage format appropriate for the given
511  * internalFormat.
512  */
513 static GLenum
514 getFormat(GLenum internalFormat)
515 {
516     for (unsigned i = 0; i < sizeof internalFormatDescs / sizeof internalFormatDescs[0]; ++i) {
517         if (internalFormatDescs[i].internalFormat == internalFormat) {
518             return internalFormatDescs[i].format;
519         }
520     }
521     return GL_RGBA;
522 }
523
524
525 static inline void
526 dumpActiveTextureLevel(JSONWriter &json, Context &context, GLenum target, GLint level)
527 {
528     ImageDesc desc;
529     if (!getActiveTextureLevelDesc(context, target, level, desc)) {
530         return;
531     }
532
533     char label[512];
534     GLint active_texture = GL_TEXTURE0;
535     glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
536     snprintf(label, sizeof label, "%s, %s, level = %d",
537              enumToString(active_texture), enumToString(target), level);
538
539     json.beginMember(label);
540
541     GLenum format = getFormat(desc.internalFormat);
542     if (context.ES && format == GL_DEPTH_COMPONENT) {
543         format = GL_RED;
544     }
545     GLuint channels = _gl_format_channels(format);
546
547     image::Image *image = new image::Image(desc.width, desc.height*desc.depth, channels, true);
548
549     context.resetPixelPackState();
550
551     if (context.ES) {
552         getTexImageOES(target, level, desc, image->pixels);
553     } else {
554         glGetTexImage(target, level, format, GL_UNSIGNED_BYTE, image->pixels);
555     }
556
557     context.restorePixelPackState();
558
559     json.writeImage(image, formatToString(desc.internalFormat), desc.depth);
560
561     delete image;
562
563     json.endMember(); // label
564 }
565
566
567 static inline void
568 dumpTexture(JSONWriter &json, Context &context, GLenum target, GLenum binding)
569 {
570     GLint texture_binding = 0;
571     glGetIntegerv(binding, &texture_binding);
572     if (!glIsEnabled(target) && !texture_binding) {
573         return;
574     }
575
576     GLint level = 0;
577     do {
578         ImageDesc desc;
579
580         if (target == GL_TEXTURE_CUBE_MAP) {
581             for (int face = 0; face < 6; ++face) {
582                 if (!getActiveTextureLevelDesc(context, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level, desc)) {
583                     return;
584                 }
585                 dumpActiveTextureLevel(json, context, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level);
586             }
587         } else {
588             if (!getActiveTextureLevelDesc(context, target, level, desc)) {
589                 return;
590             }
591             dumpActiveTextureLevel(json, context, target, level);
592         }
593
594         ++level;
595     } while(true);
596 }
597
598
599 void
600 dumpTextures(JSONWriter &json, Context &context)
601 {
602     json.beginMember("textures");
603     json.beginObject();
604     GLint active_texture = GL_TEXTURE0;
605     glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
606
607     GLint max_texture_coords = 0;
608     glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);
609     GLint max_combined_texture_image_units = 0;
610     glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_combined_texture_image_units);
611     GLint max_units = std::max(max_combined_texture_image_units, max_texture_coords);
612
613     /*
614      * At least the Android software GL implementation doesn't return the
615      * proper value for this, but rather returns 0. The GL(ES) specification
616      * mandates a minimum value of 2, so use this as a fall-back value.
617      */
618     max_units = std::max(max_units, 2);
619
620     for (GLint unit = 0; unit < max_units; ++unit) {
621         GLenum texture = GL_TEXTURE0 + unit;
622         glActiveTexture(texture);
623         dumpTexture(json, context, GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D);
624         dumpTexture(json, context, GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D);
625         dumpTexture(json, context, GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D);
626         dumpTexture(json, context, GL_TEXTURE_RECTANGLE, GL_TEXTURE_BINDING_RECTANGLE);
627         dumpTexture(json, context, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP);
628     }
629     glActiveTexture(active_texture);
630     json.endObject();
631     json.endMember(); // textures
632 }
633
634
635 static bool
636 getDrawableBounds(GLint *width, GLint *height) {
637 #if defined(__linux__)
638     if (dlsym(RTLD_DEFAULT, "eglGetCurrentContext")) {
639         EGLContext currentContext = eglGetCurrentContext();
640         if (currentContext != EGL_NO_CONTEXT) {
641             EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW);
642             if (currentSurface == EGL_NO_SURFACE) {
643                 return false;
644             }
645
646             EGLDisplay currentDisplay = eglGetCurrentDisplay();
647             if (currentDisplay == EGL_NO_DISPLAY) {
648                 return false;
649             }
650
651             if (!eglQuerySurface(currentDisplay, currentSurface, EGL_WIDTH, width) ||
652                 !eglQuerySurface(currentDisplay, currentSurface, EGL_HEIGHT, height)) {
653                 return false;
654             }
655
656             return true;
657         }
658     }
659 #endif
660
661 #if defined(_WIN32)
662
663     HDC hDC = wglGetCurrentDC();
664     if (!hDC) {
665         return false;
666     }
667
668     HWND hWnd = WindowFromDC(hDC);
669     RECT rect;
670
671     if (!GetClientRect(hWnd, &rect)) {
672        return false;
673     }
674
675     *width  = rect.right  - rect.left;
676     *height = rect.bottom - rect.top;
677     return true;
678
679 #elif defined(__APPLE__)
680
681     CGLContextObj ctx = CGLGetCurrentContext();
682     if (ctx == NULL) {
683         return false;
684     }
685
686     CGSConnectionID cid;
687     CGSWindowID wid;
688     CGSSurfaceID sid;
689
690     if (CGLGetSurface(ctx, &cid, &wid, &sid) != kCGLNoError) {
691         return false;
692     }
693
694     CGRect rect;
695
696     if (CGSGetSurfaceBounds(cid, wid, sid, &rect) != 0) {
697         return false;
698     }
699
700     *width = rect.size.width;
701     *height = rect.size.height;
702     return true;
703
704 #elif defined(HAVE_X11)
705
706     Display *display;
707     Drawable drawable;
708     Window root;
709     int x, y;
710     unsigned int w, h, bw, depth;
711
712     display = glXGetCurrentDisplay();
713     if (!display) {
714         return false;
715     }
716
717     drawable = glXGetCurrentDrawable();
718     if (drawable == None) {
719         return false;
720     }
721
722     if (!XGetGeometry(display, drawable, &root, &x, &y, &w, &h, &bw, &depth)) {
723         return false;
724     }
725
726     *width = w;
727     *height = h;
728     return true;
729
730 #else
731
732     return false;
733
734 #endif
735 }
736
737
738 struct TextureTargetBinding
739 {
740     GLenum target;
741     GLenum binding;
742 };
743
744
745 static const GLenum
746 textureTargets[] = {
747     GL_TEXTURE_1D,
748     GL_TEXTURE_2D,
749     GL_TEXTURE_RECTANGLE,
750     GL_TEXTURE_CUBE_MAP,
751     GL_TEXTURE_3D,
752     GL_TEXTURE_2D_MULTISAMPLE,
753     GL_TEXTURE_1D_ARRAY,
754     GL_TEXTURE_2D_ARRAY,
755     GL_TEXTURE_CUBE_MAP_ARRAY,
756 };
757
758
759 static GLenum
760 getTextureTarget(GLint texture)
761 {
762     if (!glIsTexture(texture)) {
763         return GL_NONE;
764     }
765
766     for (unsigned i = 0; i < sizeof(textureTargets)/sizeof(textureTargets[0]); ++i) {
767         GLenum target = textureTargets[i];
768         GLenum binding = getTextureBinding(target);
769
770         while (glGetError() != GL_NO_ERROR)
771             ;
772
773         GLint bound_texture = 0;
774         glGetIntegerv(binding, &bound_texture);
775         glBindTexture(target, texture);
776
777         bool succeeded = glGetError() == GL_NO_ERROR;
778
779         glBindTexture(target, bound_texture);
780
781         if (succeeded) {
782             return target;
783         }
784     }
785
786     return GL_NONE;
787 }
788
789
790 static bool
791 getBoundRenderbufferDesc(Context &context, ImageDesc &desc)
792 {
793     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &desc.width);
794     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &desc.height);
795     desc.depth = 1;
796     
797     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &desc.samples);
798
799     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_INTERNAL_FORMAT, &desc.internalFormat);
800     
801     return desc.valid();
802 }
803
804
805 static bool
806 getRenderbufferDesc(Context &context, GLint renderbuffer, ImageDesc &desc)
807 {
808     GLint bound_renderbuffer = 0;
809     glGetIntegerv(GL_RENDERBUFFER_BINDING, &bound_renderbuffer);
810     glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
811
812     getBoundRenderbufferDesc(context, desc);
813
814     glBindRenderbuffer(GL_RENDERBUFFER, bound_renderbuffer);
815     
816     return desc.valid();
817 }
818
819
820 static bool
821 getFramebufferAttachmentDesc(Context &context, GLenum target, GLenum attachment, ImageDesc &desc)
822 {
823     GLint object_type = GL_NONE;
824     glGetFramebufferAttachmentParameteriv(target, attachment,
825                                           GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
826                                           &object_type);
827     if (object_type == GL_NONE) {
828         return false;
829     }
830
831     GLint object_name = 0;
832     glGetFramebufferAttachmentParameteriv(target, attachment,
833                                           GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
834                                           &object_name);
835     if (object_name == 0) {
836         return false;
837     }
838
839     if (object_type == GL_RENDERBUFFER) {
840         return getRenderbufferDesc(context, object_name, desc);
841     } else if (object_type == GL_TEXTURE) {
842         GLint texture_face = 0;
843         glGetFramebufferAttachmentParameteriv(target, attachment,
844                                               GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE,
845                                               &texture_face);
846
847         GLint texture_level = 0;
848         glGetFramebufferAttachmentParameteriv(target, attachment,
849                                               GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL,
850                                               &texture_level);
851
852         GLint bound_texture = 0;
853         if (texture_face != 0) {
854             glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &bound_texture);
855             glBindTexture(GL_TEXTURE_CUBE_MAP, object_name);
856             getActiveTextureLevelDesc(context, texture_face, texture_level, desc);
857             glBindTexture(GL_TEXTURE_CUBE_MAP, bound_texture);
858         } else {
859             GLenum texture_target = getTextureTarget(object_name);
860             GLenum texture_binding = getTextureBinding(texture_target);
861             glGetIntegerv(texture_binding, &bound_texture);
862             glBindTexture(texture_target, object_name);
863             getActiveTextureLevelDesc(context, texture_target, texture_level, desc);
864             glBindTexture(texture_target, bound_texture);
865         }
866
867         return desc.valid();
868     } else {
869         std::cerr << "warning: unexpected GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = " << object_type << "\n";
870         return false;
871     }
872 }
873
874
875
876 image::Image *
877 getDrawBufferImage() {
878     GLenum format = GL_RGB;
879     GLint channels = _gl_format_channels(format);
880     if (channels > 4) {
881         return NULL;
882     }
883
884     Context context;
885
886     GLenum framebuffer_binding;
887     GLenum framebuffer_target;
888     if (context.ES) {
889         framebuffer_binding = GL_FRAMEBUFFER_BINDING;
890         framebuffer_target = GL_FRAMEBUFFER;
891     } else {
892         framebuffer_binding = GL_DRAW_FRAMEBUFFER_BINDING;
893         framebuffer_target = GL_DRAW_FRAMEBUFFER;
894     }
895
896     GLint draw_framebuffer = 0;
897     glGetIntegerv(framebuffer_binding, &draw_framebuffer);
898
899     GLint draw_buffer = GL_NONE;
900     ImageDesc desc;
901     if (draw_framebuffer) {
902         if (context.ARB_draw_buffers) {
903             glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer);
904             if (draw_buffer == GL_NONE) {
905                 return NULL;
906             }
907         } else {
908             // GL_COLOR_ATTACHMENT0 is implied
909             draw_buffer = GL_COLOR_ATTACHMENT0;
910         }
911
912         if (!getFramebufferAttachmentDesc(context, framebuffer_target, draw_buffer, desc)) {
913             return NULL;
914         }
915     } else {
916         if (context.ES) {
917             // XXX: Draw buffer is always FRONT for single buffer context, BACK
918             // for double buffered contexts. There is no way to know which (as
919             // GL_DOUBLEBUFFER state is also unavailable), so always assume
920             // double-buffering.
921             draw_buffer = GL_BACK;
922         } else {
923             glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
924             if (draw_buffer == GL_NONE) {
925                 return NULL;
926             }
927         }
928
929         if (!getDrawableBounds(&desc.width, &desc.height)) {
930             return NULL;
931         }
932
933         desc.depth = 1;
934     }
935
936     GLenum type = GL_UNSIGNED_BYTE;
937     image::ChannelType channelType = image::TYPE_UNORM8;
938
939     if (format == GL_DEPTH_COMPONENT) {
940         type = GL_FLOAT;
941         channels = 1;
942         channelType = image::TYPE_FLOAT;
943     }
944
945     image::Image *image = new image::Image(desc.width, desc.height, channels, true, channelType);
946     if (!image) {
947         return NULL;
948     }
949
950     while (glGetError() != GL_NO_ERROR) {}
951
952     GLint read_framebuffer = 0;
953     GLint read_buffer = GL_NONE;
954     if (!context.ES) {
955         glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
956         glBindFramebuffer(GL_READ_FRAMEBUFFER, draw_framebuffer);
957
958         glGetIntegerv(GL_READ_BUFFER, &read_buffer);
959         glReadBuffer(draw_buffer);
960     }
961
962     // TODO: reset imaging state too
963     context.resetPixelPackState();
964
965     glReadPixels(0, 0, desc.width, desc.height, format, type, image->pixels);
966
967     context.restorePixelPackState();
968
969     if (!context.ES) {
970         glReadBuffer(read_buffer);
971         glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
972     }
973
974     GLenum error = glGetError();
975     if (error != GL_NO_ERROR) {
976         do {
977             std::cerr << "warning: " << enumToString(error) << " while getting snapshot\n";
978             error = glGetError();
979         } while(error != GL_NO_ERROR);
980         delete image;
981         return NULL;
982     }
983      
984     return image;
985 }
986
987
988 /**
989  * Dump the image of the currently bound read buffer.
990  */
991 static inline void
992 dumpReadBufferImage(JSONWriter &json, GLint width, GLint height, GLenum format,
993                     GLint internalFormat = GL_NONE)
994 {
995     GLint channels = _gl_format_channels(format);
996
997     if (internalFormat == GL_NONE) {
998         internalFormat = format;
999     }
1000
1001     Context context;
1002
1003     GLenum type = GL_UNSIGNED_BYTE;
1004     getFormat(internalFormat);
1005     image::ChannelType channelType = image::TYPE_UNORM8;
1006
1007     if (format == GL_DEPTH_COMPONENT) {
1008         type = GL_FLOAT;
1009         channels = 1;
1010         channelType = image::TYPE_FLOAT;
1011     }
1012
1013     image::Image *image = new image::Image(width, height, channels, true, channelType);
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 = GL_NONE)
1198 {
1199     ImageDesc desc;
1200     if (!getFramebufferAttachmentDesc(context, target, attachment, desc)) {
1201         return;
1202     }
1203
1204     assert(desc.samples == 0);
1205
1206     if (format == GL_NONE) {
1207         assert(desc.internalFormat != GL_NONE);
1208         format = getFormat(desc.internalFormat);
1209     }
1210
1211     json.beginMember(enumToString(attachment));
1212     dumpReadBufferImage(json, desc.width, desc.height, format, desc.internalFormat);
1213     json.endMember();
1214 }
1215
1216
1217 static void
1218 dumpFramebufferAttachments(JSONWriter &json, Context &context, GLenum target)
1219 {
1220     GLint read_framebuffer = 0;
1221     glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
1222
1223     GLint read_buffer = GL_NONE;
1224     glGetIntegerv(GL_READ_BUFFER, &read_buffer);
1225
1226     GLint max_draw_buffers = 1;
1227     glGetIntegerv(GL_MAX_DRAW_BUFFERS, &max_draw_buffers);
1228     GLint max_color_attachments = 0;
1229     glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments);
1230
1231     for (GLint i = 0; i < max_draw_buffers; ++i) {
1232         GLint draw_buffer = GL_NONE;
1233         glGetIntegerv(GL_DRAW_BUFFER0 + i, &draw_buffer);
1234         if (draw_buffer != GL_NONE) {
1235             glReadBuffer(draw_buffer);
1236             GLint attachment;
1237             if (draw_buffer >= GL_COLOR_ATTACHMENT0 && draw_buffer < GL_COLOR_ATTACHMENT0 + max_color_attachments) {
1238                 attachment = draw_buffer;
1239             } else {
1240                 std::cerr << "warning: unexpected GL_DRAW_BUFFER" << i << " = " << draw_buffer << "\n";
1241                 attachment = GL_COLOR_ATTACHMENT0;
1242             }
1243             dumpFramebufferAttachment(json, context, target, attachment);
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 */