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