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