in renderdoc/driver/gl/gl_shader_refl.cpp [1227:2403]
void MakeShaderReflection(GLenum shadType, GLuint sepProg, ShaderReflection &refl,
const FixedFunctionVertexOutputs &outputUsage)
{
refl.stage = MakeShaderStage(shadType);
refl.debugInfo.entrySourceName = refl.entryPoint = "main";
refl.encoding = ShaderEncoding::GLSL;
refl.debugInfo.compiler = KnownShaderTool::Unknown;
refl.debugInfo.encoding = ShaderEncoding::GLSL;
if(shadType == eGL_COMPUTE_SHADER)
{
GL.glGetProgramiv(sepProg, eGL_COMPUTE_WORK_GROUP_SIZE,
(GLint *)refl.dispatchThreadsDimension.data());
}
else
{
RDCEraseEl(refl.dispatchThreadsDimension);
}
rdcarray<ShaderResource> &roresources = refl.readOnlyResources;
rdcarray<ShaderResource> &rwresources = refl.readWriteResources;
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.isReadOnly = true;
res.isTexture = true;
res.variableType.rows = 1;
res.variableType.columns = 4;
res.variableType.elements = 1;
res.variableType.arrayByteStride = 0;
res.variableType.matrixByteStride = 0;
res.descriptorType = DescriptorType::ImageSampler;
// float samplers
if(values[0] == eGL_SAMPLER_BUFFER)
{
res.textureType = TextureType::Buffer;
res.variableType.name = "samplerBuffer";
res.variableType.baseType = VarType::Float;
res.descriptorType = DescriptorType::TypedBuffer;
}
else if(values[0] == eGL_SAMPLER_1D)
{
res.textureType = TextureType::Texture1D;
res.variableType.name = "sampler1D";
res.variableType.baseType = VarType::Float;
}
else if(values[0] == eGL_SAMPLER_1D_ARRAY)
{
res.textureType = TextureType::Texture1DArray;
res.variableType.name = "sampler1DArray";
res.variableType.baseType = VarType::Float;
}
else if(values[0] == eGL_SAMPLER_1D_SHADOW)
{
res.textureType = TextureType::Texture1D;
res.variableType.name = "sampler1DShadow";
res.variableType.baseType = VarType::Float;
}
else if(values[0] == eGL_SAMPLER_1D_ARRAY_SHADOW)
{
res.textureType = TextureType::Texture1DArray;
res.variableType.name = "sampler1DArrayShadow";
res.variableType.baseType = VarType::Float;
}
else if(values[0] == eGL_SAMPLER_2D)
{
res.textureType = TextureType::Texture2D;
res.variableType.name = "sampler2D";
res.variableType.baseType = VarType::Float;
}
else if(values[0] == eGL_SAMPLER_2D_ARRAY)
{
res.textureType = TextureType::Texture2DArray;
res.variableType.name = "sampler2DArray";
res.variableType.baseType = VarType::Float;
}
else if(values[0] == eGL_SAMPLER_2D_SHADOW)
{
res.textureType = TextureType::Texture2D;
res.variableType.name = "sampler2DShadow";
res.variableType.baseType = VarType::Float;
}
else if(values[0] == eGL_SAMPLER_2D_ARRAY_SHADOW)
{
res.textureType = TextureType::Texture2DArray;
res.variableType.name = "sampler2DArrayShadow";
res.variableType.baseType = VarType::Float;
}
else if(values[0] == eGL_SAMPLER_2D_RECT)
{
res.textureType = TextureType::TextureRect;
res.variableType.name = "sampler2DRect";
res.variableType.baseType = VarType::Float;
}
else if(values[0] == eGL_SAMPLER_2D_RECT_SHADOW)
{
res.textureType = TextureType::TextureRect;
res.variableType.name = "sampler2DRectShadow";
res.variableType.baseType = VarType::Float;
}
else if(values[0] == eGL_SAMPLER_3D)
{
res.textureType = TextureType::Texture3D;
res.variableType.name = "sampler3D";
res.variableType.baseType = VarType::Float;
}
else if(values[0] == eGL_SAMPLER_CUBE)
{
res.textureType = TextureType::TextureCube;
res.variableType.name = "samplerCube";
res.variableType.baseType = VarType::Float;
}
else if(values[0] == eGL_SAMPLER_CUBE_SHADOW)
{
res.textureType = TextureType::TextureCube;
res.variableType.name = "samplerCubeShadow";
res.variableType.baseType = VarType::Float;
}
else if(values[0] == eGL_SAMPLER_CUBE_MAP_ARRAY)
{
res.textureType = TextureType::TextureCubeArray;
res.variableType.name = "samplerCubeArray";
res.variableType.baseType = VarType::Float;
}
else if(values[0] == eGL_SAMPLER_2D_MULTISAMPLE)
{
res.textureType = TextureType::Texture2DMS;
res.variableType.name = "sampler2DMS";
res.variableType.baseType = VarType::Float;
}
else if(values[0] == eGL_SAMPLER_2D_MULTISAMPLE_ARRAY)
{
res.textureType = TextureType::Texture2DMSArray;
res.variableType.name = "sampler2DMSArray";
res.variableType.baseType = VarType::Float;
}
// int samplers
else if(values[0] == eGL_INT_SAMPLER_BUFFER)
{
res.textureType = TextureType::Buffer;
res.variableType.name = "isamplerBuffer";
res.variableType.baseType = VarType::SInt;
res.descriptorType = DescriptorType::TypedBuffer;
}
else if(values[0] == eGL_INT_SAMPLER_1D)
{
res.textureType = TextureType::Texture1D;
res.variableType.name = "isampler1D";
res.variableType.baseType = VarType::SInt;
}
else if(values[0] == eGL_INT_SAMPLER_1D_ARRAY)
{
res.textureType = TextureType::Texture1DArray;
res.variableType.name = "isampler1DArray";
res.variableType.baseType = VarType::SInt;
}
else if(values[0] == eGL_INT_SAMPLER_2D)
{
res.textureType = TextureType::Texture2D;
res.variableType.name = "isampler2D";
res.variableType.baseType = VarType::SInt;
}
else if(values[0] == eGL_INT_SAMPLER_2D_ARRAY)
{
res.textureType = TextureType::Texture2DArray;
res.variableType.name = "isampler2DArray";
res.variableType.baseType = VarType::SInt;
}
else if(values[0] == eGL_INT_SAMPLER_2D_RECT)
{
res.textureType = TextureType::TextureRect;
res.variableType.name = "isampler2DRect";
res.variableType.baseType = VarType::SInt;
}
else if(values[0] == eGL_INT_SAMPLER_3D)
{
res.textureType = TextureType::Texture3D;
res.variableType.name = "isampler3D";
res.variableType.baseType = VarType::SInt;
}
else if(values[0] == eGL_INT_SAMPLER_CUBE)
{
res.textureType = TextureType::TextureCube;
res.variableType.name = "isamplerCube";
res.variableType.baseType = VarType::SInt;
}
else if(values[0] == eGL_INT_SAMPLER_CUBE_MAP_ARRAY)
{
res.textureType = TextureType::TextureCubeArray;
res.variableType.name = "isamplerCubeArray";
res.variableType.baseType = VarType::SInt;
}
else if(values[0] == eGL_INT_SAMPLER_2D_MULTISAMPLE)
{
res.textureType = TextureType::Texture2DMS;
res.variableType.name = "isampler2DMS";
res.variableType.baseType = VarType::SInt;
}
else if(values[0] == eGL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY)
{
res.textureType = TextureType::Texture2DMSArray;
res.variableType.name = "isampler2DMSArray";
res.variableType.baseType = VarType::SInt;
}
// unsigned int samplers
else if(values[0] == eGL_UNSIGNED_INT_SAMPLER_BUFFER)
{
res.textureType = TextureType::Buffer;
res.variableType.name = "usamplerBuffer";
res.variableType.baseType = VarType::UInt;
res.descriptorType = DescriptorType::TypedBuffer;
}
else if(values[0] == eGL_UNSIGNED_INT_SAMPLER_1D)
{
res.textureType = TextureType::Texture1D;
res.variableType.name = "usampler1D";
res.variableType.baseType = VarType::UInt;
}
else if(values[0] == eGL_UNSIGNED_INT_SAMPLER_1D_ARRAY)
{
res.textureType = TextureType::Texture1DArray;
res.variableType.name = "usampler1DArray";
res.variableType.baseType = VarType::UInt;
}
else if(values[0] == eGL_UNSIGNED_INT_SAMPLER_2D)
{
res.textureType = TextureType::Texture2D;
res.variableType.name = "usampler2D";
res.variableType.baseType = VarType::UInt;
}
else if(values[0] == eGL_UNSIGNED_INT_SAMPLER_2D_ARRAY)
{
res.textureType = TextureType::Texture2DArray;
res.variableType.name = "usampler2DArray";
res.variableType.baseType = VarType::UInt;
}
else if(values[0] == eGL_UNSIGNED_INT_SAMPLER_2D_RECT)
{
res.textureType = TextureType::TextureRect;
res.variableType.name = "usampler2DRect";
res.variableType.baseType = VarType::UInt;
}
else if(values[0] == eGL_UNSIGNED_INT_SAMPLER_3D)
{
res.textureType = TextureType::Texture3D;
res.variableType.name = "usampler3D";
res.variableType.baseType = VarType::UInt;
}
else if(values[0] == eGL_UNSIGNED_INT_SAMPLER_CUBE)
{
res.textureType = TextureType::TextureCube;
res.variableType.name = "usamplerCube";
res.variableType.baseType = VarType::UInt;
}
else if(values[0] == eGL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY)
{
res.textureType = TextureType::TextureCubeArray;
res.variableType.name = "usamplerCubeArray";
res.variableType.baseType = VarType::UInt;
}
else if(values[0] == eGL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE)
{
res.textureType = TextureType::Texture2DMS;
res.variableType.name = "usampler2DMS";
res.variableType.baseType = VarType::UInt;
}
else if(values[0] == eGL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY)
{
res.textureType = TextureType::Texture2DMSArray;
res.variableType.name = "usampler2DMSArray";
res.variableType.baseType = VarType::UInt;
}
// float images
else if(values[0] == eGL_IMAGE_BUFFER)
{
res.textureType = TextureType::Buffer;
res.variableType.name = "imageBuffer";
res.variableType.baseType = VarType::Float;
res.isReadOnly = false;
res.descriptorType = DescriptorType::ReadWriteTypedBuffer;
}
else if(values[0] == eGL_IMAGE_1D)
{
res.textureType = TextureType::Texture1D;
res.variableType.name = "image1D";
res.variableType.baseType = VarType::Float;
res.isReadOnly = false;
}
else if(values[0] == eGL_IMAGE_1D_ARRAY)
{
res.textureType = TextureType::Texture1DArray;
res.variableType.name = "image1DArray";
res.variableType.baseType = VarType::Float;
res.isReadOnly = false;
}
else if(values[0] == eGL_IMAGE_2D)
{
res.textureType = TextureType::Texture2D;
res.variableType.name = "image2D";
res.variableType.baseType = VarType::Float;
res.isReadOnly = false;
}
else if(values[0] == eGL_IMAGE_2D_ARRAY)
{
res.textureType = TextureType::Texture2DArray;
res.variableType.name = "image2DArray";
res.variableType.baseType = VarType::Float;
res.isReadOnly = false;
}
else if(values[0] == eGL_IMAGE_2D_RECT)
{
res.textureType = TextureType::TextureRect;
res.variableType.name = "image2DRect";
res.variableType.baseType = VarType::Float;
res.isReadOnly = false;
}
else if(values[0] == eGL_IMAGE_3D)
{
res.textureType = TextureType::Texture3D;
res.variableType.name = "image3D";
res.variableType.baseType = VarType::Float;
res.isReadOnly = false;
}
else if(values[0] == eGL_IMAGE_CUBE)
{
res.textureType = TextureType::TextureCube;
res.variableType.name = "imageCube";
res.variableType.baseType = VarType::Float;
res.isReadOnly = false;
}
else if(values[0] == eGL_IMAGE_CUBE_MAP_ARRAY)
{
res.textureType = TextureType::TextureCubeArray;
res.variableType.name = "imageCubeArray";
res.variableType.baseType = VarType::Float;
res.isReadOnly = false;
}
else if(values[0] == eGL_IMAGE_2D_MULTISAMPLE)
{
res.textureType = TextureType::Texture2DMS;
res.variableType.name = "image2DMS";
res.variableType.baseType = VarType::Float;
res.isReadOnly = false;
}
else if(values[0] == eGL_IMAGE_2D_MULTISAMPLE_ARRAY)
{
res.textureType = TextureType::Texture2DMSArray;
res.variableType.name = "image2DMSArray";
res.variableType.baseType = VarType::Float;
res.isReadOnly = false;
}
// int images
else if(values[0] == eGL_INT_IMAGE_BUFFER)
{
res.textureType = TextureType::Buffer;
res.variableType.name = "iimageBuffer";
res.variableType.baseType = VarType::SInt;
res.isReadOnly = false;
res.descriptorType = DescriptorType::ReadWriteTypedBuffer;
}
else if(values[0] == eGL_INT_IMAGE_1D)
{
res.textureType = TextureType::Texture1D;
res.variableType.name = "iimage1D";
res.variableType.baseType = VarType::SInt;
res.isReadOnly = false;
}
else if(values[0] == eGL_INT_IMAGE_1D_ARRAY)
{
res.textureType = TextureType::Texture1DArray;
res.variableType.name = "iimage1DArray";
res.variableType.baseType = VarType::SInt;
res.isReadOnly = false;
}
else if(values[0] == eGL_INT_IMAGE_2D)
{
res.textureType = TextureType::Texture2D;
res.variableType.name = "iimage2D";
res.variableType.baseType = VarType::SInt;
res.isReadOnly = false;
}
else if(values[0] == eGL_INT_IMAGE_2D_ARRAY)
{
res.textureType = TextureType::Texture2DArray;
res.variableType.name = "iimage2DArray";
res.variableType.baseType = VarType::SInt;
res.isReadOnly = false;
}
else if(values[0] == eGL_INT_IMAGE_2D_RECT)
{
res.textureType = TextureType::TextureRect;
res.variableType.name = "iimage2DRect";
res.variableType.baseType = VarType::SInt;
res.isReadOnly = false;
}
else if(values[0] == eGL_INT_IMAGE_3D)
{
res.textureType = TextureType::Texture3D;
res.variableType.name = "iimage3D";
res.variableType.baseType = VarType::SInt;
res.isReadOnly = false;
}
else if(values[0] == eGL_INT_IMAGE_CUBE)
{
res.textureType = TextureType::TextureCube;
res.variableType.name = "iimageCube";
res.variableType.baseType = VarType::SInt;
res.isReadOnly = false;
}
else if(values[0] == eGL_INT_IMAGE_CUBE_MAP_ARRAY)
{
res.textureType = TextureType::TextureCubeArray;
res.variableType.name = "iimageCubeArray";
res.variableType.baseType = VarType::SInt;
res.isReadOnly = false;
}
else if(values[0] == eGL_INT_IMAGE_2D_MULTISAMPLE)
{
res.textureType = TextureType::Texture2DMS;
res.variableType.name = "iimage2DMS";
res.variableType.baseType = VarType::SInt;
res.isReadOnly = false;
}
else if(values[0] == eGL_INT_IMAGE_2D_MULTISAMPLE_ARRAY)
{
res.textureType = TextureType::Texture2DMSArray;
res.variableType.name = "iimage2DMSArray";
res.variableType.baseType = VarType::SInt;
res.isReadOnly = false;
}
// unsigned int images
else if(values[0] == eGL_UNSIGNED_INT_IMAGE_BUFFER)
{
res.textureType = TextureType::Buffer;
res.variableType.name = "uimageBuffer";
res.variableType.baseType = VarType::UInt;
res.isReadOnly = false;
res.descriptorType = DescriptorType::ReadWriteTypedBuffer;
}
else if(values[0] == eGL_UNSIGNED_INT_IMAGE_1D)
{
res.textureType = TextureType::Texture1D;
res.variableType.name = "uimage1D";
res.variableType.baseType = VarType::UInt;
res.isReadOnly = false;
}
else if(values[0] == eGL_UNSIGNED_INT_IMAGE_1D_ARRAY)
{
res.textureType = TextureType::Texture1DArray;
res.variableType.name = "uimage1DArray";
res.variableType.baseType = VarType::UInt;
res.isReadOnly = false;
}
else if(values[0] == eGL_UNSIGNED_INT_IMAGE_2D)
{
res.textureType = TextureType::Texture2D;
res.variableType.name = "uimage2D";
res.variableType.baseType = VarType::UInt;
res.isReadOnly = false;
}
else if(values[0] == eGL_UNSIGNED_INT_IMAGE_2D_ARRAY)
{
res.textureType = TextureType::Texture2DArray;
res.variableType.name = "uimage2DArray";
res.variableType.baseType = VarType::UInt;
res.isReadOnly = false;
}
else if(values[0] == eGL_UNSIGNED_INT_IMAGE_2D_RECT)
{
res.textureType = TextureType::TextureRect;
res.variableType.name = "uimage2DRect";
res.variableType.baseType = VarType::UInt;
res.isReadOnly = false;
}
else if(values[0] == eGL_UNSIGNED_INT_IMAGE_3D)
{
res.textureType = TextureType::Texture3D;
res.variableType.name = "uimage3D";
res.variableType.baseType = VarType::UInt;
res.isReadOnly = false;
}
else if(values[0] == eGL_UNSIGNED_INT_IMAGE_CUBE)
{
res.textureType = TextureType::TextureCube;
res.variableType.name = "uimageCube";
res.variableType.baseType = VarType::UInt;
res.isReadOnly = false;
}
else if(values[0] == eGL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY)
{
res.textureType = TextureType::TextureCubeArray;
res.variableType.name = "uimageCubeArray";
res.variableType.baseType = VarType::UInt;
res.isReadOnly = false;
}
else if(values[0] == eGL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE)
{
res.textureType = TextureType::Texture2DMS;
res.variableType.name = "uimage2DMS";
res.variableType.baseType = VarType::UInt;
res.isReadOnly = false;
}
else if(values[0] == eGL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY)
{
res.textureType = TextureType::Texture2DMSArray;
res.variableType.name = "uimage2DMSArray";
res.variableType.baseType = VarType::UInt;
res.isReadOnly = false;
}
// atomic counter
else if(values[0] == eGL_UNSIGNED_INT_ATOMIC_COUNTER)
{
res.textureType = TextureType::Buffer;
res.variableType.name = "atomic_uint";
res.variableType.baseType = VarType::UInt;
res.isReadOnly = false;
res.isTexture = false;
res.variableType.columns = 1;
res.descriptorType = DescriptorType::ReadWriteBuffer;
}
else
{
// not a sampler
continue;
}
if(!res.isReadOnly && res.textureType == TextureType::Buffer)
res.descriptorType = DescriptorType::ReadWriteImage;
res.hasSampler = res.isReadOnly;
char *namebuf = new char[values[1] + 1];
GL.glGetProgramResourceName(sepProg, eGL_UNIFORM, u, values[1], NULL, namebuf);
namebuf[values[1]] = 0;
rdcstr name = namebuf;
delete[] namebuf;
res.name = name;
rdcarray<ShaderResource> &reslist = (res.isReadOnly ? roresources : rwresources);
reslist.push_back(res);
// array of samplers
if(values[4] > 1)
{
name = name.substr(0, name.length() - 3); // trim off [0] on the end
for(int i = 1; i < values[4]; i++)
{
rdcstr arrname = StringFormat::Fmt("%s[%d]", name.c_str(), i);
res.name = arrname;
reslist.push_back(res);
}
}
}
rdcarray<size_t> ssbos;
uint32_t ssboMembers = 0;
GLint numSSBOs = 0;
if(HasExt[ARB_shader_storage_buffer_object])
{
GL.glGetProgramInterfaceiv(sepProg, eGL_SHADER_STORAGE_BLOCK, eGL_ACTIVE_RESOURCES, &numSSBOs);
for(GLint u = 0; u < numSSBOs; u++)
{
GLenum propName = eGL_NAME_LENGTH;
GLint len;
GL.glGetProgramResourceiv(sepProg, eGL_SHADER_STORAGE_BLOCK, u, 1, &propName, 1, NULL, &len);
char *nm = new char[len + 1];
GL.glGetProgramResourceName(sepProg, eGL_SHADER_STORAGE_BLOCK, u, len + 1, NULL, nm);
ShaderResource res;
res.isReadOnly = false;
res.isTexture = false;
res.textureType = TextureType::Buffer;
res.variableType.rows = 0;
res.variableType.columns = 0;
res.variableType.elements = 1;
res.variableType.arrayByteStride = 0;
res.variableType.matrixByteStride = 0;
res.variableType.name = "buffer";
res.variableType.baseType = VarType::UInt;
res.name = nm;
res.descriptorType = DescriptorType::ReadWriteBuffer;
GLint numMembers = 0;
propName = eGL_NUM_ACTIVE_VARIABLES;
GL.glGetProgramResourceiv(sepProg, eGL_SHADER_STORAGE_BLOCK, u, 1, &propName, 1, NULL,
(GLint *)&numMembers);
char *arr = strchr(nm, '[');
const bool isArray = (arr != NULL);
uint32_t arrayIdx = 0;
if(isArray)
{
arr++;
while(*arr >= '0' && *arr <= '9')
{
arrayIdx *= 10;
arrayIdx += int(*arr) - int('0');
arr++;
}
}
ssbos.push_back(rwresources.size());
rwresources.push_back(res);
// only count members from the first array index
if(!isArray || arrayIdx == 0)
ssboMembers += numMembers;
delete[] nm;
}
}
{
rdcarray<ShaderConstant> *members = new rdcarray<ShaderConstant>[ssbos.size()];
for(uint32_t i = 0; i < ssboMembers; i++)
{
ReconstructVarTree(eGL_BUFFER_VARIABLE, sepProg, i, (GLint)ssbos.size(), members, NULL);
}
// if we have members in an array ssbo, broadcast this to any ssbo with the same basename
// without members, since the variables will only be reported as belonging to one block
for(size_t ssbo = 0; ssbo < ssbos.size(); ssbo++)
{
rdcstr basename = rwresources[ssbos[ssbo]].name;
int arrOffs = basename.indexOf('[');
if(arrOffs > 0)
{
basename.erase(arrOffs, basename.size());
if(!members[ssbo].empty())
{
for(size_t ssbo2 = 0; ssbo2 < ssbos.size(); ssbo2++)
{
if(!members[ssbo2].empty())
continue;
rdcstr basename2 = rwresources[ssbos[ssbo2]].name;
arrOffs = basename2.indexOf('[');
if(arrOffs > 0)
{
basename2.erase(arrOffs, basename2.size());
if(basename == basename2)
{
members[ssbo2] = members[ssbo];
}
}
}
}
}
}
for(size_t ssbo = 0; ssbo < ssbos.size(); ssbo++)
{
sort(members[ssbo]);
rdcstr basename = rwresources[ssbos[ssbo]].name;
int arrOffs = basename.indexOf('[');
if(arrOffs > 0)
basename.erase(arrOffs, basename.size());
if(!members[ssbo].empty() && basename == members[ssbo][0].name)
std::swap(rwresources[ssbos[ssbo]].variableType.members, members[ssbo][0].type.members);
else
std::swap(rwresources[ssbos[ssbo]].variableType.members, members[ssbo]);
}
// patch-up reflection data. For top-level arrays use the stride & rough size to calculate the
// number of elements, and make all child byteOffset values relative to their parent
for(size_t ssbo = 0; ssbo < ssbos.size(); ssbo++)
{
rdcarray<ShaderConstant> &ssboVars = rwresources[ssbos[ssbo]].variableType.members;
// can't make perfect guesses of struct alignment but assume std430 for ssbos
for(ShaderConstant &member : ssboVars)
FixupStructOffsetsAndSize(false, member);
for(size_t rootMember = 0; rootMember + 1 < ssboVars.size(); rootMember++)
{
ShaderConstant &member = ssboVars[rootMember];
const uint32_t memberSizeBound = ssboVars[rootMember + 1].byteOffset - member.byteOffset;
const uint32_t stride = member.type.arrayByteStride;
if(stride != 0 && member.type.elements == ~0U)
{
if(memberSizeBound >= 2 * stride)
member.type.elements = memberSizeBound / stride;
else
member.type.elements = 1;
}
}
}
delete[] members;
}
rdcarray<ShaderConstant> globalUniforms;
GLint numUBOs = 0;
rdcarray<rdcstr> uboNames;
rdcarray<ShaderConstant> *ubos = NULL;
{
GL.glGetProgramInterfaceiv(sepProg, eGL_UNIFORM_BLOCK, eGL_ACTIVE_RESOURCES, &numUBOs);
ubos = new rdcarray<ShaderConstant>[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++)
{
ReconstructVarTree(eGL_UNIFORM, sepProg, u, numUBOs, ubos, &globalUniforms);
}
refl.constantBlocks.reserve(numUBOs + (globalUniforms.empty() ? 0 : 1));
if(ubos)
{
for(int i = 0; i < numUBOs; i++)
{
if(!ubos[i].empty())
{
ConstantBlock cblock;
cblock.name = uboNames[i];
cblock.bufferBacked = true;
GLenum bufSize = eGL_BUFFER_DATA_SIZE;
GL.glGetProgramResourceiv(sepProg, eGL_UNIFORM_BLOCK, i, 1, &bufSize, 1, NULL,
(GLint *)&cblock.byteSize);
sort(ubos[i]);
// can't make perfect guesses of struct alignment but assume std140 for ubos
for(ShaderConstant &member : ubos[i])
FixupStructOffsetsAndSize(true, member);
std::swap(cblock.variables, ubos[i]);
refl.constantBlocks.push_back(cblock);
}
}
}
if(!globalUniforms.empty())
{
ConstantBlock globals;
globals.name = "$Globals";
globals.bufferBacked = false;
// 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);
}
delete[] ubos;
for(int sigType = 0; sigType < 2; sigType++)
{
GLenum sigEnum = (sigType == 0 ? eGL_PROGRAM_INPUT : eGL_PROGRAM_OUTPUT);
rdcarray<SigParameter> *sigArray = (sigType == 0 ? &refl.inputSignature : &refl.outputSignature);
GLint numInputs;
GL.glGetProgramInterfaceiv(sepProg, sigEnum, eGL_ACTIVE_RESOURCES, &numInputs);
if(numInputs > 0)
{
rdcarray<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_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)
// 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);
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;
int rows = 1;
switch(values[1])
{
case eGL_DOUBLE:
case eGL_DOUBLE_VEC2:
case eGL_DOUBLE_VEC3:
case eGL_DOUBLE_VEC4:
case eGL_DOUBLE_MAT4:
case eGL_DOUBLE_MAT4x3:
case eGL_DOUBLE_MAT4x2:
case eGL_DOUBLE_MAT3:
case eGL_DOUBLE_MAT3x4:
case eGL_DOUBLE_MAT3x2:
case eGL_DOUBLE_MAT2:
case eGL_DOUBLE_MAT2x3:
case eGL_DOUBLE_MAT2x4: sig.varType = VarType::Double; break;
case eGL_FLOAT:
case eGL_FLOAT_VEC2:
case eGL_FLOAT_VEC3:
case eGL_FLOAT_VEC4:
case eGL_FLOAT_MAT4:
case eGL_FLOAT_MAT4x3:
case eGL_FLOAT_MAT4x2:
case eGL_FLOAT_MAT3:
case eGL_FLOAT_MAT3x4:
case eGL_FLOAT_MAT3x2:
case eGL_FLOAT_MAT2:
case eGL_FLOAT_MAT2x3:
case eGL_FLOAT_MAT2x4: sig.varType = VarType::Float; break;
case eGL_INT:
case eGL_INT_VEC2:
case eGL_INT_VEC3:
case eGL_INT_VEC4: sig.varType = VarType::SInt; break;
case eGL_UNSIGNED_INT:
case eGL_UNSIGNED_INT_VEC2:
case eGL_UNSIGNED_INT_VEC3:
case eGL_UNSIGNED_INT_VEC4: sig.varType = VarType::UInt; break;
case eGL_BOOL:
case eGL_BOOL_VEC2:
case eGL_BOOL_VEC3:
case eGL_BOOL_VEC4: sig.varType = VarType::Bool; break;
default:
sig.varType = VarType::Float;
RDCWARN("Unhandled signature element type %s", ToStr((GLenum)values[1]).c_str());
}
switch(values[1])
{
case eGL_FLOAT:
case eGL_DOUBLE:
case eGL_INT:
case eGL_UNSIGNED_INT:
case eGL_BOOL:
sig.compCount = 1;
sig.regChannelMask = 0x1;
break;
case eGL_FLOAT_VEC2:
case eGL_DOUBLE_VEC2:
case eGL_INT_VEC2:
case eGL_UNSIGNED_INT_VEC2:
case eGL_BOOL_VEC2:
sig.compCount = 2;
sig.regChannelMask = 0x3;
break;
case eGL_FLOAT_VEC3:
case eGL_DOUBLE_VEC3:
case eGL_INT_VEC3:
case eGL_UNSIGNED_INT_VEC3:
case eGL_BOOL_VEC3:
sig.compCount = 3;
sig.regChannelMask = 0x7;
break;
case eGL_FLOAT_VEC4:
case eGL_DOUBLE_VEC4:
case eGL_INT_VEC4:
case eGL_UNSIGNED_INT_VEC4:
case eGL_BOOL_VEC4:
sig.compCount = 4;
sig.regChannelMask = 0xf;
break;
case eGL_FLOAT_MAT4:
case eGL_DOUBLE_MAT4:
sig.compCount = 4;
rows = 4;
sig.regChannelMask = 0xf;
break;
case eGL_FLOAT_MAT4x3:
case eGL_DOUBLE_MAT4x3:
sig.compCount = 4;
rows = 3;
sig.regChannelMask = 0xf;
break;
case eGL_FLOAT_MAT4x2:
case eGL_DOUBLE_MAT4x2:
sig.compCount = 4;
rows = 2;
sig.regChannelMask = 0xf;
break;
case eGL_FLOAT_MAT3:
case eGL_DOUBLE_MAT3:
sig.compCount = 3;
rows = 3;
sig.regChannelMask = 0x7;
break;
case eGL_FLOAT_MAT3x4:
case eGL_DOUBLE_MAT3x4:
sig.compCount = 3;
rows = 4;
sig.regChannelMask = 0x7;
break;
case eGL_FLOAT_MAT3x2:
case eGL_DOUBLE_MAT3x2:
sig.compCount = 3;
rows = 2;
sig.regChannelMask = 0x7;
break;
case eGL_FLOAT_MAT2:
case eGL_DOUBLE_MAT2:
sig.compCount = 2;
rows = 2;
sig.regChannelMask = 0x3;
break;
case eGL_FLOAT_MAT2x3:
case eGL_DOUBLE_MAT2x3:
sig.compCount = 2;
rows = 3;
sig.regChannelMask = 0x3;
break;
case eGL_FLOAT_MAT2x4:
case eGL_DOUBLE_MAT2x4:
sig.compCount = 2;
rows = 4;
sig.regChannelMask = 0x3;
break;
default:
RDCWARN("Unhandled signature element type %s", ToStr((GLenum)values[1]).c_str());
sig.compCount = 4;
sig.regChannelMask = 0xf;
break;
}
sig.systemValue = ShaderBuiltin::Undefined;
const char *varname = nm;
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
rdcstr 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)
{
delete[] nm;
continue;
}
// VS built-in inputs
if(IS_BUILTIN("gl_VertexID"))
sig.systemValue = ShaderBuiltin::VertexIndex;
if(IS_BUILTIN("gl_InstanceID"))
sig.systemValue = ShaderBuiltin::InstanceIndex;
if(IS_BUILTIN("gl_BaseVertex"))
sig.systemValue = ShaderBuiltin::BaseVertex;
if(IS_BUILTIN("gl_BaseInstance"))
sig.systemValue = ShaderBuiltin::BaseInstance;
if(IS_BUILTIN("gl_DrawID"))
sig.systemValue = ShaderBuiltin::DrawIndex;
// VS built-in outputs
if(IS_BUILTIN("gl_Position"))
sig.systemValue = ShaderBuiltin::Position;
if(IS_BUILTIN("gl_PointSize"))
sig.systemValue = ShaderBuiltin::PointSize;
if(IS_BUILTIN("gl_ClipDistance"))
sig.systemValue = ShaderBuiltin::ClipDistance;
// TCS built-in inputs
if(IS_BUILTIN("gl_PatchVerticesIn"))
sig.systemValue = ShaderBuiltin::PatchNumVertices;
if(IS_BUILTIN("gl_PrimitiveID"))
sig.systemValue = ShaderBuiltin::PrimitiveIndex;
if(IS_BUILTIN("gl_InvocationID"))
sig.systemValue = ShaderBuiltin::OutputControlPointIndex;
// TCS built-in outputs
if(IS_BUILTIN("gl_TessLevelOuter"))
sig.systemValue = ShaderBuiltin::OuterTessFactor;
if(IS_BUILTIN("gl_TessLevelInner"))
sig.systemValue = ShaderBuiltin::InsideTessFactor;
// TES built-in inputs
if(IS_BUILTIN("gl_TessCoord"))
sig.systemValue = ShaderBuiltin::DomainLocation;
if(IS_BUILTIN("gl_PatchVerticesIn"))
sig.systemValue = ShaderBuiltin::PatchNumVertices;
if(IS_BUILTIN("gl_PrimitiveID"))
sig.systemValue = ShaderBuiltin::PrimitiveIndex;
// GS built-in inputs
if(IS_BUILTIN("gl_PrimitiveIDIn"))
sig.systemValue = ShaderBuiltin::PrimitiveIndex;
if(IS_BUILTIN("gl_InvocationID") && shadType == eGL_GEOMETRY_SHADER)
sig.systemValue = ShaderBuiltin::GSInstanceIndex;
if(IS_BUILTIN("gl_Layer"))
sig.systemValue = ShaderBuiltin::RTIndex;
if(IS_BUILTIN("gl_ViewID_OVR"))
sig.systemValue = ShaderBuiltin::MultiViewIndex;
if(IS_BUILTIN("gl_ViewportIndex"))
sig.systemValue = ShaderBuiltin::ViewportIndex;
// GS built-in outputs
if(IS_BUILTIN("gl_Layer"))
sig.systemValue = ShaderBuiltin::RTIndex;
if(IS_BUILTIN("gl_ViewportIndex"))
sig.systemValue = ShaderBuiltin::ViewportIndex;
// PS built-in inputs
if(IS_BUILTIN("gl_FragCoord"))
sig.systemValue = ShaderBuiltin::Position;
if(IS_BUILTIN("gl_FrontFacing"))
sig.systemValue = ShaderBuiltin::IsFrontFace;
if(IS_BUILTIN("gl_PointCoord"))
sig.systemValue = ShaderBuiltin::RTIndex;
if(IS_BUILTIN("gl_SampleID"))
sig.systemValue = ShaderBuiltin::MSAASampleIndex;
if(IS_BUILTIN("gl_SamplePosition"))
sig.systemValue = ShaderBuiltin::MSAASamplePosition;
if(IS_BUILTIN("gl_SampleMaskIn"))
sig.systemValue = ShaderBuiltin::MSAACoverage;
// PS built-in outputs
if(IS_BUILTIN("gl_FragDepth"))
sig.systemValue = ShaderBuiltin::DepthOutput;
if(IS_BUILTIN("gl_SampleMask"))
sig.systemValue = ShaderBuiltin::MSAACoverage;
if(IS_BUILTIN("gl_FragStencilRefARB"))
sig.systemValue = ShaderBuiltin::StencilReference;
// CS built-in inputs
if(IS_BUILTIN("gl_NumWorkGroups"))
sig.systemValue = ShaderBuiltin::DispatchSize;
if(IS_BUILTIN("gl_WorkGroupID"))
sig.systemValue = ShaderBuiltin::GroupIndex;
if(IS_BUILTIN("gl_LocalInvocationID"))
sig.systemValue = ShaderBuiltin::GroupThreadIndex;
if(IS_BUILTIN("gl_GlobalInvocationID"))
sig.systemValue = ShaderBuiltin::DispatchThreadIndex;
if(IS_BUILTIN("gl_LocalInvocationIndex"))
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.regChannelMask <<= values[4];
sig.channelUsedMask = sig.regChannelMask;
if(values[3] <= 1)
{
AddSigParameter(sigs, regIndex, sig, nm, rows, -1);
}
else
{
rdcstr basename = nm;
if(basename[basename.size() - 3] == '[' && basename[basename.size() - 2] == '0' &&
basename[basename.size() - 1] == ']')
{
basename.resize(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);
}
}
delete[] nm;
}
struct sig_param_sort
{
bool operator()(const SigParameter &a, const SigParameter &b)
{
if(a.systemValue == b.systemValue)
{
if(a.regIndex != b.regIndex)
return a.regIndex < b.regIndex;
return a.varName < b.varName;
}
if(a.systemValue == ShaderBuiltin::Undefined)
return false;
if(b.systemValue == ShaderBuiltin::Undefined)
return true;
return a.systemValue < b.systemValue;
}
};
std::sort(sigs.begin(), sigs.end(), sig_param_sort());
*sigArray = sigs;
}
}
// TODO: fill in Interfaces with shader subroutines?
}