]> git.cworth.org Git - apitrace/blob - glstate.cpp
Basic uniform state dumping
[apitrace] / glstate.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 #include <map>
32
33 #include "image.hpp"
34 #include "json.hpp"
35 #include "glproc.hpp"
36 #include "glsize.hpp"
37 #include "glstate.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 namespace glstate {
58
59
60 static inline void
61 resetPixelPackState(void) {
62     glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT);
63     glPixelStorei(GL_PACK_SWAP_BYTES, GL_FALSE);
64     glPixelStorei(GL_PACK_LSB_FIRST, GL_FALSE);
65     glPixelStorei(GL_PACK_ROW_LENGTH, 0);
66     glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0);
67     glPixelStorei(GL_PACK_SKIP_ROWS, 0);
68     glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
69     glPixelStorei(GL_PACK_SKIP_IMAGES, 0);
70     glPixelStorei(GL_PACK_ALIGNMENT, 1);
71     glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
72 }
73
74
75 static inline void
76 restorePixelPackState(void) {
77     glPopClientAttrib();
78 }
79
80
81 // Mapping from shader type to shader source, used to accumulated the sources
82 // of different shaders with same type.
83 typedef std::map<std::string, std::string> ShaderMap;
84
85
86 static void
87 getShaderSource(ShaderMap &shaderMap, GLuint shader)
88 {
89     if (!shader) {
90         return;
91     }
92
93     GLint shader_type = 0;
94     glGetShaderiv(shader, GL_SHADER_TYPE, &shader_type);
95     if (!shader_type) {
96         return;
97     }
98
99     GLint source_length = 0;
100     glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &source_length);
101     if (!source_length) {
102         return;
103     }
104
105     GLchar *source = new GLchar[source_length];
106     GLsizei length = 0;
107     source[0] = 0;
108     glGetShaderSource(shader, source_length, &length, source);
109
110     shaderMap[enumToString(shader_type)] += source;
111
112     delete [] source;
113 }
114
115
116 static void
117 getShaderObjSource(ShaderMap &shaderMap, GLhandleARB shaderObj)
118 {
119     if (!shaderObj) {
120         return;
121     }
122
123     GLint shader_type = 0;
124     glGetObjectParameterivARB(shaderObj, GL_OBJECT_TYPE_ARB, &shader_type);
125     if (!shader_type) {
126         return;
127     }
128
129     GLint source_length = 0;
130     glGetObjectParameterivARB(shaderObj, GL_OBJECT_SHADER_SOURCE_LENGTH_ARB, &source_length);
131     if (!source_length) {
132         return;
133     }
134
135     GLcharARB *source = new GLcharARB[source_length];
136     GLsizei length = 0;
137     source[0] = 0;
138     glGetShaderSource(shaderObj, source_length, &length, source);
139
140     shaderMap[enumToString(shader_type)] += source;
141
142     delete [] source;
143 }
144
145
146 static inline void
147 dumpCurrentProgram(JSONWriter &json)
148 {
149     GLint program = 0;
150     glGetIntegerv(GL_CURRENT_PROGRAM, &program);
151     if (!program) {
152         return;
153     }
154
155     GLint attached_shaders = 0;
156     glGetProgramiv(program, GL_ATTACHED_SHADERS, &attached_shaders);
157     if (!attached_shaders) {
158         return;
159     }
160
161     ShaderMap shaderMap;
162
163     GLuint *shaders = new GLuint[attached_shaders];
164     GLsizei count = 0;
165     glGetAttachedShaders(program, attached_shaders, &count, shaders);
166     std::sort(shaders, shaders + count);
167     for (GLsizei i = 0; i < count; ++ i) {
168        getShaderSource(shaderMap, shaders[i]);
169     }
170     delete [] shaders;
171
172     for (ShaderMap::const_iterator it = shaderMap.begin(); it != shaderMap.end(); ++it) {
173         json.beginMember(it->first);
174         json.writeString(it->second);
175         json.endMember();
176     }
177 }
178
179
180 static inline void
181 dumpCurrentProgramObj(JSONWriter &json)
182 {
183     GLhandleARB programObj = glGetHandleARB(GL_PROGRAM_OBJECT_ARB);
184     if (!programObj) {
185         return;
186     }
187
188     GLint attached_shaders = 0;
189     glGetProgramivARB(programObj, GL_OBJECT_ATTACHED_OBJECTS_ARB, &attached_shaders);
190     if (!attached_shaders) {
191         return;
192     }
193
194     ShaderMap shaderMap;
195
196     GLhandleARB *shaderObjs = new GLhandleARB[attached_shaders];
197     GLsizei count = 0;
198     glGetAttachedObjectsARB(programObj, attached_shaders, &count, shaderObjs);
199     std::sort(shaderObjs, shaderObjs + count);
200     for (GLsizei i = 0; i < count; ++ i) {
201        getShaderObjSource(shaderMap, shaderObjs[i]);
202     }
203     delete [] shaderObjs;
204
205     for (ShaderMap::const_iterator it = shaderMap.begin(); it != shaderMap.end(); ++it) {
206         json.beginMember(it->first);
207         json.writeString(it->second);
208         json.endMember();
209     }
210 }
211
212 static void
213 dumpUniform(JSONWriter &json, GLint program, GLenum type, const GLchar *name) {
214     
215     GLenum basicType;
216     GLint length;
217
218     switch (type) {
219     case GL_FLOAT_VEC4:
220         basicType = GL_FLOAT;
221         length = 4;
222         break;
223     case GL_FLOAT_MAT4:
224         basicType = GL_FLOAT;
225         length = 4*4;
226         break;
227     default:
228         json.writeNull();
229         return;
230     }
231     
232     GLint location = glGetUniformLocation(program, name);
233
234     GLfloat fvalues[4*4];
235
236     json.beginArray();
237     switch (basicType) {
238     case GL_FLOAT:
239         glGetUniformfv(program, location, fvalues);
240         for (GLint index = 0; index < length; ++index) {
241             json.writeNumber(fvalues[index]);
242         }
243         break;
244     }
245     json.endArray();
246 }
247
248 static inline void
249 dumpCurrentProgramUniforms(JSONWriter &json)
250 {
251     GLint program = 0;
252     glGetIntegerv(GL_CURRENT_PROGRAM, &program);
253     if (!program) {
254         return;
255     }
256
257     GLint active_uniforms = 0;
258     glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniforms);
259     if (!active_uniforms) {
260         return;
261     }
262
263     GLint active_uniform_max_length = 0;
264     glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &active_uniform_max_length);
265     GLchar *name = new GLchar[active_uniform_max_length];
266     if (!name) {
267         return;
268     }
269
270     for (GLint index = 0; index < active_uniforms; ++index) {
271         GLsizei length = 0;
272         GLint size = 0;
273         GLenum type = GL_NONE;
274         glGetActiveUniform(program, index, active_uniform_max_length, &length, &size, &type, name);
275
276
277         json.beginMember(name);
278         json.beginObject();
279         json.writeNumberMember("size", size);
280         json.writeStringMember("type", enumToString(type));
281         
282         json.beginMember("value");
283         dumpUniform(json, program, type, name);
284         json.endMember();
285
286         json.endObject();
287         json.endMember();
288     }
289
290     delete [] name;
291 }
292
293
294 static inline void
295 dumpArbProgram(JSONWriter &json, GLenum target)
296 {
297     if (!glIsEnabled(target)) {
298         return;
299     }
300
301     GLint program_length = 0;
302     glGetProgramivARB(target, GL_PROGRAM_LENGTH_ARB, &program_length);
303     if (!program_length) {
304         return;
305     }
306
307     GLchar *source = new GLchar[program_length + 1];
308     source[0] = 0;
309     glGetProgramStringARB(target, GL_PROGRAM_STRING_ARB, source);
310     source[program_length] = 0;
311
312     json.beginMember(enumToString(target));
313     json.writeString(source);
314     json.endMember();
315
316     delete [] source;
317 }
318
319
320 static inline void
321 dumpShaders(JSONWriter &json)
322 {
323     json.beginMember("shaders");
324     json.beginObject();
325     dumpCurrentProgram(json);
326     dumpCurrentProgramObj(json);
327     dumpArbProgram(json, GL_FRAGMENT_PROGRAM_ARB);
328     dumpArbProgram(json, GL_VERTEX_PROGRAM_ARB);
329     json.endObject();
330     json.endMember(); // shaders
331 }
332
333
334 static inline void
335 dumpUniforms(JSONWriter &json)
336 {
337     json.beginMember("uniforms");
338     json.beginObject();
339     dumpCurrentProgramUniforms(json);
340     json.endObject();
341     json.endMember(); // uniforms
342 }
343
344
345 static inline void
346 dumpTextureImage(JSONWriter &json, GLenum target, GLint level)
347 {
348     GLint width, height = 1, depth = 1;
349
350     width = 0;
351     glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &width);
352
353     if (target != GL_TEXTURE_1D) {
354         height = 0;
355         glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &height);
356         if (target == GL_TEXTURE_3D) {
357             depth = 0;
358             glGetTexLevelParameteriv(target, level, GL_TEXTURE_DEPTH, &depth);
359         }
360     }
361
362     if (width <= 0 || height <= 0 || depth <= 0) {
363         return;
364     } else {
365         char label[512];
366
367         GLint active_texture = GL_TEXTURE0;
368         glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
369         snprintf(label, sizeof label, "%s, %s, level = %i", enumToString(active_texture), enumToString(target), level);
370
371         json.beginMember(label);
372
373         json.beginObject();
374
375         // Tell the GUI this is no ordinary object, but an image
376         json.writeStringMember("__class__", "image");
377
378         json.writeNumberMember("__width__", width);
379         json.writeNumberMember("__height__", height);
380         json.writeNumberMember("__depth__", depth);
381
382         // Hardcoded for now, but we could chose types more adequate to the
383         // texture internal format
384         json.writeStringMember("__type__", "uint8");
385         json.writeBoolMember("__normalized__", true);
386         json.writeNumberMember("__channels__", 4);
387
388         GLubyte *pixels = new GLubyte[depth*width*height*4];
389
390         resetPixelPackState();
391
392         glGetTexImage(target, level, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
393
394         restorePixelPackState();
395
396         json.beginMember("__data__");
397         char *pngBuffer;
398         int pngBufferSize;
399         Image::writePixelsToBuffer(pixels, width, height, 4, false, &pngBuffer, &pngBufferSize);
400         json.writeBase64(pngBuffer, pngBufferSize);
401         free(pngBuffer);
402         json.endMember(); // __data__
403
404         delete [] pixels;
405         json.endObject();
406     }
407 }
408
409
410 static inline void
411 dumpTexture(JSONWriter &json, GLenum target, GLenum binding)
412 {
413     GLint texture_binding = 0;
414     glGetIntegerv(binding, &texture_binding);
415     if (!glIsEnabled(target) && !texture_binding) {
416         return;
417     }
418
419     GLint level = 0;
420     do {
421         GLint width = 0, height = 0;
422         glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &width);
423         glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &height);
424         if (!width || !height) {
425             break;
426         }
427
428         if (target == GL_TEXTURE_CUBE_MAP) {
429             for (int face = 0; face < 6; ++face) {
430                 dumpTextureImage(json, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level);
431             }
432         } else {
433             dumpTextureImage(json, target, level);
434         }
435
436         ++level;
437     } while(true);
438 }
439
440
441 static inline void
442 dumpTextures(JSONWriter &json)
443 {
444     json.beginMember("textures");
445     json.beginObject();
446     GLint active_texture = GL_TEXTURE0;
447     glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
448     GLint max_texture_coords = 0;
449     glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);
450     GLint max_combined_texture_image_units = 0;
451     glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_combined_texture_image_units);
452     GLint max_units = std::max(max_combined_texture_image_units, max_texture_coords);
453     for (GLint unit = 0; unit < max_units; ++unit) {
454         GLenum texture = GL_TEXTURE0 + unit;
455         glActiveTexture(texture);
456         dumpTexture(json, GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D);
457         dumpTexture(json, GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D);
458         dumpTexture(json, GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D);
459         dumpTexture(json, GL_TEXTURE_RECTANGLE, GL_TEXTURE_BINDING_RECTANGLE);
460         dumpTexture(json, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP);
461     }
462     glActiveTexture(active_texture);
463     json.endObject();
464     json.endMember(); // textures
465 }
466
467
468 static bool
469 getDrawableBounds(GLint *width, GLint *height) {
470 #if defined(_WIN32)
471
472     HDC hDC = wglGetCurrentDC();
473     if (!hDC) {
474         return false;
475     }
476
477     HWND hWnd = WindowFromDC(hDC);
478     RECT rect;
479
480     if (!GetClientRect(hWnd, &rect)) {
481        return false;
482     }
483
484     *width  = rect.right  - rect.left;
485     *height = rect.bottom - rect.top;
486
487 #elif defined(__APPLE__)
488
489     CGLContextObj ctx = CGLGetCurrentContext();
490     if (ctx == NULL) {
491         return false;
492     }
493
494     CGSConnectionID cid;
495     CGSWindowID wid;
496     CGSSurfaceID sid;
497
498     if (CGLGetSurface(ctx, &cid, &wid, &sid) != kCGLNoError) {
499         return false;
500     }
501
502     CGRect rect;
503
504     if (CGSGetSurfaceBounds(cid, wid, sid, &rect) != 0) {
505         return false;
506     }
507
508     *width = rect.size.width;
509     *height = rect.size.height;
510
511 #else
512
513     Display *display;
514     Drawable drawable;
515     Window root;
516     int x, y;
517     unsigned int w, h, bw, depth;
518
519     display = glXGetCurrentDisplay();
520     if (!display) {
521         return false;
522     }
523
524     drawable = glXGetCurrentDrawable();
525     if (drawable == None) {
526         return false;
527     }
528
529     if (!XGetGeometry(display, drawable, &root, &x, &y, &w, &h, &bw, &depth)) {
530         return false;
531     }
532
533     *width = w;
534     *height = h;
535
536 #endif
537
538     return true;
539 }
540
541
542 static const GLenum texture_bindings[][2] = {
543     {GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D},
544     {GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D},
545     {GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D},
546     {GL_TEXTURE_RECTANGLE, GL_TEXTURE_BINDING_RECTANGLE},
547     {GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP}
548 };
549
550
551 static bool
552 bindTexture(GLint texture, GLenum &target, GLint &bound_texture)
553 {
554
555     for (unsigned i = 0; i < sizeof(texture_bindings)/sizeof(texture_bindings[0]); ++i) {
556         target  = texture_bindings[i][0];
557
558         GLenum binding = texture_bindings[i][1];
559
560         while (glGetError() != GL_NO_ERROR)
561             ;
562
563         glGetIntegerv(binding, &bound_texture);
564         glBindTexture(target, texture);
565
566         if (glGetError() == GL_NO_ERROR) {
567             return true;
568         }
569
570         glBindTexture(target, bound_texture);
571     }
572
573     target = GL_NONE;
574
575     return false;
576 }
577
578
579 static bool
580 getTextureLevelSize(GLint texture, GLint level, GLint *width, GLint *height)
581 {
582     *width = 0;
583     *height = 0;
584
585     GLenum target;
586     GLint bound_texture = 0;
587     if (!bindTexture(texture, target, bound_texture)) {
588         return false;
589     }
590
591     glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, width);
592     glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, height);
593
594     glBindTexture(target, bound_texture);
595
596     return *width > 0 && *height > 0;
597 }
598
599
600 static bool
601 getRenderbufferSize(GLint renderbuffer, GLint *width, GLint *height)
602 {
603     GLint bound_renderbuffer = 0;
604     glGetIntegerv(GL_RENDERBUFFER_BINDING, &bound_renderbuffer);
605     glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
606
607     *width = 0;
608     *height = 0;
609     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, width);
610     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, height);
611
612     glBindRenderbuffer(GL_RENDERBUFFER, bound_renderbuffer);
613     
614     return *width > 0 && *height > 0;
615 }
616
617
618 static bool
619 getFramebufferAttachmentSize(GLenum target, GLenum attachment, GLint *width, GLint *height)
620 {
621     GLint object_type = GL_NONE;
622     glGetFramebufferAttachmentParameteriv(target, attachment,
623                                           GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
624                                           &object_type);
625     if (object_type == GL_NONE) {
626         return false;
627     }
628
629     GLint object_name = 0;
630     glGetFramebufferAttachmentParameteriv(target, attachment,
631                                           GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
632                                           &object_name);
633     if (object_name == 0) {
634         return false;
635     }
636
637     if (object_type == GL_RENDERBUFFER) {
638         return getRenderbufferSize(object_name, width, height);
639     } else if (object_type == GL_TEXTURE) {
640         GLint texture_level = 0;
641         glGetFramebufferAttachmentParameteriv(target, attachment,
642                                               GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL,
643                                               &texture_level);
644         return getTextureLevelSize(object_name, texture_level, width, height);
645     } else {
646         std::cerr << "warning: unexpected GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = " << object_type << "\n";
647         return false;
648     }
649 }
650
651
652 Image::Image *
653 getDrawBufferImage(GLenum format) {
654     GLint channels = __gl_format_channels(format);
655     if (channels > 4) {
656         return NULL;
657     }
658
659     GLint draw_framebuffer = 0;
660     glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &draw_framebuffer);
661
662     GLint draw_buffer = GL_NONE;
663     GLint width, height;
664     if (draw_framebuffer) {
665         glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer);
666         if (draw_buffer == GL_NONE) {
667             return NULL;
668         }
669
670         if (!getFramebufferAttachmentSize(GL_DRAW_FRAMEBUFFER, draw_buffer, &width, &height)) {
671             return NULL;
672         }
673     } else {
674         glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
675         if (draw_buffer == GL_NONE) {
676             return NULL;
677         }
678
679         if (!getDrawableBounds(&width, &height)) {
680             return NULL;
681         }
682     }
683
684     Image::Image *image = new Image::Image(width, height, channels, true);
685     if (!image) {
686         return NULL;
687     }
688
689     while (glGetError() != GL_NO_ERROR) {}
690
691     GLint read_framebuffer = 0;
692     glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
693     glBindFramebuffer(GL_READ_FRAMEBUFFER, draw_framebuffer);
694
695     GLint read_buffer = 0;
696     glGetIntegerv(GL_READ_BUFFER, &read_buffer);
697     glReadBuffer(draw_buffer);
698
699     // TODO: reset imaging state too
700     resetPixelPackState();
701
702     glReadPixels(0, 0, width, height, format, GL_UNSIGNED_BYTE, image->pixels);
703
704     restorePixelPackState();
705     glReadBuffer(read_buffer);
706     glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
707
708     GLenum error = glGetError();
709     if (error != GL_NO_ERROR) {
710         do {
711             std::cerr << "warning: " << enumToString(error) << " while getting snapshot\n";
712             error = glGetError();
713         } while(error != GL_NO_ERROR);
714         delete image;
715         return NULL;
716     }
717      
718     return image;
719 }
720
721
722 /**
723  * Dump the image of the currently bound read buffer.
724  */
725 static inline void
726 dumpReadBufferImage(JSONWriter &json, GLint width, GLint height, GLenum format)
727 {
728     GLint channels = __gl_format_channels(format);
729
730     json.beginObject();
731
732     // Tell the GUI this is no ordinary object, but an image
733     json.writeStringMember("__class__", "image");
734
735     json.writeNumberMember("__width__", width);
736     json.writeNumberMember("__height__", height);
737     json.writeNumberMember("__depth__", 1);
738
739     // Hardcoded for now, but we could chose types more adequate to the
740     // texture internal format
741     json.writeStringMember("__type__", "uint8");
742     json.writeBoolMember("__normalized__", true);
743     json.writeNumberMember("__channels__", channels);
744
745     GLubyte *pixels = new GLubyte[width*height*channels];
746
747     // TODO: reset imaging state too
748     resetPixelPackState();
749
750     glReadPixels(0, 0, width, height, format, GL_UNSIGNED_BYTE, pixels);
751
752     restorePixelPackState();
753
754     json.beginMember("__data__");
755     char *pngBuffer;
756     int pngBufferSize;
757     Image::writePixelsToBuffer(pixels, width, height, channels, false, &pngBuffer, &pngBufferSize);
758     //std::cerr <<" Before = "<<(width * height * channels * sizeof *pixels)
759     //          <<", after = "<<pngBufferSize << ", ratio = " << double(width * height * channels * sizeof *pixels)/pngBufferSize;
760     json.writeBase64(pngBuffer, pngBufferSize);
761     free(pngBuffer);
762     json.endMember(); // __data__
763
764     delete [] pixels;
765     json.endObject();
766 }
767
768
769 static inline GLuint
770 downsampledFramebuffer(GLuint oldFbo, GLint drawbuffer,
771                        GLint colorRb, GLint depthRb, GLint stencilRb,
772                        GLuint *rbs, GLint *numRbs)
773 {
774     GLuint fbo;
775     GLint format;
776     GLint w, h;
777
778     *numRbs = 0;
779
780     glGenFramebuffers(1, &fbo);
781     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
782
783     glBindRenderbuffer(GL_RENDERBUFFER, colorRb);
784     glGetRenderbufferParameteriv(GL_RENDERBUFFER,
785                                  GL_RENDERBUFFER_WIDTH, &w);
786     glGetRenderbufferParameteriv(GL_RENDERBUFFER,
787                                  GL_RENDERBUFFER_HEIGHT, &h);
788     glGetRenderbufferParameteriv(GL_RENDERBUFFER,
789                                  GL_RENDERBUFFER_INTERNAL_FORMAT, &format);
790
791     glGenRenderbuffers(1, &rbs[*numRbs]);
792     glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
793     glRenderbufferStorage(GL_RENDERBUFFER, format, w, h);
794     glFramebufferRenderbuffer(GL_FRAMEBUFFER, drawbuffer,
795                               GL_RENDERBUFFER, rbs[*numRbs]);
796
797     glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
798     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
799     glDrawBuffer(drawbuffer);
800     glReadBuffer(drawbuffer);
801     glBlitFramebuffer(0, 0, w, h, 0, 0, w, h,
802                       GL_COLOR_BUFFER_BIT, GL_NEAREST);
803     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
804     ++*numRbs;
805
806     if (stencilRb == depthRb && stencilRb) {
807         //combined depth and stencil buffer
808         glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
809         glGetRenderbufferParameteriv(GL_RENDERBUFFER,
810                                      GL_RENDERBUFFER_WIDTH, &w);
811         glGetRenderbufferParameteriv(GL_RENDERBUFFER,
812                                      GL_RENDERBUFFER_HEIGHT, &h);
813         glGetRenderbufferParameteriv(GL_RENDERBUFFER,
814                                      GL_RENDERBUFFER_INTERNAL_FORMAT, &format);
815
816         glGenRenderbuffers(1, &rbs[*numRbs]);
817         glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
818         glRenderbufferStorage(GL_RENDERBUFFER, format, w, h);
819         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
820                                   GL_RENDERBUFFER, rbs[*numRbs]);
821         glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
822         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
823         glBlitFramebuffer(0, 0, w, h, 0, 0, w, h,
824                           GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
825         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
826         ++*numRbs;
827     } else {
828         if (depthRb) {
829             glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
830             glGetRenderbufferParameteriv(GL_RENDERBUFFER,
831                                          GL_RENDERBUFFER_WIDTH, &w);
832             glGetRenderbufferParameteriv(GL_RENDERBUFFER,
833                                          GL_RENDERBUFFER_HEIGHT, &h);
834             glGetRenderbufferParameteriv(GL_RENDERBUFFER,
835                                          GL_RENDERBUFFER_INTERNAL_FORMAT, &format);
836
837             glGenRenderbuffers(1, &rbs[*numRbs]);
838             glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
839             glRenderbufferStorage(GL_RENDERBUFFER, format, w, h);
840             glFramebufferRenderbuffer(GL_FRAMEBUFFER,
841                                       GL_DEPTH_ATTACHMENT,
842                                       GL_RENDERBUFFER, rbs[*numRbs]);
843             glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
844             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
845             glDrawBuffer(GL_DEPTH_ATTACHMENT);
846             glReadBuffer(GL_DEPTH_ATTACHMENT);
847             glBlitFramebuffer(0, 0, w, h, 0, 0, w, h,
848                               GL_DEPTH_BUFFER_BIT, GL_NEAREST);
849             ++*numRbs;
850         }
851         if (stencilRb) {
852             glBindRenderbuffer(GL_RENDERBUFFER, stencilRb);
853             glGetRenderbufferParameteriv(GL_RENDERBUFFER,
854                                          GL_RENDERBUFFER_WIDTH, &w);
855             glGetRenderbufferParameteriv(GL_RENDERBUFFER,
856                                          GL_RENDERBUFFER_HEIGHT, &h);
857             glGetRenderbufferParameteriv(GL_RENDERBUFFER,
858                                      GL_RENDERBUFFER_INTERNAL_FORMAT, &format);
859
860             glGenRenderbuffers(1, &rbs[*numRbs]);
861             glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
862             glRenderbufferStorage(GL_RENDERBUFFER, format, w, h);
863             glFramebufferRenderbuffer(GL_FRAMEBUFFER,
864                                       GL_STENCIL_ATTACHMENT,
865                                       GL_RENDERBUFFER, rbs[*numRbs]);
866             glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
867             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
868             glDrawBuffer(GL_STENCIL_ATTACHMENT);
869             glReadBuffer(GL_STENCIL_ATTACHMENT);
870             glBlitFramebuffer(0, 0, w, h, 0, 0, w, h,
871                               GL_STENCIL_BUFFER_BIT, GL_NEAREST);
872             ++*numRbs;
873         }
874     }
875
876     return fbo;
877 }
878
879
880 /**
881  * Dump images of current draw drawable/window.
882  */
883 static void
884 dumpDrawableImages(JSONWriter &json)
885 {
886     GLint width, height;
887
888     if (!getDrawableBounds(&width, &height)) {
889         return;
890     }
891
892     GLint draw_buffer = GL_NONE;
893     glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
894     glReadBuffer(draw_buffer);
895
896     if (draw_buffer != GL_NONE) {
897         GLint read_buffer = GL_NONE;
898         glGetIntegerv(GL_READ_BUFFER, &read_buffer);
899
900         GLint alpha_bits = 0;
901         glGetIntegerv(GL_ALPHA_BITS, &alpha_bits);
902         GLenum format = alpha_bits ? GL_RGBA : GL_RGB;
903         json.beginMember(enumToString(draw_buffer));
904         dumpReadBufferImage(json, width, height, format);
905         json.endMember();
906
907         glReadBuffer(read_buffer);
908     }
909
910     GLint depth_bits = 0;
911     glGetIntegerv(GL_DEPTH_BITS, &depth_bits);
912     if (depth_bits) {
913         json.beginMember("GL_DEPTH_COMPONENT");
914         dumpReadBufferImage(json, width, height, GL_DEPTH_COMPONENT);
915         json.endMember();
916     }
917
918     GLint stencil_bits = 0;
919     glGetIntegerv(GL_STENCIL_BITS, &stencil_bits);
920     if (stencil_bits) {
921         json.beginMember("GL_STENCIL_INDEX");
922         dumpReadBufferImage(json, width, height, GL_STENCIL_INDEX);
923         json.endMember();
924     }
925 }
926 /**
927  * Dump the specified framebuffer attachment.
928  *
929  * In the case of a color attachment, it assumes it is already bound for read.
930  */
931 static void
932 dumpFramebufferAttachment(JSONWriter &json, GLenum target, GLenum attachment, GLenum format)
933 {
934     GLint width = 0, height = 0;
935     if (!getFramebufferAttachmentSize(target, attachment, &width, &height)) {
936         return;
937     }
938
939     json.beginMember(enumToString(attachment));
940     dumpReadBufferImage(json, width, height, format);
941     json.endMember();
942 }
943
944
945 static void
946 dumpFramebufferAttachments(JSONWriter &json, GLenum target)
947 {
948     GLint read_framebuffer = 0;
949     glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
950
951     GLint read_buffer = GL_NONE;
952     glGetIntegerv(GL_READ_BUFFER, &read_buffer);
953
954     GLint max_draw_buffers = 1;
955     glGetIntegerv(GL_MAX_DRAW_BUFFERS, &max_draw_buffers);
956     GLint max_color_attachments = 0;
957     glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments);
958
959     for (GLint i = 0; i < max_draw_buffers; ++i) {
960         GLint draw_buffer = GL_NONE;
961         glGetIntegerv(GL_DRAW_BUFFER0 + i, &draw_buffer);
962         if (draw_buffer != GL_NONE) {
963             glReadBuffer(draw_buffer);
964             GLint attachment;
965             if (draw_buffer >= GL_COLOR_ATTACHMENT0 && draw_buffer < GL_COLOR_ATTACHMENT0 + max_color_attachments) {
966                 attachment = draw_buffer;
967             } else {
968                 std::cerr << "warning: unexpected GL_DRAW_BUFFER" << i << " = " << draw_buffer << "\n";
969                 attachment = GL_COLOR_ATTACHMENT0;
970             }
971             GLint alpha_size = 0;
972             glGetFramebufferAttachmentParameteriv(target, attachment,
973                                                   GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE,
974                                                   &alpha_size);
975             GLenum format = alpha_size ? GL_RGBA : GL_RGB;
976             dumpFramebufferAttachment(json, target, attachment, format);
977         }
978     }
979
980     glReadBuffer(read_buffer);
981
982     dumpFramebufferAttachment(json, target, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT);
983     dumpFramebufferAttachment(json, target, GL_STENCIL_ATTACHMENT, GL_STENCIL_INDEX);
984
985     glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
986 }
987
988
989 static void
990 dumpFramebuffer(JSONWriter &json)
991 {
992     json.beginMember("framebuffer");
993     json.beginObject();
994
995     GLint boundDrawFbo = 0, boundReadFbo = 0;
996     glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &boundDrawFbo);
997     glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &boundReadFbo);
998     if (!boundDrawFbo) {
999         dumpDrawableImages(json);
1000     } else {
1001         GLint colorRb = 0, stencilRb = 0, depthRb = 0;
1002         GLint draw_buffer0 = GL_NONE;
1003         glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer0);
1004         bool multisample = false;
1005
1006         GLint boundRb = 0;
1007         glGetIntegerv(GL_RENDERBUFFER_BINDING, &boundRb);
1008
1009         GLint object_type;
1010         glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, draw_buffer0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
1011         if (object_type == GL_RENDERBUFFER) {
1012             glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, draw_buffer0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &colorRb);
1013             glBindRenderbuffer(GL_RENDERBUFFER, colorRb);
1014             GLint samples = 0;
1015             glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
1016             if (samples) {
1017                 multisample = true;
1018             }
1019         }
1020
1021         glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
1022         if (object_type == GL_RENDERBUFFER) {
1023             glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthRb);
1024             glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
1025             GLint samples = 0;
1026             glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
1027             if (samples) {
1028                 multisample = true;
1029             }
1030         }
1031
1032         glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
1033         if (object_type == GL_RENDERBUFFER) {
1034             glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &stencilRb);
1035             glBindRenderbuffer(GL_RENDERBUFFER, stencilRb);
1036             GLint samples = 0;
1037             glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
1038             if (samples) {
1039                 multisample = true;
1040             }
1041         }
1042
1043         glBindRenderbuffer(GL_RENDERBUFFER, boundRb);
1044
1045         GLuint rbs[3];
1046         GLint numRbs = 0;
1047         GLuint fboCopy = 0;
1048
1049         if (multisample) {
1050             // glReadPixels doesnt support multisampled buffers so we need
1051             // to blit the fbo to a temporary one
1052             fboCopy = downsampledFramebuffer(boundDrawFbo, draw_buffer0,
1053                                              colorRb, depthRb, stencilRb,
1054                                              rbs, &numRbs);
1055         }
1056
1057         dumpFramebufferAttachments(json, GL_DRAW_FRAMEBUFFER);
1058
1059         if (multisample) {
1060             glBindRenderbuffer(GL_RENDERBUFFER_BINDING, boundRb);
1061             glDeleteRenderbuffers(numRbs, rbs);
1062             glDeleteFramebuffers(1, &fboCopy);
1063         }
1064
1065         glBindFramebuffer(GL_READ_FRAMEBUFFER, boundReadFbo);
1066         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, boundDrawFbo);
1067     }
1068
1069     json.endObject();
1070     json.endMember(); // framebuffer
1071 }
1072
1073
1074 static const GLenum bindings[] = {
1075     GL_DRAW_BUFFER,
1076     GL_READ_BUFFER,
1077     GL_PIXEL_PACK_BUFFER_BINDING,
1078     GL_PIXEL_UNPACK_BUFFER_BINDING,
1079     GL_TEXTURE_BINDING_1D,
1080     GL_TEXTURE_BINDING_2D,
1081     GL_TEXTURE_BINDING_3D,
1082     GL_TEXTURE_BINDING_RECTANGLE,
1083     GL_TEXTURE_BINDING_CUBE_MAP,
1084     GL_DRAW_FRAMEBUFFER_BINDING,
1085     GL_READ_FRAMEBUFFER_BINDING,
1086     GL_RENDERBUFFER_BINDING,
1087     GL_DRAW_BUFFER0,
1088     GL_DRAW_BUFFER1,
1089     GL_DRAW_BUFFER2,
1090     GL_DRAW_BUFFER3,
1091     GL_DRAW_BUFFER4,
1092     GL_DRAW_BUFFER5,
1093     GL_DRAW_BUFFER6,
1094     GL_DRAW_BUFFER7,
1095 };
1096
1097
1098 #define NUM_BINDINGS sizeof(bindings)/sizeof(bindings[0])
1099
1100
1101 void dumpCurrentContext(std::ostream &os)
1102 {
1103     JSONWriter json(os);
1104
1105 #ifndef NDEBUG
1106     GLint old_bindings[NUM_BINDINGS];
1107     for (unsigned i = 0; i < NUM_BINDINGS; ++i) {
1108         old_bindings[i] = 0;
1109         glGetIntegerv(bindings[i], &old_bindings[i]);
1110     }
1111 #endif
1112
1113     dumpParameters(json);
1114     dumpShaders(json);
1115     dumpUniforms(json);
1116     dumpTextures(json);
1117     dumpFramebuffer(json);
1118
1119 #ifndef NDEBUG
1120     for (unsigned i = 0; i < NUM_BINDINGS; ++i) {
1121         GLint new_binding = 0;
1122         glGetIntegerv(bindings[i], &new_binding);
1123         if (new_binding != old_bindings[i]) {
1124             std::cerr << "warning: " << enumToString(bindings[i]) << " was clobbered\n";
1125         }
1126     }
1127 #endif
1128
1129 }
1130
1131
1132 } /* namespace glstate */