X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=glstate.cpp;h=aaf4d4bd6b6b5b15c4a67c9c3f51947b1a5b0d91;hb=14b78f815fa02ffda48f8a6599d6af45868266ff;hp=71aad7d6f1f90870d207923d347ffa4644496437;hpb=95d41043d238ca7c7928e7f73353ec39f8a33ead;p=apitrace diff --git a/glstate.cpp b/glstate.cpp index 71aad7d..aaf4d4b 100644 --- a/glstate.cpp +++ b/glstate.cpp @@ -25,8 +25,11 @@ #include -#include + #include +#include +#include +#include #include "image.hpp" #include "json.hpp" @@ -43,11 +46,6 @@ extern "C" { #endif -typedef int CGSConnectionID; -typedef int CGSWindowID; -typedef int CGSSurfaceID; - -CGLError CGLGetSurface(CGLContextObj, CGSConnectionID*, CGSWindowID*, CGSSurfaceID*); OSStatus CGSGetSurfaceBounds(CGSConnectionID, CGWindowID, CGSSurfaceID, CGRect *); #ifdef __cplusplus @@ -60,8 +58,34 @@ OSStatus CGSGetSurfaceBounds(CGSConnectionID, CGWindowID, CGSSurfaceID, CGRect * namespace glstate { +static inline void +resetPixelPackState(void) { + glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); + glPixelStorei(GL_PACK_SWAP_BYTES, GL_FALSE); + glPixelStorei(GL_PACK_LSB_FIRST, GL_FALSE); + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0); + glPixelStorei(GL_PACK_SKIP_ROWS, 0); + glPixelStorei(GL_PACK_SKIP_PIXELS, 0); + glPixelStorei(GL_PACK_SKIP_IMAGES, 0); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); +} + + +static inline void +restorePixelPackState(void) { + glPopClientAttrib(); +} + + +// Mapping from shader type to shader source, used to accumulated the sources +// of different shaders with same type. +typedef std::map ShaderMap; + + static void -dumpShader(JSONWriter &json, GLuint shader) +getShaderSource(ShaderMap &shaderMap, GLuint shader) { if (!shader) { return; @@ -84,23 +108,27 @@ dumpShader(JSONWriter &json, GLuint shader) source[0] = 0; glGetShaderSource(shader, source_length, &length, source); - json.beginMember(enumToString(shader_type)); - json.writeString(source); - json.endMember(); + shaderMap[enumToString(shader_type)] += source; delete [] source; } static void -dumpShaderObj(JSONWriter &json, GLhandleARB shaderObj) +getShaderObjSource(ShaderMap &shaderMap, GLhandleARB shaderObj) { if (!shaderObj) { return; } + GLint object_type = 0; + glGetObjectParameterivARB(shaderObj, GL_OBJECT_TYPE_ARB, &object_type); + if (object_type != GL_SHADER_OBJECT_ARB) { + return; + } + GLint shader_type = 0; - glGetObjectParameterivARB(shaderObj, GL_OBJECT_TYPE_ARB, &shader_type); + glGetObjectParameterivARB(shaderObj, GL_OBJECT_SUBTYPE_ARB, &shader_type); if (!shader_type) { return; } @@ -116,60 +144,273 @@ dumpShaderObj(JSONWriter &json, GLhandleARB shaderObj) source[0] = 0; glGetShaderSource(shaderObj, source_length, &length, source); - json.beginMember(enumToString(shader_type)); - json.writeString(source); - json.endMember(); + shaderMap[enumToString(shader_type)] += source; delete [] source; } static inline void -dumpCurrentProgram(JSONWriter &json) +dumpProgram(JSONWriter &json, GLint program) { - GLint program = 0; - glGetIntegerv(GL_CURRENT_PROGRAM, &program); - if (!program) { - return; - } - GLint attached_shaders = 0; glGetProgramiv(program, GL_ATTACHED_SHADERS, &attached_shaders); if (!attached_shaders) { return; } + ShaderMap shaderMap; + GLuint *shaders = new GLuint[attached_shaders]; GLsizei count = 0; glGetAttachedShaders(program, attached_shaders, &count, shaders); + std::sort(shaders, shaders + count); for (GLsizei i = 0; i < count; ++ i) { - dumpShader(json, shaders[i]); + getShaderSource(shaderMap, shaders[i]); } delete [] shaders; + + for (ShaderMap::const_iterator it = shaderMap.begin(); it != shaderMap.end(); ++it) { + json.beginMember(it->first); + json.writeString(it->second); + json.endMember(); + } } static inline void -dumpCurrentProgramObj(JSONWriter &json) +dumpProgramObj(JSONWriter &json, GLhandleARB programObj) { - GLhandleARB programObj = glGetHandleARB(GL_PROGRAM_OBJECT_ARB); - if (!programObj) { - return; - } - GLint attached_shaders = 0; - glGetProgramivARB(programObj, GL_OBJECT_ATTACHED_OBJECTS_ARB, &attached_shaders); + glGetObjectParameterivARB(programObj, GL_OBJECT_ATTACHED_OBJECTS_ARB, &attached_shaders); if (!attached_shaders) { return; } + ShaderMap shaderMap; + GLhandleARB *shaderObjs = new GLhandleARB[attached_shaders]; GLsizei count = 0; glGetAttachedObjectsARB(programObj, attached_shaders, &count, shaderObjs); + std::sort(shaderObjs, shaderObjs + count); for (GLsizei i = 0; i < count; ++ i) { - dumpShaderObj(json, shaderObjs[i]); + getShaderObjSource(shaderMap, shaderObjs[i]); } delete [] shaderObjs; + + for (ShaderMap::const_iterator it = shaderMap.begin(); it != shaderMap.end(); ++it) { + json.beginMember(it->first); + json.writeString(it->second); + json.endMember(); + } +} + + +static void +dumpUniform(JSONWriter &json, GLint program, GLint size, GLenum type, const GLchar *name) { + + GLenum elemType; + GLint numElems; + __gl_uniform_size(type, elemType, numElems); + if (elemType == GL_NONE) { + return; + } + + GLfloat fvalues[4*4]; + GLdouble dvalues[4*4]; + GLint ivalues[4*4]; + GLuint uivalues[4*4]; + + GLint i, j; + + for (i = 0; i < size; ++i) { + std::stringstream ss; + ss << name; + + if (size > 1) { + ss << '[' << i << ']'; + } + + std::string elemName = ss.str(); + + json.beginMember(elemName); + + GLint location = glGetUniformLocation(program, elemName.c_str()); + + if (numElems > 1) { + json.beginArray(); + } + + switch (elemType) { + case GL_FLOAT: + glGetUniformfv(program, location, fvalues); + for (j = 0; j < numElems; ++j) { + json.writeNumber(fvalues[j]); + } + break; + case GL_DOUBLE: + glGetUniformdv(program, location, dvalues); + for (j = 0; j < numElems; ++j) { + json.writeNumber(dvalues[j]); + } + break; + case GL_INT: + glGetUniformiv(program, location, ivalues); + for (j = 0; j < numElems; ++j) { + json.writeNumber(ivalues[j]); + } + break; + case GL_UNSIGNED_INT: + glGetUniformuiv(program, location, uivalues); + for (j = 0; j < numElems; ++j) { + json.writeNumber(uivalues[j]); + } + break; + case GL_BOOL: + glGetUniformiv(program, location, ivalues); + for (j = 0; j < numElems; ++j) { + json.writeBool(ivalues[j]); + } + break; + default: + assert(0); + break; + } + + if (numElems > 1) { + json.endArray(); + } + + json.endMember(); + } +} + + +static void +dumpUniformARB(JSONWriter &json, GLhandleARB programObj, GLint size, GLenum type, const GLchar *name) { + + GLenum elemType; + GLint numElems; + __gl_uniform_size(type, elemType, numElems); + if (elemType == GL_NONE) { + return; + } + + GLfloat fvalues[4*4]; + GLint ivalues[4*4]; + + GLint i, j; + + for (i = 0; i < size; ++i) { + std::stringstream ss; + ss << name; + + if (size > 1) { + ss << '[' << i << ']'; + } + + std::string elemName = ss.str(); + + json.beginMember(elemName); + + GLint location = glGetUniformLocationARB(programObj, elemName.c_str()); + + if (numElems > 1) { + json.beginArray(); + } + + switch (elemType) { + case GL_DOUBLE: + // glGetUniformdvARB does not exists + case GL_FLOAT: + glGetUniformfvARB(programObj, location, fvalues); + for (j = 0; j < numElems; ++j) { + json.writeNumber(fvalues[j]); + } + break; + case GL_UNSIGNED_INT: + // glGetUniformuivARB does not exists + case GL_INT: + glGetUniformivARB(programObj, location, ivalues); + for (j = 0; j < numElems; ++j) { + json.writeNumber(ivalues[j]); + } + break; + case GL_BOOL: + glGetUniformivARB(programObj, location, ivalues); + for (j = 0; j < numElems; ++j) { + json.writeBool(ivalues[j]); + } + break; + default: + assert(0); + break; + } + + if (numElems > 1) { + json.endArray(); + } + + json.endMember(); + } +} + + +static inline void +dumpProgramUniforms(JSONWriter &json, GLint program) +{ + GLint active_uniforms = 0; + glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniforms); + if (!active_uniforms) { + return; + } + + GLint active_uniform_max_length = 0; + glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &active_uniform_max_length); + GLchar *name = new GLchar[active_uniform_max_length]; + if (!name) { + return; + } + + for (GLint index = 0; index < active_uniforms; ++index) { + GLsizei length = 0; + GLint size = 0; + GLenum type = GL_NONE; + glGetActiveUniform(program, index, active_uniform_max_length, &length, &size, &type, name); + + dumpUniform(json, program, size, type, name); + } + + delete [] name; +} + + +static inline void +dumpProgramObjUniforms(JSONWriter &json, GLhandleARB programObj) +{ + GLint active_uniforms = 0; + glGetObjectParameterivARB(programObj, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &active_uniforms); + if (!active_uniforms) { + return; + } + + GLint active_uniform_max_length = 0; + glGetObjectParameterivARB(programObj, GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB, &active_uniform_max_length); + GLchar *name = new GLchar[active_uniform_max_length]; + if (!name) { + return; + } + + for (GLint index = 0; index < active_uniforms; ++index) { + GLsizei length = 0; + GLint size = 0; + GLenum type = GL_NONE; + glGetActiveUniformARB(programObj, index, active_uniform_max_length, &length, &size, &type, name); + + dumpUniformARB(json, programObj, size, type, name); + } + + delete [] name; } @@ -200,15 +441,99 @@ dumpArbProgram(JSONWriter &json, GLenum target) static inline void -dumpShaders(JSONWriter &json) +dumpArbProgramUniforms(JSONWriter &json, GLenum target, const char *prefix) +{ + if (!glIsEnabled(target)) { + return; + } + + GLint program_parameters = 0; + glGetProgramivARB(target, GL_PROGRAM_PARAMETERS_ARB, &program_parameters); + if (!program_parameters) { + return; + } + + GLint max_program_local_parameters = 0; + glGetProgramivARB(target, GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB, &max_program_local_parameters); + for (GLuint index = 0; index < max_program_local_parameters; ++index) { + GLdouble params[4] = {0, 0, 0, 0}; + glGetProgramLocalParameterdvARB(target, index, params); + + if (!params[0] && !params[1] && !params[2] && !params[3]) { + continue; + } + + char name[256]; + snprintf(name, sizeof name, "%sprogram.local[%u]", prefix, index); + + json.beginMember(name); + json.beginArray(); + json.writeNumber(params[0]); + json.writeNumber(params[1]); + json.writeNumber(params[2]); + json.writeNumber(params[3]); + json.endArray(); + json.endMember(); + } + + GLint max_program_env_parameters = 0; + glGetProgramivARB(target, GL_MAX_PROGRAM_ENV_PARAMETERS_ARB, &max_program_env_parameters); + for (GLuint index = 0; index < max_program_env_parameters; ++index) { + GLdouble params[4] = {0, 0, 0, 0}; + glGetProgramEnvParameterdvARB(target, index, params); + + if (!params[0] && !params[1] && !params[2] && !params[3]) { + continue; + } + + char name[256]; + snprintf(name, sizeof name, "%sprogram.env[%u]", prefix, index); + + json.beginMember(name); + json.beginArray(); + json.writeNumber(params[0]); + json.writeNumber(params[1]); + json.writeNumber(params[2]); + json.writeNumber(params[3]); + json.endArray(); + json.endMember(); + } +} + + +static inline void +dumpShadersUniforms(JSONWriter &json) { + GLint program = 0; + glGetIntegerv(GL_CURRENT_PROGRAM, &program); + + GLhandleARB programObj = glGetHandleARB(GL_PROGRAM_OBJECT_ARB); + json.beginMember("shaders"); json.beginObject(); - dumpCurrentProgram(json); - dumpArbProgram(json, GL_FRAGMENT_PROGRAM_ARB); - dumpArbProgram(json, GL_VERTEX_PROGRAM_ARB); + if (program) { + dumpProgram(json, program); + } else if (programObj) { + dumpProgramObj(json, programObj); + } else { + dumpArbProgram(json, GL_FRAGMENT_PROGRAM_ARB); + dumpArbProgram(json, GL_VERTEX_PROGRAM_ARB); + } json.endObject(); - json.endMember(); //shaders + json.endMember(); // shaders + + json.beginMember("uniforms"); + json.beginObject(); + if (program) { + dumpProgramUniforms(json, program); + } else if (programObj) { + dumpProgramObjUniforms(json, programObj); + } else { + dumpArbProgramUniforms(json, GL_FRAGMENT_PROGRAM_ARB, "fp."); + dumpArbProgramUniforms(json, GL_VERTEX_PROGRAM_ARB, "vp."); + } + json.endObject(); + json.endMember(); // uniforms } @@ -257,8 +582,12 @@ dumpTextureImage(JSONWriter &json, GLenum target, GLint level) GLubyte *pixels = new GLubyte[depth*width*height*4]; + resetPixelPackState(); + glGetTexImage(target, level, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + restorePixelPackState(); + json.beginMember("__data__"); char *pngBuffer; int pngBufferSize; @@ -405,6 +734,116 @@ getDrawableBounds(GLint *width, GLint *height) { } +static const GLenum texture_bindings[][2] = { + {GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D}, + {GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D}, + {GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D}, + {GL_TEXTURE_RECTANGLE, GL_TEXTURE_BINDING_RECTANGLE}, + {GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP} +}; + + +static bool +bindTexture(GLint texture, GLenum &target, GLint &bound_texture) +{ + + for (unsigned i = 0; i < sizeof(texture_bindings)/sizeof(texture_bindings[0]); ++i) { + target = texture_bindings[i][0]; + + GLenum binding = texture_bindings[i][1]; + + while (glGetError() != GL_NO_ERROR) + ; + + glGetIntegerv(binding, &bound_texture); + glBindTexture(target, texture); + + if (glGetError() == GL_NO_ERROR) { + return true; + } + + glBindTexture(target, bound_texture); + } + + target = GL_NONE; + + return false; +} + + +static bool +getTextureLevelSize(GLint texture, GLint level, GLint *width, GLint *height) +{ + *width = 0; + *height = 0; + + GLenum target; + GLint bound_texture = 0; + if (!bindTexture(texture, target, bound_texture)) { + return false; + } + + glGetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, width); + glGetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, height); + + glBindTexture(target, bound_texture); + + return *width > 0 && *height > 0; +} + + +static bool +getRenderbufferSize(GLint renderbuffer, GLint *width, GLint *height) +{ + GLint bound_renderbuffer = 0; + glGetIntegerv(GL_RENDERBUFFER_BINDING, &bound_renderbuffer); + glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); + + *width = 0; + *height = 0; + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, width); + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, height); + + glBindRenderbuffer(GL_RENDERBUFFER, bound_renderbuffer); + + return *width > 0 && *height > 0; +} + + +static bool +getFramebufferAttachmentSize(GLenum target, GLenum attachment, GLint *width, GLint *height) +{ + GLint object_type = GL_NONE; + glGetFramebufferAttachmentParameteriv(target, attachment, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, + &object_type); + if (object_type == GL_NONE) { + return false; + } + + GLint object_name = 0; + glGetFramebufferAttachmentParameteriv(target, attachment, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, + &object_name); + if (object_name == 0) { + return false; + } + + if (object_type == GL_RENDERBUFFER) { + return getRenderbufferSize(object_name, width, height); + } else if (object_type == GL_TEXTURE) { + GLint texture_level = 0; + glGetFramebufferAttachmentParameteriv(target, attachment, + GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL, + &texture_level); + return getTextureLevelSize(object_name, texture_level, width, height); + } else { + std::cerr << "warning: unexpected GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = " << object_type << "\n"; + return false; + } +} + + Image::Image * getDrawBufferImage(GLenum format) { GLint channels = __gl_format_channels(format); @@ -412,9 +851,29 @@ getDrawBufferImage(GLenum format) { return NULL; } + GLint draw_framebuffer = 0; + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &draw_framebuffer); + + GLint draw_buffer = GL_NONE; GLint width, height; - if (!getDrawableBounds(&width, &height)) { - return NULL; + if (draw_framebuffer) { + glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer); + if (draw_buffer == GL_NONE) { + return NULL; + } + + if (!getFramebufferAttachmentSize(GL_DRAW_FRAMEBUFFER, draw_buffer, &width, &height)) { + return NULL; + } + } else { + glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer); + if (draw_buffer == GL_NONE) { + return NULL; + } + + if (!getDrawableBounds(&width, &height)) { + return NULL; + } } Image::Image *image = new Image::Image(width, height, channels, true); @@ -422,93 +881,83 @@ getDrawBufferImage(GLenum format) { return NULL; } - GLint drawbuffer = GL_NONE; - GLint readbuffer = GL_NONE; - glGetIntegerv(GL_DRAW_BUFFER, &drawbuffer); - glGetIntegerv(GL_READ_BUFFER, &readbuffer); - glReadBuffer(drawbuffer); + while (glGetError() != GL_NO_ERROR) {} - glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); - glPixelStorei(GL_PACK_SWAP_BYTES, GL_FALSE); - glPixelStorei(GL_PACK_LSB_FIRST, GL_FALSE); - glPixelStorei(GL_PACK_ROW_LENGTH, 0); - glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0); - glPixelStorei(GL_PACK_SKIP_ROWS, 0); - glPixelStorei(GL_PACK_SKIP_PIXELS, 0); - glPixelStorei(GL_PACK_SKIP_IMAGES, 0); - glPixelStorei(GL_PACK_ALIGNMENT, 1); + GLint read_framebuffer = 0; + glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer); + glBindFramebuffer(GL_READ_FRAMEBUFFER, draw_framebuffer); - glReadPixels(0, 0, width, height, format, GL_UNSIGNED_BYTE, image->pixels); + GLint read_buffer = 0; + glGetIntegerv(GL_READ_BUFFER, &read_buffer); + glReadBuffer(draw_buffer); - glPopClientAttrib(); + // TODO: reset imaging state too + resetPixelPackState(); - glReadBuffer(readbuffer); + glReadPixels(0, 0, width, height, format, GL_UNSIGNED_BYTE, image->pixels); + restorePixelPackState(); + glReadBuffer(read_buffer); + glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer); + + GLenum error = glGetError(); + if (error != GL_NO_ERROR) { + do { + std::cerr << "warning: " << enumToString(error) << " while getting snapshot\n"; + error = glGetError(); + } while(error != GL_NO_ERROR); + delete image; + return NULL; + } + return image; } +/** + * Dump the image of the currently bound read buffer. + */ static inline void -dumpDrawBufferImage(JSONWriter &json, GLenum format) +dumpReadBufferImage(JSONWriter &json, GLint width, GLint height, GLenum format) { GLint channels = __gl_format_channels(format); - GLint width, height; - - if (!getDrawableBounds(&width, &height)) { - json.writeNull(); - } else { - json.beginObject(); - - // Tell the GUI this is no ordinary object, but an image - json.writeStringMember("__class__", "image"); + json.beginObject(); - json.writeNumberMember("__width__", width); - json.writeNumberMember("__height__", height); - json.writeNumberMember("__depth__", 1); + // Tell the GUI this is no ordinary object, but an image + json.writeStringMember("__class__", "image"); - // Hardcoded for now, but we could chose types more adequate to the - // texture internal format - json.writeStringMember("__type__", "uint8"); - json.writeBoolMember("__normalized__", true); - json.writeNumberMember("__channels__", channels); + json.writeNumberMember("__width__", width); + json.writeNumberMember("__height__", height); + json.writeNumberMember("__depth__", 1); - GLubyte *pixels = new GLubyte[width*height*channels]; + // Hardcoded for now, but we could chose types more adequate to the + // texture internal format + json.writeStringMember("__type__", "uint8"); + json.writeBoolMember("__normalized__", true); + json.writeNumberMember("__channels__", channels); - GLint drawbuffer = GL_NONE; - GLint readbuffer = GL_NONE; - glGetIntegerv(GL_DRAW_BUFFER, &drawbuffer); - glGetIntegerv(GL_READ_BUFFER, &readbuffer); - glReadBuffer(drawbuffer); + GLubyte *pixels = new GLubyte[width*height*channels]; - glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); - glPixelStorei(GL_PACK_SWAP_BYTES, GL_FALSE); - glPixelStorei(GL_PACK_LSB_FIRST, GL_FALSE); - glPixelStorei(GL_PACK_ROW_LENGTH, 0); - glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0); - glPixelStorei(GL_PACK_SKIP_ROWS, 0); - glPixelStorei(GL_PACK_SKIP_PIXELS, 0); - glPixelStorei(GL_PACK_SKIP_IMAGES, 0); - glPixelStorei(GL_PACK_ALIGNMENT, 1); + // TODO: reset imaging state too + resetPixelPackState(); - glReadPixels(0, 0, width, height, format, GL_UNSIGNED_BYTE, pixels); + glReadPixels(0, 0, width, height, format, GL_UNSIGNED_BYTE, pixels); - glPopClientAttrib(); - glReadBuffer(readbuffer); + restorePixelPackState(); - json.beginMember("__data__"); - char *pngBuffer; - int pngBufferSize; - Image::writePixelsToBuffer(pixels, width, height, channels, false, &pngBuffer, &pngBufferSize); - //std::cerr <<" Before = "<<(width * height * channels * sizeof *pixels) - // <<", after = "<= GL_COLOR_ATTACHMENT0 && draw_buffer < GL_COLOR_ATTACHMENT0 + max_color_attachments) { + attachment = draw_buffer; + } else { + std::cerr << "warning: unexpected GL_DRAW_BUFFER" << i << " = " << draw_buffer << "\n"; + attachment = GL_COLOR_ATTACHMENT0; + } + GLint alpha_size = 0; + glGetFramebufferAttachmentParameteriv(target, attachment, + GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE, + &alpha_size); + GLenum format = alpha_size ? GL_RGBA : GL_RGB; + dumpFramebufferAttachment(json, target, attachment, format); + } + } + + glReadBuffer(read_buffer); + + dumpFramebufferAttachment(json, target, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT); + dumpFramebufferAttachment(json, target, GL_STENCIL_ATTACHMENT, GL_STENCIL_INDEX); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer); +} static void @@ -654,66 +1191,74 @@ dumpFramebuffer(JSONWriter &json) glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &boundDrawFbo); glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &boundReadFbo); if (!boundDrawFbo) { - GLint depth_bits = 0; - glGetIntegerv(GL_DEPTH_BITS, &depth_bits); - GLint stencil_bits = 0; - glGetIntegerv(GL_STENCIL_BITS, &stencil_bits); - dumpDrawBuffers(json, depth_bits, stencil_bits); + dumpDrawableImages(json); } else { - GLint colorRb, stencilRb, depthRb; - GLint boundRb; + GLint colorRb = 0, stencilRb = 0, depthRb = 0; + GLint draw_buffer0 = GL_NONE; + glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer0); + bool multisample = false; + + GLint boundRb = 0; glGetIntegerv(GL_RENDERBUFFER_BINDING, &boundRb); - GLint drawbuffer = GL_NONE; - glGetIntegerv(GL_DRAW_BUFFER, &drawbuffer); - - glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, - drawbuffer, - GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, - &colorRb); - glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, - GL_DEPTH_ATTACHMENT, - GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, - &depthRb); - glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, - GL_STENCIL_ATTACHMENT, - GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, - &stencilRb); - - GLint colorSamples, depthSamples, stencilSamples; + + GLint object_type; + glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, draw_buffer0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type); + if (object_type == GL_RENDERBUFFER) { + glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, draw_buffer0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &colorRb); + glBindRenderbuffer(GL_RENDERBUFFER, colorRb); + GLint samples = 0; + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples); + if (samples) { + multisample = true; + } + } + + glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type); + if (object_type == GL_RENDERBUFFER) { + glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthRb); + glBindRenderbuffer(GL_RENDERBUFFER, depthRb); + GLint samples = 0; + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples); + if (samples) { + multisample = true; + } + } + + glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &object_type); + if (object_type == GL_RENDERBUFFER) { + glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &stencilRb); + glBindRenderbuffer(GL_RENDERBUFFER, stencilRb); + GLint samples = 0; + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples); + if (samples) { + multisample = true; + } + } + + glBindRenderbuffer(GL_RENDERBUFFER, boundRb); + GLuint rbs[3]; GLint numRbs = 0; GLuint fboCopy = 0; - glBindRenderbuffer(GL_RENDERBUFFER, colorRb); - glGetRenderbufferParameteriv(GL_RENDERBUFFER, - GL_RENDERBUFFER_SAMPLES, &colorSamples); - glBindRenderbuffer(GL_RENDERBUFFER, depthRb); - glGetRenderbufferParameteriv(GL_RENDERBUFFER, - GL_RENDERBUFFER_SAMPLES, &depthSamples); - glBindRenderbuffer(GL_RENDERBUFFER, stencilRb); - glGetRenderbufferParameteriv(GL_RENDERBUFFER, - GL_RENDERBUFFER_SAMPLES, &stencilSamples); - glBindRenderbuffer(GL_RENDERBUFFER, boundRb); - if (colorSamples || depthSamples || stencilSamples) { - //glReadPixels doesnt support multisampled buffers so we need + if (multisample) { + // glReadPixels doesnt support multisampled buffers so we need // to blit the fbo to a temporary one - fboCopy = downsampledFramebuffer(boundDrawFbo, drawbuffer, + fboCopy = downsampledFramebuffer(boundDrawFbo, draw_buffer0, colorRb, depthRb, stencilRb, rbs, &numRbs); } - glDrawBuffer(drawbuffer); - glReadBuffer(drawbuffer); - dumpDrawBuffers(json, depthRb, stencilRb); + dumpFramebufferAttachments(json, GL_DRAW_FRAMEBUFFER); - if (fboCopy) { - glBindFramebuffer(GL_FRAMEBUFFER, boundDrawFbo); - glBindFramebuffer(GL_READ_FRAMEBUFFER, boundReadFbo); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, boundDrawFbo); + if (multisample) { glBindRenderbuffer(GL_RENDERBUFFER_BINDING, boundRb); glDeleteRenderbuffers(numRbs, rbs); glDeleteFramebuffers(1, &fboCopy); } + + glBindFramebuffer(GL_READ_FRAMEBUFFER, boundReadFbo); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, boundDrawFbo); } json.endObject(); @@ -721,13 +1266,60 @@ dumpFramebuffer(JSONWriter &json) } +static const GLenum bindings[] = { + GL_DRAW_BUFFER, + GL_READ_BUFFER, + GL_PIXEL_PACK_BUFFER_BINDING, + GL_PIXEL_UNPACK_BUFFER_BINDING, + GL_TEXTURE_BINDING_1D, + GL_TEXTURE_BINDING_2D, + GL_TEXTURE_BINDING_3D, + GL_TEXTURE_BINDING_RECTANGLE, + GL_TEXTURE_BINDING_CUBE_MAP, + GL_DRAW_FRAMEBUFFER_BINDING, + GL_READ_FRAMEBUFFER_BINDING, + GL_RENDERBUFFER_BINDING, + GL_DRAW_BUFFER0, + GL_DRAW_BUFFER1, + GL_DRAW_BUFFER2, + GL_DRAW_BUFFER3, + GL_DRAW_BUFFER4, + GL_DRAW_BUFFER5, + GL_DRAW_BUFFER6, + GL_DRAW_BUFFER7, +}; + + +#define NUM_BINDINGS sizeof(bindings)/sizeof(bindings[0]) + + void dumpCurrentContext(std::ostream &os) { JSONWriter json(os); + +#ifndef NDEBUG + GLint old_bindings[NUM_BINDINGS]; + for (unsigned i = 0; i < NUM_BINDINGS; ++i) { + old_bindings[i] = 0; + glGetIntegerv(bindings[i], &old_bindings[i]); + } +#endif + dumpParameters(json); - dumpShaders(json); + dumpShadersUniforms(json); dumpTextures(json); dumpFramebuffer(json); + +#ifndef NDEBUG + for (unsigned i = 0; i < NUM_BINDINGS; ++i) { + GLint new_binding = 0; + glGetIntegerv(bindings[i], &new_binding); + if (new_binding != old_bindings[i]) { + std::cerr << "warning: " << enumToString(bindings[i]) << " was clobbered\n"; + } + } +#endif + }