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:
baldurk
2018-04-20 17:23:52 +01:00
parent af4dbcd29b
commit 1096025963
+217 -178
View File
@@ -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);