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