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