1 /**************************************************************************
2 *
3 * Copyright 2011-2014 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 <assert.h>
28 #include <string.h>
29
30 #include <algorithm>
31 #include <iostream>
32 #include <sstream>
33 #include <vector>
34
35 #include "image.hpp"
36 #include "state_writer.hpp"
37 #include "retrace.hpp"
38 #include "glproc.hpp"
39 #include "glsize.hpp"
40 #include "glstate.hpp"
41 #include "glstate_internal.hpp"
42
43
44 #ifdef __unix__
45 #include <dlfcn.h>
46 #endif
47
48 #ifdef __APPLE__
49
50 #include <Carbon/Carbon.h>
51
52 #ifdef __cplusplus
53 extern "C" {
54 #endif
55
56 OSStatus CGSGetSurfaceBounds(CGSConnectionID, CGWindowID, CGSSurfaceID, CGRect *);
57
58 #ifdef __cplusplus
59 }
60 #endif
61
62 #endif /* __APPLE__ */
63
64
65 namespace retrace {
66 extern bool resolveMSAA;
67 }
68
69 namespace glstate {
70
71
72 struct ImageDesc
73 {
74 GLint width;
75 GLint height;
76 GLint depth;
77 GLint samples;
78 GLint internalFormat;
79
80 inline
ImageDescglstate::ImageDesc81 ImageDesc() :
82 width(0),
83 height(0),
84 depth(0),
85 samples(0),
86 internalFormat(GL_NONE)
87 {}
88
89 inline bool
operator ==glstate::ImageDesc90 operator == (const ImageDesc &other) const {
91 return width == other.width &&
92 height == other.height &&
93 depth == other.depth &&
94 samples == other.samples &&
95 internalFormat == other.internalFormat;
96 }
97
98 inline bool
validglstate::ImageDesc99 valid(void) const {
100 return width > 0 && height > 0 && depth > 0;
101 }
102 };
103
104
105 static GLenum
106 getTextureTarget(Context &context, GLuint texture);
107
108
109 /**
110 * OpenGL ES does not support glGetTexLevelParameteriv, but it is possible to
111 * probe whether a texture has a given size by crafting a dummy glTexSubImage()
112 * call.
113 */
114 static bool
probeTextureLevelSizeOES(GLenum target,GLint level,const GLint size[3],GLenum internalFormat,GLenum type)115 probeTextureLevelSizeOES(GLenum target, GLint level, const GLint size[3],
116 GLenum internalFormat, GLenum type)
117 {
118 flushErrors();
119
120 GLint dummy = 0;
121
122 switch (target) {
123 case GL_TEXTURE_2D:
124 case GL_TEXTURE_CUBE_MAP:
125 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
126 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
127 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
128 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
129 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
130 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
131 glTexSubImage2D(target, level, size[0], size[1], 0, 0, internalFormat, type, &dummy);
132 break;
133 case GL_TEXTURE_3D_OES:
134 glTexSubImage3DOES(target, level, size[0], size[1], size[2], 0, 0, 0, internalFormat, type, &dummy);
135 break;
136 default:
137 assert(0);
138 return false;
139 }
140
141 GLenum error = glGetError();
142
143 if (0) {
144 std::cerr << "(" << size[0] << ", " << size[1] << ", " << size[2] << ") = " << enumToString(error) << "\n";
145 }
146
147 if (error == GL_NO_ERROR) {
148 return true;
149 }
150
151 flushErrors();
152
153 return false;
154 }
155
156
157 static bool
probeTextureFormatOES(GLenum target,GLint level,GLenum * internalFormat,GLenum * type)158 probeTextureFormatOES(GLenum target, GLint level,
159 GLenum *internalFormat, GLenum *type)
160 {
161 static const struct {
162 GLenum internalFormat;
163 GLenum type;
164 } info[] = {
165 /* internalFormat */ /* type */
166 { GL_RGBA, GL_UNSIGNED_BYTE },
167 { GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV },
168 { GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8 },
169 { GL_DEPTH_COMPONENT, GL_FLOAT },
170 { GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT },
171 { GL_STENCIL_INDEX, GL_UNSIGNED_BYTE },
172 /* others? */
173 { 0, 0 },
174 };
175 static const GLint size[3] = {1, 1, 1};
176
177 for (int i = 0; info[i].internalFormat; i++) {
178 if (probeTextureLevelSizeOES(target, level, size,
179 info[i].internalFormat,
180 info[i].type)) {
181 *internalFormat = info[i].internalFormat;
182 *type = info[i].type;
183 return true;
184 }
185 }
186
187 return false;
188 }
189
190
191 /**
192 * Bisect the texture size along an axis.
193 *
194 * It is assumed that the texture exists.
195 */
196 static GLint
bisectTextureLevelSizeOES(GLenum target,GLint level,GLint axis,GLint max,GLenum internalFormat,GLenum type)197 bisectTextureLevelSizeOES(GLenum target, GLint level, GLint axis, GLint max,
198 GLenum internalFormat, GLenum type)
199 {
200 GLint size[3] = {0, 0, 0};
201
202 assert(axis < 3);
203 assert(max >= 0);
204
205 GLint min = 0;
206 while (true) {
207 GLint test = (min + max) / 2;
208 if (test == min) {
209 return min;
210 }
211
212 size[axis] = test;
213
214 if (probeTextureLevelSizeOES(target, level, size, internalFormat, type)) {
215 min = test;
216 } else {
217 max = test;
218 }
219 }
220 }
221
222
223 /**
224 * Special path to obtain texture size on OpenGL ES, that does not rely on
225 * glGetTexLevelParameteriv
226 */
227 static bool
getActiveTextureLevelDescOES(Context & context,GLenum target,GLint level,ImageDesc & desc)228 getActiveTextureLevelDescOES(Context &context, GLenum target, GLint level, ImageDesc &desc)
229 {
230 if (target == GL_TEXTURE_1D) {
231 // OpenGL ES does not support 1D textures
232 return false;
233 }
234 GLenum internalFormat, type;
235
236 if (!probeTextureFormatOES(target, level, &internalFormat, &type)) {
237 return false;
238 }
239
240 desc.internalFormat = internalFormat;
241
242 GLint maxSize = 0;
243 switch (target) {
244 case GL_TEXTURE_2D:
245 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
246 desc.width = bisectTextureLevelSizeOES(target, level, 0, maxSize, internalFormat, type);
247 desc.height = bisectTextureLevelSizeOES(target, level, 1, maxSize, internalFormat, type);
248 desc.depth = 1;
249 break;
250 case GL_TEXTURE_CUBE_MAP:
251 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
252 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
253 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
254 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
255 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
256 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
257 glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &maxSize);
258 desc.width = bisectTextureLevelSizeOES(target, level, 0, maxSize, internalFormat, type);
259 desc.height = desc.width;
260 desc.depth = 1;
261 break;
262 case GL_TEXTURE_3D_OES:
263 glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE_OES, &maxSize);
264 desc.width = bisectTextureLevelSizeOES(target, level, 0, maxSize, internalFormat, type);
265 desc.width = bisectTextureLevelSizeOES(target, level, 1, maxSize, internalFormat, type);
266 desc.depth = bisectTextureLevelSizeOES(target, level, 2, maxSize, internalFormat, type);
267 break;
268 default:
269 return false;
270 }
271
272 if (0) {
273 std::cerr
274 << enumToString(target) << " "
275 << level << " "
276 << desc.width << "x" << desc.height << "x" << desc.depth
277 << "\n";
278 }
279
280 return desc.valid();
281 }
282
283
284 static inline bool
getActiveTextureLevelDesc(Context & context,GLenum target,GLint level,ImageDesc & desc)285 getActiveTextureLevelDesc(Context &context, GLenum target, GLint level, ImageDesc &desc)
286 {
287 assert(target != GL_TEXTURE_CUBE_MAP);
288
289 if (context.ES) {
290 return getActiveTextureLevelDescOES(context, target, level, desc);
291 }
292
293 if (target == GL_TEXTURE_BUFFER) {
294 assert(level == 0);
295
296 GLint buffer = 0;
297 glGetIntegerv(GL_TEXTURE_BUFFER_DATA_STORE_BINDING, &buffer);
298 if (!buffer) {
299 return false;
300 }
301
302 // This is the general binding point, not the texture's
303 GLint active_buffer = 0;
304 glGetIntegerv(GL_TEXTURE_BUFFER, &active_buffer);
305 glBindBuffer(GL_TEXTURE_BUFFER, buffer);
306
307 GLint buffer_size = 0;
308 glGetBufferParameteriv(GL_TEXTURE_BUFFER, GL_BUFFER_SIZE, &buffer_size);
309
310 glBindBuffer(GL_TEXTURE_BUFFER, active_buffer);
311
312 glGetIntegerv(GL_TEXTURE_BUFFER_FORMAT_ARB, &desc.internalFormat);
313
314 const InternalFormatDesc &formatDesc = getInternalFormatDesc(desc.internalFormat);
315 if (formatDesc.type == GL_NONE) {
316 std::cerr << "error: unexpected GL_TEXTURE_BUFFER internal format "
317 << enumToString(desc.internalFormat)
318 << " (https://github.com/apitrace/apitrace/issues/426)\n";
319 return false;
320 }
321
322 unsigned bits_per_pixel = _gl_format_size(formatDesc.format, formatDesc.type);
323
324 desc.width = buffer_size * 8 / bits_per_pixel;
325 desc.height = 1;
326 desc.depth = 1;
327
328 return desc.valid();
329 }
330
331 glGetTexLevelParameteriv(target, level, GL_TEXTURE_INTERNAL_FORMAT, &desc.internalFormat);
332
333 desc.width = 0;
334 glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &desc.width);
335
336 if (target == GL_TEXTURE_BUFFER ||
337 target == GL_TEXTURE_1D) {
338 desc.height = 1;
339 desc.depth = 1;
340 } else {
341 desc.height = 0;
342 glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &desc.height);
343 if (target != GL_TEXTURE_3D &&
344 target != GL_TEXTURE_2D_ARRAY &&
345 target != GL_TEXTURE_2D_MULTISAMPLE_ARRAY &&
346 target != GL_TEXTURE_CUBE_MAP_ARRAY) {
347 desc.depth = 1;
348 } else {
349 desc.depth = 0;
350 glGetTexLevelParameteriv(target, level, GL_TEXTURE_DEPTH, &desc.depth);
351 }
352 }
353
354 glGetTexLevelParameteriv(target, level, GL_TEXTURE_SAMPLES, &desc.samples);
355
356 return desc.valid();
357 }
358
359
360 const GLenum
361 textureTargets[] = {
362 GL_TEXTURE_2D,
363 GL_TEXTURE_1D,
364 GL_TEXTURE_RECTANGLE,
365 GL_TEXTURE_CUBE_MAP,
366 GL_TEXTURE_3D,
367 GL_TEXTURE_2D_MULTISAMPLE,
368 GL_TEXTURE_2D_ARRAY,
369 GL_TEXTURE_2D_MULTISAMPLE_ARRAY,
370 GL_TEXTURE_1D_ARRAY,
371 GL_TEXTURE_CUBE_MAP_ARRAY,
372 GL_TEXTURE_BUFFER,
373 };
374
375 const unsigned
376 numTextureTargets = ARRAYSIZE(textureTargets);
377
378
379 GLenum
getTextureBinding(GLenum target)380 getTextureBinding(GLenum target)
381 {
382 switch (target) {
383 case GL_TEXTURE_1D:
384 return GL_TEXTURE_BINDING_1D;
385 case GL_TEXTURE_1D_ARRAY:
386 return GL_TEXTURE_BINDING_1D_ARRAY;
387 case GL_TEXTURE_2D:
388 return GL_TEXTURE_BINDING_2D;
389 case GL_TEXTURE_2D_ARRAY:
390 return GL_TEXTURE_BINDING_2D_ARRAY;
391 case GL_TEXTURE_2D_MULTISAMPLE:
392 return GL_TEXTURE_BINDING_2D_MULTISAMPLE;
393 case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
394 return GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY;
395 case GL_TEXTURE_RECTANGLE:
396 return GL_TEXTURE_BINDING_RECTANGLE;
397 case GL_TEXTURE_CUBE_MAP:
398 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
399 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
400 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
401 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
402 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
403 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
404 return GL_TEXTURE_BINDING_CUBE_MAP;
405 case GL_TEXTURE_CUBE_MAP_ARRAY:
406 return GL_TEXTURE_BINDING_CUBE_MAP_ARRAY;
407 case GL_TEXTURE_3D:
408 return GL_TEXTURE_BINDING_3D;
409 case GL_TEXTURE_BUFFER:
410 return GL_TEXTURE_BINDING_BUFFER;
411 default:
412 assert(false);
413 return GL_NONE;
414 }
415 }
416
417
418 /**
419 * OpenGL ES does not support glGetTexImage. Obtain the pixels by attaching the
420 * texture to a framebuffer.
421 */
422 static inline void
getTexImageOES(GLenum target,GLint level,GLenum format,GLenum type,ImageDesc & desc,GLubyte * pixels)423 getTexImageOES(GLenum target, GLint level, GLenum format, GLenum type,
424 ImageDesc &desc, GLubyte *pixels)
425 {
426 memset(pixels, 0x80, desc.height * desc.width * 4);
427
428 GLenum texture_binding = getTextureBinding(target);
429 if (texture_binding == GL_NONE) {
430 return;
431 }
432
433 GLint texture = 0;
434 glGetIntegerv(texture_binding, &texture);
435 if (!texture) {
436 return;
437 }
438
439 GLint prev_fbo = 0;
440 GLuint fbo = 0;
441 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prev_fbo);
442 glGenFramebuffers(1, &fbo);
443 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
444
445 GLenum status;
446
447 switch (target) {
448 case GL_TEXTURE_2D:
449 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
450 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
451 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
452 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
453 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
454 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
455 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, level);
456 status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
457 if (status != GL_FRAMEBUFFER_COMPLETE) {
458 std::cerr << __FUNCTION__ << ": " << enumToString(status) << "\n";
459 }
460 glReadPixels(0, 0, desc.width, desc.height, format, type, pixels);
461 break;
462 case GL_TEXTURE_3D_OES:
463 for (int i = 0; i < desc.depth; i++) {
464 glFramebufferTexture3D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_3D, texture, level, i);
465 glReadPixels(0, 0, desc.width, desc.height, format, type, pixels + 4 * i * desc.width * desc.height);
466 }
467 break;
468 }
469
470 glBindFramebuffer(GL_FRAMEBUFFER, prev_fbo);
471
472 glDeleteFramebuffers(1, &fbo);
473 }
474
475 /**
476 * Simple Shader compile
477 */
478 static inline int
compileShader(const GLchar * shaderSource,GLenum shaderType)479 compileShader(const GLchar* shaderSource, GLenum shaderType )
480 {
481
482 GLuint id;
483 GLint result;
484 int logLength;
485 char logInfo[1000];
486
487 id = glCreateShader(shaderType);
488 glShaderSource(id, 1, (const GLchar**)&shaderSource, NULL);
489 glCompileShader(id);
490 glGetShaderiv(id, GL_COMPILE_STATUS, &result);
491
492 if (result == GL_FALSE) {
493 glGetShaderiv(id, GL_INFO_LOG_LENGTH, &logLength);
494 glGetShaderInfoLog(id, logLength, NULL, &logInfo[0]);
495 std::cerr << std::endl << logInfo << std::endl;
496 return -1;
497 }
498
499 return id;
500 }
501
502 /**
503 * Program Creation / Linking.
504 */
505 static inline void
createProgram(GLuint program_id,const GLchar * vShaderSource,const GLchar * pShaderSource)506 createProgram(GLuint program_id, const GLchar* vShaderSource, const GLchar* pShaderSource)
507 {
508 GLint result;
509 int logLength;
510 char logInfo[1000];
511
512 GLint vshaderID = compileShader(vShaderSource, GL_VERTEX_SHADER);
513 GLint pshaderID = compileShader(pShaderSource, GL_FRAGMENT_SHADER);
514
515 glAttachShader(program_id, vshaderID);
516 glAttachShader(program_id, pshaderID);
517 glLinkProgram(program_id);
518 glGetProgramiv(program_id, GL_LINK_STATUS, &result);
519
520 if (result == GL_FALSE) {
521 glGetShaderiv(program_id, GL_INFO_LOG_LENGTH, &logLength);
522 glGetShaderInfoLog(program_id, logLength, NULL, &logInfo[0]);
523 std::cerr << std::endl << logInfo << std::endl;
524 }
525
526 glDeleteShader(vshaderID);
527 glDeleteShader(pshaderID);
528 return;
529 }
530
531 /**
532 * Obtain unresolved/resolved MSAA surface by attaching the
533 * texture to a framebuffer and using a multisample texel fetch inside custom shader.
534 */
535 static inline void
getTexImageMSAA(GLenum target,GLenum format,GLenum type,ImageDesc & desc,GLubyte * pixels,bool resolve)536 getTexImageMSAA(GLenum target, GLenum format, GLenum type,
537 ImageDesc &desc, GLubyte *pixels, bool resolve)
538 {
539 TempId prog = TempId(GL_PROGRAM);
540 TempId vao = TempId(GL_VERTEX_ARRAY);
541 TempId vbo = TempId(GL_ARRAY_BUFFER);
542
543 /*
544 * Vertices for 2 tri strip
545 */
546 const GLint channels = 4;
547 const GLint vertices = 4;
548 static const float vertArray[vertices][channels] = {
549 { 1.0f, -1.0f, 0.0f, 1.0f },
550 { 1.0f, 1.0f, 0.0f, 1.0f },
551 { -1.0f, -1.0f, 0.0f, 1.0f },
552 { -1.0f, 1.0f, 0.0f, 1.0f },
553 };
554
555 /*
556 * Create an empty texture + fbo of correct size.
557 */
558 TempId tex = TempId(GL_TEXTURE);
559 TextureBinding bt = TextureBinding(GL_TEXTURE_2D, tex.ID());
560
561 TempId fbo = TempId(GL_FRAMEBUFFER);
562 BufferBinding fb = BufferBinding(GL_FRAMEBUFFER, fbo.ID());
563 BufferBinding Db = BufferBinding(GL_DRAW_BUFFER, GL_COLOR_ATTACHMENT0);
564 BufferBinding Rb = BufferBinding(GL_READ_BUFFER, GL_COLOR_ATTACHMENT0);
565
566 GLint savedProgram;
567 glGetIntegerv(GL_CURRENT_PROGRAM, &savedProgram);
568
569 GLint savedViewport[4];
570 glGetIntegerv(GL_VIEWPORT, savedViewport);
571 GLuint viewport_height = desc.height;
572
573 if (resolve){
574
575 /*
576 * Create blitting program to perform resolve.
577 */
578
579 const GLchar* const vShaderSource =
580 "in vec4 Position;\n "
581 "void main() {\n "
582 " gl_Position = Position;\n "
583 "}\n ";
584
585 const GLchar* const pShaderSource =
586 "#version 430\n; "
587 "uniform ivec3 texDim; // Width, Height, samples\n "
588 "uniform sampler2DMS Sampler;\n "
589 "layout(location = 0) out vec4 FragColor;\n "
590 "void main() {\n "
591 " ivec2 coord = ivec2(gl_FragCoord.xy);\n "
592 " coord.x = texDim.x - coord.x - 1;\n "
593 " vec4 outColor = { 0, 0, 0, 0 }; \n "
594 " for (int i=0; i<int(texDim.z); i++) { "
595 " outColor += texelFetch(Sampler, coord, i);\n "
596 " } \n "
597 " FragColor = outColor / float(texDim.z); \n "
598 "}\n ";
599
600 createProgram(prog.ID(), vShaderSource, pShaderSource);
601 glUseProgram(prog.ID());
602
603 /*
604 * Pass uniform for texture dimensions and number of samples
605 */
606 GLint myLoc = glGetUniformLocation(prog.ID(), "texDim");
607 glProgramUniform3i(prog.ID(), myLoc, desc.width, desc.height, desc.samples);
608
609 } else { /* no resolve */
610
611 /*
612 * Read out each individual sample surface with border for MSAA surface
613 * Create blitting program to copy unresolved samples into tall texture.
614 */
615 const GLchar* const vShaderSource =
616 "in vec4 Position;\n "
617 "void main() {\n "
618 " gl_Position = Position;\n "
619 "}\n ";
620
621 const GLchar* const pShaderSource =
622 "#version 430\n; "
623 "uniform ivec2 texDim; // Width, Height\n "
624 "uniform sampler2DMS Sampler;\n "
625 "layout(location = 0) out vec4 FragColor;\n "
626 "void main() {\n "
627 " ivec2 coord = ivec2(gl_FragCoord.xy);\n "
628 " int height = int(gl_FragCoord.y / texDim.y);\n "
629 " coord.y = (coord.y % texDim.y);\n "
630 " coord.x = texDim.x - coord.x - 1;\n "
631 " FragColor = texelFetch(Sampler, coord, height);\n "
632 "}\n ";
633
634
635 createProgram(prog.ID(), vShaderSource, pShaderSource);
636 glUseProgram(prog.ID());
637
638 /*
639 * Pass uniform for texture dimensions and viewport height
640 * Add bottom border to each sample window only (in-between samples)
641 */
642 viewport_height = desc.height * desc.samples;
643 GLint myLoc = glGetUniformLocation(prog.ID(), "texDim");
644 glProgramUniform2i(prog.ID(), myLoc, desc.width, desc.height);
645 }
646
647 glViewport(0, 0, desc.width, viewport_height);
648 glBufferData(GL_ARRAY_BUFFER, sizeof(vertArray), vertArray, GL_STATIC_DRAW);
649 glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
650 glEnableVertexAttribArray(0);
651
652 glTexImage2D(GL_TEXTURE_2D, 0, desc.internalFormat, desc.width, viewport_height, 0, format, type, NULL);
653
654 GLuint clearBufferBit;
655 switch(format)
656 {
657 case GL_DEPTH_COMPONENT:
658 glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, bt.ID(), 0);
659 clearBufferBit = GL_DEPTH_BUFFER_BIT;
660 break;
661 case GL_STENCIL_INDEX:
662 glFramebufferTexture(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, bt.ID(), 0);
663 clearBufferBit = GL_STENCIL_BUFFER_BIT;
664 break;
665 default:
666 glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, bt.ID(), 0);
667 clearBufferBit = GL_COLOR_BUFFER_BIT;
668 break;
669 }
670
671 GLuint result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
672 if (result != GL_FRAMEBUFFER_COMPLETE)
673 {
674 std::cerr << "Framebuffer not done..." << std::endl;
675 goto exit_clean;
676 }
677
678 glClearColor(0.1f, 0.2f, 0.3f, 1.0f);
679 glClear( clearBufferBit );
680
681 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
682 glReadPixels(0, 0, desc.width, viewport_height, format, type, pixels);
683
684 exit_clean:
685 // Put back state
686 glUseProgram(savedProgram);
687 glDisableVertexAttribArray(0);
688 glViewport(savedViewport[0],savedViewport[1],savedViewport[2],savedViewport[3]);
689
690 return;
691 }
692
693 static inline void
dumpActiveTextureLevel(StateWriter & writer,Context & context,GLenum target,GLint level,const std::string & label,const char * userLabel)694 dumpActiveTextureLevel(StateWriter &writer, Context &context,
695 GLenum target, GLint level,
696 const std::string & label,
697 const char *userLabel)
698 {
699
700 bool multiSample = (target == GL_TEXTURE_2D_MULTISAMPLE
701 || target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) ? true : false;
702
703 ImageDesc desc;
704 if (!getActiveTextureLevelDesc(context, target, level, desc)) {
705 return;
706 }
707
708 const InternalFormatDesc &formatDesc = getInternalFormatDesc(desc.internalFormat);
709
710 GLenum format;
711 GLenum type;
712 const PixelFormat *pixelFormat = nullptr;
713
714 if (target == GL_TEXTURE_BUFFER) {
715 pixelFormat = getPixelFormat(desc.internalFormat);
716 if (!pixelFormat) {
717 std::cerr << "warning: unsupported texture buffer internal format " << formatToString(desc.internalFormat) << "\n";
718 return;
719 }
720 format = GL_RGBA;
721 type = GL_FLOAT;
722 } else {
723 chooseReadBackFormat(formatDesc, format, type);
724 }
725
726 writer.beginMember(label);
727
728 if (context.ES && format == GL_DEPTH_COMPONENT) {
729 format = GL_RED;
730 }
731
732 GLuint channels;
733 image::ChannelType channelType;
734 getImageFormat(format, type, channels, channelType);
735
736 if (0) {
737 std::cerr << std::endl << std::endl;
738 std::cerr << label << ", " << userLabel << std::endl;
739 std::cerr << enumToString(desc.internalFormat) << ", " << enumToString(format) << ", " << enumToString(type) << std::endl;
740 std::cerr << "desc (w,h,d) (" << desc.width << ", " << desc.height << ", " << desc.depth << "), "
741 << "channels: " << channels << ", channelType: " << channelType << std::endl;
742 }
743
744 image::Image *image;
745 PixelPackState pps(context);
746
747 if (target == GL_TEXTURE_BUFFER) {
748 assert(desc.height == 1);
749 assert(desc.depth == 1);
750 assert(pixelFormat);
751 assert(format == GL_RGBA);
752 assert(type == GL_FLOAT);
753
754 image = new image::Image(desc.width, desc.height * desc.depth, channels, true, channelType);
755 assert(image->bytesPerPixel == sizeof(float[4]));
756
757 GLint buffer = 0;
758 glGetIntegerv(GL_TEXTURE_BUFFER_DATA_STORE_BINDING, &buffer);
759 assert(buffer);
760
761 BufferMapping bm;
762
763 const GLvoid *map = bm.map(GL_TEXTURE_BUFFER, buffer);
764 if (map) {
765 pixelFormat->unpackSpan(static_cast<const uint8_t *>(map),
766 reinterpret_cast<float *>(image->pixels), image->width);
767 }
768 } else if (multiSample) {
769 // Perform multisample retrieval here.
770
771 if (retrace::resolveMSAA){
772 // For resolved MSAA...
773 image = new image::Image(desc.width, desc.height, channels, true, channelType);
774 memset(image->pixels, 0x0, desc.width * desc.height * sizeof(int));
775 getTexImageMSAA(target, format, type, desc, image->pixels, true);
776 }
777 else {
778 // For unresolved MSAA...
779 GLuint samples = std::max(desc.samples, 1);
780 GLuint total_height = desc.height * samples;
781 image = new image::Image(desc.width, total_height, channels, true, channelType);
782 memset(image->pixels, 0x0, desc.width * total_height * sizeof(int));
783 getTexImageMSAA(target, format, type, desc, image->pixels, false);
784 }
785 }
786 else {
787 // Create a simple image single sample size.
788 image = new image::Image(desc.width, desc.height * desc.depth, channels, true, channelType);
789
790 if (context.ES) {
791 getTexImageOES(target, level, format, type, desc, image->pixels);
792 } else {
793 glGetTexImage(target, level, format, type, image->pixels);
794 }
795 }
796
797 if (userLabel) {
798 image->label = userLabel;
799 }
800
801 StateWriter::ImageDesc imageDesc;
802 imageDesc.depth = desc.depth;
803 imageDesc.format = formatToString(desc.internalFormat);
804 writer.writeImage(image, imageDesc);
805
806 delete image;
807
808 writer.endMember(); // label
809 }
810
811
812 static inline void
dumpActiveTexture(StateWriter & writer,Context & context,GLenum target,GLuint texture)813 dumpActiveTexture(StateWriter &writer, Context &context, GLenum target, GLuint texture)
814 {
815 char *object_label = getObjectLabel(context, GL_TEXTURE, texture);
816
817 GLint active_texture = GL_TEXTURE0;
818 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
819 assert(active_texture >= GL_TEXTURE0);
820
821 GLenum start_subtarget;
822 GLenum stop_subtarget;
823 if (target == GL_TEXTURE_CUBE_MAP) {
824 start_subtarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
825 stop_subtarget = start_subtarget + 6;
826 } else {
827 start_subtarget = target;
828 stop_subtarget = start_subtarget + 1;
829 }
830
831 GLboolean allowMipmaps = target != GL_TEXTURE_RECTANGLE &&
832 target != GL_TEXTURE_BUFFER &&
833 target != GL_TEXTURE_2D_MULTISAMPLE &&
834 target != GL_TEXTURE_2D_MULTISAMPLE_ARRAY;
835
836 GLint level = 0;
837 do {
838 ImageDesc desc;
839
840 for (GLenum subtarget = start_subtarget; subtarget < stop_subtarget; ++subtarget) {
841 std::stringstream label;
842 label << "GL_TEXTURE" << (active_texture - GL_TEXTURE0) << ", "
843 << enumToString(subtarget);
844 if (allowMipmaps) {
845 label << ", level = " << level;
846 }
847
848 if (!getActiveTextureLevelDesc(context, subtarget, level, desc)) {
849 goto finished;
850 }
851 dumpActiveTextureLevel(writer, context, subtarget, level, label.str(), object_label);
852 }
853
854 if (!allowMipmaps) {
855 // no mipmaps
856 break;
857 }
858
859 ++level;
860 } while(true);
861
862 finished:
863 free(object_label);
864 }
865
866 static void
dumpTextureImages(StateWriter & writer,Context & context)867 dumpTextureImages(StateWriter &writer, Context &context)
868 {
869 if (!context.ARB_shader_image_load_store) {
870 return;
871 }
872
873 GLint maxImageUnits = 0;
874 glGetIntegerv(GL_MAX_IMAGE_UNITS, &maxImageUnits);
875 glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT);
876
877 for (GLint imageUnit = 0; imageUnit < maxImageUnits; ++imageUnit) {
878 GLint texture = 0;
879 glGetIntegeri_v(GL_IMAGE_BINDING_NAME, imageUnit, &texture);
880 if (texture) {
881 GLint level = 0;
882 glGetIntegeri_v(GL_IMAGE_BINDING_LEVEL, imageUnit, &level);
883 GLint isLayered = 0;
884 glGetIntegeri_v(GL_IMAGE_BINDING_LAYERED, imageUnit, &isLayered);
885 GLint layer = 0;
886 glGetIntegeri_v(GL_IMAGE_BINDING_LAYER, imageUnit, &layer);
887 std::stringstream label;
888 label << "Image Unit " << imageUnit;
889 if (level) {
890 label << ", level = " << level;
891 }
892 if (isLayered) {
893 label << ", layer = " << layer;
894 }
895 GLenum target = getTextureTarget(context, texture);
896 GLint previousTexture = 0;
897 glGetIntegerv(getTextureBinding(target), &previousTexture);
898
899 glBindTexture(target, texture);
900 char *object_label = getObjectLabel(context, GL_TEXTURE, texture);
901 dumpActiveTextureLevel(writer, context, target, level, label.str(),
902 object_label);
903 free(object_label);
904 glBindTexture(target, previousTexture);
905 }
906 }
907 }
908
909
910 void
dumpTextures(StateWriter & writer,Context & context)911 dumpTextures(StateWriter &writer, Context &context)
912 {
913 writer.beginMember("textures");
914 writer.beginObject();
915
916 GLint max_texture_coords = 0;
917 if (!context.core) {
918 glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);
919 }
920
921 GLint max_combined_texture_image_units = 0;
922 glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_combined_texture_image_units);
923
924 /*
925 * At least the Android software GL implementation doesn't return the
926 * proper value for this, but rather returns 0. The GL(ES) specification
927 * mandates a minimum value of 2, so use this as a fall-back value.
928 */
929 max_combined_texture_image_units = std::max(max_combined_texture_image_units, 2);
930
931 GLint max_units = std::max(max_combined_texture_image_units, max_texture_coords);
932
933 GLint active_texture = GL_TEXTURE0;
934 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
935
936 for (GLint unit = 0; unit < max_units; ++unit) {
937 GLenum texture = GL_TEXTURE0 + unit;
938 glActiveTexture(texture);
939
940 for (auto target : textureTargets) {
941
942 // Whether this fixed-function stage is enabled
943 GLboolean enabled = GL_FALSE;
944 if (unit < max_texture_coords &&
945 (target == GL_TEXTURE_1D ||
946 target == GL_TEXTURE_2D ||
947 target == GL_TEXTURE_3D ||
948 target == GL_TEXTURE_CUBE_MAP ||
949 target == GL_TEXTURE_RECTANGLE)) {
950 glGetBooleanv(target, &enabled);
951 }
952
953 // Whether a texture object is bound
954 GLint texture = 0;
955 if (unit < max_combined_texture_image_units) {
956 GLenum binding = getTextureBinding(target);
957 glGetIntegerv(binding, &texture);
958 }
959
960 if (enabled || texture) {
961 dumpActiveTexture(writer, context, target, texture);
962 }
963 }
964 }
965
966 glActiveTexture(active_texture);
967
968 dumpTextureImages(writer, context);
969
970 writer.endObject();
971 writer.endMember(); // textures
972 }
973
974
975 bool
getDrawableBounds(GLint * width,GLint * height)976 getDrawableBounds(GLint *width, GLint *height) {
977 #if defined(__unix__)
978 if (_getPublicProcAddress("eglGetCurrentContext")) {
979 EGLContext currentContext = eglGetCurrentContext();
980 if (currentContext != EGL_NO_CONTEXT) {
981 EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW);
982 if (currentSurface == EGL_NO_SURFACE) {
983 return false;
984 }
985
986 EGLDisplay currentDisplay = eglGetCurrentDisplay();
987 if (currentDisplay == EGL_NO_DISPLAY) {
988 return false;
989 }
990
991 if (!eglQuerySurface(currentDisplay, currentSurface, EGL_WIDTH, width) ||
992 !eglQuerySurface(currentDisplay, currentSurface, EGL_HEIGHT, height)) {
993 return false;
994 }
995
996 return true;
997 }
998 }
999 #endif
1000
1001 #if defined(_WIN32)
1002
1003 HDC hDC = wglGetCurrentDC();
1004 if (!hDC) {
1005 return false;
1006 }
1007
1008 HWND hWnd = WindowFromDC(hDC);
1009 RECT rect;
1010
1011 if (!GetClientRect(hWnd, &rect)) {
1012 return false;
1013 }
1014
1015 *width = rect.right - rect.left;
1016 *height = rect.bottom - rect.top;
1017 return true;
1018
1019 #elif defined(__APPLE__)
1020
1021 CGLContextObj ctx = CGLGetCurrentContext();
1022 if (ctx == NULL) {
1023 return false;
1024 }
1025
1026 CGSConnectionID cid;
1027 CGSWindowID wid;
1028 CGSSurfaceID sid;
1029
1030 if (CGLGetSurface(ctx, &cid, &wid, &sid) != kCGLNoError) {
1031 return false;
1032 }
1033
1034 CGRect rect;
1035
1036 if (CGSGetSurfaceBounds(cid, wid, sid, &rect) != 0) {
1037 return false;
1038 }
1039
1040 *width = rect.size.width;
1041 *height = rect.size.height;
1042 return true;
1043
1044 #elif defined(HAVE_X11)
1045
1046 Display *display;
1047 GLXDrawable drawable;
1048
1049 display = glXGetCurrentDisplay();
1050 if (!display) {
1051 return false;
1052 }
1053
1054 drawable = glXGetCurrentDrawable();
1055 if (drawable == None) {
1056 return false;
1057 }
1058
1059 int major = 0, minor = 0;
1060 if (!glXQueryVersion(display, &major, &minor)) {
1061 return false;
1062 }
1063
1064 // XGetGeometry will fail for PBuffers, whereas glXQueryDrawable should not.
1065 unsigned w = 0;
1066 unsigned h = 0;
1067 if (major > 1 || (major == 1 && minor >= 3)) {
1068 glXQueryDrawable(display, drawable, GLX_WIDTH, &w);
1069 glXQueryDrawable(display, drawable, GLX_HEIGHT, &h);
1070 } else {
1071 Window root;
1072 int x, y;
1073 unsigned bw, depth;
1074 XGetGeometry(display, drawable, &root, &x, &y, &w, &h, &bw, &depth);
1075 }
1076
1077 *width = w;
1078 *height = h;
1079 return true;
1080
1081 #else
1082
1083 return false;
1084
1085 #endif
1086 }
1087
1088
1089 static GLenum
getTextureTarget(Context & context,GLuint texture)1090 getTextureTarget(Context &context, GLuint texture)
1091 {
1092 if (!glIsTexture(texture)) {
1093 return GL_NONE;
1094 }
1095
1096 /*
1097 * GL_ARB_direct_state_access's glGetTextureParameteriv(GL_TEXTURE_TARGET)
1098 * would be perfect, except the fact it's not supported for
1099 * TEXTURE_BUFFERS...
1100 */
1101
1102 GLint result = GL_NONE;
1103
1104 flushErrors();
1105
1106 // Temporarily disable debug messages
1107 GLDEBUGPROC prevDebugCallbackFunction = 0;
1108 void *prevDebugCallbackUserParam = 0;
1109 if (context.KHR_debug) {
1110 glGetPointerv(GL_DEBUG_CALLBACK_FUNCTION, (GLvoid **) &prevDebugCallbackFunction);
1111 glGetPointerv(GL_DEBUG_CALLBACK_USER_PARAM, &prevDebugCallbackUserParam);
1112 glDebugMessageCallback(NULL, NULL);
1113 }
1114
1115 for (auto target : textureTargets) {
1116 GLenum binding = getTextureBinding(target);
1117
1118 GLint bound_texture = 0;
1119 glGetIntegerv(binding, &bound_texture);
1120 glBindTexture(target, texture);
1121
1122 bool succeeded = glGetError() == GL_NO_ERROR;
1123
1124 glBindTexture(target, bound_texture);
1125
1126 if (succeeded) {
1127 result = target;
1128 break;
1129 }
1130
1131 flushErrors();
1132 }
1133
1134 if (context.KHR_debug) {
1135 glDebugMessageCallback(prevDebugCallbackFunction, prevDebugCallbackUserParam);
1136 }
1137
1138 return result;
1139 }
1140
1141
1142 static bool
getBoundRenderbufferDesc(Context & context,ImageDesc & desc)1143 getBoundRenderbufferDesc(Context &context, ImageDesc &desc)
1144 {
1145 glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &desc.width);
1146 glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &desc.height);
1147 desc.depth = 1;
1148
1149 glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &desc.samples);
1150
1151 glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_INTERNAL_FORMAT, &desc.internalFormat);
1152
1153 return desc.valid();
1154 }
1155
1156
1157 static bool
getRenderbufferDesc(Context & context,GLint renderbuffer,ImageDesc & desc)1158 getRenderbufferDesc(Context &context, GLint renderbuffer, ImageDesc &desc)
1159 {
1160 GLint bound_renderbuffer = 0;
1161 glGetIntegerv(GL_RENDERBUFFER_BINDING, &bound_renderbuffer);
1162 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1163
1164 getBoundRenderbufferDesc(context, desc);
1165
1166 glBindRenderbuffer(GL_RENDERBUFFER, bound_renderbuffer);
1167
1168 return desc.valid();
1169 }
1170
1171
1172 static bool
getFramebufferAttachmentDesc(Context & context,GLenum target,GLenum attachment,ImageDesc & desc)1173 getFramebufferAttachmentDesc(Context &context, GLenum target, GLenum attachment, ImageDesc &desc)
1174 {
1175 GLint object_type = GL_NONE;
1176 glGetFramebufferAttachmentParameteriv(target, attachment,
1177 GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
1178 &object_type);
1179 if (object_type == GL_NONE) {
1180 return false;
1181 }
1182
1183 GLint object_name = 0;
1184 glGetFramebufferAttachmentParameteriv(target, attachment,
1185 GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
1186 &object_name);
1187 if (object_name == 0) {
1188 return false;
1189 }
1190
1191 if (object_type == GL_RENDERBUFFER) {
1192 return getRenderbufferDesc(context, object_name, desc);
1193 } else if (object_type == GL_TEXTURE) {
1194 GLint texture_face = 0;
1195 glGetFramebufferAttachmentParameteriv(target, attachment,
1196 GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE,
1197 &texture_face);
1198
1199 GLint texture_level = 0;
1200 glGetFramebufferAttachmentParameteriv(target, attachment,
1201 GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL,
1202 &texture_level);
1203
1204 GLint bound_texture = 0;
1205 if (texture_face != 0) {
1206 glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &bound_texture);
1207 glBindTexture(GL_TEXTURE_CUBE_MAP, object_name);
1208 getActiveTextureLevelDesc(context, texture_face, texture_level, desc);
1209 glBindTexture(GL_TEXTURE_CUBE_MAP, bound_texture);
1210 } else {
1211 GLenum texture_target = getTextureTarget(context, object_name);
1212 GLenum texture_binding = getTextureBinding(texture_target);
1213 glGetIntegerv(texture_binding, &bound_texture);
1214 glBindTexture(texture_target, object_name);
1215 getActiveTextureLevelDesc(context, texture_target, texture_level, desc);
1216 glBindTexture(texture_target, bound_texture);
1217 }
1218
1219 return desc.valid();
1220 } else {
1221 std::cerr << "warning: unexpected GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = " << enumToString(object_type) << "\n";
1222 return false;
1223 }
1224 }
1225
1226
1227 int
getDrawBufferImageCount()1228 getDrawBufferImageCount()
1229 {
1230 Context context;
1231 GLint count;
1232
1233 if (context.framebuffer_object) {
1234 glGetIntegerv(GL_MAX_DRAW_BUFFERS, &count);
1235 flushErrors();
1236 } else {
1237 return 0;
1238 }
1239
1240 return count;
1241 }
1242
1243
1244 image::Image *
getDrawBufferImage(int n)1245 getDrawBufferImage(int n)
1246 {
1247 Context context;
1248
1249 GLenum format = GL_RGB;
1250 GLenum type = GL_UNSIGNED_BYTE;
1251 if (context.ES) {
1252 format = GL_RGBA;
1253 if (n < 0 && !context.NV_read_depth_stencil) {
1254 return nullptr;
1255 }
1256 }
1257
1258 GLenum framebuffer_binding;
1259 GLenum framebuffer_target;
1260 if (context.read_framebuffer_object) {
1261 framebuffer_binding = GL_DRAW_FRAMEBUFFER_BINDING;
1262 framebuffer_target = GL_DRAW_FRAMEBUFFER;
1263 } else {
1264 framebuffer_binding = GL_FRAMEBUFFER_BINDING;
1265 framebuffer_target = GL_FRAMEBUFFER;
1266 }
1267 GLint draw_framebuffer = 0;
1268 if (context.framebuffer_object) {
1269 glGetIntegerv(framebuffer_binding, &draw_framebuffer);
1270 }
1271
1272 /*
1273 * TODO: Use alpha for non-FBOs once we are able to match the traced
1274 * visuals.
1275 */
1276 if (draw_framebuffer) {
1277 format = GL_RGBA;
1278 }
1279
1280 if (n == -2) {
1281 /* read stencil */
1282 format = GL_STENCIL_INDEX;
1283 n = 0;
1284 } else if (n == -1) {
1285 /* read depth */
1286 format = GL_DEPTH_COMPONENT;
1287 n = 0;
1288 }
1289
1290 GLint draw_buffer = GL_NONE;
1291 ImageDesc desc;
1292 if (draw_framebuffer) {
1293 if (context.ARB_draw_buffers) {
1294 glGetIntegerv(GL_DRAW_BUFFER0 + n, &draw_buffer);
1295 if (draw_buffer == GL_NONE) {
1296 return NULL;
1297 }
1298 } else {
1299 // GL_COLOR_ATTACHMENT0 is implied
1300 draw_buffer = GL_COLOR_ATTACHMENT0 + n;
1301 }
1302
1303 if (!getFramebufferAttachmentDesc(context, framebuffer_target, draw_buffer, desc)) {
1304 return NULL;
1305 }
1306 } else if (n == 0) {
1307 if (context.ES) {
1308 // XXX: Draw buffer is always FRONT for single buffer context, BACK
1309 // for double buffered contexts. There is no way to know which (as
1310 // GL_DOUBLEBUFFER state is also unavailable), so always assume
1311 // double-buffering.
1312 draw_buffer = GL_BACK;
1313 } else {
1314 glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
1315 if (draw_buffer == GL_NONE) {
1316 return NULL;
1317 }
1318 }
1319
1320 if (!getDrawableBounds(&desc.width, &desc.height)) {
1321 return NULL;
1322 }
1323
1324 desc.depth = 1;
1325 } else {
1326 return NULL;
1327 }
1328
1329 GLint channels = _gl_format_channels(format);
1330 if (channels > 4) {
1331 return NULL;
1332 }
1333
1334 image::ChannelType channelType = image::TYPE_UNORM8;
1335
1336 if (format == GL_DEPTH_COMPONENT) {
1337 type = GL_FLOAT;
1338 channels = 1;
1339 channelType = image::TYPE_FLOAT;
1340 }
1341
1342 image::Image *image = new image::Image(desc.width, desc.height, channels, true, channelType);
1343 if (!image) {
1344 return NULL;
1345 }
1346
1347 flushErrors();
1348
1349 GLint read_framebuffer = 0;
1350 GLint read_buffer = GL_NONE;
1351 if (context.read_framebuffer_object) {
1352 glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
1353 glBindFramebuffer(GL_READ_FRAMEBUFFER, draw_framebuffer);
1354 }
1355
1356 if (context.read_buffer) {
1357 glGetIntegerv(GL_READ_BUFFER, &read_buffer);
1358 glReadBuffer(draw_buffer);
1359 }
1360
1361 {
1362 // TODO: reset imaging state too
1363 PixelPackState pps(context);
1364 glReadPixels(0, 0, desc.width, desc.height, format, type, image->pixels);
1365 }
1366
1367
1368 if (context.read_buffer) {
1369 glReadBuffer(read_buffer);
1370 }
1371 if (context.read_framebuffer_object) {
1372 glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
1373 }
1374
1375 GLenum error = glGetError();
1376 if (error != GL_NO_ERROR) {
1377 do {
1378 std::cerr << "warning: " << enumToString(error) << " while getting snapshot\n";
1379 error = glGetError();
1380 } while(error != GL_NO_ERROR);
1381 delete image;
1382 return NULL;
1383 }
1384
1385 return image;
1386 }
1387
1388
1389 /**
1390 * Dump the image of the currently bound read buffer.
1391 */
1392 static inline void
dumpReadBufferImage(StateWriter & writer,Context & context,const char * label,const char * userLabel,GLint width,GLint height,GLenum format,GLenum type,GLenum internalFormat=GL_NONE)1393 dumpReadBufferImage(StateWriter &writer,
1394 Context & context,
1395 const char *label,
1396 const char *userLabel,
1397 GLint width, GLint height,
1398 GLenum format, GLenum type,
1399 GLenum internalFormat = GL_NONE)
1400 {
1401 if (internalFormat == GL_NONE) {
1402 internalFormat = format;
1403 }
1404
1405 if (context.ES) {
1406 switch (format) {
1407 case GL_DEPTH_COMPONENT:
1408 case GL_DEPTH_STENCIL:
1409 if (!context.NV_read_depth_stencil) {
1410 return;
1411 }
1412 /* FIXME: NV_read_depth_stencil states that type must match the
1413 * internal format, whereas we always request GL_FLOAT, as that's
1414 * what we want. To fix this we should probe the adequate type
1415 * here, and then manually convert the pixels to float after
1416 * glReadPixels */
1417 break;
1418 case GL_STENCIL_INDEX:
1419 if (!context.NV_read_depth_stencil) {
1420 return;
1421 }
1422 type = GL_UNSIGNED_BYTE;
1423 break;
1424 default:
1425 // Color formats -- GLES glReadPixels only supports the following combinations:
1426 // - GL_RGBA and GL_UNSIGNED_BYTE
1427 // - values of IMPLEMENTATION_COLOR_READ_FORMAT and IMPLEMENTATION_COLOR_READ_TYPE
1428 format = GL_RGBA;
1429 type = GL_UNSIGNED_BYTE;
1430 break;
1431 }
1432 }
1433
1434 GLuint channels;
1435 image::ChannelType channelType;
1436 getImageFormat(format, type, channels, channelType);
1437
1438 if (0) {
1439 std::cerr << enumToString(internalFormat) << " "
1440 << enumToString(format) << " "
1441 << enumToString(type) << "\n";
1442 }
1443
1444 image::Image *image = new image::Image(width, height, channels, true, channelType);
1445
1446 flushErrors();
1447
1448 {
1449 // TODO: reset imaging state too
1450 PixelPackState pps(context);
1451
1452 glReadPixels(0, 0, width, height, format, type, image->pixels);
1453 }
1454
1455 GLenum error = glGetError();
1456 if (error != GL_NO_ERROR) {
1457 do {
1458 std::cerr << "warning: " << enumToString(error) << " while reading framebuffer\n";
1459 error = glGetError();
1460 } while(error != GL_NO_ERROR);
1461 } else {
1462 if (userLabel) {
1463 image->label = userLabel;
1464 }
1465
1466 StateWriter::ImageDesc imageDesc;
1467 imageDesc.format = formatToString(internalFormat);
1468 writer.beginMember(label);
1469 writer.writeImage(image, imageDesc);
1470 writer.endMember();
1471 }
1472
1473 delete image;
1474 }
1475
1476
1477 static inline GLuint
downsampledFramebuffer(Context & context,GLuint oldFbo,GLint drawbuffer,const ImageDesc & colorDesc,const ImageDesc & depthDesc,const ImageDesc & stencilDesc,GLuint * rbs,GLint * numRbs)1478 downsampledFramebuffer(Context &context,
1479 GLuint oldFbo, GLint drawbuffer,
1480 const ImageDesc &colorDesc,
1481 const ImageDesc &depthDesc,
1482 const ImageDesc &stencilDesc,
1483 GLuint *rbs, GLint *numRbs)
1484 {
1485 GLuint fbo;
1486
1487 *numRbs = 0;
1488
1489 glGenFramebuffers(1, &fbo);
1490 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1491
1492 GLboolean scissor_test = glIsEnabled(GL_SCISSOR_TEST);
1493 if (scissor_test) {
1494 glDisable(GL_SCISSOR_TEST);
1495 }
1496
1497 {
1498 // color buffer
1499 GLint maxColorAtt = 0;
1500 glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxColorAtt);
1501 std::vector<GLenum> drawbufs(maxColorAtt);
1502 for (int iColorAtt = 0; iColorAtt < maxColorAtt; ++iColorAtt) {
1503 GLenum colorAtt = GL_COLOR_ATTACHMENT0 + iColorAtt;
1504 drawbufs[iColorAtt] = colorAtt;
1505
1506 glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
1507 GLint objType = GL_NONE;
1508 glGetFramebufferAttachmentParameteriv(GL_READ_FRAMEBUFFER, colorAtt,
1509 GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &objType);
1510 if (objType != GL_NONE) {
1511 glGenRenderbuffers(1, &rbs[*numRbs]);
1512
1513 glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
1514 glRenderbufferStorage(GL_RENDERBUFFER, colorDesc.internalFormat, colorDesc.width, colorDesc.height);
1515 glFramebufferRenderbuffer(GL_FRAMEBUFFER, colorAtt,
1516 GL_RENDERBUFFER, rbs[*numRbs]);
1517
1518 glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
1519 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
1520 glReadBuffer(colorAtt);
1521 glDrawBuffer(colorAtt);
1522
1523 glBlitFramebuffer(0, 0, colorDesc.width, colorDesc.height, 0, 0, colorDesc.width, colorDesc.height,
1524 GL_COLOR_BUFFER_BIT, GL_NEAREST);
1525 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1526 ++*numRbs;
1527 }
1528 }
1529 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1530 glDrawBuffers(maxColorAtt, &drawbufs[0]);
1531 }
1532
1533 if (stencilDesc == depthDesc &&
1534 depthDesc.valid()) {
1535 //combined depth and stencil buffer
1536 glGenRenderbuffers(1, &rbs[*numRbs]);
1537 glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
1538 glRenderbufferStorage(GL_RENDERBUFFER, depthDesc.internalFormat, depthDesc.width, depthDesc.height);
1539 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
1540 GL_RENDERBUFFER, rbs[*numRbs]);
1541 glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
1542 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
1543 glDrawBuffer(drawbuffer);
1544 glReadBuffer(drawbuffer);
1545 glBlitFramebuffer(0, 0, depthDesc.width, depthDesc.height, 0, 0, depthDesc.width, depthDesc.height,
1546 GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
1547 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1548 ++*numRbs;
1549 } else {
1550 if (depthDesc.valid()) {
1551 glGenRenderbuffers(1, &rbs[*numRbs]);
1552 glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
1553 glRenderbufferStorage(GL_RENDERBUFFER, depthDesc.internalFormat, depthDesc.width, depthDesc.height);
1554 glFramebufferRenderbuffer(GL_FRAMEBUFFER,
1555 GL_DEPTH_ATTACHMENT,
1556 GL_RENDERBUFFER, rbs[*numRbs]);
1557 glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
1558 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
1559 glDrawBuffer(drawbuffer);
1560 glReadBuffer(drawbuffer);
1561 glBlitFramebuffer(0, 0, depthDesc.width, depthDesc.height, 0, 0, depthDesc.width, depthDesc.height,
1562 GL_DEPTH_BUFFER_BIT, GL_NEAREST);
1563 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1564 ++*numRbs;
1565 }
1566 if (stencilDesc.valid()) {
1567 glGenRenderbuffers(1, &rbs[*numRbs]);
1568 glBindRenderbuffer(GL_RENDERBUFFER, rbs[*numRbs]);
1569 glRenderbufferStorage(GL_RENDERBUFFER, stencilDesc.internalFormat, stencilDesc.width, stencilDesc.height);
1570 glFramebufferRenderbuffer(GL_FRAMEBUFFER,
1571 GL_STENCIL_ATTACHMENT,
1572 GL_RENDERBUFFER, rbs[*numRbs]);
1573 glBindFramebuffer(GL_READ_FRAMEBUFFER, oldFbo);
1574 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
1575 glDrawBuffer(drawbuffer);
1576 glReadBuffer(drawbuffer);
1577 glBlitFramebuffer(0, 0, stencilDesc.width, stencilDesc.height, 0, 0, stencilDesc.width, stencilDesc.height,
1578 GL_STENCIL_BUFFER_BIT, GL_NEAREST);
1579 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1580 ++*numRbs;
1581 }
1582 }
1583
1584 if (scissor_test) {
1585 glEnable(GL_SCISSOR_TEST);
1586 }
1587
1588 return fbo;
1589 }
1590
1591
1592 /**
1593 * Dump images of current draw drawable/window.
1594 */
1595 static void
dumpDrawableImages(StateWriter & writer,Context & context)1596 dumpDrawableImages(StateWriter &writer, Context &context)
1597 {
1598 GLint width, height;
1599
1600 if (!getDrawableBounds(&width, &height)) {
1601 return;
1602 }
1603
1604 GLint draw_buffer = GL_NONE;
1605 if (context.ES) {
1606 // XXX: Draw buffer is always FRONT for single buffer context, BACK for
1607 // double buffered contexts. There is no way to know which (as
1608 // GL_DOUBLEBUFFER state is also unavailable), so always assume
1609 // double-buffering.
1610 draw_buffer = GL_BACK;
1611 } else {
1612 glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
1613 }
1614
1615 // Reset read framebuffer
1616 GLint read_framebuffer = 0;
1617 if (context.read_framebuffer_object) {
1618 glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
1619 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
1620 } else if (context.framebuffer_object) {
1621 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &read_framebuffer);
1622 glBindFramebuffer(GL_FRAMEBUFFER, 0);
1623 }
1624
1625 if (draw_buffer != GL_NONE) {
1626 // Read from current draw buffer
1627 GLint read_buffer = GL_NONE;
1628 if (context.read_buffer) {
1629 glGetIntegerv(GL_READ_BUFFER, &read_buffer);
1630 glReadBuffer(draw_buffer);
1631 }
1632
1633 GLint alpha_bits = 0;
1634 #if 0
1635 // XXX: Ignore alpha until we are able to match the traced visual
1636 glGetIntegerv(GL_ALPHA_BITS, &alpha_bits);
1637 #endif
1638 GLenum format = alpha_bits ? GL_RGBA : GL_RGB;
1639 GLenum type = GL_UNSIGNED_BYTE;
1640
1641 dumpReadBufferImage(writer, context, enumToString(draw_buffer), NULL, width, height, format, type);
1642
1643 // Restore original read buffer
1644 if (context.read_buffer) {
1645 glReadBuffer(read_buffer);
1646 }
1647 }
1648
1649 if (!context.ES || context.NV_read_depth_stencil) {
1650 GLint depth_bits = 0;
1651 if (context.core) {
1652 glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH, GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, &depth_bits);
1653 } else {
1654 glGetIntegerv(GL_DEPTH_BITS, &depth_bits);
1655 }
1656 if (depth_bits) {
1657 dumpReadBufferImage(writer, context, "GL_DEPTH_COMPONENT", NULL, width, height, GL_DEPTH_COMPONENT, GL_FLOAT);
1658 }
1659
1660 GLint stencil_bits = 0;
1661 if (context.core) {
1662 glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, &stencil_bits);
1663 } else {
1664 glGetIntegerv(GL_STENCIL_BITS, &stencil_bits);
1665 }
1666 if (stencil_bits) {
1667 assert(stencil_bits <= 8);
1668 dumpReadBufferImage(writer, context, "GL_STENCIL_INDEX", NULL, width, height, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE);
1669 }
1670 }
1671
1672 // Restore original read framebuffer
1673 if (context.read_framebuffer_object) {
1674 glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
1675 } else if (context.framebuffer_object) {
1676 glBindFramebuffer(GL_FRAMEBUFFER, read_framebuffer);
1677 }
1678 }
1679
1680
1681 /**
1682 * Dump the specified framebuffer attachment.
1683 *
1684 * In the case of a color attachment, it assumes it is already bound for read.
1685 */
1686 static void
dumpFramebufferAttachment(StateWriter & writer,Context & context,GLenum target,GLenum attachment)1687 dumpFramebufferAttachment(StateWriter &writer, Context &context, GLenum target, GLenum attachment)
1688 {
1689 ImageDesc desc;
1690 if (!getFramebufferAttachmentDesc(context, target, attachment, desc)) {
1691 return;
1692 }
1693
1694 assert(desc.samples == 0);
1695
1696 GLenum format;
1697 GLenum type;
1698 switch (attachment) {
1699 case GL_DEPTH_ATTACHMENT:
1700 format = GL_DEPTH_COMPONENT;
1701 type = GL_FLOAT;
1702 break;
1703 case GL_STENCIL_ATTACHMENT:
1704 format = GL_STENCIL_INDEX;
1705 type = GL_UNSIGNED_BYTE;
1706 break;
1707 default:
1708 assert(desc.internalFormat != GL_NONE);
1709 const InternalFormatDesc &formatDesc = getInternalFormatDesc(desc.internalFormat);
1710 chooseReadBackFormat(formatDesc, format, type);
1711 }
1712
1713 GLint object_type = GL_NONE;
1714 glGetFramebufferAttachmentParameteriv(target, attachment,
1715 GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
1716 &object_type);
1717 GLint object_name = 0;
1718 glGetFramebufferAttachmentParameteriv(target, attachment,
1719 GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
1720 &object_name);
1721 char *object_label = getObjectLabel(context, object_type, object_name);
1722
1723 const char *label = enumToString(attachment);
1724
1725 if (object_type == GL_TEXTURE) {
1726 GLint layered = GL_FALSE;
1727 glGetFramebufferAttachmentParameteriv(target, attachment,
1728 GL_FRAMEBUFFER_ATTACHMENT_LAYERED,
1729 &layered);
1730 if (layered &&
1731 isGeometryShaderBound(context)) {
1732 /*
1733 * Dump the whole texture array.
1734 *
1735 * Unfortunately we can't tell whether the bound GS writes or not gl_Layer.
1736 */
1737
1738 GLint level = 0;
1739 glGetFramebufferAttachmentParameteriv(target, attachment,
1740 GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL,
1741 &level);
1742
1743 GLenum texture_target = getTextureTarget(context, object_name);
1744 GLenum texture_binding = getTextureBinding(texture_target);
1745
1746 GLint bound_texture = 0;
1747 glGetIntegerv(texture_binding, &bound_texture);
1748 glBindTexture(texture_target, object_name);
1749
1750 dumpActiveTextureLevel(writer, context, texture_target, level, label, object_label);
1751
1752 glBindTexture(texture_target, bound_texture);
1753
1754 free(object_label);
1755 return;
1756 }
1757 }
1758
1759
1760 dumpReadBufferImage(writer, context,
1761 label, object_label,
1762 desc.width, desc.height,
1763 format, type, desc.internalFormat);
1764 free(object_label);
1765 }
1766
1767
1768 static void
dumpFramebufferAttachments(StateWriter & writer,Context & context,GLenum target)1769 dumpFramebufferAttachments(StateWriter &writer, Context &context, GLenum target)
1770 {
1771 GLenum status = glCheckFramebufferStatus(target);
1772 if (status != GL_FRAMEBUFFER_COMPLETE) {
1773 std::cerr
1774 << "warning: incomplete " << enumToString(target)
1775 << " (" << enumToString(status) << ")\n";
1776 return;
1777 }
1778
1779 GLint read_framebuffer = 0;
1780 glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
1781
1782 GLint read_buffer = GL_NONE;
1783 glGetIntegerv(GL_READ_BUFFER, &read_buffer);
1784
1785 GLint max_draw_buffers = 1;
1786 glGetIntegerv(GL_MAX_DRAW_BUFFERS, &max_draw_buffers);
1787 GLint max_color_attachments = 0;
1788 glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments);
1789
1790 for (GLint i = 0; i < max_draw_buffers; ++i) {
1791 GLint draw_buffer = GL_NONE;
1792 glGetIntegerv(GL_DRAW_BUFFER0 + i, &draw_buffer);
1793 if (draw_buffer != GL_NONE) {
1794 glReadBuffer(draw_buffer);
1795 GLint attachment;
1796 if (draw_buffer >= GL_COLOR_ATTACHMENT0 && draw_buffer < GL_COLOR_ATTACHMENT0 + max_color_attachments) {
1797 attachment = draw_buffer;
1798 } else {
1799 std::cerr << "warning: unexpected GL_DRAW_BUFFER" << i << " = " << draw_buffer << "\n";
1800 attachment = GL_COLOR_ATTACHMENT0;
1801 }
1802 dumpFramebufferAttachment(writer, context, target, attachment);
1803 }
1804 }
1805
1806 glReadBuffer(read_buffer);
1807
1808 if (!context.ES || context.NV_read_depth_stencil) {
1809 dumpFramebufferAttachment(writer, context, target, GL_DEPTH_ATTACHMENT);
1810 dumpFramebufferAttachment(writer, context, target, GL_STENCIL_ATTACHMENT);
1811 }
1812
1813 glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
1814 }
1815
1816
1817 void
dumpFramebuffer(StateWriter & writer,Context & context)1818 dumpFramebuffer(StateWriter &writer, Context &context)
1819 {
1820 writer.beginMember("framebuffer");
1821 writer.beginObject();
1822
1823 GLint boundDrawFbo = 0, boundReadFbo = 0;
1824 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &boundDrawFbo);
1825 glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &boundReadFbo);
1826 if (!boundDrawFbo) {
1827 dumpDrawableImages(writer, context);
1828 } else if (context.ES) {
1829 dumpFramebufferAttachments(writer, context, GL_FRAMEBUFFER);
1830 } else {
1831 GLint draw_buffer0 = GL_NONE;
1832 glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer0);
1833 bool multisample = false;
1834
1835 GLint boundRb = 0;
1836 glGetIntegerv(GL_RENDERBUFFER_BINDING, &boundRb);
1837
1838 ImageDesc colorDesc;
1839 /* If a draw buffer is not bound then we shouldn't query the attachment. */
1840 if (draw_buffer0 != 0) {
1841 if (getFramebufferAttachmentDesc(context, GL_DRAW_FRAMEBUFFER, draw_buffer0, colorDesc)) {
1842 if (colorDesc.samples) {
1843 multisample = true;
1844 }
1845 }
1846 }
1847
1848 ImageDesc depthDesc;
1849 if (getFramebufferAttachmentDesc(context, GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthDesc)) {
1850 if (depthDesc.samples) {
1851 multisample = true;
1852 }
1853 }
1854
1855 ImageDesc stencilDesc;
1856 if (getFramebufferAttachmentDesc(context, GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, stencilDesc)) {
1857 if (stencilDesc.samples) {
1858 multisample = true;
1859 }
1860 }
1861
1862 GLuint rbs[2 + 8];
1863 GLint numRbs = 0;
1864 GLuint fboCopy = 0;
1865
1866 if (multisample) {
1867 // glReadPixels doesnt support multisampled buffers so we need
1868 // to blit the fbo to a temporary one
1869 fboCopy = downsampledFramebuffer(context,
1870 boundDrawFbo, draw_buffer0,
1871 colorDesc, depthDesc, stencilDesc,
1872 rbs, &numRbs);
1873 } else {
1874 glBindFramebuffer(GL_READ_FRAMEBUFFER, boundDrawFbo);
1875 }
1876
1877 dumpFramebufferAttachments(writer, context, GL_READ_FRAMEBUFFER);
1878
1879 if (multisample) {
1880 glBindRenderbuffer(GL_RENDERBUFFER, boundRb);
1881 assert(numRbs < sizeof rbs / sizeof rbs[0]);
1882 glDeleteRenderbuffers(numRbs, rbs);
1883 glDeleteFramebuffers(1, &fboCopy);
1884 }
1885
1886 glBindFramebuffer(GL_READ_FRAMEBUFFER, boundReadFbo);
1887 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, boundDrawFbo);
1888 }
1889
1890 writer.endObject();
1891 writer.endMember(); // framebuffer
1892 }
1893
1894
1895 } /* namespace glstate */
1896