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