]> git.cworth.org Git - apitrace/blob - glstate.cpp
19c9135b141861a7a03e7e5ca358c968dc4a735f
[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     glGetObjectParameterivARB(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 void
295 dumpUniformARB(JSONWriter &json, GLhandleARB programObj, GLint size, GLenum type, const GLchar *name) {
296
297     GLenum elemType;
298     GLint numElems;
299     __gl_uniform_size(type, elemType, numElems);
300     if (elemType == GL_NONE) {
301         return;
302     }
303
304     GLfloat fvalues[4*4];
305     GLint ivalues[4*4];
306
307     GLint i, j;
308
309     for (i = 0; i < size; ++i) {
310         std::stringstream ss;
311         ss << name;
312
313         if (size > 1) {
314             ss << '[' << i << ']';
315         }
316
317         std::string elemName = ss.str();
318
319         json.beginMember(elemName);
320
321         GLint location = glGetUniformLocationARB(programObj, elemName.c_str());
322
323         if (numElems > 1) {
324             json.beginArray();
325         }
326
327         switch (elemType) {
328         case GL_DOUBLE:
329             // glGetUniformdvARB does not exists
330         case GL_FLOAT:
331             glGetUniformfvARB(programObj, location, fvalues);
332             for (j = 0; j < numElems; ++j) {
333                 json.writeNumber(fvalues[j]);
334             }
335             break;
336         case GL_UNSIGNED_INT:
337             // glGetUniformuivARB does not exists
338         case GL_INT:
339             glGetUniformivARB(programObj, location, ivalues);
340             for (j = 0; j < numElems; ++j) {
341                 json.writeNumber(ivalues[j]);
342             }
343             break;
344         case GL_BOOL:
345             glGetUniformivARB(programObj, location, ivalues);
346             for (j = 0; j < numElems; ++j) {
347                 json.writeBool(ivalues[j]);
348             }
349             break;
350         default:
351             assert(0);
352             break;
353         }
354
355         if (numElems > 1) {
356             json.endArray();
357         }
358
359         json.endMember();
360     }
361 }
362
363
364 static inline void
365 dumpCurrentProgramUniforms(JSONWriter &json)
366 {
367     GLint program = 0;
368     glGetIntegerv(GL_CURRENT_PROGRAM, &program);
369     if (!program) {
370         return;
371     }
372
373     GLint active_uniforms = 0;
374     glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniforms);
375     if (!active_uniforms) {
376         return;
377     }
378
379     GLint active_uniform_max_length = 0;
380     glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &active_uniform_max_length);
381     GLchar *name = new GLchar[active_uniform_max_length];
382     if (!name) {
383         return;
384     }
385
386     for (GLint index = 0; index < active_uniforms; ++index) {
387         GLsizei length = 0;
388         GLint size = 0;
389         GLenum type = GL_NONE;
390         glGetActiveUniform(program, index, active_uniform_max_length, &length, &size, &type, name);
391
392         dumpUniform(json, program, size, type, name);
393     }
394
395     delete [] name;
396 }
397
398
399 static inline void
400 dumpCurrentProgramUniformsARB(JSONWriter &json)
401 {
402     GLhandleARB programObj = glGetHandleARB(GL_PROGRAM_OBJECT_ARB);
403     if (!programObj) {
404         return;
405     }
406
407     GLint active_uniforms = 0;
408     glGetObjectParameterivARB(programObj, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &active_uniforms);
409     if (!active_uniforms) {
410         return;
411     }
412
413     GLint active_uniform_max_length = 0;
414     glGetObjectParameterivARB(programObj, GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB, &active_uniform_max_length);
415     GLchar *name = new GLchar[active_uniform_max_length];
416     if (!name) {
417         return;
418     }
419
420     for (GLint index = 0; index < active_uniforms; ++index) {
421         GLsizei length = 0;
422         GLint size = 0;
423         GLenum type = GL_NONE;
424         glGetActiveUniformARB(programObj, index, active_uniform_max_length, &length, &size, &type, name);
425
426         dumpUniformARB(json, programObj, size, type, name);
427     }
428
429     delete [] name;
430 }
431
432
433 static inline void
434 dumpArbProgram(JSONWriter &json, GLenum target)
435 {
436     if (!glIsEnabled(target)) {
437         return;
438     }
439
440     GLint program_length = 0;
441     glGetProgramivARB(target, GL_PROGRAM_LENGTH_ARB, &program_length);
442     if (!program_length) {
443         return;
444     }
445
446     GLchar *source = new GLchar[program_length + 1];
447     source[0] = 0;
448     glGetProgramStringARB(target, GL_PROGRAM_STRING_ARB, source);
449     source[program_length] = 0;
450
451     json.beginMember(enumToString(target));
452     json.writeString(source);
453     json.endMember();
454
455     delete [] source;
456 }
457
458
459 static inline void
460 dumpShaders(JSONWriter &json)
461 {
462     json.beginMember("shaders");
463     json.beginObject();
464     dumpCurrentProgram(json);
465     dumpCurrentProgramObj(json);
466     dumpArbProgram(json, GL_FRAGMENT_PROGRAM_ARB);
467     dumpArbProgram(json, GL_VERTEX_PROGRAM_ARB);
468     json.endObject();
469     json.endMember(); // shaders
470 }
471
472
473 static inline void
474 dumpUniforms(JSONWriter &json)
475 {
476     json.beginMember("uniforms");
477     json.beginObject();
478     dumpCurrentProgramUniforms(json);
479     dumpCurrentProgramUniformsARB(json);
480     json.endObject();
481     json.endMember(); // uniforms
482 }
483
484
485 static inline void
486 dumpTextureImage(JSONWriter &json, GLenum target, GLint level)
487 {
488     GLint width, height = 1, depth = 1;
489
490     width = 0;
491     glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &width);
492
493     if (target != GL_TEXTURE_1D) {
494         height = 0;
495         glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &height);
496         if (target == GL_TEXTURE_3D) {
497             depth = 0;
498             glGetTexLevelParameteriv(target, level, GL_TEXTURE_DEPTH, &depth);
499         }
500     }
501
502     if (width <= 0 || height <= 0 || depth <= 0) {
503         return;
504     } else {
505         char label[512];
506
507         GLint active_texture = GL_TEXTURE0;
508         glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
509         snprintf(label, sizeof label, "%s, %s, level = %i", enumToString(active_texture), enumToString(target), level);
510
511         json.beginMember(label);
512
513         json.beginObject();
514
515         // Tell the GUI this is no ordinary object, but an image
516         json.writeStringMember("__class__", "image");
517
518         json.writeNumberMember("__width__", width);
519         json.writeNumberMember("__height__", height);
520         json.writeNumberMember("__depth__", depth);
521
522         // Hardcoded for now, but we could chose types more adequate to the
523         // texture internal format
524         json.writeStringMember("__type__", "uint8");
525         json.writeBoolMember("__normalized__", true);
526         json.writeNumberMember("__channels__", 4);
527
528         GLubyte *pixels = new GLubyte[depth*width*height*4];
529
530         resetPixelPackState();
531
532         glGetTexImage(target, level, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
533
534         restorePixelPackState();
535
536         json.beginMember("__data__");
537         char *pngBuffer;
538         int pngBufferSize;
539         Image::writePixelsToBuffer(pixels, width, height, 4, false, &pngBuffer, &pngBufferSize);
540         json.writeBase64(pngBuffer, pngBufferSize);
541         free(pngBuffer);
542         json.endMember(); // __data__
543
544         delete [] pixels;
545         json.endObject();
546     }
547 }
548
549
550 static inline void
551 dumpTexture(JSONWriter &json, GLenum target, GLenum binding)
552 {
553     GLint texture_binding = 0;
554     glGetIntegerv(binding, &texture_binding);
555     if (!glIsEnabled(target) && !texture_binding) {
556         return;
557     }
558
559     GLint level = 0;
560     do {
561         GLint width = 0, height = 0;
562         glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &width);
563         glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &height);
564         if (!width || !height) {
565             break;
566         }
567
568         if (target == GL_TEXTURE_CUBE_MAP) {
569             for (int face = 0; face < 6; ++face) {
570                 dumpTextureImage(json, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level);
571             }
572         } else {
573             dumpTextureImage(json, target, level);
574         }
575
576         ++level;
577     } while(true);
578 }
579
580
581 static inline void
582 dumpTextures(JSONWriter &json)
583 {
584     json.beginMember("textures");
585     json.beginObject();
586     GLint active_texture = GL_TEXTURE0;
587     glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
588     GLint max_texture_coords = 0;
589     glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);
590     GLint max_combined_texture_image_units = 0;
591     glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_combined_texture_image_units);
592     GLint max_units = std::max(max_combined_texture_image_units, max_texture_coords);
593     for (GLint unit = 0; unit < max_units; ++unit) {
594         GLenum texture = GL_TEXTURE0 + unit;
595         glActiveTexture(texture);
596         dumpTexture(json, GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D);
597         dumpTexture(json, GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D);
598         dumpTexture(json, GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D);
599         dumpTexture(json, GL_TEXTURE_RECTANGLE, GL_TEXTURE_BINDING_RECTANGLE);
600         dumpTexture(json, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP);
601     }
602     glActiveTexture(active_texture);
603     json.endObject();
604     json.endMember(); // textures
605 }
606
607
608 static bool
609 getDrawableBounds(GLint *width, GLint *height) {
610 #if defined(_WIN32)
611
612     HDC hDC = wglGetCurrentDC();
613     if (!hDC) {
614         return false;
615     }
616
617     HWND hWnd = WindowFromDC(hDC);
618     RECT rect;
619
620     if (!GetClientRect(hWnd, &rect)) {
621        return false;
622     }
623
624     *width  = rect.right  - rect.left;
625     *height = rect.bottom - rect.top;
626
627 #elif defined(__APPLE__)
628
629     CGLContextObj ctx = CGLGetCurrentContext();
630     if (ctx == NULL) {
631         return false;
632     }
633
634     CGSConnectionID cid;
635     CGSWindowID wid;
636     CGSSurfaceID sid;
637
638     if (CGLGetSurface(ctx, &cid, &wid, &sid) != kCGLNoError) {
639         return false;
640     }
641
642     CGRect rect;
643
644     if (CGSGetSurfaceBounds(cid, wid, sid, &rect) != 0) {
645         return false;
646     }
647
648     *width = rect.size.width;
649     *height = rect.size.height;
650
651 #else
652
653     Display *display;
654     Drawable drawable;
655     Window root;
656     int x, y;
657     unsigned int w, h, bw, depth;
658
659     display = glXGetCurrentDisplay();
660     if (!display) {
661         return false;
662     }
663
664     drawable = glXGetCurrentDrawable();
665     if (drawable == None) {
666         return false;
667     }
668
669     if (!XGetGeometry(display, drawable, &root, &x, &y, &w, &h, &bw, &depth)) {
670         return false;
671     }
672
673     *width = w;
674     *height = h;
675
676 #endif
677
678     return true;
679 }
680
681
682 static const GLenum texture_bindings[][2] = {
683     {GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D},
684     {GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D},
685     {GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D},
686     {GL_TEXTURE_RECTANGLE, GL_TEXTURE_BINDING_RECTANGLE},
687     {GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP}
688 };
689
690
691 static bool
692 bindTexture(GLint texture, GLenum &target, GLint &bound_texture)
693 {
694
695     for (unsigned i = 0; i < sizeof(texture_bindings)/sizeof(texture_bindings[0]); ++i) {
696         target  = texture_bindings[i][0];
697
698         GLenum binding = texture_bindings[i][1];
699
700         while (glGetError() != GL_NO_ERROR)
701             ;
702
703         glGetIntegerv(binding, &bound_texture);
704         glBindTexture(target, texture);
705
706         if (glGetError() == GL_NO_ERROR) {
707             return true;
708         }
709
710         glBindTexture(target, bound_texture);
711     }
712
713     target = GL_NONE;
714
715     return false;
716 }
717
718
719 static bool
720 getTextureLevelSize(GLint texture, GLint level, GLint *width, GLint *height)
721 {
722     *width = 0;
723     *height = 0;
724
725     GLenum target;
726     GLint bound_texture = 0;
727     if (!bindTexture(texture, target, bound_texture)) {
728         return false;
729     }
730
731     glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, width);
732     glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, height);
733
734     glBindTexture(target, bound_texture);
735
736     return *width > 0 && *height > 0;
737 }
738
739
740 static bool
741 getRenderbufferSize(GLint renderbuffer, GLint *width, GLint *height)
742 {
743     GLint bound_renderbuffer = 0;
744     glGetIntegerv(GL_RENDERBUFFER_BINDING, &bound_renderbuffer);
745     glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
746
747     *width = 0;
748     *height = 0;
749     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, width);
750     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, height);
751
752     glBindRenderbuffer(GL_RENDERBUFFER, bound_renderbuffer);
753     
754     return *width > 0 && *height > 0;
755 }
756
757
758 static bool
759 getFramebufferAttachmentSize(GLenum target, GLenum attachment, GLint *width, GLint *height)
760 {
761     GLint object_type = GL_NONE;
762     glGetFramebufferAttachmentParameteriv(target, attachment,
763                                           GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
764                                           &object_type);
765     if (object_type == GL_NONE) {
766         return false;
767     }
768
769     GLint object_name = 0;
770     glGetFramebufferAttachmentParameteriv(target, attachment,
771                                           GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
772                                           &object_name);
773     if (object_name == 0) {
774         return false;
775     }
776
777     if (object_type == GL_RENDERBUFFER) {
778         return getRenderbufferSize(object_name, width, height);
779     } else if (object_type == GL_TEXTURE) {
780         GLint texture_level = 0;
781         glGetFramebufferAttachmentParameteriv(target, attachment,
782                                               GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL,
783                                               &texture_level);
784         return getTextureLevelSize(object_name, texture_level, width, height);
785     } else {
786         std::cerr << "warning: unexpected GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = " << object_type << "\n";
787         return false;
788     }
789 }
790
791
792 Image::Image *
793 getDrawBufferImage(GLenum format) {
794     GLint channels = __gl_format_channels(format);
795     if (channels > 4) {
796         return NULL;
797     }
798
799     GLint draw_framebuffer = 0;
800     glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &draw_framebuffer);
801
802     GLint draw_buffer = GL_NONE;
803     GLint width, height;
804     if (draw_framebuffer) {
805         glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer);
806         if (draw_buffer == GL_NONE) {
807             return NULL;
808         }
809
810         if (!getFramebufferAttachmentSize(GL_DRAW_FRAMEBUFFER, draw_buffer, &width, &height)) {
811             return NULL;
812         }
813     } else {
814         glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
815         if (draw_buffer == GL_NONE) {
816             return NULL;
817         }
818
819         if (!getDrawableBounds(&width, &height)) {
820             return NULL;
821         }
822     }
823
824     Image::Image *image = new Image::Image(width, height, channels, true);
825     if (!image) {
826         return NULL;
827     }
828
829     while (glGetError() != GL_NO_ERROR) {}
830
831     GLint read_framebuffer = 0;
832     glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
833     glBindFramebuffer(GL_READ_FRAMEBUFFER, draw_framebuffer);
834
835     GLint read_buffer = 0;
836     glGetIntegerv(GL_READ_BUFFER, &read_buffer);
837     glReadBuffer(draw_buffer);
838
839     // TODO: reset imaging state too
840     resetPixelPackState();
841
842     glReadPixels(0, 0, width, height, format, GL_UNSIGNED_BYTE, image->pixels);
843
844     restorePixelPackState();
845     glReadBuffer(read_buffer);
846     glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
847
848     GLenum error = glGetError();
849     if (error != GL_NO_ERROR) {
850         do {
851             std::cerr << "warning: " << enumToString(error) << " while getting snapshot\n";
852             error = glGetError();
853         } while(error != GL_NO_ERROR);
854         delete image;
855         return NULL;
856     }
857      
858     return image;
859 }
860
861
862 /**
863  * Dump the image of the currently bound read buffer.
864  */
865 static inline void
866 dumpReadBufferImage(JSONWriter &json, GLint width, GLint height, GLenum format)
867 {
868     GLint channels = __gl_format_channels(format);
869
870     json.beginObject();
871
872     // Tell the GUI this is no ordinary object, but an image
873     json.writeStringMember("__class__", "image");
874
875     json.writeNumberMember("__width__", width);
876     json.writeNumberMember("__height__", height);
877     json.writeNumberMember("__depth__", 1);
878
879     // Hardcoded for now, but we could chose types more adequate to the
880     // texture internal format
881     json.writeStringMember("__type__", "uint8");
882     json.writeBoolMember("__normalized__", true);
883     json.writeNumberMember("__channels__", channels);
884
885     GLubyte *pixels = new GLubyte[width*height*channels];
886
887     // TODO: reset imaging state too
888     resetPixelPackState();
889
890     glReadPixels(0, 0, width, height, format, GL_UNSIGNED_BYTE, pixels);
891
892     restorePixelPackState();
893
894     json.beginMember("__data__");
895     char *pngBuffer;
896     int pngBufferSize;
897     Image::writePixelsToBuffer(pixels, width, height, channels, false, &pngBuffer, &pngBufferSize);
898     //std::cerr <<" Before = "<<(width * height * channels * sizeof *pixels)
899     //          <<", after = "<<pngBufferSize << ", ratio = " << double(width * height * channels * sizeof *pixels)/pngBufferSize;
900     json.writeBase64(pngBuffer, pngBufferSize);
901     free(pngBuffer);
902     json.endMember(); // __data__
903
904     delete [] pixels;
905     json.endObject();
906 }
907
908
909 static inline GLuint
910 downsampledFramebuffer(GLuint oldFbo, GLint drawbuffer,
911                        GLint colorRb, GLint depthRb, GLint stencilRb,
912                        GLuint *rbs, GLint *numRbs)
913 {
914     GLuint fbo;
915     GLint format;
916     GLint w, h;
917
918     *numRbs = 0;
919
920     glGenFramebuffers(1, &fbo);
921     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
922
923     glBindRenderbuffer(GL_RENDERBUFFER, colorRb);
924     glGetRenderbufferParameteriv(GL_RENDERBUFFER,
925                                  GL_RENDERBUFFER_WIDTH, &w);
926     glGetRenderbufferParameteriv(GL_RENDERBUFFER,
927                                  GL_RENDERBUFFER_HEIGHT, &h);
928     glGetRenderbufferParameteriv(GL_RENDERBUFFER,
929                                  GL_RENDERBUFFER_INTERNAL_FORMAT, &format);
930
931     glGenRenderbuffers(1, &rbs[*numRbs]);
932     glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
933     glRenderbufferStorage(GL_RENDERBUFFER, format, w, h);
934     glFramebufferRenderbuffer(GL_FRAMEBUFFER, drawbuffer,
935                               GL_RENDERBUFFER, rbs[*numRbs]);
936
937     glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
938     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
939     glDrawBuffer(drawbuffer);
940     glReadBuffer(drawbuffer);
941     glBlitFramebuffer(0, 0, w, h, 0, 0, w, h,
942                       GL_COLOR_BUFFER_BIT, GL_NEAREST);
943     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
944     ++*numRbs;
945
946     if (stencilRb == depthRb && stencilRb) {
947         //combined depth and stencil buffer
948         glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
949         glGetRenderbufferParameteriv(GL_RENDERBUFFER,
950                                      GL_RENDERBUFFER_WIDTH, &w);
951         glGetRenderbufferParameteriv(GL_RENDERBUFFER,
952                                      GL_RENDERBUFFER_HEIGHT, &h);
953         glGetRenderbufferParameteriv(GL_RENDERBUFFER,
954                                      GL_RENDERBUFFER_INTERNAL_FORMAT, &format);
955
956         glGenRenderbuffers(1, &rbs[*numRbs]);
957         glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
958         glRenderbufferStorage(GL_RENDERBUFFER, format, w, h);
959         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
960                                   GL_RENDERBUFFER, rbs[*numRbs]);
961         glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
962         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
963         glBlitFramebuffer(0, 0, w, h, 0, 0, w, h,
964                           GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
965         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
966         ++*numRbs;
967     } else {
968         if (depthRb) {
969             glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
970             glGetRenderbufferParameteriv(GL_RENDERBUFFER,
971                                          GL_RENDERBUFFER_WIDTH, &w);
972             glGetRenderbufferParameteriv(GL_RENDERBUFFER,
973                                          GL_RENDERBUFFER_HEIGHT, &h);
974             glGetRenderbufferParameteriv(GL_RENDERBUFFER,
975                                          GL_RENDERBUFFER_INTERNAL_FORMAT, &format);
976
977             glGenRenderbuffers(1, &rbs[*numRbs]);
978             glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
979             glRenderbufferStorage(GL_RENDERBUFFER, format, w, h);
980             glFramebufferRenderbuffer(GL_FRAMEBUFFER,
981                                       GL_DEPTH_ATTACHMENT,
982                                       GL_RENDERBUFFER, rbs[*numRbs]);
983             glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
984             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
985             glDrawBuffer(GL_DEPTH_ATTACHMENT);
986             glReadBuffer(GL_DEPTH_ATTACHMENT);
987             glBlitFramebuffer(0, 0, w, h, 0, 0, w, h,
988                               GL_DEPTH_BUFFER_BIT, GL_NEAREST);
989             ++*numRbs;
990         }
991         if (stencilRb) {
992             glBindRenderbuffer(GL_RENDERBUFFER, stencilRb);
993             glGetRenderbufferParameteriv(GL_RENDERBUFFER,
994                                          GL_RENDERBUFFER_WIDTH, &w);
995             glGetRenderbufferParameteriv(GL_RENDERBUFFER,
996                                          GL_RENDERBUFFER_HEIGHT, &h);
997             glGetRenderbufferParameteriv(GL_RENDERBUFFER,
998                                      GL_RENDERBUFFER_INTERNAL_FORMAT, &format);
999
1000             glGenRenderbuffers(1, &rbs[*numRbs]);
1001             glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
1002             glRenderbufferStorage(GL_RENDERBUFFER, format, w, h);
1003             glFramebufferRenderbuffer(GL_FRAMEBUFFER,
1004                                       GL_STENCIL_ATTACHMENT,
1005                                       GL_RENDERBUFFER, rbs[*numRbs]);
1006             glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
1007             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
1008             glDrawBuffer(GL_STENCIL_ATTACHMENT);
1009             glReadBuffer(GL_STENCIL_ATTACHMENT);
1010             glBlitFramebuffer(0, 0, w, h, 0, 0, w, h,
1011                               GL_STENCIL_BUFFER_BIT, GL_NEAREST);
1012             ++*numRbs;
1013         }
1014     }
1015
1016     return fbo;
1017 }
1018
1019
1020 /**
1021  * Dump images of current draw drawable/window.
1022  */
1023 static void
1024 dumpDrawableImages(JSONWriter &json)
1025 {
1026     GLint width, height;
1027
1028     if (!getDrawableBounds(&width, &height)) {
1029         return;
1030     }
1031
1032     GLint draw_buffer = GL_NONE;
1033     glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
1034     glReadBuffer(draw_buffer);
1035
1036     if (draw_buffer != GL_NONE) {
1037         GLint read_buffer = GL_NONE;
1038         glGetIntegerv(GL_READ_BUFFER, &read_buffer);
1039
1040         GLint alpha_bits = 0;
1041         glGetIntegerv(GL_ALPHA_BITS, &alpha_bits);
1042         GLenum format = alpha_bits ? GL_RGBA : GL_RGB;
1043         json.beginMember(enumToString(draw_buffer));
1044         dumpReadBufferImage(json, width, height, format);
1045         json.endMember();
1046
1047         glReadBuffer(read_buffer);
1048     }
1049
1050     GLint depth_bits = 0;
1051     glGetIntegerv(GL_DEPTH_BITS, &depth_bits);
1052     if (depth_bits) {
1053         json.beginMember("GL_DEPTH_COMPONENT");
1054         dumpReadBufferImage(json, width, height, GL_DEPTH_COMPONENT);
1055         json.endMember();
1056     }
1057
1058     GLint stencil_bits = 0;
1059     glGetIntegerv(GL_STENCIL_BITS, &stencil_bits);
1060     if (stencil_bits) {
1061         json.beginMember("GL_STENCIL_INDEX");
1062         dumpReadBufferImage(json, width, height, GL_STENCIL_INDEX);
1063         json.endMember();
1064     }
1065 }
1066 /**
1067  * Dump the specified framebuffer attachment.
1068  *
1069  * In the case of a color attachment, it assumes it is already bound for read.
1070  */
1071 static void
1072 dumpFramebufferAttachment(JSONWriter &json, GLenum target, GLenum attachment, GLenum format)
1073 {
1074     GLint width = 0, height = 0;
1075     if (!getFramebufferAttachmentSize(target, attachment, &width, &height)) {
1076         return;
1077     }
1078
1079     json.beginMember(enumToString(attachment));
1080     dumpReadBufferImage(json, width, height, format);
1081     json.endMember();
1082 }
1083
1084
1085 static void
1086 dumpFramebufferAttachments(JSONWriter &json, GLenum target)
1087 {
1088     GLint read_framebuffer = 0;
1089     glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
1090
1091     GLint read_buffer = GL_NONE;
1092     glGetIntegerv(GL_READ_BUFFER, &read_buffer);
1093
1094     GLint max_draw_buffers = 1;
1095     glGetIntegerv(GL_MAX_DRAW_BUFFERS, &max_draw_buffers);
1096     GLint max_color_attachments = 0;
1097     glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments);
1098
1099     for (GLint i = 0; i < max_draw_buffers; ++i) {
1100         GLint draw_buffer = GL_NONE;
1101         glGetIntegerv(GL_DRAW_BUFFER0 + i, &draw_buffer);
1102         if (draw_buffer != GL_NONE) {
1103             glReadBuffer(draw_buffer);
1104             GLint attachment;
1105             if (draw_buffer >= GL_COLOR_ATTACHMENT0 && draw_buffer < GL_COLOR_ATTACHMENT0 + max_color_attachments) {
1106                 attachment = draw_buffer;
1107             } else {
1108                 std::cerr << "warning: unexpected GL_DRAW_BUFFER" << i << " = " << draw_buffer << "\n";
1109                 attachment = GL_COLOR_ATTACHMENT0;
1110             }
1111             GLint alpha_size = 0;
1112             glGetFramebufferAttachmentParameteriv(target, attachment,
1113                                                   GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE,
1114                                                   &alpha_size);
1115             GLenum format = alpha_size ? GL_RGBA : GL_RGB;
1116             dumpFramebufferAttachment(json, target, attachment, format);
1117         }
1118     }
1119
1120     glReadBuffer(read_buffer);
1121
1122     dumpFramebufferAttachment(json, target, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT);
1123     dumpFramebufferAttachment(json, target, GL_STENCIL_ATTACHMENT, GL_STENCIL_INDEX);
1124
1125     glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
1126 }
1127
1128
1129 static void
1130 dumpFramebuffer(JSONWriter &json)
1131 {
1132     json.beginMember("framebuffer");
1133     json.beginObject();
1134
1135     GLint boundDrawFbo = 0, boundReadFbo = 0;
1136     glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &boundDrawFbo);
1137     glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &boundReadFbo);
1138     if (!boundDrawFbo) {
1139         dumpDrawableImages(json);
1140     } else {
1141         GLint colorRb = 0, stencilRb = 0, depthRb = 0;
1142         GLint draw_buffer0 = GL_NONE;
1143         glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer0);
1144         bool multisample = false;
1145
1146         GLint boundRb = 0;
1147         glGetIntegerv(GL_RENDERBUFFER_BINDING, &boundRb);
1148
1149         GLint object_type;
1150         glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, draw_buffer0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
1151         if (object_type == GL_RENDERBUFFER) {
1152             glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, draw_buffer0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &colorRb);
1153             glBindRenderbuffer(GL_RENDERBUFFER, colorRb);
1154             GLint samples = 0;
1155             glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
1156             if (samples) {
1157                 multisample = true;
1158             }
1159         }
1160
1161         glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
1162         if (object_type == GL_RENDERBUFFER) {
1163             glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthRb);
1164             glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
1165             GLint samples = 0;
1166             glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
1167             if (samples) {
1168                 multisample = true;
1169             }
1170         }
1171
1172         glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
1173         if (object_type == GL_RENDERBUFFER) {
1174             glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &stencilRb);
1175             glBindRenderbuffer(GL_RENDERBUFFER, stencilRb);
1176             GLint samples = 0;
1177             glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
1178             if (samples) {
1179                 multisample = true;
1180             }
1181         }
1182
1183         glBindRenderbuffer(GL_RENDERBUFFER, boundRb);
1184
1185         GLuint rbs[3];
1186         GLint numRbs = 0;
1187         GLuint fboCopy = 0;
1188
1189         if (multisample) {
1190             // glReadPixels doesnt support multisampled buffers so we need
1191             // to blit the fbo to a temporary one
1192             fboCopy = downsampledFramebuffer(boundDrawFbo, draw_buffer0,
1193                                              colorRb, depthRb, stencilRb,
1194                                              rbs, &numRbs);
1195         }
1196
1197         dumpFramebufferAttachments(json, GL_DRAW_FRAMEBUFFER);
1198
1199         if (multisample) {
1200             glBindRenderbuffer(GL_RENDERBUFFER_BINDING, boundRb);
1201             glDeleteRenderbuffers(numRbs, rbs);
1202             glDeleteFramebuffers(1, &fboCopy);
1203         }
1204
1205         glBindFramebuffer(GL_READ_FRAMEBUFFER, boundReadFbo);
1206         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, boundDrawFbo);
1207     }
1208
1209     json.endObject();
1210     json.endMember(); // framebuffer
1211 }
1212
1213
1214 static const GLenum bindings[] = {
1215     GL_DRAW_BUFFER,
1216     GL_READ_BUFFER,
1217     GL_PIXEL_PACK_BUFFER_BINDING,
1218     GL_PIXEL_UNPACK_BUFFER_BINDING,
1219     GL_TEXTURE_BINDING_1D,
1220     GL_TEXTURE_BINDING_2D,
1221     GL_TEXTURE_BINDING_3D,
1222     GL_TEXTURE_BINDING_RECTANGLE,
1223     GL_TEXTURE_BINDING_CUBE_MAP,
1224     GL_DRAW_FRAMEBUFFER_BINDING,
1225     GL_READ_FRAMEBUFFER_BINDING,
1226     GL_RENDERBUFFER_BINDING,
1227     GL_DRAW_BUFFER0,
1228     GL_DRAW_BUFFER1,
1229     GL_DRAW_BUFFER2,
1230     GL_DRAW_BUFFER3,
1231     GL_DRAW_BUFFER4,
1232     GL_DRAW_BUFFER5,
1233     GL_DRAW_BUFFER6,
1234     GL_DRAW_BUFFER7,
1235 };
1236
1237
1238 #define NUM_BINDINGS sizeof(bindings)/sizeof(bindings[0])
1239
1240
1241 void dumpCurrentContext(std::ostream &os)
1242 {
1243     JSONWriter json(os);
1244
1245 #ifndef NDEBUG
1246     GLint old_bindings[NUM_BINDINGS];
1247     for (unsigned i = 0; i < NUM_BINDINGS; ++i) {
1248         old_bindings[i] = 0;
1249         glGetIntegerv(bindings[i], &old_bindings[i]);
1250     }
1251 #endif
1252
1253     dumpParameters(json);
1254     dumpShaders(json);
1255     dumpUniforms(json);
1256     dumpTextures(json);
1257     dumpFramebuffer(json);
1258
1259 #ifndef NDEBUG
1260     for (unsigned i = 0; i < NUM_BINDINGS; ++i) {
1261         GLint new_binding = 0;
1262         glGetIntegerv(bindings[i], &new_binding);
1263         if (new_binding != old_bindings[i]) {
1264             std::cerr << "warning: " << enumToString(bindings[i]) << " was clobbered\n";
1265         }
1266     }
1267 #endif
1268
1269 }
1270
1271
1272 } /* namespace glstate */