diff options
Diffstat (limited to 'retrace/glstate_shaders.cpp')
-rw-r--r-- | retrace/glstate_shaders.cpp | 955 |
1 files changed, 654 insertions, 301 deletions
diff --git a/retrace/glstate_shaders.cpp b/retrace/glstate_shaders.cpp index 825e3aeb..2db1bfb9 100644 --- a/retrace/glstate_shaders.cpp +++ b/retrace/glstate_shaders.cpp @@ -26,13 +26,15 @@ #include <assert.h> #include <string.h> +#include <stdio.h> #include <algorithm> #include <iostream> #include <map> #include <sstream> +#include <vector> -#include "json.hpp" +#include "state_writer.hpp" #include "glproc.hpp" #include "glsize.hpp" #include "glstate.hpp" @@ -77,45 +79,13 @@ getShaderSource(ShaderMap &shaderMap, GLuint shader) } -static void -getShaderObjSource(ShaderMap &shaderMap, GLhandleARB shaderObj) +static inline void +dumpProgram(StateWriter &writer, Context &context, GLint program) { - 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_SUBTYPE_ARB, &shader_type); - if (!shader_type) { + if (program <= 0) { return; } - GLint source_length = 0; - glGetObjectParameterivARB(shaderObj, GL_OBJECT_SHADER_SOURCE_LENGTH_ARB, &source_length); - if (!source_length) { - return; - } - - GLcharARB *source = new GLcharARB[source_length]; - GLsizei length = 0; - source[0] = 0; - glGetShaderSource(shaderObj, source_length, &length, source); - - shaderMap[enumToString(shader_type)] += source; - - delete [] source; -} - - -static inline void -dumpProgram(JSONWriter &json, GLint program) -{ GLint attached_shaders = 0; glGetProgramiv(program, GL_ATTACHED_SHADERS, &attached_shaders); if (!attached_shaders) { @@ -134,45 +104,72 @@ dumpProgram(JSONWriter &json, GLint program) 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 -dumpProgramObj(JSONWriter &json, GLhandleARB programObj) -{ - GLint attached_shaders = 0; - glGetObjectParameterivARB(programObj, GL_OBJECT_ATTACHED_OBJECTS_ARB, &attached_shaders); - if (!attached_shaders) { - return; + writer.beginMember(it->first); + writer.writeString(it->second); + writer.endMember(); } - 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) { - 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(); + // Dump NVIDIA GPU programs via GL_ARB_get_program_binary + if (context.ARB_get_program_binary) { + GLint binaryLength = 0; + glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &binaryLength); + if (binaryLength > 0) { + std::vector<char> binary(binaryLength); + GLenum format = GL_NONE; + glGetProgramBinary(program, binaryLength, NULL, &format, &binary[0]); + if (format == 0x8e21) { + if (0) { + FILE *fp = fopen("program.bin", "wb"); + if (fp) { + fwrite(&binary[0], 1, binaryLength, fp); + fclose(fp); + } + } + + // Extract NVIDIA GPU programs + std::string str(binary.begin(), binary.end()); + size_t end = 0; + while (true) { + // Each program starts with a !!NV header token + size_t start = str.find("!!NV", end); + if (start == std::string::npos) { + break; + } + + // And is preceeded by a length DWORD + assert(start >= end + 4); + if (start < end + 4) { + break; + } + uint32_t length; + str.copy(reinterpret_cast<char *>(&length), 4, start - 4); + assert(start + length <= binaryLength); + if (start + length > binaryLength) { + break; + } + + std::string nvProg = str.substr(start, length); + + size_t eol = nvProg.find('\n'); + std::string nvHeader = nvProg.substr(2, eol - 2); + + writer.beginMember(nvHeader); + writer.writeString(nvProg); + writer.endMember(); + + end = start + length; + } + } + } } } + /** - * Built-in uniforms can't be queried through glGetUniform*. + * Built-in uniforms/attributes need special treatment. */ static inline bool -isBuiltinUniform(const GLchar *name) +isBuiltinName(const GLchar *name) { return name[0] == 'g' && name[1] == 'l' && name[2] == '_'; } @@ -203,78 +200,157 @@ resolveUniformName(const GLchar *name, GLint size) } -static void -dumpUniformValues(JSONWriter &json, GLenum type, const void *values, GLint matrix_stride = 0, GLboolean is_row_major = GL_FALSE) { - GLenum elemType; - GLint numCols, numRows; - _gl_uniform_size(type, elemType, numCols, numRows); - if (!numCols || !numRows) { - json.writeNull(); - } +struct AttribDesc +{ + GLenum type = GL_NONE; + GLenum size = 0; - size_t elemSize = _gl_type_size(elemType); + GLenum elemType = GL_NONE; + GLint elemStride = 0; - GLint row_stride = 0; - GLint col_stride = 0; - if (is_row_major) { - row_stride = elemSize; - col_stride = matrix_stride ? matrix_stride : numRows * elemSize; - } else { - col_stride = elemSize; - row_stride = matrix_stride ? matrix_stride : numCols * elemSize; + GLint numCols = 0; + GLint numRows = 0; + + GLsizei rowStride = 0; + GLsizei colStride = 0; + + GLsizei arrayStride = 0; + + AttribDesc(void) + {} + + AttribDesc(GLenum _type, + GLint _size, + GLint array_stride = 0, + GLint matrix_stride = 0, + GLboolean is_row_major = GL_FALSE) : + type(_type), + size(_size) + { + _gl_uniform_size(type, elemType, numCols, numRows); + + elemStride = _gl_type_size(elemType); + + rowStride = 0; + colStride = 0; + if (is_row_major) { + rowStride = elemStride; + if (!matrix_stride) { + matrix_stride = numRows * elemStride; + } + colStride = matrix_stride; + if (!array_stride) { + arrayStride = numCols * colStride; + } + } else { + colStride = elemStride; + if (!matrix_stride) { + matrix_stride = numCols * elemStride; + } + rowStride = matrix_stride; + if (!array_stride) { + arrayStride = numRows * rowStride; + } + } } - if (numRows > 1) { - json.beginArray(); + inline + operator bool (void) const { + return elemType != GL_NONE; + } +}; + + +static void +dumpAttrib(StateWriter &writer, + const AttribDesc &desc, + const GLbyte *data) +{ + assert(desc); + + if (desc.numRows > 1) { + writer.beginArray(); } - for (GLint row = 0; row < numRows; ++row) { - if (numCols > 1) { - json.beginArray(); + for (GLint row = 0; row < desc.numRows; ++row) { + if (desc.numCols > 1) { + writer.beginArray(); } - for (GLint col = 0; col < numCols; ++col) { + for (GLint col = 0; col < desc.numCols; ++col) { union { const GLbyte *rawvalue; const GLfloat *fvalue; const GLdouble *dvalue; const GLint *ivalue; const GLuint *uivalue; + const GLint64 *i64value; + const GLuint64 *ui64value; } u; - u.rawvalue = (const GLbyte *)values + row*row_stride + col*col_stride; + u.rawvalue = data + row*desc.rowStride + col*desc.colStride; - switch (elemType) { + switch (desc.elemType) { case GL_FLOAT: - json.writeFloat(*u.fvalue); + writer.writeFloat(*u.fvalue); break; case GL_DOUBLE: - json.writeFloat(*u.dvalue); + writer.writeFloat(*u.dvalue); break; case GL_INT: - json.writeInt(*u.ivalue); + writer.writeInt(*u.ivalue); break; case GL_UNSIGNED_INT: - json.writeInt(*u.uivalue); + writer.writeInt(*u.uivalue); + break; + case GL_INT64_ARB: + writer.writeInt(*u.i64value); + break; + case GL_UNSIGNED_INT64_ARB: + writer.writeInt(*u.ui64value); break; case GL_BOOL: - json.writeBool(*u.uivalue); + writer.writeBool(*u.uivalue); break; default: assert(0); - json.writeNull(); + writer.writeNull(); break; } } - if (numCols > 1) { - json.endArray(); + if (desc.numCols > 1) { + writer.endArray(); } } - if (numRows > 1) { - json.endArray(); + if (desc.numRows > 1) { + writer.endArray(); + } +} + + +static void +dumpAttribArray(StateWriter &writer, + const std::string & name, + const AttribDesc &desc, + const GLbyte *data) +{ + writer.beginMember(name); + if (desc.size > 1) { + writer.beginArray(); + } + + for (GLint i = 0; i < desc.size; ++i) { + const GLbyte *row = data + desc.arrayStride*i; + + dumpAttrib(writer, desc, row); + } + + if (desc.size > 1) { + writer.endArray(); } + writer.endMember(); } @@ -282,7 +358,14 @@ dumpUniformValues(JSONWriter &json, GLenum type, const void *values, GLint matri * Dump an uniform that belows to an uniform block. */ static void -dumpUniformBlock(JSONWriter &json, GLint program, GLint size, GLenum type, const GLchar *name, GLuint index, GLuint block_index) { +dumpUniformBlock(StateWriter &writer, + GLint program, + GLint size, + GLenum type, + const GLchar *name, + GLuint index, + GLuint block_index) +{ GLint offset = 0; GLint array_stride = 0; @@ -299,6 +382,11 @@ dumpUniformBlock(JSONWriter &json, GLint program, GLint size, GLenum type, const return; } + AttribDesc desc(type, size, array_stride, matrix_stride, is_row_major); + if (!desc) { + return; + } + if (0) { GLint active_uniform_block_max_name_length = 0; glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &active_uniform_block_max_name_length); @@ -312,58 +400,36 @@ dumpUniformBlock(JSONWriter &json, GLint program, GLint size, GLenum type, const glGetActiveUniformBlockName(program, index, active_uniform_block_max_name_length, &length, block_name); std::cerr - << "uniform `" << name << "`, size " << size << ", type " << enumToString(type) << "\n" + << "uniform `" << name << "`, size " << size << ", type " << enumToString(desc.type) << "\n" << " block " << block_index << ", name `" << block_name << "`, size " << block_data_size << "; binding " << slot << "; \n" << " offset " << offset << ", array stride " << array_stride << ", matrix stride " << matrix_stride << ", row_major " << is_row_major << "\n" ; - delete block_name; + delete [] block_name; } GLint ubo = 0; glGetIntegeri_v(GL_UNIFORM_BUFFER_BINDING, slot, &ubo); + GLint start = 0; + glGetIntegeri_v(GL_UNIFORM_BUFFER_START, slot, &start); - GLint previous_ubo = 0; - glGetIntegerv(GL_UNIFORM_BUFFER_BINDING, &previous_ubo); - - glBindBuffer(GL_UNIFORM_BUFFER, ubo); - - const GLbyte *raw_data = (const GLbyte *)glMapBuffer(GL_UNIFORM_BUFFER, GL_READ_ONLY); + BufferMapping mapping; + const GLbyte *raw_data = (const GLbyte *)mapping.map(GL_UNIFORM_BUFFER, ubo); if (raw_data) { std::string qualifiedName = resolveUniformName(name, size); - for (GLint i = 0; i < size; ++i) { - std::stringstream ss; - ss << qualifiedName; - - if (size > 1) { - ss << '[' << i << ']'; - } - - std::string elemName = ss.str(); - - json.beginMember(elemName); - - const GLbyte *row = raw_data + offset + array_stride*i; - - dumpUniformValues(json, type, row, matrix_stride, is_row_major); - - json.endMember(); - } - - glUnmapBuffer(GL_UNIFORM_BUFFER); + dumpAttribArray(writer, qualifiedName, desc, raw_data + start + offset); } - - glBindBuffer(GL_UNIFORM_BUFFER, previous_ubo); } static void -dumpUniform(JSONWriter &json, GLint program, GLint size, GLenum type, const GLchar *name) { - GLenum elemType; - GLint numCols, numRows; - _gl_uniform_size(type, elemType, numCols, numRows); - if (elemType == GL_NONE) { +dumpUniform(StateWriter &writer, + GLint program, + const AttribDesc & desc, + const GLchar *name) +{ + if (desc.elemType == GL_NONE) { return; } @@ -372,20 +438,26 @@ dumpUniform(JSONWriter &json, GLint program, GLint size, GLenum type, const GLch GLdouble dvalues[4*4]; GLint ivalues[4*4]; GLuint uivalues[4*4]; + GLint64 i64values[4*4]; + GLuint64 ui64values[4*4]; + GLbyte data[4*4*4]; } u; GLint i; - std::string qualifiedName = resolveUniformName(name, size); + std::string qualifiedName = resolveUniformName(name, desc.size); - for (i = 0; i < size; ++i) { + writer.beginMember(qualifiedName); + if (desc.size > 1) { + writer.beginArray(); + } + + for (i = 0; i < desc.size; ++i) { std::stringstream ss; ss << qualifiedName; - - if (size > 1) { + if (desc.size > 1) { ss << '[' << i << ']'; } - std::string elemName = ss.str(); GLint location = glGetUniformLocation(program, elemName.c_str()); @@ -394,9 +466,7 @@ dumpUniform(JSONWriter &json, GLint program, GLint size, GLenum type, const GLch continue; } - json.beginMember(elemName); - - switch (elemType) { + switch (desc.elemType) { case GL_FLOAT: glGetUniformfv(program, location, u.fvalues); break; @@ -409,6 +479,12 @@ dumpUniform(JSONWriter &json, GLint program, GLint size, GLenum type, const GLch case GL_UNSIGNED_INT: glGetUniformuiv(program, location, u.uivalues); break; + case GL_INT64_ARB: + glGetUniformi64vARB(program, location, u.i64values); + break; + case GL_UNSIGNED_INT64_ARB: + glGetUniformui64vARB(program, location, u.ui64values); + break; case GL_BOOL: glGetUniformiv(program, location, u.ivalues); break; @@ -417,84 +493,19 @@ dumpUniform(JSONWriter &json, GLint program, GLint size, GLenum type, const GLch break; } - dumpUniformValues(json, type, &u); - - json.endMember(); + dumpAttrib(writer, desc, u.data); } -} - -static void -dumpUniformARB(JSONWriter &json, GLhandleARB programObj, GLint size, GLenum type, const GLchar *name) { - GLenum elemType; - GLint numCols, numRows; - _gl_uniform_size(type, elemType, numCols, numRows); - GLint numElems = numCols * numRows; - if (elemType == GL_NONE) { - return; + if (desc.size > 1) { + writer.endArray(); } - GLfloat fvalues[4*4]; - union { - GLdouble dvalues[4*4]; - GLfloat fvalues[4*4]; - GLint ivalues[4*4]; - } u; - - GLint i, j; - - std::string qualifiedName = resolveUniformName(name, size); - - for (i = 0; i < size; ++i) { - std::stringstream ss; - ss << qualifiedName; - - if (size > 1) { - ss << '[' << i << ']'; - } - - std::string elemName = ss.str(); - - json.beginMember(elemName); - - GLint location = glGetUniformLocationARB(programObj, elemName.c_str()); - if (location == -1) { - continue; - } - - switch (elemType) { - case GL_DOUBLE: - // glGetUniformdvARB does not exists - glGetUniformfvARB(programObj, location, fvalues); - for (j = 0; j < numElems; ++j) { - u.dvalues[j] = fvalues[j]; - } - break; - case GL_FLOAT: - glGetUniformfvARB(programObj, location, fvalues); - break; - case GL_UNSIGNED_INT: - // glGetUniformuivARB does not exists - case GL_INT: - glGetUniformivARB(programObj, location, u.ivalues); - break; - case GL_BOOL: - glGetUniformivARB(programObj, location, u.ivalues); - break; - default: - assert(0); - break; - } - - dumpUniformValues(json, type, &u); - - json.endMember(); - } + writer.endMember(); } static inline void -dumpProgramUniforms(JSONWriter &json, GLint program) +dumpProgramUniforms(StateWriter &writer, GLint program) { GLint active_uniforms = 0; glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniforms); @@ -515,20 +526,21 @@ dumpProgramUniforms(JSONWriter &json, GLint program) GLenum type = GL_NONE; glGetActiveUniform(program, index, active_uniform_max_length, &length, &size, &type, name); - if (isBuiltinUniform(name)) { + if (isBuiltinName(name)) { continue; } GLint location = glGetUniformLocation(program, name); if (location != -1) { - dumpUniform(json, program, size, type, name); + AttribDesc desc(type, size); + dumpUniform(writer, program, desc, name); continue; } GLint block_index = -1; glGetActiveUniformsiv(program, 1, &index, GL_UNIFORM_BLOCK_INDEX, &block_index); if (block_index != -1) { - dumpUniformBlock(json, program, size, type, name, index, block_index); + dumpUniformBlock(writer, program, size, type, name, index, block_index); continue; } @@ -542,43 +554,178 @@ dumpProgramUniforms(JSONWriter &json, GLint program) } -static inline void -dumpProgramObjUniforms(JSONWriter &json, GLhandleARB programObj) +// Calculate how many elements (or vertices) can fit in the specified buffer. +static unsigned +calcNumElements(GLsizei bufferSize, + GLsizei elemOffset, + GLsizei elemSize, + GLsizei elemStride) { - GLint active_uniforms = 0; - glGetObjectParameterivARB(programObj, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &active_uniforms); - if (!active_uniforms) { - return; + if (0 >= bufferSize || + elemOffset >= bufferSize || + elemOffset + elemSize >= bufferSize) { + return 0; } - 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) { + assert(elemStride >= 0); + if (elemStride < 0) { + return 1; + } + + if (elemStride == 0) { + // XXX: It should be infinite, but lets return something more manageable here. + return 0x10000; + } + + return (bufferSize - elemOffset - elemSize) / elemStride; +} + + +struct TransformFeedbackAttrib { + std::string name; + AttribDesc desc; + GLsizei offset; + GLsizei stride; + GLsizei size; + BufferMapping mapping; + const GLbyte *map; +}; + + +static inline void +dumpTransformFeedback(StateWriter &writer, GLint program) +{ + GLint transform_feedback_varyings = 0; + glGetProgramiv(program, GL_TRANSFORM_FEEDBACK_VARYINGS, &transform_feedback_varyings); + if (!transform_feedback_varyings) { return; } - for (GLint index = 0; index < active_uniforms; ++index) { + GLint max_name_length = 0; + glGetProgramiv(program, GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, &max_name_length); + std::vector<GLchar> name(max_name_length); + + GLint buffer_mode = GL_INTERLEAVED_ATTRIBS; + glGetProgramiv(program, GL_TRANSFORM_FEEDBACK_BUFFER_MODE, &buffer_mode); + + std::vector<TransformFeedbackAttrib> attribs(transform_feedback_varyings); + + // Calculate the offsets and strides of each attribute according to + // the value of GL_TRANSFORM_FEEDBACK_BUFFER_MODE + GLsizei cum_attrib_offset = 0; + for (GLint slot = 0; slot < transform_feedback_varyings; ++slot) { + TransformFeedbackAttrib & attrib = attribs[slot]; + GLsizei length = 0; - GLint size = 0; + GLsizei size = 0; GLenum type = GL_NONE; - glGetActiveUniformARB(programObj, index, active_uniform_max_length, &length, &size, &type, name); + glGetTransformFeedbackVarying(program, slot, max_name_length, &length, &size, &type, &name[0]); + + attrib.name = &name[0]; + + const AttribDesc & desc = attrib.desc = AttribDesc(type, size); + if (!desc) { + return; + } + + attrib.size = desc.arrayStride; + + switch (buffer_mode) { + case GL_INTERLEAVED_ATTRIBS: + attrib.offset = cum_attrib_offset; + break; + case GL_SEPARATE_ATTRIBS: + attrib.offset = 0; + attrib.stride = desc.arrayStride; + break; + default: + assert(0); + attrib.offset = 0; + attrib.stride = 0; + } - if (isBuiltinUniform(name)) { - continue; + cum_attrib_offset += desc.arrayStride; } + if (buffer_mode == GL_INTERLEAVED_ATTRIBS) { + for (GLint slot = 0; slot < transform_feedback_varyings; ++slot) { + TransformFeedbackAttrib & attrib = attribs[slot]; + attrib.stride = cum_attrib_offset; + } + } + + GLint previous_tbo = 0; + glGetIntegerv(GL_TRANSFORM_FEEDBACK_BUFFER_BINDING, &previous_tbo); + + // Map the buffers and calculate how many vertices can they hold + // XXX: We currently limit to 1024, or things can get significantly slow. + unsigned numVertices = 16*1024; + for (GLint slot = 0; slot < transform_feedback_varyings; ++slot) { + TransformFeedbackAttrib & attrib = attribs[slot]; + attrib.map = NULL; + if (slot == 0 || buffer_mode != GL_INTERLEAVED_ATTRIBS) { + GLint tbo = 0; + glGetIntegeri_v(GL_TRANSFORM_FEEDBACK_BUFFER_BINDING, slot, &tbo); + if (!tbo) { + numVertices = 0; + continue; + } - dumpUniformARB(json, programObj, size, type, name); + GLint start = 0; + glGetIntegeri_v(GL_TRANSFORM_FEEDBACK_BUFFER_START, slot, &start); + GLint size = 0; + glGetIntegeri_v(GL_TRANSFORM_FEEDBACK_BUFFER_SIZE, slot, &size); + + glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, tbo); + + if (size == 0) { + glGetBufferParameteriv(GL_TRANSFORM_FEEDBACK_BUFFER, GL_BUFFER_SIZE, &size); + assert(size >= start); + size -= start; + } + + unsigned numAttribVertices = calcNumElements(size, + attrib.offset, + attrib.size, + attrib.stride); + numVertices = std::min(numVertices, numAttribVertices); + + attrib.map = (const GLbyte *)attrib.mapping.map(GL_TRANSFORM_FEEDBACK_BUFFER, tbo) + start; + } else { + attrib.map = attribs[0].map; + } } + glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, previous_tbo); + + // Actually dump the vertices + writer.beginMember("GL_TRANSFORM_FEEDBACK"); + writer.beginArray(); + for (unsigned vertex = 0; vertex < numVertices; ++vertex) { + writer.beginObject(); + for (GLint slot = 0; slot < transform_feedback_varyings; ++slot) { + TransformFeedbackAttrib & attrib = attribs[slot]; + if (!attrib.map) { + continue; + } - delete [] name; + const AttribDesc & desc = attrib.desc; + assert(desc); + + const GLbyte *vertex_data = attrib.map + attrib.stride*vertex + attrib.offset; + dumpAttribArray(writer, attrib.name, desc, vertex_data); + } + writer.endObject(); + } + writer.endArray(); + writer.endMember(); } static inline void -dumpArbProgram(JSONWriter &json, GLenum target) +dumpArbProgram(StateWriter &writer, Context &context, GLenum target) { - if (!glIsEnabled(target)) { + if (context.ES || + context.core || + !glIsEnabled(target)) { return; } @@ -593,18 +740,20 @@ dumpArbProgram(JSONWriter &json, GLenum target) glGetProgramStringARB(target, GL_PROGRAM_STRING_ARB, source); source[program_length] = 0; - json.beginMember(enumToString(target)); - json.writeString(source); - json.endMember(); + writer.beginMember(enumToString(target)); + writer.writeString(source); + writer.endMember(); delete [] source; } static inline void -dumpArbProgramUniforms(JSONWriter &json, GLenum target, const char *prefix) +dumpArbProgramUniforms(StateWriter &writer, Context &context, GLenum target, const char *prefix) { - if (!glIsEnabled(target)) { + if (context.ES || + context.core || + !glIsEnabled(target)) { return; } @@ -627,14 +776,14 @@ dumpArbProgramUniforms(JSONWriter &json, GLenum target, const char *prefix) char name[256]; snprintf(name, sizeof name, "%sprogram.local[%i]", prefix, index); - json.beginMember(name); - json.beginArray(); - json.writeFloat(params[0]); - json.writeFloat(params[1]); - json.writeFloat(params[2]); - json.writeFloat(params[3]); - json.endArray(); - json.endMember(); + writer.beginMember(name); + writer.beginArray(); + writer.writeFloat(params[0]); + writer.writeFloat(params[1]); + writer.writeFloat(params[2]); + writer.writeFloat(params[3]); + writer.endArray(); + writer.endMember(); } GLint max_program_env_parameters = 0; @@ -650,31 +799,179 @@ dumpArbProgramUniforms(JSONWriter &json, GLenum target, const char *prefix) char name[256]; snprintf(name, sizeof name, "%sprogram.env[%i]", prefix, index); - json.beginMember(name); - json.beginArray(); - json.writeFloat(params[0]); - json.writeFloat(params[1]); - json.writeFloat(params[2]); - json.writeFloat(params[3]); - json.endArray(); - json.endMember(); + writer.beginMember(name); + writer.beginArray(); + writer.writeFloat(params[0]); + writer.writeFloat(params[1]); + writer.writeFloat(params[2]); + writer.writeFloat(params[3]); + writer.endArray(); + writer.endMember(); } } static void -dumpProgramUniformsStage(JSONWriter &json, GLint program, const char *stage) +dumpProgramUniformsStage(StateWriter &writer, GLint program, const char *stage) { - if (program) { - json.beginMember(stage); - json.beginObject(); - dumpProgramUniforms(json, program); - json.endObject(); - json.endMember(); + if (program <= 0) { + return; + } + + writer.beginMember(stage); + writer.beginObject(); + dumpProgramUniforms(writer, program); + writer.endObject(); + writer.endMember(); +} + +struct VertexAttrib { + std::string name; + AttribDesc desc; + GLsizei offset = 0; + GLsizei stride = 0; + GLsizei size = 0; + const GLbyte *map; +}; + +static void +dumpVertexAttributes(StateWriter &writer, Context &context, GLint program) +{ + if (program <= 0) { + return; + } + + GLint activeAttribs = 0; + glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &activeAttribs); + if (!activeAttribs) { + return; + } + + GLint max_name_length = 0; + glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_name_length); + std::vector<GLchar> name(max_name_length); + + std::map<GLuint, BufferMapping> mappings; + std::vector<VertexAttrib> attribs; + unsigned count = ~0U; + + for (GLint index = 0; index < activeAttribs; ++index) { + GLsizei length = 0; + GLint shaderSize = 0; + GLenum shaderType = GL_NONE; + glGetActiveAttrib(program, index, max_name_length, &length, &shaderSize, &shaderType, &name[0]); + + if (isBuiltinName(&name[0])) { + // TODO: Handle built-ins too + std::cerr << "warning: dumping of built-in vertex attribute (" << &name[0] << ") not yet supported\n"; + continue; + } + + GLint location = glGetAttribLocation(program, &name[0]); + if (location < 0) { + continue; + } + + GLint buffer = 0; + glGetVertexAttribiv(location, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &buffer); + if (!buffer) { + continue; + } + + + + GLint size = 0; + glGetVertexAttribiv(location, GL_VERTEX_ATTRIB_ARRAY_SIZE, &size); + GLint type = 0; + glGetVertexAttribiv(location, GL_VERTEX_ATTRIB_ARRAY_TYPE, &type); + GLint normalized = 0; + glGetVertexAttribiv(location, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &normalized); + GLint stride = 0; + glGetVertexAttribiv(location, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &stride); + GLvoid * pointer = 0; + glGetVertexAttribPointerv(location, GL_VERTEX_ATTRIB_ARRAY_POINTER, &pointer); + + GLint offset = reinterpret_cast<intptr_t>(pointer); + assert(offset >= 0); + + GLint divisor = 0; + glGetVertexAttribiv(location, GL_VERTEX_ATTRIB_ARRAY_DIVISOR, &divisor); + if (divisor) { + // TODO: not clear the best way of presenting instanced attibutes on the dump + std::cerr << "warning: dumping of instanced attributes (" << &name[0] << ") not yet supported\n"; + return; + } + + if (size == GL_BGRA) { + std::cerr << "warning: dumping of GL_BGRA attributes (" << &name[0] << ") not yet supported\n"; + size = 4; + } + + AttribDesc desc(type, size); + if (!desc) { + std::cerr << "warning: dumping of packed attribute (" << &name[0] << ") not yet supported\n"; + // TODO: handle + continue; + } + + attribs.emplace_back(); + VertexAttrib &attrib = attribs.back(); + attrib.name = &name[0]; + + // TODO handle normalized attributes + if (normalized) { + std::cerr << "warning: dumping of normalized attribute (" << &name[0] << ") not yet supported\n"; + } + + attrib.desc = desc; + GLsizei attribSize = attrib.desc.arrayStride; + + if (stride == 0) { + // tightly packed + stride = attribSize; + } + + attrib.offset = offset; + attrib.stride = stride; + + BufferMapping &mapping = mappings[buffer]; + attrib.map = (const GLbyte *)mapping.map(GL_ARRAY_BUFFER, buffer); + + BufferBinding bb(GL_ARRAY_BUFFER, buffer); + GLint bufferSize = 0; + glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &bufferSize); + + if (bufferSize <= offset || + bufferSize <= offset + attribSize) { + return; + } else { + unsigned attribCount = (bufferSize - offset - attribSize)/stride + 1; + count = std::min(count, attribCount); + } + } + + if (count == 0 || count == ~0U || attribs.empty()) { + return; + } + + writer.beginMember("vertices"); + writer.beginArray(); + for (unsigned vertex = 0; vertex < count; ++vertex) { + writer.beginObject(); + for (auto attrib : attribs) { + const AttribDesc & desc = attrib.desc; + assert(desc); + + const GLbyte *vertex_data = attrib.map + attrib.stride*vertex + attrib.offset; + dumpAttribArray(writer, attrib.name, desc, vertex_data); + } + writer.endObject(); } + writer.endArray(); + writer.endMember(); } void -dumpShadersUniforms(JSONWriter &json, Context &context) +dumpShadersUniforms(StateWriter &writer, Context &context) { GLint pipeline = 0; GLint vertex_program = 0; @@ -697,53 +994,109 @@ dumpShadersUniforms(JSONWriter &json, Context &context) } GLint program = 0; - GLhandleARB programObj = 0; if (!pipeline) { glGetIntegerv(GL_CURRENT_PROGRAM, &program); - if (!context.ES && !program) { - programObj = glGetHandleARB(GL_PROGRAM_OBJECT_ARB); - } } - json.beginMember("shaders"); - json.beginObject(); + writer.beginMember("shaders"); + writer.beginObject(); if (pipeline) { - dumpProgram(json, vertex_program); - dumpProgram(json, fragment_program); - dumpProgram(json, geometry_program); - dumpProgram(json, tess_control_program); - dumpProgram(json, tess_evaluation_program); - dumpProgram(json, compute_program); + dumpProgram(writer, context, vertex_program); + dumpProgram(writer, context, fragment_program); + dumpProgram(writer, context, geometry_program); + dumpProgram(writer, context, tess_control_program); + dumpProgram(writer, context, tess_evaluation_program); + dumpProgram(writer, context, compute_program); } else if (program) { - dumpProgram(json, program); - } else if (programObj) { - dumpProgramObj(json, programObj); + dumpProgram(writer, context, program); } else { - dumpArbProgram(json, GL_FRAGMENT_PROGRAM_ARB); - dumpArbProgram(json, GL_VERTEX_PROGRAM_ARB); + dumpArbProgram(writer, context, GL_FRAGMENT_PROGRAM_ARB); + dumpArbProgram(writer, context, GL_VERTEX_PROGRAM_ARB); } - json.endObject(); - json.endMember(); // shaders + writer.endObject(); + writer.endMember(); // shaders - json.beginMember("uniforms"); - json.beginObject(); + writer.beginMember("uniforms"); + writer.beginObject(); if (pipeline) { - dumpProgramUniformsStage(json, vertex_program, "GL_VERTEX_SHADER"); - dumpProgramUniformsStage(json, fragment_program, "GL_FRAGMENT_SHADER"); - dumpProgramUniformsStage(json, geometry_program, "GL_GEOMETRY_SHADER"); - dumpProgramUniformsStage(json, tess_control_program, "GL_TESS_CONTROL_SHADER"); - dumpProgramUniformsStage(json, tess_evaluation_program, "GL_TESS_EVALUATION_SHADER"); - dumpProgramUniformsStage(json, compute_program, "GL_COMPUTE_SHADER"); + dumpProgramUniformsStage(writer, vertex_program, "GL_VERTEX_SHADER"); + dumpProgramUniformsStage(writer, fragment_program, "GL_FRAGMENT_SHADER"); + dumpProgramUniformsStage(writer, geometry_program, "GL_GEOMETRY_SHADER"); + dumpProgramUniformsStage(writer, tess_control_program, "GL_TESS_CONTROL_SHADER"); + dumpProgramUniformsStage(writer, tess_evaluation_program, "GL_TESS_EVALUATION_SHADER"); + dumpProgramUniformsStage(writer, compute_program, "GL_COMPUTE_SHADER"); } else if (program) { - dumpProgramUniforms(json, program); - } else if (programObj) { - dumpProgramObjUniforms(json, programObj); + dumpProgramUniforms(writer, program); } else { - dumpArbProgramUniforms(json, GL_FRAGMENT_PROGRAM_ARB, "fp."); - dumpArbProgramUniforms(json, GL_VERTEX_PROGRAM_ARB, "vp."); + dumpArbProgramUniforms(writer, context, GL_FRAGMENT_PROGRAM_ARB, "fp."); + dumpArbProgramUniforms(writer, context, GL_VERTEX_PROGRAM_ARB, "vp."); + } + writer.endObject(); + writer.endMember(); // uniforms + + writer.beginMember("buffers"); + writer.beginObject(); + if (pipeline) { + dumpVertexAttributes(writer, context, vertex_program); + } else { + dumpVertexAttributes(writer, context, program); + } + if (program) { + dumpTransformFeedback(writer, program); } - json.endObject(); - json.endMember(); // uniforms + writer.endObject(); + writer.endMember(); // buffers +} + + +bool +isGeometryShaderBound(Context &context) +{ + GLint pipeline = 0; + GLint program = 0; + + if (!context.ES) { + glGetIntegerv(GL_PROGRAM_PIPELINE_BINDING, &pipeline); + if (pipeline) { + glGetProgramPipelineiv(pipeline, GL_GEOMETRY_SHADER, &program); + } + } + + if (!pipeline) { + glGetIntegerv(GL_CURRENT_PROGRAM, &program); + } + + if (!program) { + return false; + } + + GLint attached_shaders = 0; + glGetProgramiv(program, GL_ATTACHED_SHADERS, &attached_shaders); + if (!attached_shaders) { + return false; + } + + GLuint *shaders = new GLuint[attached_shaders]; + GLsizei count = 0; + glGetAttachedShaders(program, attached_shaders, &count, shaders); + for (GLsizei i = 0; i < count; ++i) { + GLuint shader = shaders[i]; + if (!shader) { + continue; + } + + GLint shader_type = 0; + glGetShaderiv(shader, GL_SHADER_TYPE, &shader_type); + + if (shader_type == GL_GEOMETRY_SHADER) { + delete [] shaders; + return true; + } + } + + delete [] shaders; + + return false; } |