mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-24 15:36:33 +00:00
Completely emulate ARB_program_interface_query using glslang
* We already did this partially, enough for capturing, but now we also do it on replay.
This commit is contained in:
@@ -115,9 +115,6 @@ bool CheckReplayContext()
|
||||
// we require the below extensions on top of a 3.2 context. Some of these we could in theory
|
||||
// do without, but support for them is so widespread it's not worthwhile
|
||||
|
||||
// for program introspection, needed for shader reflection.
|
||||
// Possible to remove by compiling shaders to SPIR-V and reflecting ourselves.
|
||||
REQUIRE_EXTENSION(ARB_program_interface_query);
|
||||
// needed for program pipelines, glProgramUniform*, and reflecting shaders on their own
|
||||
// Possible to remove this with self-compiled SPIR-V for reflection - see above. Likewise
|
||||
// convenience for our own pipelines when replacing single shaders or such.
|
||||
@@ -2594,6 +2591,8 @@ TEST_CASE("GL formats", "[format][gl]")
|
||||
CHECK(size == GetByteSize(123, 456, 1, GetBaseFormat(f), GetDataType(f)));
|
||||
}
|
||||
};
|
||||
|
||||
GL = GLDispatchTable();
|
||||
};
|
||||
|
||||
#endif // ENABLED(ENABLE_UNIT_TESTS)
|
||||
|
||||
@@ -293,6 +293,32 @@ struct GLPlatform
|
||||
virtual void *GetReplayFunction(const char *funcname) = 0;
|
||||
};
|
||||
|
||||
class GLDummyPlatform : public GLPlatform
|
||||
{
|
||||
virtual GLWindowingData CloneTemporaryContext(GLWindowingData share) { return GLWindowingData(); }
|
||||
virtual void DeleteClonedContext(GLWindowingData context) {}
|
||||
virtual void DeleteReplayContext(GLWindowingData context) {}
|
||||
virtual bool MakeContextCurrent(GLWindowingData data) { return true; }
|
||||
virtual void SwapBuffers(GLWindowingData context) {}
|
||||
virtual void WindowResized(GLWindowingData context) {}
|
||||
virtual void GetOutputWindowDimensions(GLWindowingData context, int32_t &w, int32_t &h) {}
|
||||
virtual bool IsOutputWindowVisible(GLWindowingData context) { return false; }
|
||||
virtual GLWindowingData MakeOutputWindow(WindowingData window, bool depth,
|
||||
GLWindowingData share_context)
|
||||
{
|
||||
return GLWindowingData();
|
||||
}
|
||||
virtual void DrawQuads(float width, float height, const std::vector<Vec4f> &vertices) {}
|
||||
virtual void *GetReplayFunction(const char *funcname) { return NULL; }
|
||||
// for initialisation at replay time
|
||||
virtual bool CanCreateGLESContext() { return true; }
|
||||
virtual bool PopulateForReplay() { return true; }
|
||||
virtual ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api)
|
||||
{
|
||||
return ReplayStatus::Succeeded;
|
||||
}
|
||||
};
|
||||
|
||||
struct GLVersion
|
||||
{
|
||||
int major;
|
||||
|
||||
@@ -288,6 +288,9 @@ void GLReplay::InitDebugData()
|
||||
if(m_pDriver == NULL)
|
||||
return;
|
||||
|
||||
// don't reflect any shaders or programs we make
|
||||
m_pDriver->PushInternalShader();
|
||||
|
||||
m_HighlightCache.driver = m_pDriver->GetReplay();
|
||||
|
||||
RenderDoc::Inst().SetProgress(LoadProgress::DebugManagerInit, 0.0f);
|
||||
@@ -1048,6 +1051,8 @@ void GLReplay::InitDebugData()
|
||||
"Don't have shader image load/store or compute shaders, functionality will be degraded.");
|
||||
m_Degraded = true;
|
||||
}
|
||||
|
||||
m_pDriver->PopInternalShader();
|
||||
}
|
||||
|
||||
void GLReplay::DeleteDebugData()
|
||||
|
||||
@@ -140,6 +140,8 @@ private:
|
||||
|
||||
uintptr_t m_ShareGroupID;
|
||||
|
||||
uint32_t m_InternalShader = 0;
|
||||
|
||||
std::vector<GLWindowingData> m_LastContexts;
|
||||
|
||||
std::set<void *> m_AcceptedCtx;
|
||||
@@ -497,6 +499,9 @@ public:
|
||||
ContextPair &GetCtx();
|
||||
GLResourceRecord *GetContextRecord();
|
||||
|
||||
void PushInternalShader() { m_InternalShader++; }
|
||||
void PopInternalShader() { m_InternalShader--; }
|
||||
bool IsInternalShader() { return m_InternalShader > 0; }
|
||||
void *ShareCtx(void *ctx) { return ctx ? m_ContextData[ctx].shareGroup : NULL; }
|
||||
void SetStructuredExport(uint64_t sectionVersion)
|
||||
{
|
||||
|
||||
@@ -1254,6 +1254,8 @@ bool GLResourceManager::Serialise_InitialState(SerialiserType &ser, ResourceId r
|
||||
}
|
||||
else if(Type == eResProgram)
|
||||
{
|
||||
WrappedOpenGL &drv = *m_Driver;
|
||||
|
||||
GLuint bindingsProgram = 0, uniformsProgram = 0;
|
||||
std::map<GLint, GLint> *translationTable = NULL;
|
||||
|
||||
@@ -1261,7 +1263,7 @@ bool GLResourceManager::Serialise_InitialState(SerialiserType &ser, ResourceId r
|
||||
{
|
||||
WrappedOpenGL::ProgramData &details = m_Driver->m_Programs[GetLiveID(Id)];
|
||||
|
||||
GLuint initProg = GL.glCreateProgram();
|
||||
GLuint initProg = drv.glCreateProgram();
|
||||
|
||||
uint32_t numShaders = 0;
|
||||
|
||||
@@ -1275,7 +1277,7 @@ bool GLResourceManager::Serialise_InitialState(SerialiserType &ser, ResourceId r
|
||||
|
||||
const auto &shadDetails = m_Driver->m_Shaders[details.stageShaders[i]];
|
||||
|
||||
GLuint shad = GL.glCreateShader(shadDetails.type);
|
||||
GLuint shad = drv.glCreateShader(shadDetails.type);
|
||||
|
||||
if(shadDetails.type == eGL_VERTEX_SHADER)
|
||||
{
|
||||
@@ -1301,24 +1303,24 @@ bool GLResourceManager::Serialise_InitialState(SerialiserType &ser, ResourceId r
|
||||
char **srcs = new char *[shadDetails.sources.size()];
|
||||
for(size_t s = 0; s < shadDetails.sources.size(); s++)
|
||||
srcs[s] = (char *)shadDetails.sources[s].c_str();
|
||||
GL.glShaderSource(shad, (GLsizei)shadDetails.sources.size(), srcs, NULL);
|
||||
drv.glShaderSource(shad, (GLsizei)shadDetails.sources.size(), srcs, NULL);
|
||||
|
||||
SAFE_DELETE_ARRAY(srcs);
|
||||
GL.glCompileShader(shad);
|
||||
GL.glAttachShader(initProg, shad);
|
||||
GL.glDeleteShader(shad);
|
||||
drv.glCompileShader(shad);
|
||||
drv.glAttachShader(initProg, shad);
|
||||
drv.glDeleteShader(shad);
|
||||
}
|
||||
else if(!shadDetails.spirvWords.empty())
|
||||
{
|
||||
GL.glShaderBinary(1, &shad, eGL_SHADER_BINARY_FORMAT_SPIR_V, shadDetails.spirvWords.data(),
|
||||
(GLsizei)shadDetails.spirvWords.size() * sizeof(uint32_t));
|
||||
drv.glShaderBinary(1, &shad, eGL_SHADER_BINARY_FORMAT_SPIR_V, shadDetails.spirvWords.data(),
|
||||
(GLsizei)shadDetails.spirvWords.size() * sizeof(uint32_t));
|
||||
|
||||
GL.glSpecializeShader(shad, shadDetails.entryPoint.c_str(),
|
||||
(GLuint)shadDetails.specIDs.size(), shadDetails.specIDs.data(),
|
||||
shadDetails.specValues.data());
|
||||
drv.glSpecializeShader(shad, shadDetails.entryPoint.c_str(),
|
||||
(GLuint)shadDetails.specIDs.size(), shadDetails.specIDs.data(),
|
||||
shadDetails.specValues.data());
|
||||
|
||||
GL.glAttachShader(initProg, shad);
|
||||
GL.glDeleteShader(shad);
|
||||
drv.glAttachShader(initProg, shad);
|
||||
drv.glDeleteShader(shad);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1335,21 +1337,21 @@ bool GLResourceManager::Serialise_InitialState(SerialiserType &ser, ResourceId r
|
||||
vertexOutputsPtr.resize(vertexOutputs.size());
|
||||
for(size_t i = 0; i < vertexOutputs.size(); i++)
|
||||
vertexOutputsPtr[i] = vertexOutputs[i].c_str();
|
||||
GL.glTransformFeedbackVaryings(initProg, (GLsizei)vertexOutputsPtr.size(),
|
||||
&vertexOutputsPtr[0], eGL_INTERLEAVED_ATTRIBS);
|
||||
GL.glLinkProgram(initProg);
|
||||
drv.glTransformFeedbackVaryings(initProg, (GLsizei)vertexOutputsPtr.size(),
|
||||
&vertexOutputsPtr[0], eGL_INTERLEAVED_ATTRIBS);
|
||||
drv.glLinkProgram(initProg);
|
||||
|
||||
GLint status = 0;
|
||||
GL.glGetProgramiv(initProg, eGL_LINK_STATUS, &status);
|
||||
drv.glGetProgramiv(initProg, eGL_LINK_STATUS, &status);
|
||||
|
||||
// if it failed to link, first remove the varyings hack above as maybe the driver is barfing
|
||||
// on trying to make some output a varying
|
||||
if(status == 0)
|
||||
{
|
||||
GL.glTransformFeedbackVaryings(initProg, 0, NULL, eGL_INTERLEAVED_ATTRIBS);
|
||||
GL.glLinkProgram(initProg);
|
||||
drv.glTransformFeedbackVaryings(initProg, 0, NULL, eGL_INTERLEAVED_ATTRIBS);
|
||||
drv.glLinkProgram(initProg);
|
||||
|
||||
GL.glGetProgramiv(initProg, eGL_LINK_STATUS, &status);
|
||||
drv.glGetProgramiv(initProg, eGL_LINK_STATUS, &status);
|
||||
}
|
||||
|
||||
// if it failed to link, try again as a separable program.
|
||||
@@ -1357,10 +1359,10 @@ bool GLResourceManager::Serialise_InitialState(SerialiserType &ser, ResourceId r
|
||||
// shaders need fixup to be separable-compatible.
|
||||
if(status == 0)
|
||||
{
|
||||
GL.glProgramParameteri(initProg, eGL_PROGRAM_SEPARABLE, 1);
|
||||
GL.glLinkProgram(initProg);
|
||||
drv.glProgramParameteri(initProg, eGL_PROGRAM_SEPARABLE, 1);
|
||||
drv.glLinkProgram(initProg);
|
||||
|
||||
GL.glGetProgramiv(initProg, eGL_LINK_STATUS, &status);
|
||||
drv.glGetProgramiv(initProg, eGL_LINK_STATUS, &status);
|
||||
}
|
||||
|
||||
if(status == 0)
|
||||
@@ -1372,7 +1374,7 @@ bool GLResourceManager::Serialise_InitialState(SerialiserType &ser, ResourceId r
|
||||
else
|
||||
{
|
||||
char buffer[1025] = {0};
|
||||
GL.glGetProgramInfoLog(initProg, 1024, NULL, buffer);
|
||||
drv.glGetProgramInfoLog(initProg, 1024, NULL, buffer);
|
||||
RDCERR("Link error: %s", buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3491,32 +3491,6 @@ ReplayStatus CreateReplayDevice(RDCDriver rdcdriver, RDCFile *rdc, GLPlatform &p
|
||||
return ReplayStatus::Succeeded;
|
||||
}
|
||||
|
||||
class GLDummyPlatform : public GLPlatform
|
||||
{
|
||||
virtual GLWindowingData CloneTemporaryContext(GLWindowingData share) { return GLWindowingData(); }
|
||||
virtual void DeleteClonedContext(GLWindowingData context) {}
|
||||
virtual void DeleteReplayContext(GLWindowingData context) {}
|
||||
virtual bool MakeContextCurrent(GLWindowingData data) { return true; }
|
||||
virtual void SwapBuffers(GLWindowingData context) {}
|
||||
virtual void WindowResized(GLWindowingData context) {}
|
||||
virtual void GetOutputWindowDimensions(GLWindowingData context, int32_t &w, int32_t &h) {}
|
||||
virtual bool IsOutputWindowVisible(GLWindowingData context) { return false; }
|
||||
virtual GLWindowingData MakeOutputWindow(WindowingData window, bool depth,
|
||||
GLWindowingData share_context)
|
||||
{
|
||||
return GLWindowingData();
|
||||
}
|
||||
virtual void DrawQuads(float width, float height, const std::vector<Vec4f> &vertices) {}
|
||||
virtual void *GetReplayFunction(const char *funcname) { return NULL; }
|
||||
// for initialisation at replay time
|
||||
virtual bool CanCreateGLESContext() { return true; }
|
||||
virtual bool PopulateForReplay() { return true; }
|
||||
virtual ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api)
|
||||
{
|
||||
return ReplayStatus::Succeeded;
|
||||
}
|
||||
};
|
||||
|
||||
void GL_ProcessStructured(RDCFile *rdc, SDFile &output)
|
||||
{
|
||||
GLDummyPlatform dummy;
|
||||
|
||||
@@ -28,6 +28,41 @@
|
||||
#include "3rdparty/glslang/glslang/Public/ShaderLang.h"
|
||||
#include "gl_driver.h"
|
||||
|
||||
template <>
|
||||
std::string DoStringise(const FFVertexOutput &el)
|
||||
{
|
||||
BEGIN_ENUM_STRINGISE(FFVertexOutput);
|
||||
{
|
||||
STRINGISE_ENUM_CLASS_NAMED(PointSize, "gl_PointSize");
|
||||
STRINGISE_ENUM_CLASS_NAMED(ClipDistance, "gl_ClipDistance");
|
||||
STRINGISE_ENUM_CLASS_NAMED(ClipVertex, "gl_ClipVertex");
|
||||
STRINGISE_ENUM_CLASS_NAMED(FrontColor, "gl_FrontColor");
|
||||
STRINGISE_ENUM_CLASS_NAMED(BackColor, "gl_BackColor");
|
||||
STRINGISE_ENUM_CLASS_NAMED(FrontSecondaryColor, "gl_FrontSecondaryColor");
|
||||
STRINGISE_ENUM_CLASS_NAMED(BackSecondaryColor, "gl_BackSecondaryColor");
|
||||
STRINGISE_ENUM_CLASS_NAMED(TexCoord, "gl_TexCoord");
|
||||
STRINGISE_ENUM_CLASS_NAMED(FogFragCoord, "gl_FogFragCoord");
|
||||
STRINGISE_ENUM_CLASS_NAMED(Count, "gl_Count");
|
||||
}
|
||||
END_ENUM_STRINGISE();
|
||||
}
|
||||
|
||||
void namesort(rdcarray<ShaderConstant> &vars)
|
||||
{
|
||||
if(vars.empty())
|
||||
return;
|
||||
|
||||
struct name_sort
|
||||
{
|
||||
bool operator()(const ShaderConstant &a, const ShaderConstant &b) { return a.name < b.name; }
|
||||
};
|
||||
|
||||
std::sort(vars.begin(), vars.end(), name_sort());
|
||||
|
||||
for(size_t i = 0; i < vars.size(); i++)
|
||||
namesort(vars[i].type.members);
|
||||
}
|
||||
|
||||
void sort(rdcarray<ShaderConstant> &vars)
|
||||
{
|
||||
if(vars.empty())
|
||||
@@ -41,64 +76,42 @@ void sort(rdcarray<ShaderConstant> &vars)
|
||||
sort(vars[i].type.members);
|
||||
}
|
||||
|
||||
void CheckVertexOutputUses(const vector<string> &sources, bool &pointSizeUsed, bool &clipDistanceUsed)
|
||||
void CheckVertexOutputUses(const std::vector<std::string> &sources,
|
||||
FixedFunctionVertexOutputs &outputUsage)
|
||||
{
|
||||
pointSizeUsed = false;
|
||||
clipDistanceUsed = false;
|
||||
outputUsage = FixedFunctionVertexOutputs();
|
||||
|
||||
for(size_t i = 0; i < sources.size(); i++)
|
||||
for(FFVertexOutput output : values<FFVertexOutput>())
|
||||
{
|
||||
const string &s = sources[i];
|
||||
// we consider an output used if we encounter a '=' before either a ';' or the end of the string
|
||||
std::string name = ToStr(output);
|
||||
|
||||
size_t offs = 0;
|
||||
|
||||
for(;;)
|
||||
for(size_t i = 0; i < sources.size(); i++)
|
||||
{
|
||||
offs = s.find("gl_PointSize", offs);
|
||||
const std::string &s = sources[i];
|
||||
|
||||
if(offs == string::npos)
|
||||
break;
|
||||
size_t offs = 0;
|
||||
|
||||
// consider gl_PointSize used if we encounter a '=' before a ';' or the end of the string
|
||||
|
||||
while(offs < s.length())
|
||||
for(;;)
|
||||
{
|
||||
if(s[offs] == '=')
|
||||
offs = s.find(name, offs);
|
||||
|
||||
if(offs == string::npos)
|
||||
break;
|
||||
|
||||
while(offs < s.length())
|
||||
{
|
||||
pointSizeUsed = true;
|
||||
break;
|
||||
if(s[offs] == '=')
|
||||
{
|
||||
outputUsage.used[(int)output] = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(s[offs] == ';')
|
||||
break;
|
||||
|
||||
offs++;
|
||||
}
|
||||
|
||||
if(s[offs] == ';')
|
||||
break;
|
||||
|
||||
offs++;
|
||||
}
|
||||
}
|
||||
|
||||
offs = 0;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
offs = s.find("gl_ClipDistance", offs);
|
||||
|
||||
if(offs == string::npos)
|
||||
break;
|
||||
|
||||
// consider gl_ClipDistance used if we encounter a '=' before a ';' or the end of the string
|
||||
|
||||
while(offs < s.length())
|
||||
{
|
||||
if(s[offs] == '=')
|
||||
{
|
||||
clipDistanceUsed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(s[offs] == ';')
|
||||
break;
|
||||
|
||||
offs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,35 +129,35 @@ static GLuint CreateSepProgram(WrappedOpenGL &driver, GLenum type, GLsizei numSo
|
||||
GLuint program = 0;
|
||||
|
||||
// definition of glCreateShaderProgramv from the spec
|
||||
GLuint shader = GL.glCreateShader(type);
|
||||
GLuint shader = driver.glCreateShader(type);
|
||||
if(shader)
|
||||
{
|
||||
GL.glShaderSource(shader, numSources, sources, NULL);
|
||||
driver.glShaderSource(shader, numSources, sources, NULL);
|
||||
|
||||
if(paths == NULL)
|
||||
GL.glCompileShader(shader);
|
||||
driver.glCompileShader(shader);
|
||||
else
|
||||
GL.glCompileShaderIncludeARB(shader, numPaths, paths, NULL);
|
||||
driver.glCompileShaderIncludeARB(shader, numPaths, paths, NULL);
|
||||
|
||||
program = GL.glCreateProgram();
|
||||
program = driver.glCreateProgram();
|
||||
if(program)
|
||||
{
|
||||
GLint compiled = 0;
|
||||
|
||||
GL.glGetShaderiv(shader, eGL_COMPILE_STATUS, &compiled);
|
||||
GL.glProgramParameteri(program, eGL_PROGRAM_SEPARABLE, GL_TRUE);
|
||||
driver.glGetShaderiv(shader, eGL_COMPILE_STATUS, &compiled);
|
||||
driver.glProgramParameteri(program, eGL_PROGRAM_SEPARABLE, GL_TRUE);
|
||||
|
||||
if(compiled)
|
||||
{
|
||||
GL.glAttachShader(program, shader);
|
||||
GL.glLinkProgram(program);
|
||||
driver.glAttachShader(program, shader);
|
||||
driver.glLinkProgram(program);
|
||||
|
||||
// we deliberately leave the shaders attached so this program can be re-linked.
|
||||
// they will be cleaned up when the program is deleted
|
||||
// driver.glDetachShader(program, shader);
|
||||
}
|
||||
}
|
||||
GL.glDeleteShader(shader);
|
||||
driver.glDeleteShader(shader);
|
||||
}
|
||||
|
||||
driver.SuppressDebugMessages(false);
|
||||
@@ -1037,8 +1050,50 @@ int ParseVersionStatement(const char *version)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void AddSigParameter(vector<SigParameter> &sigs, uint32_t ®Index, const SigParameter &sig,
|
||||
const char *nm, int rows, int arrayIdx)
|
||||
{
|
||||
if(rows == 1)
|
||||
{
|
||||
SigParameter s = sig;
|
||||
|
||||
if(s.regIndex == ~0U)
|
||||
s.regIndex = regIndex++;
|
||||
|
||||
if(arrayIdx >= 0)
|
||||
{
|
||||
s.arrayIndex = arrayIdx;
|
||||
s.varName = StringFormat::Fmt("%s[%d]", nm, arrayIdx);
|
||||
}
|
||||
|
||||
sigs.push_back(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int r = 0; r < rows; r++)
|
||||
{
|
||||
SigParameter s = sig;
|
||||
|
||||
if(s.regIndex == ~0U)
|
||||
s.regIndex = regIndex++;
|
||||
|
||||
if(arrayIdx >= 0)
|
||||
{
|
||||
s.arrayIndex = arrayIdx;
|
||||
s.varName = StringFormat::Fmt("%s[%d]:row%d", nm, arrayIdx, r);
|
||||
}
|
||||
else
|
||||
{
|
||||
s.varName = StringFormat::Fmt("%s:row%d", nm, r);
|
||||
}
|
||||
|
||||
sigs.push_back(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MakeShaderReflection(GLenum shadType, GLuint sepProg, ShaderReflection &refl,
|
||||
bool pointSizeUsed, bool clipDistanceUsed)
|
||||
const FixedFunctionVertexOutputs &outputUsage)
|
||||
{
|
||||
if(shadType == eGL_COMPUTE_SHADER)
|
||||
{
|
||||
@@ -1616,7 +1671,7 @@ void MakeShaderReflection(GLenum shadType, GLuint sepProg, ShaderReflection &ref
|
||||
res.resType = TextureType::Buffer;
|
||||
res.variableType.descriptor.rows = 0;
|
||||
res.variableType.descriptor.columns = 0;
|
||||
res.variableType.descriptor.elements = len;
|
||||
res.variableType.descriptor.elements = 0;
|
||||
res.variableType.descriptor.rowMajorStorage = false;
|
||||
res.variableType.descriptor.arrayByteStride = 0;
|
||||
res.variableType.descriptor.matrixByteStride = 0;
|
||||
@@ -1625,13 +1680,15 @@ void MakeShaderReflection(GLenum shadType, GLuint sepProg, ShaderReflection &ref
|
||||
res.bindPoint = (int32_t)rwresources.size();
|
||||
res.name = nm;
|
||||
|
||||
GLint numMembers = 0;
|
||||
|
||||
propName = eGL_NUM_ACTIVE_VARIABLES;
|
||||
GL.glGetProgramResourceiv(sepProg, eGL_SHADER_STORAGE_BLOCK, u, 1, &propName, 1, NULL,
|
||||
(GLint *)&res.variableType.descriptor.elements);
|
||||
(GLint *)&numMembers);
|
||||
|
||||
rwresources.push_back(res);
|
||||
ssbos.push_back(res.bindPoint);
|
||||
ssboMembers += res.variableType.descriptor.elements;
|
||||
ssboMembers += numMembers;
|
||||
|
||||
delete[] nm;
|
||||
}
|
||||
@@ -1739,7 +1796,9 @@ void MakeShaderReflection(GLenum shadType, GLuint sepProg, ShaderReflection &ref
|
||||
globals.bufferBacked = false;
|
||||
globals.bindPoint = (int32_t)refl.constantBlocks.size();
|
||||
|
||||
sort(globalUniforms);
|
||||
// global uniforms have no defined order, location will be per implementation, so sort instead
|
||||
// alphabetically
|
||||
namesort(globalUniforms);
|
||||
std::swap(globals.variables, globalUniforms);
|
||||
|
||||
refl.constantBlocks.push_back(globals);
|
||||
@@ -1759,16 +1818,20 @@ void MakeShaderReflection(GLenum shadType, GLuint sepProg, ShaderReflection &ref
|
||||
{
|
||||
vector<SigParameter> sigs;
|
||||
sigs.reserve(numInputs);
|
||||
|
||||
uint32_t regIndex = 0;
|
||||
|
||||
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};
|
||||
GLenum props[] = {eGL_NAME_LENGTH, eGL_TYPE, eGL_LOCATION, eGL_ARRAY_SIZE,
|
||||
eGL_LOCATION_COMPONENT};
|
||||
GLint values[] = {0, 0, 0, 0, 0};
|
||||
|
||||
GLsizei numSigProps = (GLsizei)ARRAY_COUNT(props);
|
||||
|
||||
// GL_LOCATION_COMPONENT not supported on core <4.4 (or without GL_ARB_enhanced_layouts)
|
||||
// and on GLES, either
|
||||
if(!HasExt[ARB_enhanced_layouts])
|
||||
// on GLES, or when we don't have native program interface query
|
||||
if(!HasExt[ARB_enhanced_layouts] || !HasExt[ARB_program_interface_query])
|
||||
numSigProps--;
|
||||
GL.glGetProgramResourceiv(sepProg, sigEnum, i, numSigProps, props, numSigProps, NULL, values);
|
||||
|
||||
@@ -1924,19 +1987,35 @@ void MakeShaderReflection(GLenum shadType, GLuint sepProg, ShaderReflection &ref
|
||||
break;
|
||||
}
|
||||
|
||||
sig.regChannelMask <<= values[3];
|
||||
|
||||
sig.channelUsedMask = sig.regChannelMask;
|
||||
|
||||
sig.systemValue = ShaderBuiltin::Undefined;
|
||||
|
||||
#define IS_BUILTIN(builtin) !strncmp(nm, builtin, sizeof(builtin) - 1)
|
||||
const char *varname = nm;
|
||||
|
||||
// if these weren't used, they were probably added just to make a separable program
|
||||
// (either by us or the program originally). Skip them from the output signature
|
||||
if(IS_BUILTIN("gl_PointSize") && !pointSizeUsed)
|
||||
continue;
|
||||
if(IS_BUILTIN("gl_ClipDistance") && !clipDistanceUsed)
|
||||
if(!strncmp(varname, "gl_PerVertex.", 13))
|
||||
varname += 13;
|
||||
|
||||
#define IS_BUILTIN(builtin) !strncmp(varname, builtin, sizeof(builtin) - 1)
|
||||
|
||||
// some vertex outputs can be reflected (especially by glslang) if they're just declared and
|
||||
// not used, which is quite common with redeclaring outputs for separable programs - either
|
||||
// by the program or by us. So instead use our manual quick-and-dirty usage check to skip
|
||||
// potential false-positives.
|
||||
bool unused = false;
|
||||
for(FFVertexOutput ffoutput : ::values<FFVertexOutput>())
|
||||
{
|
||||
// we consider an output used if we encounter a '=' before either a ';' or the end of the
|
||||
// string
|
||||
std::string outName = ToStr(ffoutput);
|
||||
|
||||
// we do a substring search so that gl_ClipDistance matches gl_ClipDistance[0]
|
||||
if(strstr(varname, outName.c_str()))
|
||||
{
|
||||
unused = !outputUsage.used[(int)ffoutput];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(unused)
|
||||
continue;
|
||||
|
||||
// VS built-in inputs
|
||||
@@ -2026,27 +2105,39 @@ void MakeShaderReflection(GLenum shadType, GLuint sepProg, ShaderReflection &ref
|
||||
sig.systemValue = ShaderBuiltin::GroupFlatIndex;
|
||||
|
||||
#undef IS_BUILTIN
|
||||
if(sig.systemValue == ShaderBuiltin::Undefined)
|
||||
sig.regIndex = values[2] >= 0 ? values[2] : ~0U;
|
||||
else
|
||||
sig.regIndex = 0;
|
||||
|
||||
if(shadType == eGL_FRAGMENT_SHADER && sigEnum == eGL_PROGRAM_OUTPUT &&
|
||||
sig.systemValue == ShaderBuiltin::Undefined)
|
||||
sig.systemValue = ShaderBuiltin::ColorOutput;
|
||||
|
||||
// don't apply location component for built-ins
|
||||
if(sig.systemValue == ShaderBuiltin::Undefined)
|
||||
sig.regIndex = values[2] >= 0 ? values[2] : i;
|
||||
else
|
||||
sig.regIndex = values[2] >= 0 ? values[2] : 0;
|
||||
sig.regChannelMask <<= values[4];
|
||||
|
||||
if(rows == 1)
|
||||
sig.channelUsedMask = sig.regChannelMask;
|
||||
|
||||
if(values[3] <= 1)
|
||||
{
|
||||
sigs.push_back(sig);
|
||||
AddSigParameter(sigs, regIndex, sig, nm, rows, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int r = 0; r < rows; r++)
|
||||
std::string basename = nm;
|
||||
if(basename[basename.size() - 3] == '[' && basename[basename.size() - 2] == '0' &&
|
||||
basename[basename.size() - 1] == ']')
|
||||
{
|
||||
SigParameter s = sig;
|
||||
s.varName = StringFormat::Fmt("%s:row%d", nm, r);
|
||||
s.regIndex += r;
|
||||
sigs.push_back(s);
|
||||
basename.erase(basename.size() - 3);
|
||||
for(int a = 0; a < values[3]; a++)
|
||||
AddSigParameter(sigs, regIndex, sig, basename.c_str(), rows, a);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDCWARN("Got signature parameter %s with array size %d but no [0] suffix", nm, values[3]);
|
||||
AddSigParameter(sigs, regIndex, sig, nm, rows, -1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,10 +27,36 @@
|
||||
|
||||
class WrappedOpenGL;
|
||||
|
||||
enum class FFVertexOutput : uint32_t
|
||||
{
|
||||
// Core members of gl_PerVertex
|
||||
PointSize,
|
||||
First = PointSize,
|
||||
ClipDistance,
|
||||
|
||||
// Compatibility implicit varyings, generally only comes back from glslang's reflection
|
||||
ClipVertex,
|
||||
FrontColor,
|
||||
BackColor,
|
||||
FrontSecondaryColor,
|
||||
BackSecondaryColor,
|
||||
TexCoord,
|
||||
FogFragCoord,
|
||||
Count,
|
||||
};
|
||||
|
||||
DECLARE_REFLECTION_ENUM(FFVertexOutput);
|
||||
ITERABLE_OPERATORS(FFVertexOutput);
|
||||
|
||||
struct FixedFunctionVertexOutputs
|
||||
{
|
||||
bool used[arraydim<FFVertexOutput>()] = {};
|
||||
};
|
||||
|
||||
int ParseVersionStatement(const char *version);
|
||||
void MakeShaderReflection(GLenum shadType, GLuint sepProg, ShaderReflection &refl,
|
||||
bool pointSizeUsed, bool clipDistanceUsed);
|
||||
const FixedFunctionVertexOutputs &outputUsage);
|
||||
GLuint MakeSeparableShaderProgram(WrappedOpenGL &drv, GLenum type, std::vector<std::string> sources,
|
||||
vector<string> *includepaths);
|
||||
void CheckVertexOutputUses(const std::vector<std::string> &sources, bool &pointSizeUsed,
|
||||
bool &clipDistanceUsed);
|
||||
void CheckVertexOutputUses(const std::vector<std::string> &sources,
|
||||
FixedFunctionVertexOutputs &outputUsage);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -120,9 +120,9 @@ void WrappedOpenGL::ShaderData::ProcessSPIRVCompilation(WrappedOpenGL &drv, Reso
|
||||
void WrappedOpenGL::ShaderData::ProcessCompilation(WrappedOpenGL &drv, ResourceId id,
|
||||
GLuint realShader)
|
||||
{
|
||||
bool pointSizeUsed = false, clipDistanceUsed = false;
|
||||
FixedFunctionVertexOutputs outputUsage = {};
|
||||
if(type == eGL_VERTEX_SHADER)
|
||||
CheckVertexOutputUses(sources, pointSizeUsed, clipDistanceUsed);
|
||||
CheckVertexOutputUses(sources, outputUsage);
|
||||
|
||||
entryPoint = "main";
|
||||
|
||||
@@ -224,9 +224,6 @@ void WrappedOpenGL::ShaderData::ProcessCompilation(WrappedOpenGL &drv, ResourceI
|
||||
if(version == 0)
|
||||
version = 100;
|
||||
|
||||
reflection.encoding = ShaderEncoding::GLSL;
|
||||
reflection.rawBytes.assign((byte *)concatenated.c_str(), concatenated.size());
|
||||
|
||||
GLuint sepProg = prog;
|
||||
|
||||
GLint status = 0;
|
||||
@@ -235,12 +232,17 @@ void WrappedOpenGL::ShaderData::ProcessCompilation(WrappedOpenGL &drv, ResourceI
|
||||
else
|
||||
drv.glGetShaderiv(realShader, eGL_COMPILE_STATUS, &status);
|
||||
|
||||
if(IsCaptureMode(drv.GetState()))
|
||||
{
|
||||
// if we don't have program_interface_query, need to compile the shader with glslang to be able
|
||||
// to reflect with. This is needed on capture or replay
|
||||
if(!HasExt[ARB_program_interface_query] && status == 1)
|
||||
glslangShader = CompileShaderForReflection(SPIRVShaderStage(ShaderIdx(type)), sources);
|
||||
}
|
||||
else
|
||||
|
||||
if(IsReplayMode(drv.GetState()) && !drv.IsInternalShader())
|
||||
{
|
||||
// no shaders made under this point should be reflected themselves, they're only used for
|
||||
// reflection
|
||||
drv.PushInternalShader();
|
||||
|
||||
if(sepProg == 0 && status == 1)
|
||||
sepProg = MakeSeparableShaderProgram(drv, type, sources, NULL);
|
||||
|
||||
@@ -257,7 +259,7 @@ void WrappedOpenGL::ShaderData::ProcessCompilation(WrappedOpenGL &drv, ResourceI
|
||||
else
|
||||
{
|
||||
prog = sepProg;
|
||||
MakeShaderReflection(type, sepProg, reflection, pointSizeUsed, clipDistanceUsed);
|
||||
MakeShaderReflection(type, sepProg, reflection, outputUsage);
|
||||
|
||||
vector<uint32_t> spirvwords;
|
||||
|
||||
@@ -275,12 +277,17 @@ void WrappedOpenGL::ShaderData::ProcessCompilation(WrappedOpenGL &drv, ResourceI
|
||||
|
||||
reflection.stage = MakeShaderStage(type);
|
||||
|
||||
reflection.encoding = ShaderEncoding::GLSL;
|
||||
reflection.rawBytes.assign((byte *)concatenated.c_str(), concatenated.size());
|
||||
|
||||
reflection.debugInfo.encoding = ShaderEncoding::GLSL;
|
||||
|
||||
reflection.debugInfo.files.resize(1);
|
||||
reflection.debugInfo.files[0].filename = "main.glsl";
|
||||
reflection.debugInfo.files[0].contents = concatenated;
|
||||
}
|
||||
|
||||
drv.PopInternalShader();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,7 +402,7 @@ bool WrappedOpenGL::Serialise_glShaderSource(SerialiserType &ser, GLuint shaderH
|
||||
// so people who do that should be moderately ashamed.
|
||||
if(m_Shaders[liveId].prog)
|
||||
{
|
||||
GL.glDeleteProgram(m_Shaders[liveId].prog);
|
||||
glDeleteProgram(m_Shaders[liveId].prog);
|
||||
m_Shaders[liveId].prog = 0;
|
||||
m_Shaders[liveId].spirv = SPVModule();
|
||||
m_Shaders[liveId].reflection = ShaderReflection();
|
||||
@@ -855,6 +862,28 @@ bool WrappedOpenGL::Serialise_glLinkProgram(SerialiserType &ser, GLuint programH
|
||||
}
|
||||
}
|
||||
|
||||
if(!HasExt[ARB_program_interface_query])
|
||||
{
|
||||
std::vector<glslang::TShader *> glslangShaders;
|
||||
|
||||
for(ResourceId id : progDetails.stageShaders)
|
||||
{
|
||||
if(id == ResourceId())
|
||||
continue;
|
||||
|
||||
glslang::TShader *s = m_Shaders[id].glslangShader;
|
||||
if(s == NULL)
|
||||
{
|
||||
RDCERR("Shader attached with no compiled glslang reflection shader!");
|
||||
continue;
|
||||
}
|
||||
|
||||
glslangShaders.push_back(m_Shaders[id].glslangShader);
|
||||
}
|
||||
|
||||
progDetails.glslangProgram = LinkProgramForReflection(glslangShaders);
|
||||
}
|
||||
|
||||
GL.glLinkProgram(program.name);
|
||||
|
||||
AddResourceInitChunk(program);
|
||||
@@ -898,12 +927,15 @@ void WrappedOpenGL::glLinkProgram(GLuint program)
|
||||
}
|
||||
}
|
||||
|
||||
if(IsCaptureMode(m_State) && !HasExt[ARB_program_interface_query])
|
||||
if(!HasExt[ARB_program_interface_query])
|
||||
{
|
||||
std::vector<glslang::TShader *> glslangShaders;
|
||||
|
||||
for(ResourceId id : progDetails.shaders)
|
||||
for(ResourceId id : progDetails.stageShaders)
|
||||
{
|
||||
if(id == ResourceId())
|
||||
continue;
|
||||
|
||||
glslang::TShader *s = m_Shaders[id].glslangShader;
|
||||
if(s == NULL)
|
||||
{
|
||||
@@ -1988,4 +2020,4 @@ INSTANTIATE_FUNCTION_SERIALISED(void, glShaderBinary, GLsizei count, const GLuin
|
||||
GLenum binaryformat, const void *binary, GLsizei length);
|
||||
INSTANTIATE_FUNCTION_SERIALISED(void, glSpecializeShader, GLuint shader, const GLchar *pEntryPoint,
|
||||
GLuint numSpecializationConstants, const GLuint *pConstantIndex,
|
||||
const GLuint *pConstantValue);
|
||||
const GLuint *pConstantValue);
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
#include "3rdparty/glslang/glslang/Include/Types.h"
|
||||
#include "3rdparty/glslang/glslang/Public/ShaderLang.h"
|
||||
|
||||
static bool inited = false;
|
||||
@@ -100,20 +101,14 @@ void glslangGetProgramInterfaceiv(glslang::TProgram *program, ReflectionInterfac
|
||||
{
|
||||
switch(programInterface)
|
||||
{
|
||||
case ReflectionInterface::Input: *params = program->getNumLiveAttributes(); break;
|
||||
case ReflectionInterface::Output:
|
||||
// unsupported
|
||||
*params = 0;
|
||||
break;
|
||||
case ReflectionInterface::Uniform: *params = program->getNumLiveUniformVariables(); break;
|
||||
case ReflectionInterface::UniformBlock: *params = program->getNumLiveUniformBlocks(); break;
|
||||
case ReflectionInterface::ShaderStorageBlock:
|
||||
// unsupported
|
||||
*params = 0;
|
||||
break;
|
||||
case ReflectionInterface::Input: *params = program->getNumPipeInputs(); break;
|
||||
case ReflectionInterface::Output: *params = program->getNumPipeOutputs(); break;
|
||||
case ReflectionInterface::Uniform: *params = program->getNumUniformVariables(); break;
|
||||
case ReflectionInterface::UniformBlock: *params = program->getNumUniformBlocks(); break;
|
||||
case ReflectionInterface::BufferVariable: *params = program->getNumBufferVariables(); break;
|
||||
case ReflectionInterface::ShaderStorageBlock: *params = program->getNumBufferBlocks(); break;
|
||||
case ReflectionInterface::AtomicCounterBuffer:
|
||||
// unsupported
|
||||
*params = 0;
|
||||
*params = program->getNumAtomicCounters();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -127,13 +122,6 @@ void glslangGetProgramResourceiv(glslang::TProgram *program, ReflectionInterface
|
||||
uint32_t index, const std::vector<ReflectionProperty> &props,
|
||||
int32_t bufSize, int32_t *length, int32_t *params)
|
||||
{
|
||||
if(programInterface == ReflectionInterface::Output ||
|
||||
programInterface == ReflectionInterface::ShaderStorageBlock ||
|
||||
programInterface == ReflectionInterface::AtomicCounterBuffer)
|
||||
{
|
||||
RDCWARN("unsupported program interface");
|
||||
}
|
||||
|
||||
// all of our properties are single-element values, so we just loop up to buffer size or number of
|
||||
// properties, whichever comes first.
|
||||
for(size_t i = 0; i < RDCMIN((size_t)bufSize, props.size()); i++)
|
||||
@@ -145,99 +133,324 @@ void glslangGetProgramResourceiv(glslang::TProgram *program, ReflectionInterface
|
||||
params[i] = 0;
|
||||
break;
|
||||
case ReflectionProperty::BufferBinding:
|
||||
RDCASSERT(programInterface == ReflectionInterface::UniformBlock);
|
||||
params[i] = program->getUniformBlockBinding(index);
|
||||
break;
|
||||
case ReflectionProperty::TopLevelArrayStride:
|
||||
// TODO glslang doesn't give us this
|
||||
params[i] = 16;
|
||||
break;
|
||||
case ReflectionProperty::BlockIndex:
|
||||
RDCASSERT(programInterface == ReflectionInterface::Uniform);
|
||||
params[i] = program->getUniformBlockIndex(index);
|
||||
break;
|
||||
case ReflectionProperty::ArraySize:
|
||||
if(programInterface == ReflectionInterface::Uniform)
|
||||
params[i] = program->getUniformArraySize(index);
|
||||
else if(programInterface == ReflectionInterface::Input)
|
||||
// TODO assuming all inputs are non-arrayed
|
||||
params[i] = 1;
|
||||
{
|
||||
if(programInterface == ReflectionInterface::UniformBlock)
|
||||
params[i] = program->getUniformBlock(index).getBinding();
|
||||
else if(programInterface == ReflectionInterface::ShaderStorageBlock)
|
||||
params[i] = program->getBufferBlock(index).getBinding();
|
||||
else
|
||||
RDCERR("Unsupported interface for BufferBinding query");
|
||||
break;
|
||||
}
|
||||
case ReflectionProperty::BlockIndex:
|
||||
{
|
||||
if(programInterface == ReflectionInterface::Uniform)
|
||||
params[i] = program->getUniform(index).index;
|
||||
else if(programInterface == ReflectionInterface::BufferVariable)
|
||||
params[i] = program->getBufferVariable(index).index;
|
||||
else
|
||||
RDCERR("Unsupported interface for BlockIndex query");
|
||||
break;
|
||||
}
|
||||
case ReflectionProperty::ArraySize:
|
||||
{
|
||||
if(programInterface == ReflectionInterface::Uniform)
|
||||
{
|
||||
params[i] = program->getUniform(index).size;
|
||||
}
|
||||
else if(programInterface == ReflectionInterface::BufferVariable)
|
||||
{
|
||||
params[i] = program->getBufferVariable(index).size;
|
||||
}
|
||||
else if(programInterface == ReflectionInterface::Input)
|
||||
{
|
||||
const glslang::TType *type = program->getPipeInput(index).getType();
|
||||
if(type->isArray())
|
||||
params[i] = type->getOuterArraySize();
|
||||
else
|
||||
params[i] = 1;
|
||||
}
|
||||
else if(programInterface == ReflectionInterface::Output)
|
||||
{
|
||||
const glslang::TType *type = program->getPipeOutput(index).getType();
|
||||
if(type->isArray())
|
||||
params[i] = type->getOuterArraySize();
|
||||
else
|
||||
params[i] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
RDCERR("Unsupported interface for ArraySize query");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ReflectionProperty::IsRowMajor:
|
||||
// TODO glslang doesn't expose this, assume column major.
|
||||
params[i] = 0;
|
||||
{
|
||||
const glslang::TType *ttype = NULL;
|
||||
|
||||
if(programInterface == ReflectionInterface::Uniform)
|
||||
ttype = program->getUniform(index).getType();
|
||||
else if(programInterface == ReflectionInterface::BufferVariable)
|
||||
ttype = program->getBufferVariable(index).getType();
|
||||
else
|
||||
RDCERR("Unsupported interface for RowMajor query");
|
||||
|
||||
if(ttype)
|
||||
params[i] = (ttype->getQualifier().layoutMatrix == glslang::ElmRowMajor);
|
||||
else
|
||||
params[i] = 0;
|
||||
break;
|
||||
}
|
||||
case ReflectionProperty::MatrixStride:
|
||||
{
|
||||
// From documentation of std140:
|
||||
//
|
||||
// 5. "If the member is a column-major matrix with C columns and R rows, the matrix is
|
||||
// stored identically to an array of C column vectors with R components each, according to
|
||||
// rule (4)."
|
||||
// 7. "If the member is a row-major matrix with C columns and R rows, the matrix is stored
|
||||
// identically to an array of R row vectors with C components each, according to rule (4)."
|
||||
//
|
||||
// So in std140 the matrix stride is always at least 16-bytes unless the matrix is doubles.
|
||||
// In std430, because the rule (4) array alignment is relaxed, it can be less.
|
||||
if(programInterface == ReflectionInterface::Uniform)
|
||||
{
|
||||
params[i] = 16;
|
||||
}
|
||||
else if(programInterface == ReflectionInterface::BufferVariable)
|
||||
{
|
||||
const glslang::TType *ttype = program->getBufferVariable(index).getType();
|
||||
|
||||
if(ttype->getQualifier().layoutMatrix == glslang::ElmRowMajor)
|
||||
params[i] = ttype->getMatrixCols() * sizeof(float);
|
||||
else
|
||||
params[i] = ttype->getMatrixRows() * sizeof(float);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDCERR("Unsupported interface for RowMajor query");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ReflectionProperty::NumActiveVariables:
|
||||
// TODO glslang doesn't give us this
|
||||
params[i] = 1;
|
||||
{
|
||||
if(programInterface == ReflectionInterface::UniformBlock)
|
||||
params[i] = program->getUniformBlock(index).numMembers;
|
||||
else if(programInterface == ReflectionInterface::ShaderStorageBlock)
|
||||
params[i] = program->getBufferBlock(index).numMembers;
|
||||
else
|
||||
RDCERR("Unsupported interface for NumActiveVariables query");
|
||||
break;
|
||||
}
|
||||
case ReflectionProperty::BufferDataSize:
|
||||
RDCASSERT(programInterface == ReflectionInterface::UniformBlock);
|
||||
params[i] = program->getUniformBlockSize(index);
|
||||
params[i] = program->getUniformBlock(index).size;
|
||||
break;
|
||||
case ReflectionProperty::NameLength:
|
||||
{
|
||||
// The name length includes a terminating null character.
|
||||
if(programInterface == ReflectionInterface::Uniform)
|
||||
params[i] = (int32_t)strlen(program->getUniformName(index)) + 1;
|
||||
params[i] = (int32_t)program->getUniform(index).name.size() + 1;
|
||||
else if(programInterface == ReflectionInterface::BufferVariable)
|
||||
params[i] = (int32_t)program->getBufferVariable(index).name.size() + 1;
|
||||
else if(programInterface == ReflectionInterface::UniformBlock)
|
||||
params[i] = (int32_t)strlen(program->getUniformBlockName(index)) + 1;
|
||||
params[i] = (int32_t)program->getUniformBlock(index).name.size() + 1;
|
||||
else if(programInterface == ReflectionInterface::Input)
|
||||
params[i] = (int32_t)strlen(program->getAttributeName(index)) + 1;
|
||||
params[i] = (int32_t)program->getPipeInput(index).name.size() + 1;
|
||||
else if(programInterface == ReflectionInterface::Output)
|
||||
params[i] = (int32_t)program->getPipeOutput(index).name.size() + 1;
|
||||
else if(programInterface == ReflectionInterface::AtomicCounterBuffer)
|
||||
params[i] = (int32_t)program->getAtomicCounter(index).name.size() + 1;
|
||||
else if(programInterface == ReflectionInterface::ShaderStorageBlock)
|
||||
params[i] = (int32_t)program->getBufferBlock(index).name.size() + 1;
|
||||
else
|
||||
RDCERR("Unsupported interface for NameLEngth query");
|
||||
RDCERR("Unsupported interface for NameLength query");
|
||||
break;
|
||||
}
|
||||
case ReflectionProperty::Type:
|
||||
{
|
||||
if(programInterface == ReflectionInterface::Uniform)
|
||||
params[i] = program->getUniformType(index);
|
||||
params[i] = program->getUniform(index).glDefineType;
|
||||
else if(programInterface == ReflectionInterface::BufferVariable)
|
||||
params[i] = program->getBufferVariable(index).glDefineType;
|
||||
else if(programInterface == ReflectionInterface::Input)
|
||||
params[i] = program->getAttributeType(index);
|
||||
params[i] = program->getPipeInput(index).glDefineType;
|
||||
else if(programInterface == ReflectionInterface::Output)
|
||||
params[i] = program->getPipeOutput(index).glDefineType;
|
||||
else
|
||||
RDCERR("Unsupported interface for Type query");
|
||||
|
||||
if(params[i] == 0)
|
||||
params[i] = 0x1406; // GL_FLOAT
|
||||
|
||||
break;
|
||||
}
|
||||
case ReflectionProperty::LocationComponent:
|
||||
// TODO glslang doesn't give us this information
|
||||
params[i] = 0;
|
||||
{
|
||||
if(programInterface == ReflectionInterface::Uniform)
|
||||
params[i] = program->getUniform(index).getType()->getQualifier().layoutComponent;
|
||||
else if(programInterface == ReflectionInterface::BufferVariable)
|
||||
params[i] = program->getBufferVariable(index).getType()->getQualifier().layoutComponent;
|
||||
else if(programInterface == ReflectionInterface::Input)
|
||||
params[i] = program->getPipeInput(index).getType()->getQualifier().layoutComponent;
|
||||
else if(programInterface == ReflectionInterface::Output)
|
||||
params[i] = program->getPipeOutput(index).getType()->getQualifier().layoutComponent;
|
||||
else
|
||||
RDCERR("Unsupported interface for LocationComponent query");
|
||||
|
||||
if(params[i] == glslang::TQualifier::layoutComponentEnd)
|
||||
params[i] = 0;
|
||||
break;
|
||||
}
|
||||
case ReflectionProperty::ReferencedByVertexShader:
|
||||
case ReflectionProperty::ReferencedByTessControlShader:
|
||||
case ReflectionProperty::ReferencedByTessEvaluationShader:
|
||||
case ReflectionProperty::ReferencedByGeometryShader:
|
||||
case ReflectionProperty::ReferencedByFragmentShader:
|
||||
case ReflectionProperty::ReferencedByComputeShader:
|
||||
// TODO glslang doesn't give us this information
|
||||
params[i] = 1;
|
||||
break;
|
||||
case ReflectionProperty::AtomicCounterBufferIndex:
|
||||
RDCERR("Atomic counters not supported");
|
||||
break;
|
||||
case ReflectionProperty::Offset:
|
||||
RDCASSERT(programInterface == ReflectionInterface::Uniform);
|
||||
params[i] = program->getUniformBufferOffset(index);
|
||||
break;
|
||||
case ReflectionProperty::MatrixStride:
|
||||
RDCASSERT(programInterface == ReflectionInterface::Uniform);
|
||||
// TODO glslang doesn't give us this information
|
||||
params[i] = 64;
|
||||
break;
|
||||
case ReflectionProperty::ArrayStride:
|
||||
RDCASSERT(programInterface == ReflectionInterface::Uniform);
|
||||
// TODO glslang doesn't give us this information
|
||||
params[i] = 64;
|
||||
break;
|
||||
case ReflectionProperty::Location:
|
||||
// have to query the actual implementation, which is handled elsewhere. We return either -1
|
||||
// for uniforms that don't have a location (i.e. are in a block) or 0 for bare uniforms
|
||||
{
|
||||
EShLanguageMask mask = {};
|
||||
switch(props[i])
|
||||
{
|
||||
case ReflectionProperty::ReferencedByVertexShader: mask = EShLangVertexMask; break;
|
||||
case ReflectionProperty::ReferencedByTessControlShader:
|
||||
mask = EShLangTessControlMask;
|
||||
break;
|
||||
case ReflectionProperty::ReferencedByTessEvaluationShader:
|
||||
mask = EShLangTessEvaluationMask;
|
||||
break;
|
||||
case ReflectionProperty::ReferencedByGeometryShader: mask = EShLangGeometryMask; break;
|
||||
case ReflectionProperty::ReferencedByFragmentShader: mask = EShLangFragmentMask; break;
|
||||
case ReflectionProperty::ReferencedByComputeShader: mask = EShLangComputeMask; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if(programInterface == ReflectionInterface::Uniform)
|
||||
params[i] = program->getUniformBlockIndex(index) >= 0 ? -1 : 0;
|
||||
params[i] = (program->getUniform(index).stages & mask) != 0;
|
||||
else if(programInterface == ReflectionInterface::UniformBlock)
|
||||
params[i] = (program->getUniformBlock(index).stages & mask) != 0;
|
||||
else if(programInterface == ReflectionInterface::BufferVariable)
|
||||
params[i] = (program->getBufferVariable(index).stages & mask) != 0;
|
||||
else if(programInterface == ReflectionInterface::ShaderStorageBlock)
|
||||
params[i] = (program->getBufferBlock(index).stages & mask) != 0;
|
||||
else if(programInterface == ReflectionInterface::Input)
|
||||
params[i] = index;
|
||||
params[i] = (program->getPipeInput(index).stages & mask) != 0;
|
||||
else if(programInterface == ReflectionInterface::Output)
|
||||
params[i] = (program->getPipeOutput(index).stages & mask) != 0;
|
||||
else if(programInterface == ReflectionInterface::AtomicCounterBuffer)
|
||||
params[i] = (program->getAtomicCounter(index).stages & mask) != 0;
|
||||
else
|
||||
RDCERR("Unexpected interface being queried for referenced-by");
|
||||
|
||||
break;
|
||||
}
|
||||
case ReflectionProperty::Internal_Binding:
|
||||
{
|
||||
if(programInterface == ReflectionInterface::UniformBlock)
|
||||
{
|
||||
params[i] = program->getUniformBlock(index).getType()->getQualifier().layoutBinding;
|
||||
break;
|
||||
}
|
||||
|
||||
// deliberate fall-through
|
||||
}
|
||||
case ReflectionProperty::AtomicCounterBufferIndex:
|
||||
{
|
||||
if(programInterface == ReflectionInterface::Uniform)
|
||||
params[i] = program->getUniform(index).getType()->getQualifier().layoutBinding;
|
||||
else if(programInterface == ReflectionInterface::AtomicCounterBuffer)
|
||||
params[i] = program->getAtomicCounter(index).getType()->getQualifier().layoutBinding;
|
||||
else
|
||||
RDCERR("Unexpected interface being queried for AtomicCounterBufferIndex");
|
||||
break;
|
||||
}
|
||||
case ReflectionProperty::Offset:
|
||||
{
|
||||
if(programInterface == ReflectionInterface::Uniform)
|
||||
params[i] = program->getUniform(index).offset;
|
||||
else if(programInterface == ReflectionInterface::BufferVariable)
|
||||
params[i] = program->getBufferVariable(index).offset;
|
||||
else
|
||||
RDCERR("Unsupported interface for Offset query");
|
||||
break;
|
||||
}
|
||||
case ReflectionProperty::TopLevelArrayStride:
|
||||
{
|
||||
if(programInterface == ReflectionInterface::Uniform)
|
||||
params[i] = program->getUniform(index).topLevelArrayStride;
|
||||
else if(programInterface == ReflectionInterface::BufferVariable)
|
||||
params[i] = program->getBufferVariable(index).topLevelArrayStride;
|
||||
else
|
||||
RDCERR("Unsupported interface for ArrayStride query");
|
||||
break;
|
||||
}
|
||||
case ReflectionProperty::ArrayStride:
|
||||
{
|
||||
if(programInterface == ReflectionInterface::Uniform)
|
||||
params[i] = program->getUniform(index).arrayStride;
|
||||
else if(programInterface == ReflectionInterface::BufferVariable)
|
||||
params[i] = program->getBufferVariable(index).arrayStride;
|
||||
else
|
||||
RDCERR("Unsupported interface for ArrayStride query");
|
||||
break;
|
||||
}
|
||||
case ReflectionProperty::Location:
|
||||
{
|
||||
// want to query the actual implementation for bare uniform locations, which is handled
|
||||
// elsewhere. So we always return either -1 for uniforms that don't have a location (i.e.
|
||||
// are in a block) or 0 for bare uniforms
|
||||
if(programInterface == ReflectionInterface::Uniform)
|
||||
{
|
||||
params[i] = program->getUniform(index).index >= 0 ? -1 : 0;
|
||||
}
|
||||
// for program inputs/outputs for a vertex/fragment shader respectively, we want to do the
|
||||
// same as above and always query when possible, however for fragment inputs e.g. we want to
|
||||
// keep the locations that might be present in the shader. So we do the reverse - return -1
|
||||
// when it's a vertex input to force a query, and otherwise return the layout set.
|
||||
else if(programInterface == ReflectionInterface::Input)
|
||||
{
|
||||
params[i] = program->getPipeInput(index).getType()->getQualifier().layoutLocation;
|
||||
|
||||
if(params[i] == glslang::TQualifier::layoutLocationEnd)
|
||||
params[i] = -1;
|
||||
|
||||
if(program->getPipeInput(index).stages == EShLangVertexMask)
|
||||
params[i] = -1;
|
||||
}
|
||||
else if(programInterface == ReflectionInterface::Output)
|
||||
{
|
||||
params[i] = program->getPipeOutput(index).getType()->getQualifier().layoutLocation;
|
||||
|
||||
if(params[i] == glslang::TQualifier::layoutLocationEnd)
|
||||
params[i] = -1;
|
||||
|
||||
if(program->getPipeOutput(index).stages == EShLangFragmentMask)
|
||||
params[i] = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t glslangGetProgramResourceIndex(glslang::TProgram *program, const char *name)
|
||||
{
|
||||
uint32_t idx = program->getReflectionIndex(name);
|
||||
|
||||
// Additionally, if <name> would exactly match the name string of an active
|
||||
// resource if "[0]" were appended to <name>, the index of the matched
|
||||
// resource is returned.
|
||||
if(idx == ~0U)
|
||||
{
|
||||
std::string arraysuffixed = name;
|
||||
arraysuffixed += "[0]";
|
||||
idx = program->getReflectionIndex(arraysuffixed.c_str());
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
const char *glslangGetProgramResourceName(glslang::TProgram *program,
|
||||
ReflectionInterface programInterface, uint32_t index)
|
||||
{
|
||||
@@ -245,17 +458,22 @@ const char *glslangGetProgramResourceName(glslang::TProgram *program,
|
||||
|
||||
switch(programInterface)
|
||||
{
|
||||
case ReflectionInterface::Input: fetchedName = program->getAttributeName(index); break;
|
||||
case ReflectionInterface::Output: RDCWARN("Output attributes unsupported"); break;
|
||||
case ReflectionInterface::Uniform: fetchedName = program->getUniformName(index); break;
|
||||
case ReflectionInterface::Input: fetchedName = program->getPipeInput(index).name.c_str(); break;
|
||||
case ReflectionInterface::Output:
|
||||
fetchedName = program->getPipeOutput(index).name.c_str();
|
||||
break;
|
||||
case ReflectionInterface::Uniform: fetchedName = program->getUniform(index).name.c_str(); break;
|
||||
case ReflectionInterface::UniformBlock:
|
||||
fetchedName = program->getUniformBlockName(index);
|
||||
fetchedName = program->getUniformBlock(index).name.c_str();
|
||||
break;
|
||||
case ReflectionInterface::BufferVariable:
|
||||
fetchedName = program->getBufferVariable(index).name.c_str();
|
||||
break;
|
||||
case ReflectionInterface::ShaderStorageBlock:
|
||||
RDCWARN("shader storage blocks unsupported");
|
||||
fetchedName = program->getBufferBlock(index).name.c_str();
|
||||
break;
|
||||
case ReflectionInterface::AtomicCounterBuffer:
|
||||
RDCWARN("atomic counter buffers unsupported");
|
||||
fetchedName = program->getAtomicCounter(index).name.c_str();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -187,6 +187,7 @@ enum class ReflectionInterface
|
||||
UniformBlock,
|
||||
ShaderStorageBlock,
|
||||
AtomicCounterBuffer,
|
||||
BufferVariable,
|
||||
};
|
||||
|
||||
enum class ReflectionProperty
|
||||
@@ -208,10 +209,11 @@ enum class ReflectionProperty
|
||||
ReferencedByGeometryShader,
|
||||
ReferencedByFragmentShader,
|
||||
ReferencedByComputeShader,
|
||||
Internal_Binding,
|
||||
AtomicCounterBufferIndex,
|
||||
Offset,
|
||||
MatrixStride,
|
||||
ArrayStride,
|
||||
MatrixStride,
|
||||
Location,
|
||||
};
|
||||
|
||||
@@ -221,6 +223,7 @@ void glslangGetProgramInterfaceiv(glslang::TProgram *program, ReflectionInterfac
|
||||
void glslangGetProgramResourceiv(glslang::TProgram *program, ReflectionInterface programInterface,
|
||||
uint32_t index, const std::vector<ReflectionProperty> &props,
|
||||
int32_t bufSize, int32_t *length, int32_t *params);
|
||||
uint32_t glslangGetProgramResourceIndex(glslang::TProgram *program, const char *name);
|
||||
|
||||
const char *glslangGetProgramResourceName(glslang::TProgram *program,
|
||||
ReflectionInterface programInterface, uint32_t index);
|
||||
@@ -264,7 +264,9 @@ glslang::TProgram *LinkProgramForReflection(const std::vector<glslang::TShader *
|
||||
|
||||
if(program->link(EShMsgDefault))
|
||||
{
|
||||
program->buildReflection();
|
||||
program->buildReflection(EShReflectionStrictArraySuffix | EShReflectionBasicArraySuffix |
|
||||
EShReflectionIntermediateIO | EShReflectionSeparateBuffers |
|
||||
EShReflectionAllBlockVariables | EShReflectionUnwrapIOBlocks);
|
||||
allocatedPrograms.push_back(program);
|
||||
return program;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user