mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-28 17:36:36 +00:00
If normal patching fails, try to use glslang to preprocess GLSL
* We need to patch in fixed blocks, so we can try to use glslang to preprocess away confusing code, and then do the patching on the pre-processed output. This has a risk of affecting the results but it can be a last-ditch attempt which is better than having no separable program to reflect at all.
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
#include "gl_shader_refl.h"
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include "3rdparty/glslang/glslang/Public/ShaderLang.h"
|
||||
#include "gl_driver.h"
|
||||
|
||||
void sort(rdcarray<ShaderConstant> &vars)
|
||||
@@ -239,225 +240,263 @@ GLuint MakeSeparableShaderProgram(WrappedOpenGL &gl, GLenum type, vector<string>
|
||||
// (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);
|
||||
|
||||
Reference in New Issue
Block a user