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