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