]> git.cworth.org Git - apitrace/blob - retrace/glstate_images.cpp
84c6db1793b88b0581457fd39f746b2362145929
[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
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 GLboolean
334 isDepthFormat(GLenum internalFormat)
335 {
336    switch (internalFormat) {
337    case GL_DEPTH_COMPONENT:
338    case GL_DEPTH_COMPONENT16:
339    case GL_DEPTH_COMPONENT24:
340    case GL_DEPTH_COMPONENT32:
341    case GL_DEPTH_COMPONENT32F:
342    case GL_DEPTH_COMPONENT32F_NV:
343    case GL_DEPTH_STENCIL:
344    case GL_DEPTH24_STENCIL8:
345    case GL_DEPTH32F_STENCIL8:
346    case GL_DEPTH32F_STENCIL8_NV:
347       return GL_TRUE;
348    }
349    return GL_FALSE;
350 }
351
352
353 static inline void
354 dumpActiveTextureLevel(JSONWriter &json, Context &context, GLenum target, GLint level)
355 {
356     ImageDesc desc;
357     if (!getActiveTextureLevelDesc(context, target, level, desc)) {
358         return;
359     }
360
361     char label[512];
362
363     GLint active_texture = GL_TEXTURE0;
364     glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
365     snprintf(label, sizeof label, "%s, %s, level = %d",
366              enumToString(active_texture), enumToString(target), level);
367
368     json.beginMember(label);
369
370     json.beginObject();
371
372     GLuint channels;
373     GLenum format;
374     if (!context.ES && isDepthFormat(desc.internalFormat)) {
375        format = GL_DEPTH_COMPONENT;
376        channels = 1;
377     } else {
378        format = GL_RGBA;
379        channels = 4;
380     }
381
382     // Tell the GUI this is no ordinary object, but an image
383     json.writeStringMember("__class__", "image");
384
385     json.writeNumberMember("__width__", desc.width);
386     json.writeNumberMember("__height__", desc.height);
387     json.writeNumberMember("__depth__", desc.depth);
388
389     json.writeStringMember("__format__", enumToString(desc.internalFormat));
390
391     // Hardcoded for now, but we could chose types more adequate to the
392     // texture internal format
393     json.writeStringMember("__type__", "uint8");
394     json.writeBoolMember("__normalized__", true);
395     json.writeNumberMember("__channels__", channels);
396
397     GLubyte *pixels = new GLubyte[desc.depth*desc.width*desc.height*channels];
398
399     context.resetPixelPackState();
400
401     if (context.ES) {
402         getTexImageOES(target, level, desc, pixels);
403     } else {
404         glGetTexImage(target, level, format, GL_UNSIGNED_BYTE, pixels);
405     }
406
407     context.restorePixelPackState();
408
409     json.beginMember("__data__");
410     char *pngBuffer;
411     int pngBufferSize;
412     image::writePixelsToBuffer(pixels, desc.width, desc.height, channels, true, &pngBuffer, &pngBufferSize);
413     json.writeBase64(pngBuffer, pngBufferSize);
414     free(pngBuffer);
415     json.endMember(); // __data__
416
417     delete [] pixels;
418     json.endObject();
419 }
420
421
422 static inline void
423 dumpTexture(JSONWriter &json, Context &context, GLenum target, GLenum binding)
424 {
425     GLint texture_binding = 0;
426     glGetIntegerv(binding, &texture_binding);
427     if (!glIsEnabled(target) && !texture_binding) {
428         return;
429     }
430
431     GLint level = 0;
432     do {
433         ImageDesc desc;
434         if (!getActiveTextureLevelDesc(context, target, level, desc)) {
435             break;
436         }
437
438         if (target == GL_TEXTURE_CUBE_MAP) {
439             for (int face = 0; face < 6; ++face) {
440                 dumpActiveTextureLevel(json, context, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level);
441             }
442         } else {
443             dumpActiveTextureLevel(json, context, target, level);
444         }
445
446         ++level;
447     } while(true);
448 }
449
450
451 void
452 dumpTextures(JSONWriter &json, Context &context)
453 {
454     json.beginMember("textures");
455     json.beginObject();
456     GLint active_texture = GL_TEXTURE0;
457     glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
458
459     GLint max_texture_coords = 0;
460     glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);
461     GLint max_combined_texture_image_units = 0;
462     glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_combined_texture_image_units);
463     GLint max_units = std::max(max_combined_texture_image_units, max_texture_coords);
464
465     /*
466      * At least the Android software GL implementation doesn't return the
467      * proper value for this, but rather returns 0. The GL(ES) specification
468      * mandates a minimum value of 2, so use this as a fall-back value.
469      */
470     max_units = std::max(max_units, 2);
471
472     for (GLint unit = 0; unit < max_units; ++unit) {
473         GLenum texture = GL_TEXTURE0 + unit;
474         glActiveTexture(texture);
475         dumpTexture(json, context, GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D);
476         dumpTexture(json, context, GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D);
477         dumpTexture(json, context, GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D);
478         dumpTexture(json, context, GL_TEXTURE_RECTANGLE, GL_TEXTURE_BINDING_RECTANGLE);
479         dumpTexture(json, context, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP);
480     }
481     glActiveTexture(active_texture);
482     json.endObject();
483     json.endMember(); // textures
484 }
485
486
487 static bool
488 getDrawableBounds(GLint *width, GLint *height) {
489 #if defined(__linux__)
490     if (dlsym(RTLD_DEFAULT, "eglGetCurrentContext")) {
491         EGLContext currentContext = eglGetCurrentContext();
492         if (currentContext == EGL_NO_CONTEXT) {
493             return false;
494         }
495
496         EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW);
497         if (currentSurface == EGL_NO_SURFACE) {
498             return false;
499         }
500
501         EGLDisplay currentDisplay = eglGetCurrentDisplay();
502         if (currentDisplay == EGL_NO_DISPLAY) {
503             return false;
504         }
505
506         if (!eglQuerySurface(currentDisplay, currentSurface, EGL_WIDTH, width) ||
507             !eglQuerySurface(currentDisplay, currentSurface, EGL_HEIGHT, height)) {
508             return false;
509         }
510
511         return true;
512     }
513 #endif
514
515 #if defined(_WIN32)
516
517     HDC hDC = wglGetCurrentDC();
518     if (!hDC) {
519         return false;
520     }
521
522     HWND hWnd = WindowFromDC(hDC);
523     RECT rect;
524
525     if (!GetClientRect(hWnd, &rect)) {
526        return false;
527     }
528
529     *width  = rect.right  - rect.left;
530     *height = rect.bottom - rect.top;
531     return true;
532
533 #elif defined(__APPLE__)
534
535     CGLContextObj ctx = CGLGetCurrentContext();
536     if (ctx == NULL) {
537         return false;
538     }
539
540     CGSConnectionID cid;
541     CGSWindowID wid;
542     CGSSurfaceID sid;
543
544     if (CGLGetSurface(ctx, &cid, &wid, &sid) != kCGLNoError) {
545         return false;
546     }
547
548     CGRect rect;
549
550     if (CGSGetSurfaceBounds(cid, wid, sid, &rect) != 0) {
551         return false;
552     }
553
554     *width = rect.size.width;
555     *height = rect.size.height;
556     return true;
557
558 #elif defined(HAVE_X11)
559
560     Display *display;
561     Drawable drawable;
562     Window root;
563     int x, y;
564     unsigned int w, h, bw, depth;
565
566     display = glXGetCurrentDisplay();
567     if (!display) {
568         return false;
569     }
570
571     drawable = glXGetCurrentDrawable();
572     if (drawable == None) {
573         return false;
574     }
575
576     if (!XGetGeometry(display, drawable, &root, &x, &y, &w, &h, &bw, &depth)) {
577         return false;
578     }
579
580     *width = w;
581     *height = h;
582     return true;
583
584 #else
585
586     return false;
587
588 #endif
589 }
590
591
592 static const GLenum texture_bindings[][2] = {
593     {GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D},
594     {GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D},
595     {GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D},
596     {GL_TEXTURE_RECTANGLE, GL_TEXTURE_BINDING_RECTANGLE},
597     {GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP}
598 };
599
600
601 static bool
602 bindTexture(GLint texture, GLenum &target, GLint &bound_texture)
603 {
604
605     for (unsigned i = 0; i < sizeof(texture_bindings)/sizeof(texture_bindings[0]); ++i) {
606         target  = texture_bindings[i][0];
607
608         GLenum binding = texture_bindings[i][1];
609
610         while (glGetError() != GL_NO_ERROR)
611             ;
612
613         glGetIntegerv(binding, &bound_texture);
614         glBindTexture(target, texture);
615
616         if (glGetError() == GL_NO_ERROR) {
617             return true;
618         }
619
620         glBindTexture(target, bound_texture);
621     }
622
623     target = GL_NONE;
624
625     return false;
626 }
627
628
629 static bool
630 getTextureLevelDesc(Context &context, GLint texture, GLint level, ImageDesc &desc)
631 {
632     GLenum target;
633     GLint bound_texture = 0;
634     if (!bindTexture(texture, target, bound_texture)) {
635         return false;
636     }
637
638     getActiveTextureLevelDesc(context, target, level, desc);
639
640     glBindTexture(target, bound_texture);
641
642     return desc.valid();
643 }
644
645
646 static bool
647 getBoundRenderbufferDesc(Context &context, ImageDesc &desc)
648 {
649     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &desc.width);
650     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &desc.height);
651     desc.depth = 1;
652     
653     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_INTERNAL_FORMAT, &desc.internalFormat);
654     
655     return desc.valid();
656 }
657
658
659 static bool
660 getRenderbufferDesc(Context &context, GLint renderbuffer, ImageDesc &desc)
661 {
662     GLint bound_renderbuffer = 0;
663     glGetIntegerv(GL_RENDERBUFFER_BINDING, &bound_renderbuffer);
664     glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
665
666     getBoundRenderbufferDesc(context, desc);
667
668     glBindRenderbuffer(GL_RENDERBUFFER, bound_renderbuffer);
669     
670     return desc.valid();
671 }
672
673
674 static bool
675 getFramebufferAttachmentDesc(Context &context, GLenum target, GLenum attachment, ImageDesc &desc)
676 {
677     GLint object_type = GL_NONE;
678     glGetFramebufferAttachmentParameteriv(target, attachment,
679                                           GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
680                                           &object_type);
681     if (object_type == GL_NONE) {
682         return false;
683     }
684
685     GLint object_name = 0;
686     glGetFramebufferAttachmentParameteriv(target, attachment,
687                                           GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
688                                           &object_name);
689     if (object_name == 0) {
690         return false;
691     }
692
693     if (object_type == GL_RENDERBUFFER) {
694         return getRenderbufferDesc(context, object_name, desc);
695     } else if (object_type == GL_TEXTURE) {
696         GLint texture_level = 0;
697         glGetFramebufferAttachmentParameteriv(target, attachment,
698                                               GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL,
699                                               &texture_level);
700         return getTextureLevelDesc(context, object_name, texture_level, desc);
701     } else {
702         std::cerr << "warning: unexpected GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = " << object_type << "\n";
703         return false;
704     }
705 }
706
707
708
709 image::Image *
710 getDrawBufferImage() {
711     GLenum format = GL_RGB;
712     GLint channels = __gl_format_channels(format);
713     if (channels > 4) {
714         return NULL;
715     }
716
717     Context context;
718
719     GLenum framebuffer_binding;
720     GLenum framebuffer_target;
721     if (context.ES) {
722         framebuffer_binding = GL_FRAMEBUFFER_BINDING;
723         framebuffer_target = GL_FRAMEBUFFER;
724     } else {
725         framebuffer_binding = GL_DRAW_FRAMEBUFFER_BINDING;
726         framebuffer_target = GL_DRAW_FRAMEBUFFER;
727     }
728
729     GLint draw_framebuffer = 0;
730     glGetIntegerv(framebuffer_binding, &draw_framebuffer);
731
732     GLint draw_buffer = GL_NONE;
733     ImageDesc desc;
734     if (draw_framebuffer) {
735         if (context.ARB_draw_buffers) {
736             glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer);
737             if (draw_buffer == GL_NONE) {
738                 return NULL;
739             }
740         }
741
742         if (!getFramebufferAttachmentDesc(context, framebuffer_target, draw_buffer, desc)) {
743             return NULL;
744         }
745     } else {
746         if (!context.ES) {
747             glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
748             if (draw_buffer == GL_NONE) {
749                 return NULL;
750             }
751         }
752
753         if (!getDrawableBounds(&desc.width, &desc.height)) {
754             return NULL;
755         }
756
757         desc.depth = 1;
758     }
759
760     GLenum type = GL_UNSIGNED_BYTE;
761
762 #if DEPTH_AS_RGBA
763     if (format == GL_DEPTH_COMPONENT) {
764         type = GL_UNSIGNED_INT;
765         channels = 4;
766     }
767 #endif
768
769     image::Image *image = new image::Image(desc.width, desc.height, channels, true);
770     if (!image) {
771         return NULL;
772     }
773
774     while (glGetError() != GL_NO_ERROR) {}
775
776     GLint read_framebuffer = 0;
777     GLint read_buffer = GL_NONE;
778     if (!context.ES) {
779         glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
780         glBindFramebuffer(GL_READ_FRAMEBUFFER, draw_framebuffer);
781
782         glGetIntegerv(GL_READ_BUFFER, &read_buffer);
783         glReadBuffer(draw_buffer);
784     }
785
786     // TODO: reset imaging state too
787     context.resetPixelPackState();
788
789     glReadPixels(0, 0, desc.width, desc.height, format, type, image->pixels);
790
791     context.restorePixelPackState();
792
793     if (!context.ES) {
794         glReadBuffer(read_buffer);
795         glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
796     }
797
798     GLenum error = glGetError();
799     if (error != GL_NO_ERROR) {
800         do {
801             std::cerr << "warning: " << enumToString(error) << " while getting snapshot\n";
802             error = glGetError();
803         } while(error != GL_NO_ERROR);
804         delete image;
805         return NULL;
806     }
807      
808     return image;
809 }
810
811
812 /**
813  * Dump the image of the currently bound read buffer.
814  */
815 static inline void
816 dumpReadBufferImage(JSONWriter &json, GLint width, GLint height, GLenum format,
817                     GLint internalFormat = GL_NONE)
818 {
819     GLint channels = __gl_format_channels(format);
820
821     Context context;
822
823     json.beginObject();
824
825     // Tell the GUI this is no ordinary object, but an image
826     json.writeStringMember("__class__", "image");
827
828     json.writeNumberMember("__width__", width);
829     json.writeNumberMember("__height__", height);
830     json.writeNumberMember("__depth__", 1);
831
832     json.writeStringMember("__format__", enumToString(internalFormat));
833
834     // Hardcoded for now, but we could chose types more adequate to the
835     // texture internal format
836     json.writeStringMember("__type__", "uint8");
837     json.writeBoolMember("__normalized__", true);
838     json.writeNumberMember("__channels__", channels);
839
840     GLenum type = GL_UNSIGNED_BYTE;
841
842 #if DEPTH_AS_RGBA
843     if (format == GL_DEPTH_COMPONENT) {
844         type = GL_UNSIGNED_INT;
845         channels = 4;
846     }
847 #endif
848
849     GLubyte *pixels = new GLubyte[width*height*channels];
850
851     // TODO: reset imaging state too
852     context.resetPixelPackState();
853
854     glReadPixels(0, 0, width, height, format, type, pixels);
855
856     context.restorePixelPackState();
857
858     json.beginMember("__data__");
859     char *pngBuffer;
860     int pngBufferSize;
861     image::writePixelsToBuffer(pixels, width, height, channels, true, &pngBuffer, &pngBufferSize);
862     //std::cerr <<" Before = "<<(width * height * channels * sizeof *pixels)
863     //          <<", after = "<<pngBufferSize << ", ratio = " << double(width * height * channels * sizeof *pixels)/pngBufferSize;
864     json.writeBase64(pngBuffer, pngBufferSize);
865     free(pngBuffer);
866     json.endMember(); // __data__
867
868     delete [] pixels;
869     json.endObject();
870 }
871
872
873 static inline GLuint
874 downsampledFramebuffer(Context &context,
875                        GLuint oldFbo, GLint drawbuffer,
876                        GLint colorRb, GLint depthRb, GLint stencilRb,
877                        GLuint *rbs, GLint *numRbs)
878 {
879     GLuint fbo;
880
881
882     *numRbs = 0;
883
884     glGenFramebuffers(1, &fbo);
885     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
886
887     {
888         // color buffer
889         ImageDesc desc;
890         glBindRenderbuffer(GL_RENDERBUFFER, colorRb);
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, drawbuffer,
897                                   GL_RENDERBUFFER, rbs[*numRbs]);
898
899         glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
900         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
901         glDrawBuffer(drawbuffer);
902         glReadBuffer(drawbuffer);
903         glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
904                           GL_COLOR_BUFFER_BIT, GL_NEAREST);
905         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
906         ++*numRbs;
907     }
908
909     if (stencilRb == depthRb && stencilRb) {
910         //combined depth and stencil buffer
911         ImageDesc desc;
912         glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
913         getBoundRenderbufferDesc(context, desc);
914
915         glGenRenderbuffers(1, &rbs[*numRbs]);
916         glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
917         glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
918         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
919                                   GL_RENDERBUFFER, rbs[*numRbs]);
920         glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
921         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
922         glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
923                           GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
924         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
925         ++*numRbs;
926     } else {
927         if (depthRb) {
928             ImageDesc desc;
929             glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
930             getBoundRenderbufferDesc(context, desc);
931
932             glGenRenderbuffers(1, &rbs[*numRbs]);
933             glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
934             glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
935             glFramebufferRenderbuffer(GL_FRAMEBUFFER,
936                                       GL_DEPTH_ATTACHMENT,
937                                       GL_RENDERBUFFER, rbs[*numRbs]);
938             glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
939             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
940             glDrawBuffer(GL_DEPTH_ATTACHMENT);
941             glReadBuffer(GL_DEPTH_ATTACHMENT);
942             glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
943                               GL_DEPTH_BUFFER_BIT, GL_NEAREST);
944             ++*numRbs;
945         }
946         if (stencilRb) {
947             ImageDesc desc;
948             glBindRenderbuffer(GL_RENDERBUFFER, stencilRb);
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_STENCIL_ATTACHMENT,
956                                       GL_RENDERBUFFER, rbs[*numRbs]);
957             glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
958             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
959             glDrawBuffer(GL_STENCIL_ATTACHMENT);
960             glReadBuffer(GL_STENCIL_ATTACHMENT);
961             glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
962                               GL_STENCIL_BUFFER_BIT, GL_NEAREST);
963             ++*numRbs;
964         }
965     }
966
967     return fbo;
968 }
969
970
971 /**
972  * Dump images of current draw drawable/window.
973  */
974 static void
975 dumpDrawableImages(JSONWriter &json, Context &context)
976 {
977     GLint width, height;
978
979     if (!getDrawableBounds(&width, &height)) {
980         return;
981     }
982
983     GLint draw_buffer = GL_NONE;
984     if (context.ES) {
985         draw_buffer = GL_BACK;
986     } else {
987         glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
988         glReadBuffer(draw_buffer);
989     }
990
991     if (draw_buffer != GL_NONE) {
992         GLint read_buffer = GL_NONE;
993         if (!context.ES) {
994             glGetIntegerv(GL_READ_BUFFER, &read_buffer);
995         }
996
997         GLint alpha_bits = 0;
998 #if 0
999         // XXX: Ignore alpha until we are able to match the traced visual
1000         glGetIntegerv(GL_ALPHA_BITS, &alpha_bits);
1001 #endif
1002         GLenum format = alpha_bits ? GL_RGBA : GL_RGB;
1003         json.beginMember(enumToString(draw_buffer));
1004         dumpReadBufferImage(json, width, height, format);
1005         json.endMember();
1006
1007         if (!context.ES) {
1008             glReadBuffer(read_buffer);
1009         }
1010     }
1011
1012     if (!context.ES) {
1013         GLint depth_bits = 0;
1014         glGetIntegerv(GL_DEPTH_BITS, &depth_bits);
1015         if (depth_bits) {
1016             json.beginMember("GL_DEPTH_COMPONENT");
1017             dumpReadBufferImage(json, width, height, GL_DEPTH_COMPONENT);
1018             json.endMember();
1019         }
1020
1021         GLint stencil_bits = 0;
1022         glGetIntegerv(GL_STENCIL_BITS, &stencil_bits);
1023         if (stencil_bits) {
1024             json.beginMember("GL_STENCIL_INDEX");
1025             dumpReadBufferImage(json, width, height, GL_STENCIL_INDEX);
1026             json.endMember();
1027         }
1028     }
1029 }
1030
1031
1032 /**
1033  * Dump the specified framebuffer attachment.
1034  *
1035  * In the case of a color attachment, it assumes it is already bound for read.
1036  */
1037 static void
1038 dumpFramebufferAttachment(JSONWriter &json, Context &context, GLenum target, GLenum attachment, GLenum format)
1039 {
1040     ImageDesc desc;
1041     if (!getFramebufferAttachmentDesc(context, target, attachment, desc)) {
1042         return;
1043     }
1044
1045     json.beginMember(enumToString(attachment));
1046     dumpReadBufferImage(json, desc.width, desc.height, format, desc.internalFormat);
1047     json.endMember();
1048 }
1049
1050
1051 static void
1052 dumpFramebufferAttachments(JSONWriter &json, Context &context, GLenum target)
1053 {
1054     GLint read_framebuffer = 0;
1055     glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
1056
1057     GLint read_buffer = GL_NONE;
1058     glGetIntegerv(GL_READ_BUFFER, &read_buffer);
1059
1060     GLint max_draw_buffers = 1;
1061     glGetIntegerv(GL_MAX_DRAW_BUFFERS, &max_draw_buffers);
1062     GLint max_color_attachments = 0;
1063     glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments);
1064
1065     for (GLint i = 0; i < max_draw_buffers; ++i) {
1066         GLint draw_buffer = GL_NONE;
1067         glGetIntegerv(GL_DRAW_BUFFER0 + i, &draw_buffer);
1068         if (draw_buffer != GL_NONE) {
1069             glReadBuffer(draw_buffer);
1070             GLint attachment;
1071             if (draw_buffer >= GL_COLOR_ATTACHMENT0 && draw_buffer < GL_COLOR_ATTACHMENT0 + max_color_attachments) {
1072                 attachment = draw_buffer;
1073             } else {
1074                 std::cerr << "warning: unexpected GL_DRAW_BUFFER" << i << " = " << draw_buffer << "\n";
1075                 attachment = GL_COLOR_ATTACHMENT0;
1076             }
1077             GLint alpha_size = 0;
1078             glGetFramebufferAttachmentParameteriv(target, attachment,
1079                                                   GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE,
1080                                                   &alpha_size);
1081             GLenum format = alpha_size ? GL_RGBA : GL_RGB;
1082             dumpFramebufferAttachment(json, context, target, attachment, format);
1083         }
1084     }
1085
1086     glReadBuffer(read_buffer);
1087
1088     if (!context.ES) {
1089         dumpFramebufferAttachment(json, context, target, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT);
1090         dumpFramebufferAttachment(json, context, target, GL_STENCIL_ATTACHMENT, GL_STENCIL_INDEX);
1091     }
1092
1093     glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
1094 }
1095
1096
1097 void
1098 dumpFramebuffer(JSONWriter &json, Context &context)
1099 {
1100     json.beginMember("framebuffer");
1101     json.beginObject();
1102
1103     GLint boundDrawFbo = 0, boundReadFbo = 0;
1104     glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &boundDrawFbo);
1105     glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &boundReadFbo);
1106     if (!boundDrawFbo) {
1107         dumpDrawableImages(json, context);
1108     } else if (context.ES) {
1109         dumpFramebufferAttachments(json, context, GL_FRAMEBUFFER);
1110     } else {
1111         GLint colorRb = 0, stencilRb = 0, depthRb = 0;
1112         GLint draw_buffer0 = GL_NONE;
1113         glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer0);
1114         bool multisample = false;
1115
1116         GLint boundRb = 0;
1117         glGetIntegerv(GL_RENDERBUFFER_BINDING, &boundRb);
1118
1119         GLint object_type;
1120         glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, draw_buffer0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
1121         if (object_type == GL_RENDERBUFFER) {
1122             glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, draw_buffer0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &colorRb);
1123             glBindRenderbuffer(GL_RENDERBUFFER, colorRb);
1124             GLint samples = 0;
1125             glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
1126             if (samples) {
1127                 multisample = true;
1128             }
1129         }
1130
1131         glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
1132         if (object_type == GL_RENDERBUFFER) {
1133             glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthRb);
1134             glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
1135             GLint samples = 0;
1136             glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
1137             if (samples) {
1138                 multisample = true;
1139             }
1140         }
1141
1142         glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
1143         if (object_type == GL_RENDERBUFFER) {
1144             glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &stencilRb);
1145             glBindRenderbuffer(GL_RENDERBUFFER, stencilRb);
1146             GLint samples = 0;
1147             glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
1148             if (samples) {
1149                 multisample = true;
1150             }
1151         }
1152
1153         glBindRenderbuffer(GL_RENDERBUFFER, boundRb);
1154
1155         GLuint rbs[3];
1156         GLint numRbs = 0;
1157         GLuint fboCopy = 0;
1158
1159         if (multisample) {
1160             // glReadPixels doesnt support multisampled buffers so we need
1161             // to blit the fbo to a temporary one
1162             fboCopy = downsampledFramebuffer(context,
1163                                              boundDrawFbo, draw_buffer0,
1164                                              colorRb, depthRb, stencilRb,
1165                                              rbs, &numRbs);
1166         }
1167
1168         dumpFramebufferAttachments(json, context, GL_DRAW_FRAMEBUFFER);
1169
1170         if (multisample) {
1171             glBindRenderbuffer(GL_RENDERBUFFER_BINDING, boundRb);
1172             glDeleteRenderbuffers(numRbs, rbs);
1173             glDeleteFramebuffers(1, &fboCopy);
1174         }
1175
1176         glBindFramebuffer(GL_READ_FRAMEBUFFER, boundReadFbo);
1177         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, boundDrawFbo);
1178     }
1179
1180     json.endObject();
1181     json.endMember(); // framebuffer
1182 }
1183
1184
1185 } /* namespace glstate */