]> git.cworth.org Git - apitrace/blob - retrace/glstate_images.cpp
Merge remote-tracking branch 'jbenton/master'
[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     if (internalFormat == GL_NONE) {
822         internalFormat = format;
823     }
824
825     Context context;
826
827     json.beginObject();
828
829     // Tell the GUI this is no ordinary object, but an image
830     json.writeStringMember("__class__", "image");
831
832     json.writeNumberMember("__width__", width);
833     json.writeNumberMember("__height__", height);
834     json.writeNumberMember("__depth__", 1);
835
836     json.writeStringMember("__format__", enumToString(internalFormat));
837
838     // Hardcoded for now, but we could chose types more adequate to the
839     // texture internal format
840     json.writeStringMember("__type__", "uint8");
841     json.writeBoolMember("__normalized__", true);
842     json.writeNumberMember("__channels__", channels);
843
844     GLenum type = GL_UNSIGNED_BYTE;
845
846 #if DEPTH_AS_RGBA
847     if (format == GL_DEPTH_COMPONENT) {
848         type = GL_UNSIGNED_INT;
849         channels = 4;
850     }
851 #endif
852
853     GLubyte *pixels = new GLubyte[width*height*channels];
854
855     // TODO: reset imaging state too
856     context.resetPixelPackState();
857
858     glReadPixels(0, 0, width, height, format, type, pixels);
859
860     context.restorePixelPackState();
861
862     json.beginMember("__data__");
863     char *pngBuffer;
864     int pngBufferSize;
865     image::writePixelsToBuffer(pixels, width, height, channels, true, &pngBuffer, &pngBufferSize);
866     //std::cerr <<" Before = "<<(width * height * channels * sizeof *pixels)
867     //          <<", after = "<<pngBufferSize << ", ratio = " << double(width * height * channels * sizeof *pixels)/pngBufferSize;
868     json.writeBase64(pngBuffer, pngBufferSize);
869     free(pngBuffer);
870     json.endMember(); // __data__
871
872     delete [] pixels;
873     json.endObject();
874 }
875
876
877 static inline GLuint
878 downsampledFramebuffer(Context &context,
879                        GLuint oldFbo, GLint drawbuffer,
880                        GLint colorRb, GLint depthRb, GLint stencilRb,
881                        GLuint *rbs, GLint *numRbs)
882 {
883     GLuint fbo;
884
885
886     *numRbs = 0;
887
888     glGenFramebuffers(1, &fbo);
889     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
890
891     {
892         // color buffer
893         ImageDesc desc;
894         glBindRenderbuffer(GL_RENDERBUFFER, colorRb);
895         getBoundRenderbufferDesc(context, desc);
896
897         glGenRenderbuffers(1, &rbs[*numRbs]);
898         glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
899         glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
900         glFramebufferRenderbuffer(GL_FRAMEBUFFER, drawbuffer,
901                                   GL_RENDERBUFFER, rbs[*numRbs]);
902
903         glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
904         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
905         glDrawBuffer(drawbuffer);
906         glReadBuffer(drawbuffer);
907         glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
908                           GL_COLOR_BUFFER_BIT, GL_NEAREST);
909         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
910         ++*numRbs;
911     }
912
913     if (stencilRb == depthRb && stencilRb) {
914         //combined depth and stencil buffer
915         ImageDesc desc;
916         glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
917         getBoundRenderbufferDesc(context, desc);
918
919         glGenRenderbuffers(1, &rbs[*numRbs]);
920         glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
921         glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
922         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
923                                   GL_RENDERBUFFER, rbs[*numRbs]);
924         glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
925         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
926         glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
927                           GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
928         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
929         ++*numRbs;
930     } else {
931         if (depthRb) {
932             ImageDesc desc;
933             glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
934             getBoundRenderbufferDesc(context, desc);
935
936             glGenRenderbuffers(1, &rbs[*numRbs]);
937             glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
938             glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
939             glFramebufferRenderbuffer(GL_FRAMEBUFFER,
940                                       GL_DEPTH_ATTACHMENT,
941                                       GL_RENDERBUFFER, rbs[*numRbs]);
942             glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
943             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
944             glDrawBuffer(GL_DEPTH_ATTACHMENT);
945             glReadBuffer(GL_DEPTH_ATTACHMENT);
946             glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
947                               GL_DEPTH_BUFFER_BIT, GL_NEAREST);
948             ++*numRbs;
949         }
950         if (stencilRb) {
951             ImageDesc desc;
952             glBindRenderbuffer(GL_RENDERBUFFER, stencilRb);
953             getBoundRenderbufferDesc(context, desc);
954
955             glGenRenderbuffers(1, &rbs[*numRbs]);
956             glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
957             glRenderbufferStorage(GL_RENDERBUFFER, desc.internalFormat, desc.width, desc.height);
958             glFramebufferRenderbuffer(GL_FRAMEBUFFER,
959                                       GL_STENCIL_ATTACHMENT,
960                                       GL_RENDERBUFFER, rbs[*numRbs]);
961             glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
962             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
963             glDrawBuffer(GL_STENCIL_ATTACHMENT);
964             glReadBuffer(GL_STENCIL_ATTACHMENT);
965             glBlitFramebuffer(0, 0, desc.width, desc.height, 0, 0, desc.width, desc.height,
966                               GL_STENCIL_BUFFER_BIT, GL_NEAREST);
967             ++*numRbs;
968         }
969     }
970
971     return fbo;
972 }
973
974
975 /**
976  * Dump images of current draw drawable/window.
977  */
978 static void
979 dumpDrawableImages(JSONWriter &json, Context &context)
980 {
981     GLint width, height;
982
983     if (!getDrawableBounds(&width, &height)) {
984         return;
985     }
986
987     GLint draw_buffer = GL_NONE;
988     if (context.ES) {
989         draw_buffer = GL_BACK;
990     } else {
991         glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
992         glReadBuffer(draw_buffer);
993     }
994
995     if (draw_buffer != GL_NONE) {
996         GLint read_buffer = GL_NONE;
997         if (!context.ES) {
998             glGetIntegerv(GL_READ_BUFFER, &read_buffer);
999         }
1000
1001         GLint alpha_bits = 0;
1002 #if 0
1003         // XXX: Ignore alpha until we are able to match the traced visual
1004         glGetIntegerv(GL_ALPHA_BITS, &alpha_bits);
1005 #endif
1006         GLenum format = alpha_bits ? GL_RGBA : GL_RGB;
1007         json.beginMember(enumToString(draw_buffer));
1008         dumpReadBufferImage(json, width, height, format);
1009         json.endMember();
1010
1011         if (!context.ES) {
1012             glReadBuffer(read_buffer);
1013         }
1014     }
1015
1016     if (!context.ES) {
1017         GLint depth_bits = 0;
1018         glGetIntegerv(GL_DEPTH_BITS, &depth_bits);
1019         if (depth_bits) {
1020             json.beginMember("GL_DEPTH_COMPONENT");
1021             dumpReadBufferImage(json, width, height, GL_DEPTH_COMPONENT);
1022             json.endMember();
1023         }
1024
1025         GLint stencil_bits = 0;
1026         glGetIntegerv(GL_STENCIL_BITS, &stencil_bits);
1027         if (stencil_bits) {
1028             json.beginMember("GL_STENCIL_INDEX");
1029             dumpReadBufferImage(json, width, height, GL_STENCIL_INDEX);
1030             json.endMember();
1031         }
1032     }
1033 }
1034
1035
1036 /**
1037  * Dump the specified framebuffer attachment.
1038  *
1039  * In the case of a color attachment, it assumes it is already bound for read.
1040  */
1041 static void
1042 dumpFramebufferAttachment(JSONWriter &json, Context &context, GLenum target, GLenum attachment, GLenum format)
1043 {
1044     ImageDesc desc;
1045     if (!getFramebufferAttachmentDesc(context, target, attachment, desc)) {
1046         return;
1047     }
1048
1049     json.beginMember(enumToString(attachment));
1050     dumpReadBufferImage(json, desc.width, desc.height, format, desc.internalFormat);
1051     json.endMember();
1052 }
1053
1054
1055 static void
1056 dumpFramebufferAttachments(JSONWriter &json, Context &context, GLenum target)
1057 {
1058     GLint read_framebuffer = 0;
1059     glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
1060
1061     GLint read_buffer = GL_NONE;
1062     glGetIntegerv(GL_READ_BUFFER, &read_buffer);
1063
1064     GLint max_draw_buffers = 1;
1065     glGetIntegerv(GL_MAX_DRAW_BUFFERS, &max_draw_buffers);
1066     GLint max_color_attachments = 0;
1067     glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments);
1068
1069     for (GLint i = 0; i < max_draw_buffers; ++i) {
1070         GLint draw_buffer = GL_NONE;
1071         glGetIntegerv(GL_DRAW_BUFFER0 + i, &draw_buffer);
1072         if (draw_buffer != GL_NONE) {
1073             glReadBuffer(draw_buffer);
1074             GLint attachment;
1075             if (draw_buffer >= GL_COLOR_ATTACHMENT0 && draw_buffer < GL_COLOR_ATTACHMENT0 + max_color_attachments) {
1076                 attachment = draw_buffer;
1077             } else {
1078                 std::cerr << "warning: unexpected GL_DRAW_BUFFER" << i << " = " << draw_buffer << "\n";
1079                 attachment = GL_COLOR_ATTACHMENT0;
1080             }
1081             GLint alpha_size = 0;
1082             glGetFramebufferAttachmentParameteriv(target, attachment,
1083                                                   GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE,
1084                                                   &alpha_size);
1085             GLenum format = alpha_size ? GL_RGBA : GL_RGB;
1086             dumpFramebufferAttachment(json, context, target, attachment, format);
1087         }
1088     }
1089
1090     glReadBuffer(read_buffer);
1091
1092     if (!context.ES) {
1093         dumpFramebufferAttachment(json, context, target, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT);
1094         dumpFramebufferAttachment(json, context, target, GL_STENCIL_ATTACHMENT, GL_STENCIL_INDEX);
1095     }
1096
1097     glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
1098 }
1099
1100
1101 void
1102 dumpFramebuffer(JSONWriter &json, Context &context)
1103 {
1104     json.beginMember("framebuffer");
1105     json.beginObject();
1106
1107     GLint boundDrawFbo = 0, boundReadFbo = 0;
1108     glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &boundDrawFbo);
1109     glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &boundReadFbo);
1110     if (!boundDrawFbo) {
1111         dumpDrawableImages(json, context);
1112     } else if (context.ES) {
1113         dumpFramebufferAttachments(json, context, GL_FRAMEBUFFER);
1114     } else {
1115         GLint colorRb = 0, stencilRb = 0, depthRb = 0;
1116         GLint draw_buffer0 = GL_NONE;
1117         glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer0);
1118         bool multisample = false;
1119
1120         GLint boundRb = 0;
1121         glGetIntegerv(GL_RENDERBUFFER_BINDING, &boundRb);
1122
1123         GLint object_type;
1124         glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, draw_buffer0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
1125         if (object_type == GL_RENDERBUFFER) {
1126             glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, draw_buffer0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &colorRb);
1127             glBindRenderbuffer(GL_RENDERBUFFER, colorRb);
1128             GLint samples = 0;
1129             glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
1130             if (samples) {
1131                 multisample = true;
1132             }
1133         }
1134
1135         glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
1136         if (object_type == GL_RENDERBUFFER) {
1137             glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthRb);
1138             glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
1139             GLint samples = 0;
1140             glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
1141             if (samples) {
1142                 multisample = true;
1143             }
1144         }
1145
1146         glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
1147         if (object_type == GL_RENDERBUFFER) {
1148             glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &stencilRb);
1149             glBindRenderbuffer(GL_RENDERBUFFER, stencilRb);
1150             GLint samples = 0;
1151             glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
1152             if (samples) {
1153                 multisample = true;
1154             }
1155         }
1156
1157         glBindRenderbuffer(GL_RENDERBUFFER, boundRb);
1158
1159         GLuint rbs[3];
1160         GLint numRbs = 0;
1161         GLuint fboCopy = 0;
1162
1163         if (multisample) {
1164             // glReadPixels doesnt support multisampled buffers so we need
1165             // to blit the fbo to a temporary one
1166             fboCopy = downsampledFramebuffer(context,
1167                                              boundDrawFbo, draw_buffer0,
1168                                              colorRb, depthRb, stencilRb,
1169                                              rbs, &numRbs);
1170         }
1171
1172         dumpFramebufferAttachments(json, context, GL_DRAW_FRAMEBUFFER);
1173
1174         if (multisample) {
1175             glBindRenderbuffer(GL_RENDERBUFFER_BINDING, boundRb);
1176             glDeleteRenderbuffers(numRbs, rbs);
1177             glDeleteFramebuffers(1, &fboCopy);
1178         }
1179
1180         glBindFramebuffer(GL_READ_FRAMEBUFFER, boundReadFbo);
1181         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, boundDrawFbo);
1182     }
1183
1184     json.endObject();
1185     json.endMember(); // framebuffer
1186 }
1187
1188
1189 } /* namespace glstate */