From 8158af7a9f97ef2c9e1c8fb776162e301f400b95 Mon Sep 17 00:00:00 2001 From: baldurk Date: Tue, 12 Aug 2014 17:01:18 +0100 Subject: [PATCH] Attempt to make separable shader prog, add input/output fetching * Also moved shader reflection code into separate file --- renderdoc/Makefile | 1 + renderdoc/driver/gl/gl_replay.cpp | 479 +------------ renderdoc/driver/gl/gl_shader_refl.cpp | 676 ++++++++++++++++++ renderdoc/driver/gl/gl_shader_refl.h | 30 + .../driver/gl/wrappers/gl_shader_funcs.cpp | 29 +- renderdoc/renderdoc.vcxproj | 2 + renderdoc/renderdoc.vcxproj.filters | 6 + 7 files changed, 752 insertions(+), 471 deletions(-) create mode 100644 renderdoc/driver/gl/gl_shader_refl.cpp create mode 100644 renderdoc/driver/gl/gl_shader_refl.h diff --git a/renderdoc/Makefile b/renderdoc/Makefile index 80bf1a9d4..9b144e5f9 100644 --- a/renderdoc/Makefile +++ b/renderdoc/Makefile @@ -41,6 +41,7 @@ driver/gl/gl_manager.o \ driver/gl/gl_debug.o \ driver/gl/gl_replay.o \ driver/gl/gl_replay_linux.o \ +driver/gl/gl_shader_refl.o \ driver/gl/gl_resources.o \ driver/gl/gl_renderstate.o \ driver/gl/wrappers/gl_buffer_funcs.o \ diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index 6a4f4635f..23e73a62d 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -571,485 +571,24 @@ ShaderReflection *GLReplay::GetShader(ResourceId id) MakeCurrentReplayContext(&m_ReplayCtx); - // TODO this shouldn't be tied to the current program - // This is only really needed to fill the latest sampler uniform values, - // which could potentially be done instead in SavePipelineState? - GLuint curProg = 0; - gl.glGetIntegerv(eGL_CURRENT_PROGRAM, (GLint*)&curProg); - void *ctx = m_ReplayCtx.ctx; auto &shaderDetails = m_pDriver->m_Shaders[id]; - - if(curProg == 0 && shaderDetails.prog != 0) + + if(shaderDetails.prog == 0) { - curProg = shaderDetails.prog; - } - else if(curProg == 0) - { - RDCERR("Can't get shader details without program"); + RDCERR("Can't get shader details without separable program"); return NULL; } + // TODO shader reflection struct shouldn't be tied to the current program + // This is only really needed to fill the latest sampler uniform values, + // which should be saved in a mapping structure + GLuint curProg = 0; + gl.glGetIntegerv(eGL_CURRENT_PROGRAM, (GLint*)&curProg); + auto &refl = shaderDetails.reflection; - // initialise reflection data - // TODO: do this earlier. In glLinkProgram? - if(refl.DebugInfo.files.count == 0) - { - refl.DebugInfo.entryFunc = "main"; - refl.DebugInfo.compileFlags = 0; - create_array_uninit(refl.DebugInfo.files, shaderDetails.sources.size()); - for(size_t i=0; i < shaderDetails.sources.size(); i++) - { - refl.DebugInfo.files[i].first = StringFormat::Fmt("source%u.glsl", (uint32_t)i); - refl.DebugInfo.files[i].second = shaderDetails.sources[i]; - } - - refl.Disassembly = ""; - - vector resources; - - GLint numUniforms = 0; - gl.glGetProgramInterfaceiv(curProg, eGL_UNIFORM, eGL_ACTIVE_RESOURCES, &numUniforms); - - const size_t numProps = 8; - - GLenum resProps[numProps] = { - eGL_REFERENCED_BY_VERTEX_SHADER, - - eGL_TYPE, eGL_NAME_LENGTH, eGL_LOCATION, eGL_BLOCK_INDEX, eGL_ARRAY_SIZE, eGL_OFFSET, eGL_IS_ROW_MAJOR, - }; - - if(shaderDetails.type == eGL_VERTEX_SHADER) resProps[0] = eGL_REFERENCED_BY_VERTEX_SHADER; - if(shaderDetails.type == eGL_TESS_CONTROL_SHADER) resProps[0] = eGL_REFERENCED_BY_TESS_CONTROL_SHADER; - if(shaderDetails.type == eGL_TESS_EVALUATION_SHADER) resProps[0] = eGL_REFERENCED_BY_TESS_EVALUATION_SHADER; - if(shaderDetails.type == eGL_GEOMETRY_SHADER) resProps[0] = eGL_REFERENCED_BY_GEOMETRY_SHADER; - if(shaderDetails.type == eGL_FRAGMENT_SHADER) resProps[0] = eGL_REFERENCED_BY_FRAGMENT_SHADER; - if(shaderDetails.type == eGL_COMPUTE_SHADER) resProps[0] = eGL_REFERENCED_BY_COMPUTE_SHADER; - - for(GLint u=0; u < numUniforms; u++) - { - GLint values[numProps]; - gl.glGetProgramResourceiv(curProg, eGL_UNIFORM, u, numProps, resProps, numProps, NULL, values); - - // skip if unused by this stage - if(values[0] == GL_FALSE) - continue; - - ShaderResource res; - res.IsSampler = false; // no separate sampler objects in GL - res.IsSRV = true; - res.IsTexture = true; - res.IsUAV = false; - res.variableType.descriptor.rows = 1; - res.variableType.descriptor.cols = 4; - res.variableType.descriptor.elements = 1; - - // float samplers - if(values[1] == GL_SAMPLER_BUFFER) - { - res.resType = eResType_Buffer; - res.variableType.descriptor.name = "samplerBuffer"; - } - else if(values[1] == GL_SAMPLER_1D) - { - res.resType = eResType_Texture1D; - res.variableType.descriptor.name = "sampler1D"; - } - else if(values[1] == GL_SAMPLER_1D_ARRAY) - { - res.resType = eResType_Texture1DArray; - res.variableType.descriptor.name = "sampler1DArray"; - } - else if(values[1] == GL_SAMPLER_1D_SHADOW) - { - res.resType = eResType_Texture1D; - res.variableType.descriptor.name = "sampler1DShadow"; - } - else if(values[1] == GL_SAMPLER_1D_ARRAY_SHADOW) - { - res.resType = eResType_Texture1DArray; - res.variableType.descriptor.name = "sampler1DArrayShadow"; - } - else if(values[1] == GL_SAMPLER_2D) - { - res.resType = eResType_Texture2D; - res.variableType.descriptor.name = "sampler2D"; - } - else if(values[1] == GL_SAMPLER_2D_ARRAY) - { - res.resType = eResType_Texture2DArray; - res.variableType.descriptor.name = "sampler2DArray"; - } - else if(values[1] == GL_SAMPLER_2D_SHADOW) - { - res.resType = eResType_Texture2D; - res.variableType.descriptor.name = "sampler2DShadow"; - } - else if(values[1] == GL_SAMPLER_2D_ARRAY_SHADOW) - { - res.resType = eResType_Texture2DArray; - res.variableType.descriptor.name = "sampler2DArrayShadow"; - } - else if(values[1] == GL_SAMPLER_2D_RECT) - { - res.resType = eResType_Texture2D; - res.variableType.descriptor.name = "sampler2DRect"; - } - else if(values[1] == GL_SAMPLER_2D_RECT_SHADOW) - { - res.resType = eResType_Texture2D; - res.variableType.descriptor.name = "sampler2DRectShadow"; - } - else if(values[1] == GL_SAMPLER_3D) - { - res.resType = eResType_Texture3D; - res.variableType.descriptor.name = "sampler3D"; - } - else if(values[1] == GL_SAMPLER_CUBE) - { - res.resType = eResType_TextureCube; - res.variableType.descriptor.name = "samplerCube"; - } - else if(values[1] == GL_SAMPLER_CUBE_SHADOW) - { - res.resType = eResType_TextureCube; - res.variableType.descriptor.name = "samplerCubeShadow"; - } - else if(values[1] == GL_SAMPLER_CUBE_MAP_ARRAY) - { - res.resType = eResType_TextureCubeArray; - res.variableType.descriptor.name = "samplerCubeArray"; - } - else if(values[1] == GL_SAMPLER_2D_MULTISAMPLE) - { - res.resType = eResType_Texture2DMS; - res.variableType.descriptor.name = "sampler2DMS"; - } - else if(values[1] == GL_SAMPLER_2D_MULTISAMPLE_ARRAY) - { - res.resType = eResType_Texture2DMSArray; - res.variableType.descriptor.name = "sampler2DMSArray"; - } - // int samplers - else if(values[1] == GL_INT_SAMPLER_BUFFER) - { - res.resType = eResType_Buffer; - res.variableType.descriptor.name = "samplerBuffer"; - } - else if(values[1] == GL_INT_SAMPLER_1D) - { - res.resType = eResType_Texture1D; - res.variableType.descriptor.name = "sampler1D"; - } - else if(values[1] == GL_INT_SAMPLER_1D_ARRAY) - { - res.resType = eResType_Texture1DArray; - res.variableType.descriptor.name = "sampler1DArray"; - } - else if(values[1] == GL_INT_SAMPLER_2D) - { - res.resType = eResType_Texture2D; - res.variableType.descriptor.name = "sampler2D"; - } - else if(values[1] == GL_INT_SAMPLER_2D_ARRAY) - { - res.resType = eResType_Texture2DArray; - res.variableType.descriptor.name = "sampler2DArray"; - } - else if(values[1] == GL_INT_SAMPLER_2D_RECT) - { - res.resType = eResType_Texture2D; - res.variableType.descriptor.name = "sampler2DRect"; - } - else if(values[1] == GL_INT_SAMPLER_3D) - { - res.resType = eResType_Texture3D; - res.variableType.descriptor.name = "sampler3D"; - } - else if(values[1] == GL_INT_SAMPLER_CUBE) - { - res.resType = eResType_TextureCube; - res.variableType.descriptor.name = "samplerCube"; - } - else if(values[1] == GL_INT_SAMPLER_CUBE_MAP_ARRAY) - { - res.resType = eResType_TextureCubeArray; - res.variableType.descriptor.name = "samplerCubeArray"; - } - else if(values[1] == GL_INT_SAMPLER_2D_MULTISAMPLE) - { - res.resType = eResType_Texture2DMS; - res.variableType.descriptor.name = "sampler2DMS"; - } - else if(values[1] == GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY) - { - res.resType = eResType_Texture2DMSArray; - res.variableType.descriptor.name = "sampler2DMSArray"; - } - // unsigned int samplers - else if(values[1] == GL_UNSIGNED_INT_SAMPLER_BUFFER) - { - res.resType = eResType_Buffer; - res.variableType.descriptor.name = "samplerBuffer"; - } - else if(values[1] == GL_UNSIGNED_INT_SAMPLER_1D) - { - res.resType = eResType_Texture1D; - res.variableType.descriptor.name = "sampler1D"; - } - else if(values[1] == GL_UNSIGNED_INT_SAMPLER_1D_ARRAY) - { - res.resType = eResType_Texture1DArray; - res.variableType.descriptor.name = "sampler1DArray"; - } - else if(values[1] == GL_UNSIGNED_INT_SAMPLER_2D) - { - res.resType = eResType_Texture2D; - res.variableType.descriptor.name = "sampler2D"; - } - else if(values[1] == GL_UNSIGNED_INT_SAMPLER_2D_ARRAY) - { - res.resType = eResType_Texture2DArray; - res.variableType.descriptor.name = "sampler2DArray"; - } - else if(values[1] == GL_UNSIGNED_INT_SAMPLER_2D_RECT) - { - res.resType = eResType_Texture2D; - res.variableType.descriptor.name = "sampler2DRect"; - } - else if(values[1] == GL_UNSIGNED_INT_SAMPLER_3D) - { - res.resType = eResType_Texture3D; - res.variableType.descriptor.name = "sampler3D"; - } - else if(values[1] == GL_UNSIGNED_INT_SAMPLER_CUBE) - { - res.resType = eResType_TextureCube; - res.variableType.descriptor.name = "samplerCube"; - } - else if(values[1] == GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY) - { - res.resType = eResType_TextureCubeArray; - res.variableType.descriptor.name = "samplerCubeArray"; - } - else if(values[1] == GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE) - { - res.resType = eResType_Texture2DMS; - res.variableType.descriptor.name = "sampler2DMS"; - } - else if(values[1] == GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY) - { - res.resType = eResType_Texture2DMSArray; - res.variableType.descriptor.name = "sampler2DMSArray"; - } - else - { - // not a sampler - continue; - } - - res.variableAddress = values[3]; - - create_array_uninit(res.name, values[2]); - gl.glGetProgramResourceName(curProg, eGL_UNIFORM, u, values[2], NULL, res.name.elems); - res.name.count--; // trim off trailing null - - resources.push_back(res); - } - - refl.Resources = resources; - - vector globalUniforms; - - GLint numUBOs = 0; - vector uboNames; - vector uboUsed; - vector *ubos = NULL; - - { - gl.glGetProgramInterfaceiv(curProg, eGL_UNIFORM_BLOCK, eGL_ACTIVE_RESOURCES, &numUBOs); - - ubos = new vector[numUBOs]; - uboNames.resize(numUBOs); - uboUsed.resize(numUBOs); - - for(GLint u=0; u < numUBOs; u++) - { - const size_t propnum = 2; - GLenum UBOResProps[propnum] = { - resProps[0], eGL_NAME_LENGTH, - }; - GLint UBOValues[propnum]; - gl.glGetProgramResourceiv(curProg, eGL_UNIFORM_BLOCK, u, propnum, UBOResProps, propnum, NULL, UBOValues); - - // skip if unused by this stage - if(UBOValues[0] == GL_FALSE) - continue; - - char *nm = new char[UBOValues[1]+1]; - gl.glGetProgramResourceName(curProg, eGL_UNIFORM_BLOCK, u, UBOValues[1]+1, NULL, nm); - uboNames[u] = nm; - delete[] nm; - - uboUsed[u] = true; - } - } - - for(GLint u=0; u < numUniforms; u++) - { - GLint values[numProps]; - gl.glGetProgramResourceiv(curProg, eGL_UNIFORM, u, numProps, resProps, numProps, NULL, values); - - // skip if global and unused by this stage, or if a UBO that is unused by this stage - if(values[0] == GL_FALSE && (values[4] == -1 || !uboUsed[ values[4] ])) - continue; - - ShaderConstant var; - - if(values[1] == GL_FLOAT_VEC4) - { - var.type.descriptor.name = "vec4"; - var.type.descriptor.type = eVar_Float; - var.type.descriptor.rows = 1; - var.type.descriptor.cols = 4; - var.type.descriptor.elements = RDCMAX(1, values[5]); - } - else if(values[1] == GL_FLOAT_VEC3) - { - var.type.descriptor.name = "vec3"; - var.type.descriptor.type = eVar_Float; - var.type.descriptor.rows = 1; - var.type.descriptor.cols = 3; - var.type.descriptor.elements = RDCMAX(1, values[5]); - } - else if(values[1] == GL_FLOAT_MAT4) - { - var.type.descriptor.name = "mat4"; - var.type.descriptor.type = eVar_Float; - var.type.descriptor.rows = 4; - var.type.descriptor.cols = 4; - var.type.descriptor.elements = RDCMAX(1, values[5]); - } - else if(values[1] == GL_UNSIGNED_INT_VEC4) - { - var.type.descriptor.name = "uvec4"; - var.type.descriptor.type = eVar_UInt; - var.type.descriptor.rows = 1; - var.type.descriptor.cols = 4; - var.type.descriptor.elements = RDCMAX(1, values[5]); - } - else if(values[1] == GL_UNSIGNED_INT) - { - var.type.descriptor.name = "uint"; - var.type.descriptor.type = eVar_UInt; - var.type.descriptor.rows = 1; - var.type.descriptor.cols = 1; - var.type.descriptor.elements = RDCMAX(1, values[5]); - } - else - { - // fill in more uniform types - continue; - } - - if(values[6] == -1 && values[3] >= 0) - { - var.reg.vec = values[3]; - var.reg.comp = 0; - } - else if(values[6] >= 0) - { - var.reg.vec = values[6] / 16; - var.reg.comp = (values[6] / 4) % 4; - - RDCASSERT((values[6] % 4) == 0); - } - else - { - var.reg.vec = var.reg.comp = ~0U; - } - - var.type.descriptor.rowMajorStorage = (values[7] >= 0); - - create_array_uninit(var.name, values[2]); - gl.glGetProgramResourceName(curProg, eGL_UNIFORM, u, values[2], NULL, var.name.elems); - var.name.count--; // trim off trailing null - - int32_t c = var.name.count; - - // trim off trailing [0] if it's an array - if(values[5] > 1 && var.name[c-3] == '[' && var.name[c-2] == '0' && var.name[c-1] == ']') - var.name.count -= 3; - - if(strchr(var.name.elems, '.')) - { - GLNOTIMP("Variable contains . - structure not reconstructed"); - } - - vector *UBO = &globalUniforms; - - // don't look at block uniforms just yet - if(values[4] != -1) - { - RDCASSERT(values[4] < numUBOs); - UBO = &ubos[ values[4] ]; - } - - UBO->push_back(var); - } - - vector cbuffers; - - if(ubos) - { - cbuffers.reserve(numUBOs + (globalUniforms.empty() ? 0 : 1)); - - for(int i=0; i < numUBOs; i++) - { - if(!ubos[i].empty()) - { - struct ubo_offset_sort - { - bool operator() (const ShaderConstant &a, const ShaderConstant &b) - { if(a.reg.vec == b.reg.vec) return a.reg.comp < b.reg.comp; else return a.reg.vec < b.reg.vec; } - }; - - ConstantBlock cblock; - cblock.name = uboNames[i]; - cblock.bufferAddress = i; - cblock.bindPoint = -1; - std::sort(ubos[i].begin(), ubos[i].end(), ubo_offset_sort()); - - cblock.variables = ubos[i]; - - cbuffers.push_back(cblock); - } - } - } - - if(!globalUniforms.empty()) - { - ConstantBlock globals; - globals.name = "$Globals"; - globals.bufferAddress = -1; - globals.bindPoint = -1; - globals.variables = globalUniforms; - - cbuffers.push_back(globals); - } - - delete[] ubos; - - // TODO: fill in Interfaces with shader subroutines? - // TODO: find a way of generating input/output signature. - // The only way I can think of doing this is to generate separable programs for each - // shader stage, but that requires modifying the glsl to redeclare built-in blocks if necessary. - - refl.ConstantBlocks = cbuffers; - } - // update with latest uniform values for(int32_t i=0; i < refl.Resources.count; i++) { diff --git a/renderdoc/driver/gl/gl_shader_refl.cpp b/renderdoc/driver/gl/gl_shader_refl.cpp new file mode 100644 index 000000000..f254ac74c --- /dev/null +++ b/renderdoc/driver/gl/gl_shader_refl.cpp @@ -0,0 +1,676 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2014 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "gl_shader_refl.h" + +#include + +GLuint MakeSeparableShaderProgram(const GLHookSet &gl, GLenum type, vector sources) +{ + const string block = "\nout gl_PerVertex { vec4 gl_Position; float gl_PointSize; float gl_ClipDistance[]; };"; + + const char **strings = new const char*[sources.size()]; + for(size_t i=0; i < sources.size(); i++) + strings[i] = sources[i].c_str(); + + GLuint sepProg = gl.glCreateShaderProgramv(type, (GLsizei)sources.size(), strings); + + GLint status; + gl.glGetProgramiv(sepProg, eGL_LINK_STATUS, &status); + + if(status == 0 && type == eGL_VERTEX_SHADER) + { + gl.glDeleteProgram(sepProg); + sepProg = 0; + + // try and patch up shader + // naively insert gl_PerVertex block as soon as it's valid (after #version) + // this will fail if e.g. a member of gl_PerVertex is declared at global scope + // (this is probably most likely for clipdistance if it's redeclared with a size) + + string substituted; + + for(size_t i=0; i < sources.size(); i++) + { + string &src = sources[i]; + + size_t len = src.length(); + size_t it = src.find("#version"); + if(it == string::npos) + continue; + + // skip #version + it += sizeof("#version")-1; + + // skip whitespace + while(it < len && (src[it] == ' ' || src[it] == '\t')) + ++it; + + // skip number + while(it < len && src[it] >= '0' && src[it] <= '9') + ++it; + + // skip whitespace + while(it < len && (src[it] == ' ' || src[it] == '\t')) + ++it; + + substituted = src; + + if(!strncmp(&substituted[it], "core" , 4)) it += sizeof("core")-1; + if(!strncmp(&substituted[it], "compatibility", 13)) it += sizeof("compatibility")-1; + if(!strncmp(&substituted[it], "es" , 2)) it += sizeof("es")-1; + + substituted.insert(it, block); + + strings[i] = substituted.c_str(); + + break; + } + + sepProg = gl.glCreateShaderProgramv(type, (GLsizei)sources.size(), strings); + + gl.glGetProgramiv(sepProg, eGL_LINK_STATUS, &status); + if(status == 0) + { + gl.glDeleteProgram(sepProg); + sepProg = 0; + } + } + + delete[] strings; + + return sepProg; +} + +void MakeShaderReflection(const GLHookSet &gl, GLenum shadType, GLuint sepProg, ShaderReflection &refl) +{ + refl.DebugInfo.entryFunc = "main"; + refl.DebugInfo.compileFlags = 0; + + refl.Disassembly = ""; + + vector resources; + + GLint numUniforms = 0; + gl.glGetProgramInterfaceiv(sepProg, eGL_UNIFORM, eGL_ACTIVE_RESOURCES, &numUniforms); + + const size_t numProps = 7; + + GLenum resProps[numProps] = { + eGL_TYPE, eGL_NAME_LENGTH, eGL_LOCATION, eGL_BLOCK_INDEX, eGL_ARRAY_SIZE, eGL_OFFSET, eGL_IS_ROW_MAJOR, + }; + + for(GLint u=0; u < numUniforms; u++) + { + GLint values[numProps]; + gl.glGetProgramResourceiv(sepProg, eGL_UNIFORM, u, numProps, resProps, numProps, NULL, values); + + ShaderResource res; + res.IsSampler = false; // no separate sampler objects in GL + res.IsSRV = true; + res.IsTexture = true; + res.IsUAV = false; + res.variableType.descriptor.rows = 1; + res.variableType.descriptor.cols = 4; + res.variableType.descriptor.elements = 1; + + // float samplers + if(values[0] == GL_SAMPLER_BUFFER) + { + res.resType = eResType_Buffer; + res.variableType.descriptor.name = "samplerBuffer"; + } + else if(values[0] == GL_SAMPLER_1D) + { + res.resType = eResType_Texture1D; + res.variableType.descriptor.name = "sampler1D"; + } + else if(values[0] == GL_SAMPLER_1D_ARRAY) + { + res.resType = eResType_Texture1DArray; + res.variableType.descriptor.name = "sampler1DArray"; + } + else if(values[0] == GL_SAMPLER_1D_SHADOW) + { + res.resType = eResType_Texture1D; + res.variableType.descriptor.name = "sampler1DShadow"; + } + else if(values[0] == GL_SAMPLER_1D_ARRAY_SHADOW) + { + res.resType = eResType_Texture1DArray; + res.variableType.descriptor.name = "sampler1DArrayShadow"; + } + else if(values[0] == GL_SAMPLER_2D) + { + res.resType = eResType_Texture2D; + res.variableType.descriptor.name = "sampler2D"; + } + else if(values[0] == GL_SAMPLER_2D_ARRAY) + { + res.resType = eResType_Texture2DArray; + res.variableType.descriptor.name = "sampler2DArray"; + } + else if(values[0] == GL_SAMPLER_2D_SHADOW) + { + res.resType = eResType_Texture2D; + res.variableType.descriptor.name = "sampler2DShadow"; + } + else if(values[0] == GL_SAMPLER_2D_ARRAY_SHADOW) + { + res.resType = eResType_Texture2DArray; + res.variableType.descriptor.name = "sampler2DArrayShadow"; + } + else if(values[0] == GL_SAMPLER_2D_RECT) + { + res.resType = eResType_Texture2D; + res.variableType.descriptor.name = "sampler2DRect"; + } + else if(values[0] == GL_SAMPLER_2D_RECT_SHADOW) + { + res.resType = eResType_Texture2D; + res.variableType.descriptor.name = "sampler2DRectShadow"; + } + else if(values[0] == GL_SAMPLER_3D) + { + res.resType = eResType_Texture3D; + res.variableType.descriptor.name = "sampler3D"; + } + else if(values[0] == GL_SAMPLER_CUBE) + { + res.resType = eResType_TextureCube; + res.variableType.descriptor.name = "samplerCube"; + } + else if(values[0] == GL_SAMPLER_CUBE_SHADOW) + { + res.resType = eResType_TextureCube; + res.variableType.descriptor.name = "samplerCubeShadow"; + } + else if(values[0] == GL_SAMPLER_CUBE_MAP_ARRAY) + { + res.resType = eResType_TextureCubeArray; + res.variableType.descriptor.name = "samplerCubeArray"; + } + else if(values[0] == GL_SAMPLER_2D_MULTISAMPLE) + { + res.resType = eResType_Texture2DMS; + res.variableType.descriptor.name = "sampler2DMS"; + } + else if(values[0] == GL_SAMPLER_2D_MULTISAMPLE_ARRAY) + { + res.resType = eResType_Texture2DMSArray; + res.variableType.descriptor.name = "sampler2DMSArray"; + } + // int samplers + else if(values[0] == GL_INT_SAMPLER_BUFFER) + { + res.resType = eResType_Buffer; + res.variableType.descriptor.name = "samplerBuffer"; + } + else if(values[0] == GL_INT_SAMPLER_1D) + { + res.resType = eResType_Texture1D; + res.variableType.descriptor.name = "sampler1D"; + } + else if(values[0] == GL_INT_SAMPLER_1D_ARRAY) + { + res.resType = eResType_Texture1DArray; + res.variableType.descriptor.name = "sampler1DArray"; + } + else if(values[0] == GL_INT_SAMPLER_2D) + { + res.resType = eResType_Texture2D; + res.variableType.descriptor.name = "sampler2D"; + } + else if(values[0] == GL_INT_SAMPLER_2D_ARRAY) + { + res.resType = eResType_Texture2DArray; + res.variableType.descriptor.name = "sampler2DArray"; + } + else if(values[0] == GL_INT_SAMPLER_2D_RECT) + { + res.resType = eResType_Texture2D; + res.variableType.descriptor.name = "sampler2DRect"; + } + else if(values[0] == GL_INT_SAMPLER_3D) + { + res.resType = eResType_Texture3D; + res.variableType.descriptor.name = "sampler3D"; + } + else if(values[0] == GL_INT_SAMPLER_CUBE) + { + res.resType = eResType_TextureCube; + res.variableType.descriptor.name = "samplerCube"; + } + else if(values[0] == GL_INT_SAMPLER_CUBE_MAP_ARRAY) + { + res.resType = eResType_TextureCubeArray; + res.variableType.descriptor.name = "samplerCubeArray"; + } + else if(values[0] == GL_INT_SAMPLER_2D_MULTISAMPLE) + { + res.resType = eResType_Texture2DMS; + res.variableType.descriptor.name = "sampler2DMS"; + } + else if(values[0] == GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY) + { + res.resType = eResType_Texture2DMSArray; + res.variableType.descriptor.name = "sampler2DMSArray"; + } + // unsigned int samplers + else if(values[0] == GL_UNSIGNED_INT_SAMPLER_BUFFER) + { + res.resType = eResType_Buffer; + res.variableType.descriptor.name = "samplerBuffer"; + } + else if(values[0] == GL_UNSIGNED_INT_SAMPLER_1D) + { + res.resType = eResType_Texture1D; + res.variableType.descriptor.name = "sampler1D"; + } + else if(values[0] == GL_UNSIGNED_INT_SAMPLER_1D_ARRAY) + { + res.resType = eResType_Texture1DArray; + res.variableType.descriptor.name = "sampler1DArray"; + } + else if(values[0] == GL_UNSIGNED_INT_SAMPLER_2D) + { + res.resType = eResType_Texture2D; + res.variableType.descriptor.name = "sampler2D"; + } + else if(values[0] == GL_UNSIGNED_INT_SAMPLER_2D_ARRAY) + { + res.resType = eResType_Texture2DArray; + res.variableType.descriptor.name = "sampler2DArray"; + } + else if(values[0] == GL_UNSIGNED_INT_SAMPLER_2D_RECT) + { + res.resType = eResType_Texture2D; + res.variableType.descriptor.name = "sampler2DRect"; + } + else if(values[0] == GL_UNSIGNED_INT_SAMPLER_3D) + { + res.resType = eResType_Texture3D; + res.variableType.descriptor.name = "sampler3D"; + } + else if(values[0] == GL_UNSIGNED_INT_SAMPLER_CUBE) + { + res.resType = eResType_TextureCube; + res.variableType.descriptor.name = "samplerCube"; + } + else if(values[0] == GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY) + { + res.resType = eResType_TextureCubeArray; + res.variableType.descriptor.name = "samplerCubeArray"; + } + else if(values[0] == GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE) + { + res.resType = eResType_Texture2DMS; + res.variableType.descriptor.name = "sampler2DMS"; + } + else if(values[0] == GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY) + { + res.resType = eResType_Texture2DMSArray; + res.variableType.descriptor.name = "sampler2DMSArray"; + } + else + { + // not a sampler + continue; + } + + res.variableAddress = values[2]; + + create_array_uninit(res.name, values[1]); + gl.glGetProgramResourceName(sepProg, eGL_UNIFORM, u, values[1], NULL, res.name.elems); + res.name.count--; // trim off trailing null + + resources.push_back(res); + } + + refl.Resources = resources; + + vector globalUniforms; + + GLint numUBOs = 0; + vector uboNames; + vector *ubos = NULL; + + { + gl.glGetProgramInterfaceiv(sepProg, eGL_UNIFORM_BLOCK, eGL_ACTIVE_RESOURCES, &numUBOs); + + ubos = new vector[numUBOs]; + uboNames.resize(numUBOs); + + for(GLint u=0; u < numUBOs; u++) + { + GLenum nameLen = eGL_NAME_LENGTH; + GLint len; + gl.glGetProgramResourceiv(sepProg, eGL_UNIFORM_BLOCK, u, 1, &nameLen, 1, NULL, &len); + + char *nm = new char[len+1]; + gl.glGetProgramResourceName(sepProg, eGL_UNIFORM_BLOCK, u, len+1, NULL, nm); + uboNames[u] = nm; + delete[] nm; + } + } + + for(GLint u=0; u < numUniforms; u++) + { + GLint values[numProps]; + gl.glGetProgramResourceiv(sepProg, eGL_UNIFORM, u, numProps, resProps, numProps, NULL, values); + + ShaderConstant var; + + if(values[0] == GL_FLOAT_VEC4) + { + var.type.descriptor.name = "vec4"; + var.type.descriptor.type = eVar_Float; + var.type.descriptor.rows = 1; + var.type.descriptor.cols = 4; + var.type.descriptor.elements = RDCMAX(1, values[4]); + } + else if(values[0] == GL_FLOAT_VEC3) + { + var.type.descriptor.name = "vec3"; + var.type.descriptor.type = eVar_Float; + var.type.descriptor.rows = 1; + var.type.descriptor.cols = 3; + var.type.descriptor.elements = RDCMAX(1, values[4]); + } + else if(values[0] == GL_FLOAT_MAT4) + { + var.type.descriptor.name = "mat4"; + var.type.descriptor.type = eVar_Float; + var.type.descriptor.rows = 4; + var.type.descriptor.cols = 4; + var.type.descriptor.elements = RDCMAX(1, values[4]); + } + else if(values[0] == GL_UNSIGNED_INT_VEC4) + { + var.type.descriptor.name = "uvec4"; + var.type.descriptor.type = eVar_UInt; + var.type.descriptor.rows = 1; + var.type.descriptor.cols = 4; + var.type.descriptor.elements = RDCMAX(1, values[4]); + } + else if(values[0] == GL_UNSIGNED_INT) + { + var.type.descriptor.name = "uint"; + var.type.descriptor.type = eVar_UInt; + var.type.descriptor.rows = 1; + var.type.descriptor.cols = 1; + var.type.descriptor.elements = RDCMAX(1, values[4]); + } + else + { + // fill in more uniform types + continue; + } + + if(values[5] == -1 && values[2] >= 0) + { + var.reg.vec = values[3]; + var.reg.comp = 0; + } + else if(values[5] >= 0) + { + var.reg.vec = values[5] / 16; + var.reg.comp = (values[5] / 4) % 4; + + RDCASSERT((values[5] % 4) == 0); + } + else + { + var.reg.vec = var.reg.comp = ~0U; + } + + var.type.descriptor.rowMajorStorage = (values[6] >= 0); + + create_array_uninit(var.name, values[0]); + gl.glGetProgramResourceName(sepProg, eGL_UNIFORM, u, values[0], NULL, var.name.elems); + var.name.count--; // trim off trailing null + + int32_t c = var.name.count; + + // trim off trailing [0] if it's an array + if(values[4] > 1 && var.name[c-3] == '[' && var.name[c-2] == '0' && var.name[c-1] == ']') + var.name.count -= 3; + + if(strchr(var.name.elems, '.')) + { + GLNOTIMP("Variable contains . - structure not reconstructed"); + } + + vector *UBO = &globalUniforms; + + // don't look at block uniforms just yet + if(values[3] != -1) + { + RDCASSERT(values[3] < numUBOs); + UBO = &ubos[ values[3] ]; + } + + UBO->push_back(var); + } + + vector cbuffers; + + if(ubos) + { + cbuffers.reserve(numUBOs + (globalUniforms.empty() ? 0 : 1)); + + for(int i=0; i < numUBOs; i++) + { + if(!ubos[i].empty()) + { + struct ubo_offset_sort + { + bool operator() (const ShaderConstant &a, const ShaderConstant &b) + { if(a.reg.vec == b.reg.vec) return a.reg.comp < b.reg.comp; else return a.reg.vec < b.reg.vec; } + }; + + ConstantBlock cblock; + cblock.name = uboNames[i]; + cblock.bufferAddress = i; + cblock.bindPoint = -1; + std::sort(ubos[i].begin(), ubos[i].end(), ubo_offset_sort()); + + cblock.variables = ubos[i]; + + cbuffers.push_back(cblock); + } + } + } + + if(!globalUniforms.empty()) + { + ConstantBlock globals; + globals.name = "$Globals"; + globals.bufferAddress = -1; + globals.bindPoint = -1; + globals.variables = globalUniforms; + + cbuffers.push_back(globals); + } + + delete[] ubos; + for(int sigType=0; sigType < 2; sigType++) + { + GLenum sigEnum = (sigType == 0 ? eGL_PROGRAM_INPUT : eGL_PROGRAM_OUTPUT); + rdctype::array *sigArray = (sigType == 0 ? &refl.InputSig : &refl.OutputSig); + + GLint numInputs; + gl.glGetProgramInterfaceiv(sepProg, sigEnum, eGL_ACTIVE_RESOURCES, &numInputs); + + if(numInputs > 0) + { + vector sigs; + sigs.reserve(numInputs); + for(GLint i=0; i < numInputs; i++) + { + GLenum props[] = { eGL_NAME_LENGTH, eGL_TYPE, eGL_LOCATION, eGL_LOCATION_COMPONENT }; + GLint values[] = { 0 , 0 , 0 , 0 }; + gl.glGetProgramResourceiv(sepProg, sigEnum, i, ARRAY_COUNT(props), props, ARRAY_COUNT(props), NULL, values); + + char *nm = new char[values[0]+1]; + gl.glGetProgramResourceName(sepProg, sigEnum, i, values[0]+1, NULL, nm); + + SigParameter sig; + + sig.varName = nm; + sig.semanticIndex = 0; + sig.needSemanticIndex = false; + sig.stream = 0; + + switch(values[1]) + { + case GL_FLOAT: + case GL_DOUBLE: + case GL_INT: + case GL_UNSIGNED_INT: + case GL_BOOL: + sig.compCount = 1; + sig.regChannelMask = 0x1; + break; + case GL_FLOAT_VEC2: + case GL_DOUBLE_VEC2: + case GL_INT_VEC2: + case GL_UNSIGNED_INT_VEC2: + case GL_BOOL_VEC2: + sig.compCount = 2; + sig.regChannelMask = 0x3; + break; + case GL_FLOAT_VEC3: + case GL_DOUBLE_VEC3: + case GL_INT_VEC3: + case GL_UNSIGNED_INT_VEC3: + case GL_BOOL_VEC3: + sig.compCount = 3; + sig.regChannelMask = 0x7; + break; + case GL_FLOAT_VEC4: + case GL_DOUBLE_VEC4: + case GL_INT_VEC4: + case GL_UNSIGNED_INT_VEC4: + case GL_BOOL_VEC4: + sig.compCount = 4; + sig.regChannelMask = 0xf; + break; + default: + RDCWARN("Unhandled signature element type %x", values[1]); + sig.compCount = 4; + sig.regChannelMask = 0xf; + break; + } + + sig.regChannelMask <<= values[3]; + + sig.channelUsedMask = sig.regChannelMask; + + sig.systemValue = eAttr_None; + +#define IS_BUITIN(builtin) !strncmp(nm, builtin, sizeof(builtin)-1) + + // VS built-in inputs + if(IS_BUITIN("gl_VertexID")) sig.systemValue = eAttr_VertexIndex; + if(IS_BUITIN("gl_InstanceID")) sig.systemValue = eAttr_InstanceIndex; + + // VS built-in outputs + if(IS_BUITIN("gl_Position")) sig.systemValue = eAttr_Position; + if(IS_BUITIN("gl_PointSize")) sig.systemValue = eAttr_PointSize; + if(IS_BUITIN("gl_ClipDistance")) sig.systemValue = eAttr_ClipDistance; + + // TCS built-in inputs + if(IS_BUITIN("gl_PatchVerticesIn")) sig.systemValue = eAttr_PatchNumVertices; + if(IS_BUITIN("gl_PrimitiveID")) sig.systemValue = eAttr_PrimitiveIndex; + if(IS_BUITIN("gl_InvocationID")) sig.systemValue = eAttr_InvocationIndex; + + // TCS built-in outputs + if(IS_BUITIN("gl_TessLevelOuter")) sig.systemValue = eAttr_OuterTessFactor; + if(IS_BUITIN("gl_TessLevelInner")) sig.systemValue = eAttr_InsideTessFactor; + + // TES built-in inputs + if(IS_BUITIN("gl_TessCoord")) sig.systemValue = eAttr_DomainLocation; + if(IS_BUITIN("gl_PatchVerticesIn")) sig.systemValue = eAttr_PatchNumVertices; + if(IS_BUITIN("gl_PrimitiveID")) sig.systemValue = eAttr_PrimitiveIndex; + + // GS built-in inputs + if(IS_BUITIN("gl_PrimitiveIDIn")) sig.systemValue = eAttr_PrimitiveIndex; + if(IS_BUITIN("gl_InvocationID")) sig.systemValue = eAttr_InvocationIndex; + if(IS_BUITIN("gl_Layer")) sig.systemValue = eAttr_RTIndex; + if(IS_BUITIN("gl_ViewportIndex")) sig.systemValue = eAttr_ViewportIndex; + + // GS built-in outputs + if(IS_BUITIN("gl_Layer")) sig.systemValue = eAttr_RTIndex; + if(IS_BUITIN("gl_ViewportIndex")) sig.systemValue = eAttr_ViewportIndex; + + // PS built-in inputs + if(IS_BUITIN("gl_FragCoord")) sig.systemValue = eAttr_Position; + if(IS_BUITIN("gl_FrontFacing")) sig.systemValue = eAttr_IsFrontFace; + if(IS_BUITIN("gl_PointCoord")) sig.systemValue = eAttr_RTIndex; + if(IS_BUITIN("gl_SampleID")) sig.systemValue = eAttr_MSAASampleIndex; + if(IS_BUITIN("gl_SamplePosition")) sig.systemValue = eAttr_MSAASamplePosition; + if(IS_BUITIN("gl_SampleMaskIn")) sig.systemValue = eAttr_MSAACoverage; + + // PS built-in outputs + if(IS_BUITIN("gl_FragDepth")) sig.systemValue = eAttr_DepthOutput; + if(IS_BUITIN("gl_SampleMask")) sig.systemValue = eAttr_MSAACoverage; + + // CS built-in inputs + if(IS_BUITIN("gl_NumWorkGroups")) sig.systemValue = eAttr_DispatchSize; + if(IS_BUITIN("gl_WorkGroupID")) sig.systemValue = eAttr_GroupIndex; + if(IS_BUITIN("gl_LocalInvocationID")) sig.systemValue = eAttr_GroupThreadIndex; + if(IS_BUITIN("gl_GlobalInvocationID")) sig.systemValue = eAttr_DispatchThreadIndex; + if(IS_BUITIN("gl_LocalInvocationIndex")) sig.systemValue = eAttr_GroupFlatIndex; + +#undef IS_BUITIN + if(shadType == eGL_FRAGMENT_SHADER && sigEnum == eGL_PROGRAM_OUTPUT && sig.systemValue == eAttr_None) + sig.systemValue = eAttr_ColourOutput; + + if(sig.systemValue == eAttr_None) + sig.regIndex = values[2] >= 0 ? values[2] : i; + else + sig.regIndex = values[2] >= 0 ? values[2] : 0; + + delete[] nm; + + sigs.push_back(sig); + } + struct sig_param_sort + { + bool operator() (const SigParameter &a, const SigParameter &b) + { if(a.systemValue == b.systemValue) return a.regIndex < b.regIndex; return a.systemValue < b.systemValue; } + }; + + std::sort(sigs.begin(), sigs.end(), sig_param_sort()); + + *sigArray = sigs; + } + } + + // TODO: fill in Interfaces with shader subroutines? + + refl.ConstantBlocks = cbuffers; +} + diff --git a/renderdoc/driver/gl/gl_shader_refl.h b/renderdoc/driver/gl/gl_shader_refl.h new file mode 100644 index 000000000..4147b1f08 --- /dev/null +++ b/renderdoc/driver/gl/gl_shader_refl.h @@ -0,0 +1,30 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2014 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "gl_hookset.h" + +#include "replay/replay_driver.h" + +void MakeShaderReflection(const GLHookSet &gl, GLenum shadType, GLuint sepProg, ShaderReflection &refl); +GLuint MakeSeparableShaderProgram(const GLHookSet &gl, GLenum type, std::vector sources); diff --git a/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp index 0f0edf8bb..3cd92073c 100644 --- a/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp @@ -24,8 +24,9 @@ #include "common/common.h" #include "common/string_utils.h" -#include "../gl_driver.h" +#include "../gl_driver.h" +#include "../gl_shader_refl.h" #pragma region Shaders @@ -108,6 +109,9 @@ bool WrappedOpenGL::Serialise_glShaderSource(GLuint shader, GLsizei count, const ResourceId liveId = GetResourceManager()->GetLiveID(id); + m_Shaders[liveId].sources.clear(); + m_Shaders[liveId].sources.reserve(Count); + for(uint32_t i=0; i < Count; i++) m_Shaders[liveId].sources.push_back(strings[i]); @@ -140,6 +144,29 @@ bool WrappedOpenGL::Serialise_glCompileShader(GLuint shader) if(m_State == READING) { + ResourceId liveId = GetResourceManager()->GetLiveID(id); + + auto &shadDetails = m_Shaders[liveId]; + + GLuint sepProg = MakeSeparableShaderProgram(m_Real, shadDetails.type, shadDetails.sources); + + if(sepProg == 0) + { + RDCERR("Couldn't make separable program for shader via patching - functionality will be broken."); + } + else + { + shadDetails.prog = sepProg; + MakeShaderReflection(m_Real, shadDetails.type, sepProg, shadDetails.reflection); + + create_array_uninit(shadDetails.reflection.DebugInfo.files, shadDetails.sources.size()); + for(size_t i=0; i < shadDetails.sources.size(); i++) + { + shadDetails.reflection.DebugInfo.files[i].first = StringFormat::Fmt("source%u.glsl", (uint32_t)i); + shadDetails.reflection.DebugInfo.files[i].second = shadDetails.sources[i]; + } + } + m_Real.glCompileShader(GetResourceManager()->GetLiveResource(id).name); } diff --git a/renderdoc/renderdoc.vcxproj b/renderdoc/renderdoc.vcxproj index 9a353f184..ead4f9954 100644 --- a/renderdoc/renderdoc.vcxproj +++ b/renderdoc/renderdoc.vcxproj @@ -264,6 +264,7 @@ + @@ -335,6 +336,7 @@ + diff --git a/renderdoc/renderdoc.vcxproj.filters b/renderdoc/renderdoc.vcxproj.filters index 3e3f41d65..4915089c4 100644 --- a/renderdoc/renderdoc.vcxproj.filters +++ b/renderdoc/renderdoc.vcxproj.filters @@ -291,6 +291,9 @@ OS\Win32 + + Drivers\OpenGL + @@ -512,6 +515,9 @@ OS\Win32 + + Drivers\OpenGL +