#include <string.h>
-#include <iostream>
+
#include <algorithm>
+#include <iostream>
+#include <map>
+#include <sstream>
#include "image.hpp"
#include "json.hpp"
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
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<std::string, std::string> ShaderMap;
+
+
static void
-dumpShader(JSONWriter &json, GLuint shader)
+getShaderSource(ShaderMap &shaderMap, GLuint shader)
{
if (!shader) {
return;
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;
}
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;
}
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 (GLint 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[%i]", 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 (GLint 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[%i]", 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
}
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;
- Image::writePixelsToBuffer(pixels, width, height, 4, false, &pngBuffer, &pngBufferSize);
+ Image::writePixelsToBuffer(pixels, width, height, 4, true, &pngBuffer, &pngBufferSize);
json.writeBase64(pngBuffer, pngBufferSize);
free(pngBuffer);
json.endMember(); // __data__
}
-static inline void
-dumpDrawBufferImage(JSONWriter &json, GLenum format)
+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);
+ if (channels > 4) {
+ return NULL;
+ }
+
+ GLint draw_framebuffer = 0;
+ glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &draw_framebuffer);
+ GLint draw_buffer = GL_NONE;
GLint width, height;
+ if (draw_framebuffer) {
+ glGetIntegerv(GL_DRAW_BUFFER0, &draw_buffer);
+ if (draw_buffer == GL_NONE) {
+ return NULL;
+ }
- if (!getDrawableBounds(&width, &height)) {
- json.writeNull();
+ if (!getFramebufferAttachmentSize(GL_DRAW_FRAMEBUFFER, draw_buffer, &width, &height)) {
+ return NULL;
+ }
} else {
- json.beginObject();
-
- // Tell the GUI this is no ordinary object, but an image
- json.writeStringMember("__class__", "image");
+ glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
+ if (draw_buffer == GL_NONE) {
+ return NULL;
+ }
- json.writeNumberMember("__width__", width);
- json.writeNumberMember("__height__", height);
- json.writeNumberMember("__depth__", 1);
+ if (!getDrawableBounds(&width, &height)) {
+ return NULL;
+ }
+ }
- // 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);
+ Image::Image *image = new Image::Image(width, height, channels, true);
+ if (!image) {
+ return NULL;
+ }
- GLubyte *pixels = new GLubyte[width*height*channels];
+ while (glGetError() != GL_NO_ERROR) {}
- GLint drawbuffer = GL_NONE;
- GLint readbuffer = GL_NONE;
- glGetIntegerv(GL_DRAW_BUFFER, &drawbuffer);
- glGetIntegerv(GL_READ_BUFFER, &readbuffer);
- glReadBuffer(drawbuffer);
+ GLint read_framebuffer = 0;
+ glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, draw_framebuffer);
- 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_buffer = 0;
+ glGetIntegerv(GL_READ_BUFFER, &read_buffer);
+ glReadBuffer(draw_buffer);
- glReadPixels(0, 0, width, height, format, GL_UNSIGNED_BYTE, pixels);
+ // TODO: reset imaging state too
+ resetPixelPackState();
- glPopClientAttrib();
- glReadBuffer(readbuffer);
+ glReadPixels(0, 0, width, height, format, GL_UNSIGNED_BYTE, image->pixels);
- 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 = "<<pngBufferSize << ", ratio = " << double(width * height * channels * sizeof *pixels)/pngBufferSize;
- json.writeBase64(pngBuffer, pngBufferSize);
- free(pngBuffer);
- json.endMember(); // __data__
+ restorePixelPackState();
+ glReadBuffer(read_buffer);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
- delete [] pixels;
- json.endObject();
+ 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
+dumpReadBufferImage(JSONWriter &json, GLint width, GLint height, GLenum format)
+{
+ GLint channels = __gl_format_channels(format);
+
+ json.beginObject();
+
+ // Tell the GUI this is no ordinary object, but an image
+ json.writeStringMember("__class__", "image");
+
+ json.writeNumberMember("__width__", width);
+ json.writeNumberMember("__height__", height);
+ json.writeNumberMember("__depth__", 1);
+
+ // 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);
+
+ GLubyte *pixels = new GLubyte[width*height*channels];
+
+ // TODO: reset imaging state too
+ resetPixelPackState();
+
+ glReadPixels(0, 0, width, height, format, GL_UNSIGNED_BYTE, pixels);
+
+ restorePixelPackState();
+
+ json.beginMember("__data__");
+ char *pngBuffer;
+ int pngBufferSize;
+ Image::writePixelsToBuffer(pixels, width, height, channels, true, &pngBuffer, &pngBufferSize);
+ //std::cerr <<" Before = "<<(width * height * channels * sizeof *pixels)
+ // <<", after = "<<pngBufferSize << ", ratio = " << double(width * height * channels * sizeof *pixels)/pngBufferSize;
+ json.writeBase64(pngBuffer, pngBufferSize);
+ free(pngBuffer);
+ json.endMember(); // __data__
+
+ delete [] pixels;
+ json.endObject();
}
}
+/**
+ * Dump images of current draw drawable/window.
+ */
static void
-dumpDrawBuffers(JSONWriter &json, GLboolean dumpDepth, GLboolean dumpStencil)
+dumpDrawableImages(JSONWriter &json)
{
- json.beginMember("GL_RGBA");
- dumpDrawBufferImage(json, GL_RGBA);
- json.endMember();
+ GLint width, height;
- if (dumpDepth) {
+ if (!getDrawableBounds(&width, &height)) {
+ return;
+ }
+
+ GLint draw_buffer = GL_NONE;
+ glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
+ glReadBuffer(draw_buffer);
+
+ if (draw_buffer != GL_NONE) {
+ GLint read_buffer = GL_NONE;
+ glGetIntegerv(GL_READ_BUFFER, &read_buffer);
+
+ GLint alpha_bits = 0;
+ glGetIntegerv(GL_ALPHA_BITS, &alpha_bits);
+ GLenum format = alpha_bits ? GL_RGBA : GL_RGB;
+ json.beginMember(enumToString(draw_buffer));
+ dumpReadBufferImage(json, width, height, format);
+ json.endMember();
+
+ glReadBuffer(read_buffer);
+ }
+
+ GLint depth_bits = 0;
+ glGetIntegerv(GL_DEPTH_BITS, &depth_bits);
+ if (depth_bits) {
json.beginMember("GL_DEPTH_COMPONENT");
- dumpDrawBufferImage(json, GL_DEPTH_COMPONENT);
+ dumpReadBufferImage(json, width, height, GL_DEPTH_COMPONENT);
json.endMember();
}
- if (dumpStencil) {
+ GLint stencil_bits = 0;
+ glGetIntegerv(GL_STENCIL_BITS, &stencil_bits);
+ if (stencil_bits) {
json.beginMember("GL_STENCIL_INDEX");
- dumpDrawBufferImage(json, GL_STENCIL_INDEX);
+ dumpReadBufferImage(json, width, height, GL_STENCIL_INDEX);
json.endMember();
}
}
+/**
+ * Dump the specified framebuffer attachment.
+ *
+ * In the case of a color attachment, it assumes it is already bound for read.
+ */
+static void
+dumpFramebufferAttachment(JSONWriter &json, GLenum target, GLenum attachment, GLenum format)
+{
+ GLint width = 0, height = 0;
+ if (!getFramebufferAttachmentSize(target, attachment, &width, &height)) {
+ return;
+ }
+
+ json.beginMember(enumToString(attachment));
+ dumpReadBufferImage(json, width, height, format);
+ json.endMember();
+}
+
+
+static void
+dumpFramebufferAttachments(JSONWriter &json, GLenum target)
+{
+ GLint read_framebuffer = 0;
+ glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &read_framebuffer);
+
+ GLint read_buffer = GL_NONE;
+ glGetIntegerv(GL_READ_BUFFER, &read_buffer);
+
+ GLint max_draw_buffers = 1;
+ glGetIntegerv(GL_MAX_DRAW_BUFFERS, &max_draw_buffers);
+ GLint max_color_attachments = 0;
+ glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments);
+
+ for (GLint i = 0; i < max_draw_buffers; ++i) {
+ GLint draw_buffer = GL_NONE;
+ glGetIntegerv(GL_DRAW_BUFFER0 + i, &draw_buffer);
+ if (draw_buffer != GL_NONE) {
+ glReadBuffer(draw_buffer);
+ GLint attachment;
+ if (draw_buffer >= 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
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();
}
+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
+
}