diff --git a/renderdoc/driver/gl/gl_shader_refl.cpp b/renderdoc/driver/gl/gl_shader_refl.cpp index 0504d0c7e..59cf57b27 100644 --- a/renderdoc/driver/gl/gl_shader_refl.cpp +++ b/renderdoc/driver/gl/gl_shader_refl.cpp @@ -25,6 +25,7 @@ #include "gl_shader_refl.h" #include #include +#include "3rdparty/glslang/glslang/Public/ShaderLang.h" #include "gl_driver.h" void sort(rdcarray &vars) @@ -239,225 +240,263 @@ GLuint MakeSeparableShaderProgram(WrappedOpenGL &gl, GLenum type, vector // (this is probably most likely for clipdistance if it's redeclared with a size) // we start by concatenating the source strings to make parsing easier. - std::string src; + std::string combined; for(size_t i = 0; i < sources.size(); i++) - src += sources[i]; + combined += sources[i]; - for(int blocktype = 0; blocktype < 2; blocktype++) + for(int attempt = 0; attempt < 2; attempt++) { - // vertex shaders don't have an in block - if(type == eGL_VERTEX_SHADER && blocktype == 0) - continue; + std::string src = combined; - string block = blocks[blocktype]; - const char *identifier = blockIdentifiers[blocktype]; - - // if we find the 'identifier' (ie. the block name), - // assume this block is already present and stop. - // only try and insert this block if the shader doesn't already have it - if(src.find(identifier) != string::npos) + if(attempt == 1) { - continue; + RDCLOG("Attempting to pre-process shader with glslang to allow patching"); + + glslang::TShader sh(EShLanguage(ShaderIdx(type))); + + const char *c_src = combined.c_str(); + sh.setStrings(&c_src, 1); + sh.setEnvInput(glslang::EShSourceGlsl, EShLanguage(ShaderIdx(type)), + glslang::EShClientOpenGL, 100); + sh.setEnvClient(glslang::EShClientOpenGL, 100); + sh.setEnvTarget(glslang::EShTargetNone, 100); + + TBuiltInResource res = {}; + glslang::TShader::ForbidIncluder incl; + + bool success = + sh.preprocess(&res, 100, ENoProfile, false, false, EShMsgOnlyPreprocessor, &src, incl); + + if(!success) + { + RDCLOG("glslang failed:\n\n%s\n\n%s", sh.getInfoLog(), sh.getInfoDebugLog()); + continue; + } } + for(int blocktype = 0; blocktype < 2; blocktype++) { - size_t len = src.length(); + // vertex shaders don't have an in block + if(type == eGL_VERTEX_SHADER && blocktype == 0) + continue; - // find if this source contains a #version, accounting for whitespace - size_t it = 0; + string block = blocks[blocktype]; + const char *identifier = blockIdentifiers[blocktype]; - while(it != string::npos) + // if we find the 'identifier' (ie. the block name), + // assume this block is already present and stop. + // only try and insert this block if the shader doesn't already have it + if(src.find(identifier) != string::npos) { - it = src.find("#", it); - - if(it == string::npos) - break; - - // advance past the # - ++it; - - // skip whitespace - while(it < len && isspacetab(src[it])) - ++it; - - if(it + 7 < len && !strncmp(&src[it], "version", 7)) - { - it += sizeof("version") - 1; - break; - } + continue; } - // no #version found - if(it == string::npos) { - // insert at the start - it = 0; - } - else - { - // it now points after the #version + size_t len = src.length(); - // skip whitespace - while(it < len && isspacetab(src[it])) - ++it; + // find if this source contains a #version, accounting for whitespace + size_t it = 0; - // skip number - while(it < len && src[it] >= '0' && src[it] <= '9') - ++it; - - // skip whitespace - while(it < len && isspacetab(src[it])) - ++it; - - if(!strncmp(&src[it], "core", 4)) - it += sizeof("core") - 1; - if(!strncmp(&src[it], "compatibility", 13)) - it += sizeof("compatibility") - 1; - if(!strncmp(&src[it], "es", 2)) - it += sizeof("es") - 1; - - // how deep are we in an #if. We want to place our definition - // outside of any #ifs. - int if_depth = 0; - - // now skip past comments, and any #directives - while(it < len) + while(it != string::npos) { + it = src.find("#", it); + + if(it == string::npos) + break; + + // advance past the # + ++it; + // skip whitespace - while(it < len && iswhitespace(src[it])) + while(it < len && isspacetab(src[it])) ++it; - // skip C++ style comments - if(it + 1 < len && src[it] == '/' && src[it + 1] == '/') + if(it + 7 < len && !strncmp(&src[it], "version", 7)) { - // keep going until the next newline - while(it < len && !isnewline(src[it])) - ++it; - - // skip more things - continue; + it += sizeof("version") - 1; + break; } + } - // skip preprocessor directives - if(src[it] == '#') + // no #version found + if(it == string::npos) + { + // insert at the start + it = 0; + } + else + { + // it now points after the #version + + // skip whitespace + while(it < len && isspacetab(src[it])) + ++it; + + // skip number + while(it < len && src[it] >= '0' && src[it] <= '9') + ++it; + + // skip whitespace + while(it < len && isspacetab(src[it])) + ++it; + + if(!strncmp(&src[it], "core", 4)) + it += sizeof("core") - 1; + if(!strncmp(&src[it], "compatibility", 13)) + it += sizeof("compatibility") - 1; + if(!strncmp(&src[it], "es", 2)) + it += sizeof("es") - 1; + + // how deep are we in an #if. We want to place our definition + // outside of any #ifs. + int if_depth = 0; + + // now skip past comments, and any #directives + while(it < len) { - // skip the '#' - it++; - // skip whitespace while(it < len && iswhitespace(src[it])) ++it; - // if it's an if, then increase our depth - // This covers: - // #if - // #ifdef - // #ifndef - if(!strncmp(&src[it], "if", 2)) + // skip C++ style comments + if(it + 1 < len && src[it] == '/' && src[it + 1] == '/') { - if_depth++; - } - else if(!strncmp(&src[it], "endif", 5)) - { - if_depth--; - } - // everything else is #extension or #else or #undef or anything - - // keep going until the next newline - while(it < len && !isnewline(src[it])) - { - // if we encounter a C-style comment in the middle of a #define - // we can't consume it because then we'd miss the start of it. - // Instead we break out (although we're not technically at the - // end of the pre-processor line) and let it be consumed next. - // Note that we can discount C++-style comments because they - // want to consume to the end of the line too. - if(it + 1 < len && src[it] == '/' && src[it + 1] == '*') - break; - - ++it; - } - - // skip more things - continue; - } - - // skip C style comments - if(it + 1 < len && src[it] == '/' && src[it + 1] == '*') - { - // keep going until the we reach a */ - while(it + 1 < len && (src[it] != '*' || src[it + 1] != '/')) - ++it; - - // skip the closing */ too - it += 2; - - // skip more things - continue; - } - - // see if we have a precision statement, if so skip that - const char precision[] = "precision"; - if(it + sizeof(precision) < len && !strncmp(&src[it], precision, sizeof(precision) - 1)) - { - // since we're speculating here (although what else could it be?) we don't modify - // it until we're sure. - size_t pit = it + sizeof(precision); - - // skip whitespace - while(pit < len && isspacetab(src[pit])) - ++pit; - - // if we now match any of the precisions, then continue consuming until the next ; - const char lowp[] = "lowp"; - const char mediump[] = "mediump"; - const char highp[] = "highp"; - - bool precisionMatch = - (pit + sizeof(lowp) < len && !strncmp(&src[pit], lowp, sizeof(lowp) - 1) && - isspacetab(src[pit + sizeof(lowp) - 1])); - precisionMatch |= (pit + sizeof(mediump) < len && - !strncmp(&src[pit], mediump, sizeof(mediump) - 1) && - isspacetab(src[pit + sizeof(mediump) - 1])); - precisionMatch |= - (pit + sizeof(highp) < len && !strncmp(&src[pit], highp, sizeof(highp) - 1) && - isspacetab(src[pit + sizeof(highp) - 1])); - - if(precisionMatch) - { - it = pit; - while(it < len && src[it] != ';') + // keep going until the next newline + while(it < len && !isnewline(src[it])) ++it; - ++it; // skip the ; itself - // skip more things continue; } - // otherwise just stop here, it's not a precision statement + // skip preprocessor directives + if(src[it] == '#') + { + // skip the '#' + it++; + + // skip whitespace + while(it < len && iswhitespace(src[it])) + ++it; + + // if it's an if, then increase our depth + // This covers: + // #if + // #ifdef + // #ifndef + if(!strncmp(&src[it], "if", 2)) + { + if_depth++; + } + else if(!strncmp(&src[it], "endif", 5)) + { + if_depth--; + } + // everything else is #extension or #else or #undef or anything + + // keep going until the next newline + while(it < len && !isnewline(src[it])) + { + // if we encounter a C-style comment in the middle of a #define + // we can't consume it because then we'd miss the start of it. + // Instead we break out (although we're not technically at the + // end of the pre-processor line) and let it be consumed next. + // Note that we can discount C++-style comments because they + // want to consume to the end of the line too. + if(it + 1 < len && src[it] == '/' && src[it + 1] == '*') + break; + + ++it; + } + + // skip more things + continue; + } + + // skip C style comments + if(it + 1 < len && src[it] == '/' && src[it + 1] == '*') + { + // keep going until the we reach a */ + while(it + 1 < len && (src[it] != '*' || src[it + 1] != '/')) + ++it; + + // skip the closing */ too + it += 2; + + // skip more things + continue; + } + + // see if we have a precision statement, if so skip that + const char precision[] = "precision"; + if(it + sizeof(precision) < len && !strncmp(&src[it], precision, sizeof(precision) - 1)) + { + // since we're speculating here (although what else could it be?) we don't modify + // it until we're sure. + size_t pit = it + sizeof(precision); + + // skip whitespace + while(pit < len && isspacetab(src[pit])) + ++pit; + + // if we now match any of the precisions, then continue consuming until the next ; + const char lowp[] = "lowp"; + const char mediump[] = "mediump"; + const char highp[] = "highp"; + + bool precisionMatch = + (pit + sizeof(lowp) < len && !strncmp(&src[pit], lowp, sizeof(lowp) - 1) && + isspacetab(src[pit + sizeof(lowp) - 1])); + precisionMatch |= (pit + sizeof(mediump) < len && + !strncmp(&src[pit], mediump, sizeof(mediump) - 1) && + isspacetab(src[pit + sizeof(mediump) - 1])); + precisionMatch |= + (pit + sizeof(highp) < len && !strncmp(&src[pit], highp, sizeof(highp) - 1) && + isspacetab(src[pit + sizeof(highp) - 1])); + + if(precisionMatch) + { + it = pit; + while(it < len && src[it] != ';') + ++it; + + ++it; // skip the ; itself + + // skip more things + continue; + } + + // otherwise just stop here, it's not a precision statement + } + + // nothing more to skip, check if we're outside an if + if(if_depth == 0) + break; + + // if not, this might not be a comment, etc etc. Just skip to the next line + // so we can keep going to find the #endif + while(it < len && !isnewline(src[it])) + ++it; } - - // nothing more to skip, check if we're outside an if - if(if_depth == 0) - break; - - // if not, this might not be a comment, etc etc. Just skip to the next line - // so we can keep going to find the #endif - while(it < len && !isnewline(src[it])) - ++it; } + + if(it < src.length()) + src.insert(it, block); } - - if(it < src.length()) - src.insert(it, block); } + + const char *c_src = src.c_str(); + + sepProg = CreateSepProgram(gl, type, 1, &c_src, numPaths, paths); + + // when we get it to link, bail! + gl.glGetProgramiv(sepProg, eGL_LINK_STATUS, &status); + if(status == 1) + break; + + RDCWARN("Couldn't patch separability into shader, attempt #%d", attempt + 1); } - - const char *c_src = src.c_str(); - - sepProg = CreateSepProgram(gl, type, 1, &c_src, numPaths, paths); } gl.glGetProgramiv(sepProg, eGL_LINK_STATUS, &status);