]> git.cworth.org Git - apitrace/blob - glstate_images.cpp
apitrace diff-images: Print one line for each file being compared.
[apitrace] / 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 <string.h>
28
29 #include <algorithm>
30 #include <iostream>
31
32 #include "image.hpp"
33 #include "json.hpp"
34 #include "glproc.hpp"
35 #include "glsize.hpp"
36 #include "glstate.hpp"
37 #include "glstate_internal.hpp"
38
39
40 #ifdef __linux__
41 #include <dlfcn.h>
42 #endif
43
44 #ifdef __APPLE__
45
46 #include <Carbon/Carbon.h>
47
48 #ifdef __cplusplus
49 extern "C" {
50 #endif
51
52 OSStatus CGSGetSurfaceBounds(CGSConnectionID, CGWindowID, CGSSurfaceID, CGRect *);
53
54 #ifdef __cplusplus
55 }
56 #endif
57
58 #endif /* __APPLE__ */
59
60
61 /* Change thi to one to force interpreting depth buffers as RGBA, which enables
62  * visualizing full dynamic range, until we can transmit HDR images to the GUI */
63 #define DEPTH_AS_RGBA 0
64
65
66 namespace glstate {
67
68
69 struct ImageDesc
70 {
71     GLint width;
72     GLint height;
73     GLint depth;
74     GLint internalFormat;
75
76     inline
77     ImageDesc() :
78         width(0),
79         height(0),
80         depth(0),
81         internalFormat(GL_NONE)
82     {}
83
84     inline bool
85     valid(void) const {
86         return width > 0 && height > 0 && depth > 0;
87     }
88 };
89
90
91 /**
92  * OpenGL ES does not support glGetTexLevelParameteriv, but it is possible to
93  * probe whether a texture has a given size by crafting a dummy glTexSubImage()
94  * call.
95  */
96 static bool
97 probeTextureLevelSizeOES(GLenum target, GLint level, const GLint size[3]) {
98     while (glGetError() != GL_NO_ERROR)
99         ;
100
101     GLenum internalFormat = GL_RGBA;
102     GLenum type = GL_UNSIGNED_BYTE;
103     GLint dummy = 0;
104
105     switch (target) {
106     case GL_TEXTURE_2D:
107     case GL_TEXTURE_CUBE_MAP:
108     case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
109     case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
110     case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
111     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
112     case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
113     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
114         glTexSubImage2D(target, level, size[0], size[1], 0, 0, internalFormat, type, &dummy);
115         break;
116     case GL_TEXTURE_3D_OES:
117         glTexSubImage3DOES(target, level, size[0], size[1], size[2], 0, 0, 0, internalFormat, type, &dummy);
118     default:
119         assert(0);
120         return false;
121     }
122
123     GLenum error = glGetError();
124
125     if (0) {
126         std::cerr << "(" << size[0] << ", " << size[1] << ", " << size[2] << ") = " << enumToString(error) << "\n";
127     }
128
129     if (error == GL_NO_ERROR) {
130         return true;
131     }
132
133     while (glGetError() != GL_NO_ERROR)
134         ;
135
136     return false;
137 }
138
139
140 /**
141  * Bisect the texture size along an axis.
142  *
143  * It is assumed that the texture exists.
144  */
145 static GLint
146 bisectTextureLevelSizeOES(GLenum target, GLint level, GLint axis, GLint max) {
147     GLint size[3] = {0, 0, 0};
148
149     assert(axis < 3);
150     assert(max >= 0);
151
152     GLint min = 0;
153     while (true) {
154         GLint test = (min + max) / 2;
155         if (test == min) {
156             return min;
157         }
158
159         size[axis] = test;
160
161         if (probeTextureLevelSizeOES(target, level, size)) {
162             min = test;
163         } else {
164             max = test;
165         }
166     }
167 }
168
169
170 /**
171  * Special path to obtain texture size on OpenGL ES, that does not rely on
172  * glGetTexLevelParameteriv
173  */
174 static bool
175 getActiveTextureLevelDescOES(Context &context, GLenum target, GLint level, ImageDesc &desc)
176 {
177     if (target == GL_TEXTURE_1D) {
178         // OpenGL ES does not support 1D textures
179         return false;
180     }
181
182     const GLint size[3] = {1, 1, 1}; 
183     if (!probeTextureLevelSizeOES(target, level, size)) {
184         return false;
185     }
186
187     // XXX: mere guess
188     desc.internalFormat = GL_RGBA;
189
190     GLint maxSize = 0;
191     switch (target) {
192     case GL_TEXTURE_2D:
193         glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
194         desc.width = bisectTextureLevelSizeOES(target, level, 0, maxSize);
195         desc.height = bisectTextureLevelSizeOES(target, level, 1, maxSize);
196         desc.depth = 1;
197         break;
198     case GL_TEXTURE_CUBE_MAP:
199     case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
200     case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
201     case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
202     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
203     case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
204     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
205         glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &maxSize);
206         desc.width = bisectTextureLevelSizeOES(target, level, 0, maxSize);
207         desc.height = desc.width;
208         desc.depth = 1;
209         break;
210     case GL_TEXTURE_3D_OES:
211         glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE_OES, &maxSize);
212         desc.width = bisectTextureLevelSizeOES(target, level, 0, maxSize);
213         desc.width = bisectTextureLevelSizeOES(target, level, 1, maxSize);
214         desc.depth = bisectTextureLevelSizeOES(target, level, 2, maxSize);
215         break;
216     default:
217         return false;
218     }
219
220     if (0) {
221         std::cerr
222             << enumToString(target) << " "
223             << level << " "
224             << desc.width << "x" << desc.height << "x" << desc.depth
225             << "\n";
226     }
227
228     return desc.valid();
229 }
230
231
232 static inline bool
233 getActiveTextureLevelDesc(Context &context, GLenum target, GLint level, ImageDesc &desc)
234 {
235     if (context.ES) {
236         return getActiveTextureLevelDescOES(context, target, level, desc);
237     }
238
239     glGetTexLevelParameteriv(target, level, GL_TEXTURE_INTERNAL_FORMAT, &desc.internalFormat);
240
241     desc.width = 0;
242     glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &desc.width);
243
244     if (target == GL_TEXTURE_1D) {
245         desc.height = 1;
246         desc.depth = 1;
247     } else {
248         desc.height = 0;
249         glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &desc.height);
250         if (target != GL_TEXTURE_3D) {
251             desc.depth = 1;
252         } else {
253             desc.depth = 0;
254             glGetTexLevelParameteriv(target, level, GL_TEXTURE_DEPTH, &desc.depth);
255         }
256     }
257
258     return desc.valid();
259 }
260
261
262 /**
263  * OpenGL ES does not support glGetTexImage. Obtain the pixels by attaching the
264  * texture to a framebuffer.
265  */
266 static inline void
267 getTexImageOES(GLenum target, GLint level, ImageDesc &desc, GLubyte *pixels)
268 {
269     memset(pixels, 0x80, desc.height * desc.width * 4);
270
271     GLenum texture_binding = GL_NONE;
272     switch (target) {
273     case GL_TEXTURE_2D:
274         texture_binding = GL_TEXTURE_BINDING_2D;
275         break;
276     case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
277     case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
278     case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
279     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
280     case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
281     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
282         texture_binding = GL_TEXTURE_BINDING_CUBE_MAP;
283         break;
284     case GL_TEXTURE_3D_OES:
285         texture_binding = GL_TEXTURE_BINDING_3D_OES;
286     default:
287         return;
288     }
289
290     GLint texture = 0;
291     glGetIntegerv(texture_binding, &texture);
292     if (!texture) {
293         return;
294     }
295
296     GLint prev_fbo = 0;
297     GLuint fbo = 0;
298     glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prev_fbo);
299     glGenFramebuffers(1, &fbo);
300     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
301
302     GLenum status;
303
304     switch (target) {
305     case GL_TEXTURE_2D:
306     case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
307     case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
308     case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
309     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
310     case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
311     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
312         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, level);
313         status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
314         if (status != GL_FRAMEBUFFER_COMPLETE) {
315             std::cerr << __FUNCTION__ << ": " << enumToString(status) << "\n";
316         }
317         glReadPixels(0, 0, desc.width, desc.height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
318         break;
319     case GL_TEXTURE_3D_OES:
320         for (int i = 0; i < desc.depth; i++) {
321             glFramebufferTexture3D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_3D, texture, level, i);
322             glReadPixels(0, 0, desc.width, desc.height, GL_RGBA, GL_UNSIGNED_BYTE, pixels + 4 * i * desc.width * desc.height);
323         }
324         break;
325     }
326
327     glBindFramebuffer(GL_FRAMEBUFFER, prev_fbo);
328
329     glDeleteFramebuffers(1, &fbo);
330 }
331
332
333 static inline void
334 dumpActiveTextureLevel(JSONWriter &json, Context &context, GLenum target, GLint level)
335 {
336     ImageDesc desc;
337     if (!getActiveTextureLevelDesc(context, target, level, desc)) {
338         return;
339     }
340
341     char label[512];
342
343     GLint active_texture = GL_TEXTURE0;
344     glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
345     snprintf(label, sizeof label, "%s, %s, level = %d",
346              enumToString(active_texture), enumToString(target), level);
347
348     json.beginMember(label);
349
350     json.beginObject();
351
352     // Tell the GUI this is no ordinary object, but an image
353     json.writeStringMember("__class__", "image");
354
355     json.writeNumberMember("__width__", desc.width);
356     json.writeNumberMember("__height__", desc.height);
357     json.writeNumberMember("__depth__", desc.depth);
358
359     json.writeStringMember("__format__", enumToString(desc.internalFormat));
360
361     // Hardcoded for now, but we could chose types more adequate to the
362     // texture internal format
363     json.writeStringMember("__type__", "uint8");
364     json.writeBoolMember("__normalized__", true);
365     json.writeNumberMember("__channels__", 4);
366
367     GLubyte *pixels = new GLubyte[desc.depth*desc.width*desc.height*4];
368
369     context.resetPixelPackState();
370
371     if (context.ES) {
372         getTexImageOES(target, level, desc, pixels);
373     } else {
374         glGetTexImage(target, level, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
375     }
376
377     context.restorePixelPackState();
378
379     json.beginMember("__data__");
380     char *pngBuffer;
381     int pngBufferSize;
382     image::writePixelsToBuffer(pixels, desc.width, desc.height, 4, true, &pngBuffer, &pngBufferSize);
383     json.writeBase64(pngBuffer, pngBufferSize);
384     free(pngBuffer);
385     json.endMember(); // __data__
386
387     delete [] pixels;
388     json.endObject();
389 }
390
391
392 static inline void
393 dumpTexture(JSONWriter &json, Context &context, GLenum target, GLenum binding)
394 {
395     GLint texture_binding = 0;
396     glGetIntegerv(binding, &texture_binding);
397     if (!glIsEnabled(target) && !texture_binding) {
398         return;
399     }
400
401     GLint level = 0;
402     do {
403         ImageDesc desc;
404         if (!getActiveTextureLevelDesc(context, target, level, desc)) {
405             break;
406         }
407
408         if (target == GL_TEXTURE_CUBE_MAP) {
409             for (int face = 0; face < 6; ++face) {
410                 dumpActiveTextureLevel(json, context, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level);
411             }
412         } else {
413             dumpActiveTextureLevel(json, context, target, level);
414         }
415
416         ++level;
417     } while(true);
418 }
419
420
421 void
422 dumpTextures(JSONWriter &json, Context &context)
423 {
424     json.beginMember("textures");
425     json.beginObject();
426     GLint active_texture = GL_TEXTURE0;
427     glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
428     GLint max_texture_coords = 0;
429     glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);
430     GLint max_combined_texture_image_units = 0;
431     glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_combined_texture_image_units);
432     GLint max_units = std::max(max_combined_texture_image_units, max_texture_coords);
433     for (GLint unit = 0; unit < max_units; ++unit) {
434         GLenum texture = GL_TEXTURE0 + unit;
435         glActiveTexture(texture);
436         dumpTexture(json, context, GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D);
437         dumpTexture(json, context, GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D);
438         dumpTexture(json, context, GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D);
439         dumpTexture(json, context, GL_TEXTURE_RECTANGLE, GL_TEXTURE_BINDING_RECTANGLE);
440         dumpTexture(json, context, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP);
441     }
442     glActiveTexture(active_texture);
443     json.endObject();
444     json.endMember(); // textures
445 }
446
447
448 static bool
449 getDrawableBounds(GLint *width, GLint *height) {
450 #if defined(__linux__)
451     if (dlsym(RTLD_DEFAULT, "eglGetCurrentContext")) {
452         EGLContext currentContext = eglGetCurrentContext();
453         if (currentContext == EGL_NO_CONTEXT) {
454             return false;
455         }
456
457         EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW);
458         if (currentSurface == EGL_NO_SURFACE) {
459             return false;
460         }
461
462         EGLDisplay currentDisplay = eglGetCurrentDisplay();
463         if (currentDisplay == EGL_NO_DISPLAY) {
464             return false;
465         }
466
467         if (!eglQuerySurface(currentDisplay, currentSurface, EGL_WIDTH, width) ||
468             !eglQuerySurface(currentDisplay, currentSurface, EGL_HEIGHT, height)) {
469             return false;
470         }
471
472         return true;
473     }
474 #endif
475
476 #if defined(_WIN32)
477
478     HDC hDC = wglGetCurrentDC();
479     if (!hDC) {
480         return false;
481     }
482
483     HWND hWnd = WindowFromDC(hDC);
484     RECT rect;
485
486     if (!GetClientRect(hWnd, &rect)) {
487        return false;
488     }
489
490     *width  = rect.right  - rect.left;
491     *height = rect.bottom - rect.top;
492     return true;
493
494 #elif defined(__APPLE__)
495
496     CGLContextObj ctx = CGLGetCurrentContext();
497     if (ctx == NULL) {
498         return false;
499     }
500
501     CGSConnectionID cid;
502     CGSWindowID wid;
503     CGSSurfaceID sid;
504
505     if (CGLGetSurface(ctx, &cid, &wid, &sid) != kCGLNoError) {
506         return false;
507     }
508
509     CGRect rect;
510
511     if (CGSGetSurfaceBounds(cid, wid, sid, &rect) != 0) {
512         return false;
513     }
514
515     *width = rect.size.width;
516     *height = rect.size.height;
517     return true;
518
519 #elif defined(HAVE_X11)
520
521     Display *display;
522     Drawable drawable;
523     Window root;
524     int x, y;
525     unsigned int w, h, bw, depth;
526
527     display = glXGetCurrentDisplay();
528     if (!display) {
529         return false;
530     }
531
532     drawable = glXGetCurrentDrawable();
533     if (drawable == None) {
534         return false;
535     }
536
537     if (!XGetGeometry(display, drawable, &root, &x, &y, &w, &h, &bw, &depth)) {
538         return false;
539     }
540
541     *width = w;
542     *height = h;
543     return true;
544
545 #else
546
547     return false;
548
549 #endif
550 }
551
552
553 static const GLenum texture_bindings[][2] = {
554     {GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D},
555     {GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D},
556     {GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D},
557     {GL_TEXTURE_RECTANGLE, GL_TEXTURE_BINDING_RECTANGLE},
558     {GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP}
559 };
560
561
562 static bool
563 bindTexture(GLint texture, GLenum &target, GLint &bound_texture)
564 {
565
566     for (unsigned i = 0; i < sizeof(texture_bindings)/sizeof(texture_bindings[0]); ++i) {
567         target  = texture_bindings[i][0];
568
569         GLenum binding = texture_bindings[i][1];
570
571         while (glGetError() != GL_NO_ERROR)
572             ;
573
574         glGetIntegerv(binding, &bound_texture);
575         glBindTexture(target, texture);
576
577         if (glGetError() == GL_NO_ERROR) {
578             return true;
579         }
580
581         glBindTexture(target, bound_texture);
582     }
583
584     target = GL_NONE;
585
586     return false;
587 }
588
589
590 static bool
591 getTextureLevelDesc(Context &context, GLint texture, GLint level, ImageDesc &desc)
592 {
593     GLenum target;
594     GLint bound_texture = 0;
595     if (!bindTexture(texture, target, bound_texture)) {
596         return false;
597     }
598
599     getActiveTextureLevelDesc(context, target, level, desc);
600
601     glBindTexture(target, bound_texture);
602
603     return desc.valid();
604 }
605
606
607 static bool
608 getBoundRenderbufferDesc(Context &context, ImageDesc &desc)
609 {
610     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &desc.width);
611     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &desc.height);
612     desc.depth = 1;
613     
614     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_INTERNAL_FORMAT, &desc.internalFormat);
615     
616     return desc.valid();
617 }
618
619
620 static bool
621 getRenderbufferDesc(Context &context, GLint renderbuffer, ImageDesc &desc)
622 {
623     GLint bound_renderbuffer = 0;
624     glGetIntegerv(GL_RENDERBUFFER_BINDING, &bound_renderbuffer);
625     glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
626
627     getBoundRenderbufferDesc(context, desc);
628
629     glBindRenderbuffer(GL_RENDERBUFFER, bound_renderbuffer);
630     
631     return desc.valid();
632 }
633
634
635 static bool
636 getFramebufferAttachmentDesc(Context &context, GLenum target, GLenum attachment, ImageDesc &desc)
637 {
638     GLint object_type = GL_NONE;
639     glGetFramebufferAttachmentParameteriv(target, attachment,
640                                           GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
641                                           &object_type);
642     if (object_type == GL_NONE) {
643         return false;
644     }
645
646     GLint object_name = 0;
647     glGetFramebufferAttachmentParameteriv(target, attachment,
648                                           GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
649                                           &object_name);
650     if (object_name == 0) {
651         return false;
652     }
653
654     if (object_type == GL_RENDERBUFFER) {
655         return getRenderbufferDesc(context, object_name, desc);
656     } else if (object_type == GL_TEXTURE) {
657         GLint texture_level = 0;
658         glGetFramebufferAttachmentParameteriv(target, attachment,
659                                               GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL,
660                                               &texture_level);
661         return getTextureLevelDesc(context, object_name, texture_level, desc);
662     } else {
663         std::cerr << "warning: unexpected GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = " << object_type << "\n";
664         return false;
665     }
666 }
667
668
669
670 image::Image *
671 getDrawBufferImage() {
672     GLenum format = GL_RGB;
673     GLint channels = __gl_format_channels(format);
674     if (channels > 4) {
675         return NULL;
676     }
677
678     Context context;
679
680     GLenum framebuffer_binding;
681     GLenum framebuffer_target;
682     if (context.ES) {
683         framebuffer_binding = GL_FRAMEBUFFER_BINDING;
684         framebuffer_target = GL_FRAMEBUFFER;
685     } else {
686         framebuffer_binding = GL_DRAW_FRAMEBUFFER_BINDING;
687         framebuffer_target = GL_DRAW_FRAMEBUFFER;
688     }
689
690     GLint draw_framebuffer = 0;
691     glGetIntegerv(framebuffer_binding, &draw_framebuffer);
692
693     GLint draw_buffer = GL_NONE;
694     ImageDesc desc;
695     if (draw_framebuffer) {
696         if (context.ARB_draw_buffers) {
697             glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer);
698             if (draw_buffer == GL_NONE) {
699                 return NULL;
700             }
701         }
702
703         if (!getFramebufferAttachmentDesc(context, framebuffer_target, draw_buffer, desc)) {
704             return NULL;
705         }
706     } else {
707         if (!context.ES) {
708             glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
709             if (draw_buffer == GL_NONE) {
710                 return NULL;
711             }
712         }
713
714         if (!getDrawableBounds(&desc.width, &desc.height)) {
715             return NULL;
716         }
717
718         desc.depth = 1;
719     }
720
721     GLenum type = GL_UNSIGNED_BYTE;
722
723 #if DEPTH_AS_RGBA
724     if (format == GL_DEPTH_COMPONENT) {
725         type = GL_UNSIGNED_INT;
726         channels = 4;
727     }
728 #endif
729
730     image::Image *image = new image::Image(desc.width, desc.height, channels, true);
731     if (!image) {
732         return NULL;
733     }
734
735     while (glGetError() != GL_NO_ERROR) {}
736
737     GLint read_framebuffer = 0;
738     GLint read_buffer = GL_NONE;
739     if (!context.ES) {
740         glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
741         glBindFramebuffer(GL_READ_FRAMEBUFFER, draw_framebuffer);
742
743         glGetIntegerv(GL_READ_BUFFER, &read_buffer);
744         glReadBuffer(draw_buffer);
745     }
746
747     // TODO: reset imaging state too
748     context.resetPixelPackState();
749
750     glReadPixels(0, 0, desc.width, desc.height, format, type, image->pixels);
751
752     context.restorePixelPackState();
753
754     if (!context.ES) {
755         glReadBuffer(read_buffer);
756         glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
757     }
758
759     GLenum error = glGetError();
760     if (error != GL_NO_ERROR) {
761         do {
762             std::cerr << "warning: " << enumToString(error) << " while getting snapshot\n";
763             error = glGetError();
764         } while(error != GL_NO_ERROR);
765         delete image;
766         return NULL;
767     }
768      
769     return image;
770 }
771
772
773 /**
774  * Dump the image of the currently bound read buffer.
775  */
776 static inline void
777 dumpReadBufferImage(JSONWriter &json, GLint width, GLint height, GLenum format,
778                     GLint internalFormat = GL_NONE)
779 {
780     GLint channels = __gl_format_channels(format);
781
782     Context context;
783
784     json.beginObject();
785
786     // Tell the GUI this is no ordinary object, but an image
787     json.writeStringMember("__class__", "image");
788
789     json.writeNumberMember("__width__", width);
790     json.writeNumberMember("__height__", height);
791     json.writeNumberMember("__depth__", 1);
792
793     json.writeStringMember("__format__", enumToString(internalFormat));
794
795     // Hardcoded for now, but we could chose types more adequate to the
796     // texture internal format
797     json.writeStringMember("__type__", "uint8");
798     json.writeBoolMember("__normalized__", true);
799     json.writeNumberMember("__channels__", channels);
800
801     GLenum type = GL_UNSIGNED_BYTE;
802
803 #if DEPTH_AS_RGBA
804     if (format == GL_DEPTH_COMPONENT) {
805         type = GL_UNSIGNED_INT;
806         channels = 4;
807     }
808 #endif
809
810     GLubyte *pixels = new GLubyte[width*height*channels];
811
812     // TODO: reset imaging state too
813     context.resetPixelPackState();
814
815     glReadPixels(0, 0, width, height, format, type, pixels);
816
817     context.restorePixelPackState();
818
819     json.beginMember("__data__");
820     char *pngBuffer;
821     int pngBufferSize;
822     image::writePixelsToBuffer(pixels, width, height, channels, true, &pngBuffer, &pngBufferSize);
823     //std::cerr <<" Before = "<<(width * height * channels * sizeof *pixels)
824     //          <<", after = "<<pngBufferSize << ", ratio = " << double(width * height * channels * sizeof *pixels)/pngBufferSize;
825     json.writeBase64(pngBuffer, pngBufferSize);
826     free(pngBuffer);
827     json.endMember(); // __data__
828
829     delete [] pixels;
830     json.endObject();
831 }
832
833
834 static inline GLuint
835 downsampledFramebuffer(Context &context,
836                        GLuint oldFbo, GLint drawbuffer,
837                        GLint colorRb, GLint depthRb, GLint stencilRb,
838                        GLuint *rbs, GLint *numRbs)
839 {
840     GLuint fbo;
841
842
843     *numRbs = 0;
844
845     glGenFramebuffers(1, &fbo);
846     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
847
848     {
849         // color buffer
850         ImageDesc desc;
851         glBindRenderbuffer(GL_RENDERBUFFER, colorRb);
852         getBoundRenderbufferDesc(context, desc);
853
854         glGenRenderbuffers(1, &rbs[*numRbs]);
855         glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
856         glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
857         glFramebufferRenderbuffer(GL_FRAMEBUFFER, drawbuffer,
858                                   GL_RENDERBUFFER, rbs[*numRbs]);
859
860         glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
861         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
862         glDrawBuffer(drawbuffer);
863         glReadBuffer(drawbuffer);
864         glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
865                           GL_COLOR_BUFFER_BIT, GL_NEAREST);
866         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
867         ++*numRbs;
868     }
869
870     if (stencilRb == depthRb && stencilRb) {
871         //combined depth and stencil buffer
872         ImageDesc desc;
873         glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
874         getBoundRenderbufferDesc(context, desc);
875
876         glGenRenderbuffers(1, &rbs[*numRbs]);
877         glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
878         glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
879         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
880                                   GL_RENDERBUFFER, rbs[*numRbs]);
881         glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
882         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
883         glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
884                           GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
885         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
886         ++*numRbs;
887     } else {
888         if (depthRb) {
889             ImageDesc desc;
890             glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
891             getBoundRenderbufferDesc(context, desc);
892
893             glGenRenderbuffers(1, &rbs[*numRbs]);
894             glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
895             glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
896             glFramebufferRenderbuffer(GL_FRAMEBUFFER,
897                                       GL_DEPTH_ATTACHMENT,
898                                       GL_RENDERBUFFER, rbs[*numRbs]);
899             glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
900             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
901             glDrawBuffer(GL_DEPTH_ATTACHMENT);
902             glReadBuffer(GL_DEPTH_ATTACHMENT);
903             glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
904                               GL_DEPTH_BUFFER_BIT, GL_NEAREST);
905             ++*numRbs;
906         }
907         if (stencilRb) {
908             ImageDesc desc;
909             glBindRenderbuffer(GL_RENDERBUFFER, stencilRb);
910             getBoundRenderbufferDesc(context, desc);
911
912             glGenRenderbuffers(1, &rbs[*numRbs]);
913             glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
914             glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
915             glFramebufferRenderbuffer(GL_FRAMEBUFFER,
916                                       GL_STENCIL_ATTACHMENT,
917                                       GL_RENDERBUFFER, rbs[*numRbs]);
918             glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
919             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
920             glDrawBuffer(GL_STENCIL_ATTACHMENT);
921             glReadBuffer(GL_STENCIL_ATTACHMENT);
922             glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
923                               GL_STENCIL_BUFFER_BIT, GL_NEAREST);
924             ++*numRbs;
925         }
926     }
927
928     return fbo;
929 }
930
931
932 /**
933  * Dump images of current draw drawable/window.
934  */
935 static void
936 dumpDrawableImages(JSONWriter &json, Context &context)
937 {
938     GLint width, height;
939
940     if (!getDrawableBounds(&width, &height)) {
941         return;
942     }
943
944     GLint draw_buffer = GL_NONE;
945     if (context.ES) {
946         draw_buffer = GL_BACK;
947     } else {
948         glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
949         glReadBuffer(draw_buffer);
950     }
951
952     if (draw_buffer != GL_NONE) {
953         GLint read_buffer = GL_NONE;
954         if (!context.ES) {
955             glGetIntegerv(GL_READ_BUFFER, &read_buffer);
956         }
957
958         GLint alpha_bits = 0;
959 #if 0
960         // XXX: Ignore alpha until we are able to match the traced visual
961         glGetIntegerv(GL_ALPHA_BITS, &alpha_bits);
962 #endif
963         GLenum format = alpha_bits ? GL_RGBA : GL_RGB;
964         json.beginMember(enumToString(draw_buffer));
965         dumpReadBufferImage(json, width, height, format);
966         json.endMember();
967
968         if (!context.ES) {
969             glReadBuffer(read_buffer);
970         }
971     }
972
973     if (!context.ES) {
974         GLint depth_bits = 0;
975         glGetIntegerv(GL_DEPTH_BITS, &depth_bits);
976         if (depth_bits) {
977             json.beginMember("GL_DEPTH_COMPONENT");
978             dumpReadBufferImage(json, width, height, GL_DEPTH_COMPONENT);
979             json.endMember();
980         }
981
982         GLint stencil_bits = 0;
983         glGetIntegerv(GL_STENCIL_BITS, &stencil_bits);
984         if (stencil_bits) {
985             json.beginMember("GL_STENCIL_INDEX");
986             dumpReadBufferImage(json, width, height, GL_STENCIL_INDEX);
987             json.endMember();
988         }
989     }
990 }
991
992
993 /**
994  * Dump the specified framebuffer attachment.
995  *
996  * In the case of a color attachment, it assumes it is already bound for read.
997  */
998 static void
999 dumpFramebufferAttachment(JSONWriter &json, Context &context, GLenum target, GLenum attachment, GLenum format)
1000 {
1001     ImageDesc desc;
1002     if (!getFramebufferAttachmentDesc(context, target, attachment, desc)) {
1003         return;
1004     }
1005
1006     json.beginMember(enumToString(attachment));
1007     dumpReadBufferImage(json, desc.width, desc.height, format, desc.internalFormat);
1008     json.endMember();
1009 }
1010
1011
1012 static void
1013 dumpFramebufferAttachments(JSONWriter &json, Context &context, GLenum target)
1014 {
1015     GLint read_framebuffer = 0;
1016     glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
1017
1018     GLint read_buffer = GL_NONE;
1019     glGetIntegerv(GL_READ_BUFFER, &read_buffer);
1020
1021     GLint max_draw_buffers = 1;
1022     glGetIntegerv(GL_MAX_DRAW_BUFFERS, &max_draw_buffers);
1023     GLint max_color_attachments = 0;
1024     glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments);
1025
1026     for (GLint i = 0; i < max_draw_buffers; ++i) {
1027         GLint draw_buffer = GL_NONE;
1028         glGetIntegerv(GL_DRAW_BUFFER0 + i, &draw_buffer);
1029         if (draw_buffer != GL_NONE) {
1030             glReadBuffer(draw_buffer);
1031             GLint attachment;
1032             if (draw_buffer >= GL_COLOR_ATTACHMENT0 && draw_buffer < GL_COLOR_ATTACHMENT0 + max_color_attachments) {
1033                 attachment = draw_buffer;
1034             } else {
1035                 std::cerr << "warning: unexpected GL_DRAW_BUFFER" << i << " = " << draw_buffer << "\n";
1036                 attachment = GL_COLOR_ATTACHMENT0;
1037             }
1038             GLint alpha_size = 0;
1039             glGetFramebufferAttachmentParameteriv(target, attachment,
1040                                                   GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE,
1041                                                   &alpha_size);
1042             GLenum format = alpha_size ? GL_RGBA : GL_RGB;
1043             dumpFramebufferAttachment(json, context, target, attachment, format);
1044         }
1045     }
1046
1047     glReadBuffer(read_buffer);
1048
1049     if (!context.ES) {
1050         dumpFramebufferAttachment(json, context, target, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT);
1051         dumpFramebufferAttachment(json, context, target, GL_STENCIL_ATTACHMENT, GL_STENCIL_INDEX);
1052     }
1053
1054     glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
1055 }
1056
1057
1058 void
1059 dumpFramebuffer(JSONWriter &json, Context &context)
1060 {
1061     json.beginMember("framebuffer");
1062     json.beginObject();
1063
1064     GLint boundDrawFbo = 0, boundReadFbo = 0;
1065     glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &boundDrawFbo);
1066     glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &boundReadFbo);
1067     if (!boundDrawFbo) {
1068         dumpDrawableImages(json, context);
1069     } else if (context.ES) {
1070         dumpFramebufferAttachments(json, context, GL_FRAMEBUFFER);
1071     } else {
1072         GLint colorRb = 0, stencilRb = 0, depthRb = 0;
1073         GLint draw_buffer0 = GL_NONE;
1074         glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer0);
1075         bool multisample = false;
1076
1077         GLint boundRb = 0;
1078         glGetIntegerv(GL_RENDERBUFFER_BINDING, &boundRb);
1079
1080         GLint object_type;
1081         glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, draw_buffer0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
1082         if (object_type == GL_RENDERBUFFER) {
1083             glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, draw_buffer0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &colorRb);
1084             glBindRenderbuffer(GL_RENDERBUFFER, colorRb);
1085             GLint samples = 0;
1086             glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
1087             if (samples) {
1088                 multisample = true;
1089             }
1090         }
1091
1092         glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
1093         if (object_type == GL_RENDERBUFFER) {
1094             glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthRb);
1095             glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
1096             GLint samples = 0;
1097             glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
1098             if (samples) {
1099                 multisample = true;
1100             }
1101         }
1102
1103         glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
1104         if (object_type == GL_RENDERBUFFER) {
1105             glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &stencilRb);
1106             glBindRenderbuffer(GL_RENDERBUFFER, stencilRb);
1107             GLint samples = 0;
1108             glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
1109             if (samples) {
1110                 multisample = true;
1111             }
1112         }
1113
1114         glBindRenderbuffer(GL_RENDERBUFFER, boundRb);
1115
1116         GLuint rbs[3];
1117         GLint numRbs = 0;
1118         GLuint fboCopy = 0;
1119
1120         if (multisample) {
1121             // glReadPixels doesnt support multisampled buffers so we need
1122             // to blit the fbo to a temporary one
1123             fboCopy = downsampledFramebuffer(context,
1124                                              boundDrawFbo, draw_buffer0,
1125                                              colorRb, depthRb, stencilRb,
1126                                              rbs, &numRbs);
1127         }
1128
1129         dumpFramebufferAttachments(json, context, GL_DRAW_FRAMEBUFFER);
1130
1131         if (multisample) {
1132             glBindRenderbuffer(GL_RENDERBUFFER_BINDING, boundRb);
1133             glDeleteRenderbuffers(numRbs, rbs);
1134             glDeleteFramebuffers(1, &fboCopy);
1135         }
1136
1137         glBindFramebuffer(GL_READ_FRAMEBUFFER, boundReadFbo);
1138         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, boundDrawFbo);
1139     }
1140
1141     json.endObject();
1142     json.endMember(); // framebuffer
1143 }
1144
1145
1146 } /* namespace glstate */