]> git.cworth.org Git - apitrace/blob - glstate_images.cpp
Split image dumping to a separate source file.
[apitrace] / glstate_images.cpp
1 /**************************************************************************
2  *
3  * Copyright 2011 Jose Fonseca
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26
27 #include <string.h>
28
29 #include <algorithm>
30 #include <iostream>
31
32 #include "image.hpp"
33 #include "json.hpp"
34 #include "glproc.hpp"
35 #include "glsize.hpp"
36 #include "glstate.hpp"
37 #include "glstate_internal.hpp"
38
39
40 #ifdef __APPLE__
41
42 #include <Carbon/Carbon.h>
43
44 #ifdef __cplusplus
45 extern "C" {
46 #endif
47
48 OSStatus CGSGetSurfaceBounds(CGSConnectionID, CGWindowID, CGSSurfaceID, CGRect *);
49
50 #ifdef __cplusplus
51 }
52 #endif
53
54 #endif /* __APPLE__ */
55
56
57 /* Change thi to one to force interpreting depth buffers as RGBA, which enables
58  * visualizing full dynamic range, until we can transmit HDR images to the GUI */
59 #define DEPTH_AS_RGBA 0
60
61
62 namespace glstate {
63
64
65 static inline void
66 dumpTextureImage(JSONWriter &json, Context &context, GLenum target, GLint level)
67 {
68     GLint width, height = 1, depth = 1;
69     GLint format;
70
71     glGetTexLevelParameteriv(target, level, GL_TEXTURE_INTERNAL_FORMAT, &format);
72
73     width = 0;
74     glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &width);
75
76     if (target != GL_TEXTURE_1D) {
77         height = 0;
78         glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &height);
79         if (target == GL_TEXTURE_3D) {
80             depth = 0;
81             glGetTexLevelParameteriv(target, level, GL_TEXTURE_DEPTH, &depth);
82         }
83     }
84
85     if (width <= 0 || height <= 0 || depth <= 0) {
86         return;
87     } else {
88         char label[512];
89
90         GLint active_texture = GL_TEXTURE0;
91         glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
92         snprintf(label, sizeof label, "%s, %s, level = %d",
93                  enumToString(active_texture), enumToString(target), level);
94
95         json.beginMember(label);
96
97         json.beginObject();
98
99         // Tell the GUI this is no ordinary object, but an image
100         json.writeStringMember("__class__", "image");
101
102         json.writeNumberMember("__width__", width);
103         json.writeNumberMember("__height__", height);
104         json.writeNumberMember("__depth__", depth);
105
106         json.writeStringMember("__format__", enumToString(format));
107
108         // Hardcoded for now, but we could chose types more adequate to the
109         // texture internal format
110         json.writeStringMember("__type__", "uint8");
111         json.writeBoolMember("__normalized__", true);
112         json.writeNumberMember("__channels__", 4);
113
114         GLubyte *pixels = new GLubyte[depth*width*height*4];
115
116         context.resetPixelPackState();
117
118         glGetTexImage(target, level, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
119
120         context.restorePixelPackState();
121
122         json.beginMember("__data__");
123         char *pngBuffer;
124         int pngBufferSize;
125         image::writePixelsToBuffer(pixels, width, height, 4, true, &pngBuffer, &pngBufferSize);
126         json.writeBase64(pngBuffer, pngBufferSize);
127         free(pngBuffer);
128         json.endMember(); // __data__
129
130         delete [] pixels;
131         json.endObject();
132     }
133 }
134
135
136 static inline void
137 dumpTexture(JSONWriter &json, Context &context, GLenum target, GLenum binding)
138 {
139     GLint texture_binding = 0;
140     glGetIntegerv(binding, &texture_binding);
141     if (!glIsEnabled(target) && !texture_binding) {
142         return;
143     }
144
145     GLint level = 0;
146     do {
147         GLint width = 0, height = 0;
148         glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &width);
149         glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &height);
150         if (!width || !height) {
151             break;
152         }
153
154         if (target == GL_TEXTURE_CUBE_MAP) {
155             for (int face = 0; face < 6; ++face) {
156                 dumpTextureImage(json, context, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level);
157             }
158         } else {
159             dumpTextureImage(json, context, target, level);
160         }
161
162         ++level;
163     } while(true);
164 }
165
166
167 void
168 dumpTextures(JSONWriter &json, Context &context)
169 {
170     json.beginMember("textures");
171     json.beginObject();
172     GLint active_texture = GL_TEXTURE0;
173     glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
174     GLint max_texture_coords = 0;
175     glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);
176     GLint max_combined_texture_image_units = 0;
177     glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_combined_texture_image_units);
178     GLint max_units = std::max(max_combined_texture_image_units, max_texture_coords);
179     for (GLint unit = 0; unit < max_units; ++unit) {
180         GLenum texture = GL_TEXTURE0 + unit;
181         glActiveTexture(texture);
182         dumpTexture(json, context, GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D);
183         dumpTexture(json, context, GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D);
184         dumpTexture(json, context, GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D);
185         dumpTexture(json, context, GL_TEXTURE_RECTANGLE, GL_TEXTURE_BINDING_RECTANGLE);
186         dumpTexture(json, context, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP);
187     }
188     glActiveTexture(active_texture);
189     json.endObject();
190     json.endMember(); // textures
191 }
192
193
194 static bool
195 getDrawableBounds(GLint *width, GLint *height) {
196 #if defined(TRACE_EGL)
197
198     EGLContext currentContext = eglGetCurrentContext();
199     if (currentContext == EGL_NO_CONTEXT) {
200         return false;
201     }
202
203     EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW);
204     if (currentSurface == EGL_NO_SURFACE) {
205         return false;
206     }
207
208     EGLDisplay currentDisplay = eglGetCurrentDisplay();
209     if (currentDisplay == EGL_NO_DISPLAY) {
210         return false;
211     }
212
213     if (!eglQuerySurface(currentDisplay, currentSurface, EGL_WIDTH, width) ||
214         !eglQuerySurface(currentDisplay, currentSurface, EGL_HEIGHT, height)) {
215         return false;
216     }
217
218     return true;
219
220 #elif defined(_WIN32)
221
222     HDC hDC = wglGetCurrentDC();
223     if (!hDC) {
224         return false;
225     }
226
227     HWND hWnd = WindowFromDC(hDC);
228     RECT rect;
229
230     if (!GetClientRect(hWnd, &rect)) {
231        return false;
232     }
233
234     *width  = rect.right  - rect.left;
235     *height = rect.bottom - rect.top;
236     return true;
237
238 #elif defined(__APPLE__)
239
240     CGLContextObj ctx = CGLGetCurrentContext();
241     if (ctx == NULL) {
242         return false;
243     }
244
245     CGSConnectionID cid;
246     CGSWindowID wid;
247     CGSSurfaceID sid;
248
249     if (CGLGetSurface(ctx, &cid, &wid, &sid) != kCGLNoError) {
250         return false;
251     }
252
253     CGRect rect;
254
255     if (CGSGetSurfaceBounds(cid, wid, sid, &rect) != 0) {
256         return false;
257     }
258
259     *width = rect.size.width;
260     *height = rect.size.height;
261     return true;
262
263 #elif defined(HAVE_X11)
264
265     Display *display;
266     Drawable drawable;
267     Window root;
268     int x, y;
269     unsigned int w, h, bw, depth;
270
271     display = glXGetCurrentDisplay();
272     if (!display) {
273         return false;
274     }
275
276     drawable = glXGetCurrentDrawable();
277     if (drawable == None) {
278         return false;
279     }
280
281     if (!XGetGeometry(display, drawable, &root, &x, &y, &w, &h, &bw, &depth)) {
282         return false;
283     }
284
285     *width = w;
286     *height = h;
287     return true;
288
289 #else
290
291     return false;
292
293 #endif
294 }
295
296
297 static const GLenum texture_bindings[][2] = {
298     {GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D},
299     {GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D},
300     {GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D},
301     {GL_TEXTURE_RECTANGLE, GL_TEXTURE_BINDING_RECTANGLE},
302     {GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP}
303 };
304
305
306 static bool
307 bindTexture(GLint texture, GLenum &target, GLint &bound_texture)
308 {
309
310     for (unsigned i = 0; i < sizeof(texture_bindings)/sizeof(texture_bindings[0]); ++i) {
311         target  = texture_bindings[i][0];
312
313         GLenum binding = texture_bindings[i][1];
314
315         while (glGetError() != GL_NO_ERROR)
316             ;
317
318         glGetIntegerv(binding, &bound_texture);
319         glBindTexture(target, texture);
320
321         if (glGetError() == GL_NO_ERROR) {
322             return true;
323         }
324
325         glBindTexture(target, bound_texture);
326     }
327
328     target = GL_NONE;
329
330     return false;
331 }
332
333
334 static bool
335 getTextureLevelSize(GLint texture, GLint level, GLint *width, GLint *height)
336 {
337     *width = 0;
338     *height = 0;
339
340     GLenum target;
341     GLint bound_texture = 0;
342     if (!bindTexture(texture, target, bound_texture)) {
343         return false;
344     }
345
346     glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, width);
347     glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, height);
348
349     glBindTexture(target, bound_texture);
350
351     return *width > 0 && *height > 0;
352 }
353
354
355 static GLenum
356 getTextureLevelFormat(GLint texture, GLint level)
357 {
358     GLenum target;
359     GLint bound_texture = 0;
360     if (!bindTexture(texture, target, bound_texture)) {
361         return GL_NONE;
362     }
363
364     GLint format = GL_NONE;
365     glGetTexLevelParameteriv(target, level, GL_TEXTURE_INTERNAL_FORMAT, &format);
366
367     glBindTexture(target, bound_texture);
368
369     return format;
370 }
371
372
373
374 static bool
375 getRenderbufferSize(GLint renderbuffer, GLint *width, GLint *height)
376 {
377     GLint bound_renderbuffer = 0;
378     glGetIntegerv(GL_RENDERBUFFER_BINDING, &bound_renderbuffer);
379     glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
380
381     *width = 0;
382     *height = 0;
383     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, width);
384     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, height);
385
386     glBindRenderbuffer(GL_RENDERBUFFER, bound_renderbuffer);
387     
388     return *width > 0 && *height > 0;
389 }
390
391
392 static GLenum
393 getRenderbufferFormat(GLint renderbuffer)
394 {
395     GLint bound_renderbuffer = 0;
396     glGetIntegerv(GL_RENDERBUFFER_BINDING, &bound_renderbuffer);
397     glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
398
399     GLint format = GL_NONE;
400     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_INTERNAL_FORMAT, &format);
401
402     glBindRenderbuffer(GL_RENDERBUFFER, bound_renderbuffer);
403     
404     return format;
405 }
406
407
408 static bool
409 getFramebufferAttachmentSize(GLenum target, GLenum attachment, GLint *width, GLint *height)
410 {
411     GLint object_type = GL_NONE;
412     glGetFramebufferAttachmentParameteriv(target, attachment,
413                                           GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
414                                           &object_type);
415     if (object_type == GL_NONE) {
416         return false;
417     }
418
419     GLint object_name = 0;
420     glGetFramebufferAttachmentParameteriv(target, attachment,
421                                           GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
422                                           &object_name);
423     if (object_name == 0) {
424         return false;
425     }
426
427     if (object_type == GL_RENDERBUFFER) {
428         return getRenderbufferSize(object_name, width, height);
429     } else if (object_type == GL_TEXTURE) {
430         GLint texture_level = 0;
431         glGetFramebufferAttachmentParameteriv(target, attachment,
432                                               GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL,
433                                               &texture_level);
434         return getTextureLevelSize(object_name, texture_level, width, height);
435     } else {
436         std::cerr << "warning: unexpected GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = " << object_type << "\n";
437         return false;
438     }
439 }
440
441
442
443 static GLint
444 getFramebufferAttachmentFormat(GLenum target, GLenum attachment)
445 {
446     GLint object_type = GL_NONE;
447     glGetFramebufferAttachmentParameteriv(target, attachment,
448                                           GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
449                                           &object_type);
450     if (object_type == GL_NONE) {
451         return GL_NONE;
452     }
453
454     GLint object_name = 0;
455     glGetFramebufferAttachmentParameteriv(target, attachment,
456                                           GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
457                                           &object_name);
458     if (object_name == 0) {
459         return GL_NONE;
460     }
461
462     if (object_type == GL_RENDERBUFFER) {
463         return getRenderbufferFormat(object_name);
464     } else if (object_type == GL_TEXTURE) {
465         GLint texture_level = 0;
466         glGetFramebufferAttachmentParameteriv(target, attachment,
467                                               GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL,
468                                               &texture_level);
469         return getTextureLevelFormat(object_name, texture_level);
470     } else {
471         std::cerr << "warning: unexpected GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = " << object_type << "\n";
472         return GL_NONE;
473     }
474 }
475
476
477
478 image::Image *
479 getDrawBufferImage() {
480     GLenum format = GL_RGB;
481     GLint channels = __gl_format_channels(format);
482     if (channels > 4) {
483         return NULL;
484     }
485
486     Context context;
487
488     GLenum framebuffer_binding;
489     GLenum framebuffer_target;
490     if (context.ES) {
491         framebuffer_binding = GL_FRAMEBUFFER_BINDING;
492         framebuffer_target = GL_FRAMEBUFFER;
493     } else {
494         framebuffer_binding = GL_DRAW_FRAMEBUFFER_BINDING;
495         framebuffer_target = GL_DRAW_FRAMEBUFFER;
496     }
497
498     GLint draw_framebuffer = 0;
499     glGetIntegerv(framebuffer_binding, &draw_framebuffer);
500
501     GLint draw_buffer = GL_NONE;
502     GLint width, height;
503     if (draw_framebuffer) {
504         if (context.ARB_draw_buffers) {
505             glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer);
506             if (draw_buffer == GL_NONE) {
507                 return NULL;
508             }
509         }
510
511         if (!getFramebufferAttachmentSize(framebuffer_target, draw_buffer, &width, &height)) {
512             return NULL;
513         }
514     } else {
515         if (!context.ES) {
516             glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
517             if (draw_buffer == GL_NONE) {
518                 return NULL;
519             }
520         }
521
522         if (!getDrawableBounds(&width, &height)) {
523             return NULL;
524         }
525     }
526
527     GLenum type = GL_UNSIGNED_BYTE;
528
529 #if DEPTH_AS_RGBA
530     if (format == GL_DEPTH_COMPONENT) {
531         type = GL_UNSIGNED_INT;
532         channels = 4;
533     }
534 #endif
535
536     image::Image *image = new image::Image(width, height, channels, true);
537     if (!image) {
538         return NULL;
539     }
540
541     while (glGetError() != GL_NO_ERROR) {}
542
543     GLint read_framebuffer = 0;
544     GLint read_buffer = GL_NONE;
545     if (!context.ES) {
546         glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
547         glBindFramebuffer(GL_READ_FRAMEBUFFER, draw_framebuffer);
548
549         glGetIntegerv(GL_READ_BUFFER, &read_buffer);
550         glReadBuffer(draw_buffer);
551     }
552
553     // TODO: reset imaging state too
554     context.resetPixelPackState();
555
556     glReadPixels(0, 0, width, height, format, type, image->pixels);
557
558     context.restorePixelPackState();
559
560     if (!context.ES) {
561         glReadBuffer(read_buffer);
562         glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
563     }
564
565     GLenum error = glGetError();
566     if (error != GL_NO_ERROR) {
567         do {
568             std::cerr << "warning: " << enumToString(error) << " while getting snapshot\n";
569             error = glGetError();
570         } while(error != GL_NO_ERROR);
571         delete image;
572         return NULL;
573     }
574      
575     return image;
576 }
577
578
579 /**
580  * Dump the image of the currently bound read buffer.
581  */
582 static inline void
583 dumpReadBufferImage(JSONWriter &json, GLint width, GLint height, GLenum format,
584                     GLint internalFormat = GL_NONE)
585 {
586     GLint channels = __gl_format_channels(format);
587
588     Context context;
589
590     json.beginObject();
591
592     // Tell the GUI this is no ordinary object, but an image
593     json.writeStringMember("__class__", "image");
594
595     json.writeNumberMember("__width__", width);
596     json.writeNumberMember("__height__", height);
597     json.writeNumberMember("__depth__", 1);
598
599     json.writeStringMember("__format__", enumToString(internalFormat));
600
601     // Hardcoded for now, but we could chose types more adequate to the
602     // texture internal format
603     json.writeStringMember("__type__", "uint8");
604     json.writeBoolMember("__normalized__", true);
605     json.writeNumberMember("__channels__", channels);
606
607     GLenum type = GL_UNSIGNED_BYTE;
608
609 #if DEPTH_AS_RGBA
610     if (format == GL_DEPTH_COMPONENT) {
611         type = GL_UNSIGNED_INT;
612         channels = 4;
613     }
614 #endif
615
616     GLubyte *pixels = new GLubyte[width*height*channels];
617
618     // TODO: reset imaging state too
619     context.resetPixelPackState();
620
621     glReadPixels(0, 0, width, height, format, type, pixels);
622
623     context.restorePixelPackState();
624
625     json.beginMember("__data__");
626     char *pngBuffer;
627     int pngBufferSize;
628     image::writePixelsToBuffer(pixels, width, height, channels, true, &pngBuffer, &pngBufferSize);
629     //std::cerr <<" Before = "<<(width * height * channels * sizeof *pixels)
630     //          <<", after = "<<pngBufferSize << ", ratio = " << double(width * height * channels * sizeof *pixels)/pngBufferSize;
631     json.writeBase64(pngBuffer, pngBufferSize);
632     free(pngBuffer);
633     json.endMember(); // __data__
634
635     delete [] pixels;
636     json.endObject();
637 }
638
639
640 static inline GLuint
641 downsampledFramebuffer(GLuint oldFbo, GLint drawbuffer,
642                        GLint colorRb, GLint depthRb, GLint stencilRb,
643                        GLuint *rbs, GLint *numRbs)
644 {
645     GLuint fbo;
646     GLint format;
647     GLint w, h;
648
649     *numRbs = 0;
650
651     glGenFramebuffers(1, &fbo);
652     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
653
654     glBindRenderbuffer(GL_RENDERBUFFER, colorRb);
655     glGetRenderbufferParameteriv(GL_RENDERBUFFER,
656                                  GL_RENDERBUFFER_WIDTH, &w);
657     glGetRenderbufferParameteriv(GL_RENDERBUFFER,
658                                  GL_RENDERBUFFER_HEIGHT, &h);
659     glGetRenderbufferParameteriv(GL_RENDERBUFFER,
660                                  GL_RENDERBUFFER_INTERNAL_FORMAT, &format);
661
662     glGenRenderbuffers(1, &rbs[*numRbs]);
663     glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
664     glRenderbufferStorage(GL_RENDERBUFFER, format, w, h);
665     glFramebufferRenderbuffer(GL_FRAMEBUFFER, drawbuffer,
666                               GL_RENDERBUFFER, rbs[*numRbs]);
667
668     glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
669     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
670     glDrawBuffer(drawbuffer);
671     glReadBuffer(drawbuffer);
672     glBlitFramebuffer(0, 0, w, h, 0, 0, w, h,
673                       GL_COLOR_BUFFER_BIT, GL_NEAREST);
674     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
675     ++*numRbs;
676
677     if (stencilRb == depthRb && stencilRb) {
678         //combined depth and stencil buffer
679         glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
680         glGetRenderbufferParameteriv(GL_RENDERBUFFER,
681                                      GL_RENDERBUFFER_WIDTH, &w);
682         glGetRenderbufferParameteriv(GL_RENDERBUFFER,
683                                      GL_RENDERBUFFER_HEIGHT, &h);
684         glGetRenderbufferParameteriv(GL_RENDERBUFFER,
685                                      GL_RENDERBUFFER_INTERNAL_FORMAT, &format);
686
687         glGenRenderbuffers(1, &rbs[*numRbs]);
688         glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
689         glRenderbufferStorage(GL_RENDERBUFFER, format, w, h);
690         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
691                                   GL_RENDERBUFFER, rbs[*numRbs]);
692         glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
693         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
694         glBlitFramebuffer(0, 0, w, h, 0, 0, w, h,
695                           GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
696         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
697         ++*numRbs;
698     } else {
699         if (depthRb) {
700             glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
701             glGetRenderbufferParameteriv(GL_RENDERBUFFER,
702                                          GL_RENDERBUFFER_WIDTH, &w);
703             glGetRenderbufferParameteriv(GL_RENDERBUFFER,
704                                          GL_RENDERBUFFER_HEIGHT, &h);
705             glGetRenderbufferParameteriv(GL_RENDERBUFFER,
706                                          GL_RENDERBUFFER_INTERNAL_FORMAT, &format);
707
708             glGenRenderbuffers(1, &rbs[*numRbs]);
709             glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
710             glRenderbufferStorage(GL_RENDERBUFFER, format, w, h);
711             glFramebufferRenderbuffer(GL_FRAMEBUFFER,
712                                       GL_DEPTH_ATTACHMENT,
713                                       GL_RENDERBUFFER, rbs[*numRbs]);
714             glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
715             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
716             glDrawBuffer(GL_DEPTH_ATTACHMENT);
717             glReadBuffer(GL_DEPTH_ATTACHMENT);
718             glBlitFramebuffer(0, 0, w, h, 0, 0, w, h,
719                               GL_DEPTH_BUFFER_BIT, GL_NEAREST);
720             ++*numRbs;
721         }
722         if (stencilRb) {
723             glBindRenderbuffer(GL_RENDERBUFFER, stencilRb);
724             glGetRenderbufferParameteriv(GL_RENDERBUFFER,
725                                          GL_RENDERBUFFER_WIDTH, &w);
726             glGetRenderbufferParameteriv(GL_RENDERBUFFER,
727                                          GL_RENDERBUFFER_HEIGHT, &h);
728             glGetRenderbufferParameteriv(GL_RENDERBUFFER,
729                                      GL_RENDERBUFFER_INTERNAL_FORMAT, &format);
730
731             glGenRenderbuffers(1, &rbs[*numRbs]);
732             glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
733             glRenderbufferStorage(GL_RENDERBUFFER, format, w, h);
734             glFramebufferRenderbuffer(GL_FRAMEBUFFER,
735                                       GL_STENCIL_ATTACHMENT,
736                                       GL_RENDERBUFFER, rbs[*numRbs]);
737             glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
738             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
739             glDrawBuffer(GL_STENCIL_ATTACHMENT);
740             glReadBuffer(GL_STENCIL_ATTACHMENT);
741             glBlitFramebuffer(0, 0, w, h, 0, 0, w, h,
742                               GL_STENCIL_BUFFER_BIT, GL_NEAREST);
743             ++*numRbs;
744         }
745     }
746
747     return fbo;
748 }
749
750
751 /**
752  * Dump images of current draw drawable/window.
753  */
754 static void
755 dumpDrawableImages(JSONWriter &json, Context &context)
756 {
757     GLint width, height;
758
759     if (!getDrawableBounds(&width, &height)) {
760         return;
761     }
762
763     GLint draw_buffer = GL_NONE;
764     if (context.ES) {
765         draw_buffer = GL_BACK;
766     } else {
767         glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
768         glReadBuffer(draw_buffer);
769     }
770
771     if (draw_buffer != GL_NONE) {
772         GLint read_buffer = GL_NONE;
773         if (!context.ES) {
774             glGetIntegerv(GL_READ_BUFFER, &read_buffer);
775         }
776
777         GLint alpha_bits = 0;
778 #if 0
779         // XXX: Ignore alpha until we are able to match the traced visual
780         glGetIntegerv(GL_ALPHA_BITS, &alpha_bits);
781 #endif
782         GLenum format = alpha_bits ? GL_RGBA : GL_RGB;
783         json.beginMember(enumToString(draw_buffer));
784         dumpReadBufferImage(json, width, height, format);
785         json.endMember();
786
787         if (!context.ES) {
788             glReadBuffer(read_buffer);
789         }
790     }
791
792     if (!context.ES) {
793         GLint depth_bits = 0;
794         glGetIntegerv(GL_DEPTH_BITS, &depth_bits);
795         if (depth_bits) {
796             json.beginMember("GL_DEPTH_COMPONENT");
797             dumpReadBufferImage(json, width, height, GL_DEPTH_COMPONENT);
798             json.endMember();
799         }
800
801         GLint stencil_bits = 0;
802         glGetIntegerv(GL_STENCIL_BITS, &stencil_bits);
803         if (stencil_bits) {
804             json.beginMember("GL_STENCIL_INDEX");
805             dumpReadBufferImage(json, width, height, GL_STENCIL_INDEX);
806             json.endMember();
807         }
808     }
809 }
810
811
812 /**
813  * Dump the specified framebuffer attachment.
814  *
815  * In the case of a color attachment, it assumes it is already bound for read.
816  */
817 static void
818 dumpFramebufferAttachment(JSONWriter &json, GLenum target, GLenum attachment, GLenum format)
819 {
820     GLint width = 0, height = 0;
821     if (!getFramebufferAttachmentSize(target, attachment, &width, &height)) {
822         return;
823     }
824
825     GLint internalFormat = getFramebufferAttachmentFormat(target, attachment);
826
827     json.beginMember(enumToString(attachment));
828     dumpReadBufferImage(json, width, height, format, internalFormat);
829     json.endMember();
830 }
831
832
833 static void
834 dumpFramebufferAttachments(JSONWriter &json, Context &context, GLenum target)
835 {
836     GLint read_framebuffer = 0;
837     glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
838
839     GLint read_buffer = GL_NONE;
840     glGetIntegerv(GL_READ_BUFFER, &read_buffer);
841
842     GLint max_draw_buffers = 1;
843     glGetIntegerv(GL_MAX_DRAW_BUFFERS, &max_draw_buffers);
844     GLint max_color_attachments = 0;
845     glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments);
846
847     for (GLint i = 0; i < max_draw_buffers; ++i) {
848         GLint draw_buffer = GL_NONE;
849         glGetIntegerv(GL_DRAW_BUFFER0 + i, &draw_buffer);
850         if (draw_buffer != GL_NONE) {
851             glReadBuffer(draw_buffer);
852             GLint attachment;
853             if (draw_buffer >= GL_COLOR_ATTACHMENT0 && draw_buffer < GL_COLOR_ATTACHMENT0 + max_color_attachments) {
854                 attachment = draw_buffer;
855             } else {
856                 std::cerr << "warning: unexpected GL_DRAW_BUFFER" << i << " = " << draw_buffer << "\n";
857                 attachment = GL_COLOR_ATTACHMENT0;
858             }
859             GLint alpha_size = 0;
860             glGetFramebufferAttachmentParameteriv(target, attachment,
861                                                   GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE,
862                                                   &alpha_size);
863             GLenum format = alpha_size ? GL_RGBA : GL_RGB;
864             dumpFramebufferAttachment(json, target, attachment, format);
865         }
866     }
867
868     glReadBuffer(read_buffer);
869
870     if (!context.ES) {
871         dumpFramebufferAttachment(json, target, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT);
872         dumpFramebufferAttachment(json, target, GL_STENCIL_ATTACHMENT, GL_STENCIL_INDEX);
873     }
874
875     glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
876 }
877
878
879 void
880 dumpFramebuffer(JSONWriter &json, Context &context)
881 {
882     json.beginMember("framebuffer");
883     json.beginObject();
884
885     GLint boundDrawFbo = 0, boundReadFbo = 0;
886     glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &boundDrawFbo);
887     glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &boundReadFbo);
888     if (!boundDrawFbo) {
889         dumpDrawableImages(json, context);
890     } else if (context.ES) {
891         dumpFramebufferAttachments(json, context, GL_FRAMEBUFFER);
892     } else {
893         GLint colorRb = 0, stencilRb = 0, depthRb = 0;
894         GLint draw_buffer0 = GL_NONE;
895         glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer0);
896         bool multisample = false;
897
898         GLint boundRb = 0;
899         glGetIntegerv(GL_RENDERBUFFER_BINDING, &boundRb);
900
901         GLint object_type;
902         glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, draw_buffer0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
903         if (object_type == GL_RENDERBUFFER) {
904             glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, draw_buffer0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &colorRb);
905             glBindRenderbuffer(GL_RENDERBUFFER, colorRb);
906             GLint samples = 0;
907             glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
908             if (samples) {
909                 multisample = true;
910             }
911         }
912
913         glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
914         if (object_type == GL_RENDERBUFFER) {
915             glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthRb);
916             glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
917             GLint samples = 0;
918             glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
919             if (samples) {
920                 multisample = true;
921             }
922         }
923
924         glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type);
925         if (object_type == GL_RENDERBUFFER) {
926             glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &stencilRb);
927             glBindRenderbuffer(GL_RENDERBUFFER, stencilRb);
928             GLint samples = 0;
929             glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
930             if (samples) {
931                 multisample = true;
932             }
933         }
934
935         glBindRenderbuffer(GL_RENDERBUFFER, boundRb);
936
937         GLuint rbs[3];
938         GLint numRbs = 0;
939         GLuint fboCopy = 0;
940
941         if (multisample) {
942             // glReadPixels doesnt support multisampled buffers so we need
943             // to blit the fbo to a temporary one
944             fboCopy = downsampledFramebuffer(boundDrawFbo, draw_buffer0,
945                                              colorRb, depthRb, stencilRb,
946                                              rbs, &numRbs);
947         }
948
949         dumpFramebufferAttachments(json, context, GL_DRAW_FRAMEBUFFER);
950
951         if (multisample) {
952             glBindRenderbuffer(GL_RENDERBUFFER_BINDING, boundRb);
953             glDeleteRenderbuffers(numRbs, rbs);
954             glDeleteFramebuffers(1, &fboCopy);
955         }
956
957         glBindFramebuffer(GL_READ_FRAMEBUFFER, boundReadFbo);
958         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, boundDrawFbo);
959     }
960
961     json.endObject();
962     json.endMember(); // framebuffer
963 }
964
965
966 } /* namespace glstate */