in renderdoc/driver/gl/gl_program_iterate.cpp [556:1163]
static void ForAllProgramUniforms(SerialiserType *ser, CaptureState state,
const PerStageReflections &srcStages, GLuint progSrc,
const PerStageReflections &dstStages, GLuint progDst,
std::map<GLint, GLint> *locTranslate)
{
const bool ReadSourceProgram = CopyUniforms || (SerialiseUniforms && ser && ser->IsWriting());
const bool WriteDestProgram = CopyUniforms || (SerialiseUniforms && ser && ser->IsReading());
RDCCOMPILE_ASSERT((CopyUniforms && !SerialiseUniforms) || (!CopyUniforms && SerialiseUniforms),
"Invalid call to ForAllProgramUniforms");
// When programs are SPIR-V we have to rely on our own reflection since the driver's reflection
// can't be trusted, or at least used in a normal way. Since SPIR-V is immutable for many things
// we only need to process uniform values - for compatibility we still serialise the same, but we
// skip fetching or applying UBO bindings etc.
bool IsSrcProgramSPIRV = false;
for(size_t i = 0; i < NumShaderStages; i++)
IsSrcProgramSPIRV |=
srcStages.refls[i] && srcStages.refls[i]->encoding == ShaderEncoding::OpenGLSPIRV;
bool IsDstProgramSPIRV = false;
for(size_t i = 0; i < NumShaderStages; i++)
IsDstProgramSPIRV |=
dstStages.refls[i] && dstStages.refls[i]->encoding == ShaderEncoding::OpenGLSPIRV;
RDCASSERTMSG("Expect both programs to be SPIR-V in ForAllProgramUniforms",
IsSrcProgramSPIRV == IsDstProgramSPIRV, IsSrcProgramSPIRV, IsDstProgramSPIRV);
// this struct will be serialised with the uniform binding data, or if we're just copying it will
// be used to store the data fetched from the source program, before being applied to the
// destination program. It's slightly redundant since we could unify the loops (as the code used
// to do) but it's much better for code organisation and clarity to have a single path whether
// serialising or not.
ProgramUniforms serialisedUniforms;
// if we're reading the source program, iterate over the interfaces and fetch the data.
if(CheckConstParam(ReadSourceProgram))
{
constexpr size_t numProps = 5;
constexpr GLenum resProps[numProps] = {
eGL_BLOCK_INDEX, eGL_TYPE, eGL_NAME_LENGTH, eGL_ARRAY_SIZE, eGL_LOCATION,
};
GLint values[numProps];
GLint NumUniforms = 0;
rdcarray<UnrolledSPIRVConstant> spirvGlobals;
if(IsSrcProgramSPIRV)
{
// Unfortunately since this is a program-global reflection we need to go through each shader
// and add its variables (if they don't already exist) to get a union of all shaders for the
// program.
UnrollConstants(srcStages, spirvGlobals);
NumUniforms = (GLint)spirvGlobals.size();
}
else
{
GL.glGetProgramInterfaceiv(progSrc, eGL_UNIFORM, eGL_ACTIVE_RESOURCES, &NumUniforms);
}
// this is a very conservative figure - many uniforms will be in UBOs and so will be ignored
serialisedUniforms.ValueUniforms.reserve(NumUniforms);
for(GLint i = 0; i < NumUniforms; i++)
{
GLenum type = eGL_NONE;
int32_t arraySize = 0;
int32_t srcLocation = 0;
rdcstr basename;
bool isArray = false;
if(IsSrcProgramSPIRV)
{
// hardcode manual reflection from SPIR-V constant.
RDCCOMPILE_ASSERT(numProps == 5 && resProps[0] == eGL_BLOCK_INDEX &&
resProps[1] == eGL_TYPE && resProps[2] == eGL_NAME_LENGTH &&
resProps[3] == eGL_ARRAY_SIZE && resProps[4] == eGL_LOCATION,
"reflection properties have changed - update manual SPIR-V reflection");
// these are implicitly globals
values[0] = -1;
values[1] = spirvGlobals[i].glType;
values[2] = 1; // unused
values[3] = spirvGlobals[i].arraySize;
values[4] = spirvGlobals[i].location;
}
else
{
GL.glGetProgramResourceiv(progSrc, eGL_UNIFORM, i, numProps, resProps, numProps, NULL,
values);
}
// we don't need to consider uniforms within UBOs
if(values[0] >= 0)
continue;
// get the metadata we need for fetching the data
type = (GLenum)values[1];
arraySize = values[3];
srcLocation = values[4];
char n[1024] = {0};
if(IsSrcProgramSPIRV)
{
RDCCOMPILE_ASSERT(sizeof(n) == sizeof(spirvGlobals[i].name), "Array sizes have changed");
memcpy(n, spirvGlobals[i].name, sizeof(n));
}
else
{
GL.glGetProgramResourceName(progSrc, eGL_UNIFORM, i, values[2], NULL, n);
}
if(arraySize > 1)
{
isArray = true;
size_t len = strlen(n);
if(n[len - 3] == '[' && n[len - 2] == '0' && n[len - 1] == ']')
n[len - 3] = 0;
}
else
{
arraySize = 1;
}
basename = n;
// push it onto the list
serialisedUniforms.ValueUniforms.push_back(ProgramUniform());
ProgramUniform &uniform = serialisedUniforms.ValueUniforms.back();
uniform.Basename = basename;
uniform.IsArray = isArray;
uniform.Values.resize(arraySize);
GLuint baseLocation = srcLocation;
// loop over every element in the array (arraySize = 1 for non arrays)
for(GLint arr = 0; arr < arraySize; arr++)
{
ProgramUniformValue &uniformVal = uniform.Values[arr];
uniformVal.Type = type;
uniformVal.Location = srcLocation;
rdcstr name = basename;
// atomic counters cannot be changed, don't fetch their value
if(type == eGL_UNSIGNED_INT_ATOMIC_COUNTER)
continue;
if(srcLocation == -1)
RDCWARN("Couldn't get srcLocation for %s", name.c_str());
// append the subscript if this item is an array.
if(isArray)
{
name += StringFormat::Fmt("[%d]", arr);
if(IsSrcProgramSPIRV)
uniformVal.Location = srcLocation = baseLocation + arr;
else
uniformVal.Location = srcLocation = GL.glGetUniformLocation(progSrc, name.c_str());
if(srcLocation == -1)
RDCWARN("Couldn't get srcLocation for %s", name.c_str());
}
if(srcLocation == -1)
continue;
// fetch the data into the ProgramUniformValue, with the appropriate method for its type
double *dv = uniformVal.data.dval;
float *fv = uniformVal.data.fval;
int32_t *iv = uniformVal.data.ival;
uint32_t *uiv = uniformVal.data.uval;
switch(type)
{
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_MAT2x4:
case eGL_FLOAT_MAT2x3:
case eGL_FLOAT:
case eGL_FLOAT_VEC2:
case eGL_FLOAT_VEC3:
case eGL_FLOAT_VEC4: GL.glGetUniformfv(progSrc, srcLocation, fv); break;
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_MAT2x4:
case eGL_DOUBLE_MAT2x3:
case eGL_DOUBLE:
case eGL_DOUBLE_VEC2:
case eGL_DOUBLE_VEC3:
case eGL_DOUBLE_VEC4: GL.glGetUniformdv(progSrc, srcLocation, dv); break;
// treat all samplers as just an int (since they just store their binding value)
case eGL_SAMPLER_1D:
case eGL_SAMPLER_2D:
case eGL_SAMPLER_3D:
case eGL_SAMPLER_CUBE:
case eGL_SAMPLER_CUBE_MAP_ARRAY:
case eGL_SAMPLER_1D_SHADOW:
case eGL_SAMPLER_2D_SHADOW:
case eGL_SAMPLER_1D_ARRAY:
case eGL_SAMPLER_2D_ARRAY:
case eGL_SAMPLER_1D_ARRAY_SHADOW:
case eGL_SAMPLER_2D_ARRAY_SHADOW:
case eGL_SAMPLER_2D_MULTISAMPLE:
case eGL_SAMPLER_2D_MULTISAMPLE_ARRAY:
case eGL_SAMPLER_CUBE_SHADOW:
case eGL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
case eGL_SAMPLER_BUFFER:
case eGL_SAMPLER_2D_RECT:
case eGL_SAMPLER_2D_RECT_SHADOW:
case eGL_INT_SAMPLER_1D:
case eGL_INT_SAMPLER_2D:
case eGL_INT_SAMPLER_3D:
case eGL_INT_SAMPLER_CUBE:
case eGL_INT_SAMPLER_CUBE_MAP_ARRAY:
case eGL_INT_SAMPLER_1D_ARRAY:
case eGL_INT_SAMPLER_2D_ARRAY:
case eGL_INT_SAMPLER_2D_MULTISAMPLE:
case eGL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
case eGL_INT_SAMPLER_BUFFER:
case eGL_INT_SAMPLER_2D_RECT:
case eGL_UNSIGNED_INT_SAMPLER_1D:
case eGL_UNSIGNED_INT_SAMPLER_2D:
case eGL_UNSIGNED_INT_SAMPLER_3D:
case eGL_UNSIGNED_INT_SAMPLER_CUBE:
case eGL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY:
case eGL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
case eGL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
case eGL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
case eGL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
case eGL_UNSIGNED_INT_SAMPLER_BUFFER:
case eGL_UNSIGNED_INT_SAMPLER_2D_RECT:
case eGL_IMAGE_1D:
case eGL_IMAGE_2D:
case eGL_IMAGE_3D:
case eGL_IMAGE_2D_RECT:
case eGL_IMAGE_CUBE:
case eGL_IMAGE_BUFFER:
case eGL_IMAGE_1D_ARRAY:
case eGL_IMAGE_2D_ARRAY:
case eGL_IMAGE_CUBE_MAP_ARRAY:
case eGL_IMAGE_2D_MULTISAMPLE:
case eGL_IMAGE_2D_MULTISAMPLE_ARRAY:
case eGL_INT_IMAGE_1D:
case eGL_INT_IMAGE_2D:
case eGL_INT_IMAGE_3D:
case eGL_INT_IMAGE_2D_RECT:
case eGL_INT_IMAGE_CUBE:
case eGL_INT_IMAGE_BUFFER:
case eGL_INT_IMAGE_1D_ARRAY:
case eGL_INT_IMAGE_2D_ARRAY:
case eGL_INT_IMAGE_2D_MULTISAMPLE:
case eGL_INT_IMAGE_2D_MULTISAMPLE_ARRAY:
case eGL_UNSIGNED_INT_IMAGE_1D:
case eGL_UNSIGNED_INT_IMAGE_2D:
case eGL_UNSIGNED_INT_IMAGE_3D:
case eGL_UNSIGNED_INT_IMAGE_2D_RECT:
case eGL_UNSIGNED_INT_IMAGE_CUBE:
case eGL_UNSIGNED_INT_IMAGE_BUFFER:
case eGL_UNSIGNED_INT_IMAGE_1D_ARRAY:
case eGL_UNSIGNED_INT_IMAGE_2D_ARRAY:
case eGL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY:
case eGL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE:
case eGL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY:
case eGL_INT:
case eGL_INT_VEC2:
case eGL_INT_VEC3:
case eGL_INT_VEC4: GL.glGetUniformiv(progSrc, srcLocation, iv); break;
// bools are unsigned integers
case eGL_UNSIGNED_INT:
case eGL_BOOL:
case eGL_UNSIGNED_INT_VEC2:
case eGL_BOOL_VEC2:
case eGL_UNSIGNED_INT_VEC3:
case eGL_BOOL_VEC3:
case eGL_UNSIGNED_INT_VEC4:
case eGL_BOOL_VEC4: GL.glGetUniformuiv(progSrc, srcLocation, uiv); break;
default: RDCERR("Unhandled uniform type '%s'", ToStr(type).c_str());
}
}
}
// now find how many UBOs we have, and store their binding indices
GLint numUBOs = 0;
// SPIR-V shaders don't allow changing UBO values, so simply omit them entirely
if(!IsSrcProgramSPIRV)
GL.glGetProgramInterfaceiv(progSrc, eGL_UNIFORM_BLOCK, eGL_ACTIVE_RESOURCES, &numUBOs);
serialisedUniforms.UBOBindings.reserve(numUBOs);
for(GLint i = 0; i < numUBOs; i++)
{
GLenum prop = eGL_BUFFER_BINDING;
uint32_t bind = 0;
GL.glGetProgramResourceiv(progSrc, eGL_UNIFORM_BLOCK, i, 1, &prop, 1, NULL, (GLint *)&bind);
char n[1024] = {0};
GL.glGetProgramResourceName(progSrc, eGL_UNIFORM_BLOCK, i, 1023, NULL, n);
serialisedUniforms.UBOBindings.push_back(ProgramBinding(n, bind));
}
// finally, if SSBOs are supported on this implementation, fetch their bindings
GLint numSSBOs = 0;
// SPIR-V shaders don't allow changing SSBO values, so simply omit them entirely
if(HasExt[ARB_shader_storage_buffer_object] && !IsSrcProgramSPIRV)
GL.glGetProgramInterfaceiv(progSrc, eGL_SHADER_STORAGE_BLOCK, eGL_ACTIVE_RESOURCES, &numSSBOs);
serialisedUniforms.SSBOBindings.reserve(numSSBOs);
for(GLint i = 0; i < numSSBOs; i++)
{
GLenum prop = eGL_BUFFER_BINDING;
uint32_t bind = 0;
GL.glGetProgramResourceiv(progSrc, eGL_SHADER_STORAGE_BLOCK, i, 1, &prop, 1, NULL,
(GLint *)&bind);
char n[1024] = {0};
GL.glGetProgramResourceName(progSrc, eGL_SHADER_STORAGE_BLOCK, i, 1023, NULL, n);
serialisedUniforms.SSBOBindings.push_back(ProgramBinding(n, bind));
}
}
// now serialise all the bindings if we are serialising
if(CheckConstParam(SerialiseUniforms) && ser)
{
ser->Serialise("ProgramUniforms"_lit, serialisedUniforms);
}
// if we are writing to a destination program and replaying, then apply the stored data from
// serialisedUniforms
if(CheckConstParam(WriteDestProgram) && IsReplayMode(state))
{
rdcarray<UnrolledSPIRVConstant> spirvGlobals;
if(IsDstProgramSPIRV)
UnrollConstants(dstStages, spirvGlobals);
// loop over the loose global uniforms, see if there is an equivalent, and apply it.
for(const ProgramUniform &uniform : serialisedUniforms.ValueUniforms)
{
for(size_t arr = 0; arr < uniform.Values.size(); arr++)
{
const ProgramUniformValue &val = uniform.Values[arr];
rdcstr name = uniform.Basename;
if(uniform.IsArray)
name += StringFormat::Fmt("[%u]", (uint32_t)arr);
GLint dstLocation = -1;
if(IsDstProgramSPIRV)
{
dstLocation = -1;
int32_t baseLocation = val.Location - (int32_t)arr;
RDCASSERT(baseLocation == uniform.Values[0].Location);
// for SPIR-V the locations are fixed in the shader and are not mutable. We just check for
// existance of something with this location. If nothing is found, we return -1
// (non-existant) which prevents us from trying to write to a bad location.
for(const UnrolledSPIRVConstant &var : spirvGlobals)
{
if(var.location == baseLocation)
{
dstLocation = val.Location;
break;
}
}
}
else
{
dstLocation = GL.glGetUniformLocation(progDst, name.c_str());
}
if(locTranslate)
(*locTranslate)[val.Location] = dstLocation;
// don't try and apply the uniform if the new location is -1
if(dstLocation == -1)
continue;
const double *dv = val.data.dval;
const float *fv = val.data.fval;
const int32_t *iv = val.data.ival;
const uint32_t *uiv = val.data.uval;
// call the appropriate function to apply the data to the destination program
switch(val.Type)
{
case eGL_FLOAT_MAT4:
GL.glProgramUniformMatrix4fv(progDst, dstLocation, 1, false, fv);
break;
case eGL_FLOAT_MAT4x3:
GL.glProgramUniformMatrix4x3fv(progDst, dstLocation, 1, false, fv);
break;
case eGL_FLOAT_MAT4x2:
GL.glProgramUniformMatrix4x2fv(progDst, dstLocation, 1, false, fv);
break;
case eGL_FLOAT_MAT3:
GL.glProgramUniformMatrix3fv(progDst, dstLocation, 1, false, fv);
break;
case eGL_FLOAT_MAT3x4:
GL.glProgramUniformMatrix3x4fv(progDst, dstLocation, 1, false, fv);
break;
case eGL_FLOAT_MAT3x2:
GL.glProgramUniformMatrix3x2fv(progDst, dstLocation, 1, false, fv);
break;
case eGL_FLOAT_MAT2:
GL.glProgramUniformMatrix2fv(progDst, dstLocation, 1, false, fv);
break;
case eGL_FLOAT_MAT2x4:
GL.glProgramUniformMatrix2x4fv(progDst, dstLocation, 1, false, fv);
break;
case eGL_FLOAT_MAT2x3:
GL.glProgramUniformMatrix2x3fv(progDst, dstLocation, 1, false, fv);
break;
case eGL_DOUBLE_MAT4:
GL.glProgramUniformMatrix4dv(progDst, dstLocation, 1, false, dv);
break;
case eGL_DOUBLE_MAT4x3:
GL.glProgramUniformMatrix4x3dv(progDst, dstLocation, 1, false, dv);
break;
case eGL_DOUBLE_MAT4x2:
GL.glProgramUniformMatrix4x2dv(progDst, dstLocation, 1, false, dv);
break;
case eGL_DOUBLE_MAT3:
GL.glProgramUniformMatrix3dv(progDst, dstLocation, 1, false, dv);
break;
case eGL_DOUBLE_MAT3x4:
GL.glProgramUniformMatrix3x4dv(progDst, dstLocation, 1, false, dv);
break;
case eGL_DOUBLE_MAT3x2:
GL.glProgramUniformMatrix3x2dv(progDst, dstLocation, 1, false, dv);
break;
case eGL_DOUBLE_MAT2:
GL.glProgramUniformMatrix2dv(progDst, dstLocation, 1, false, dv);
break;
case eGL_DOUBLE_MAT2x4:
GL.glProgramUniformMatrix2x4dv(progDst, dstLocation, 1, false, dv);
break;
case eGL_DOUBLE_MAT2x3:
GL.glProgramUniformMatrix2x3dv(progDst, dstLocation, 1, false, dv);
break;
case eGL_FLOAT: GL.glProgramUniform1fv(progDst, dstLocation, 1, fv); break;
case eGL_FLOAT_VEC2: GL.glProgramUniform2fv(progDst, dstLocation, 1, fv); break;
case eGL_FLOAT_VEC3: GL.glProgramUniform3fv(progDst, dstLocation, 1, fv); break;
case eGL_FLOAT_VEC4: GL.glProgramUniform4fv(progDst, dstLocation, 1, fv); break;
case eGL_DOUBLE: GL.glProgramUniform1dv(progDst, dstLocation, 1, dv); break;
case eGL_DOUBLE_VEC2: GL.glProgramUniform2dv(progDst, dstLocation, 1, dv); break;
case eGL_DOUBLE_VEC3: GL.glProgramUniform3dv(progDst, dstLocation, 1, dv); break;
case eGL_DOUBLE_VEC4: GL.glProgramUniform4dv(progDst, dstLocation, 1, dv); break;
case eGL_INT: GL.glProgramUniform1iv(progDst, dstLocation, 1, iv); break;
case eGL_INT_VEC2: GL.glProgramUniform2iv(progDst, dstLocation, 1, iv); break;
case eGL_INT_VEC3: GL.glProgramUniform3iv(progDst, dstLocation, 1, iv); break;
case eGL_INT_VEC4: GL.glProgramUniform4iv(progDst, dstLocation, 1, iv); break;
case eGL_UNSIGNED_INT:
case eGL_BOOL: GL.glProgramUniform1uiv(progDst, dstLocation, 1, uiv); break;
case eGL_UNSIGNED_INT_VEC2:
case eGL_BOOL_VEC2: GL.glProgramUniform2uiv(progDst, dstLocation, 1, uiv); break;
case eGL_UNSIGNED_INT_VEC3:
case eGL_BOOL_VEC3: GL.glProgramUniform3uiv(progDst, dstLocation, 1, uiv); break;
case eGL_UNSIGNED_INT_VEC4:
case eGL_BOOL_VEC4: GL.glProgramUniform4uiv(progDst, dstLocation, 1, uiv); break;
case eGL_IMAGE_1D:
case eGL_IMAGE_2D:
case eGL_IMAGE_3D:
case eGL_IMAGE_2D_RECT:
case eGL_IMAGE_CUBE:
case eGL_IMAGE_BUFFER:
case eGL_IMAGE_1D_ARRAY:
case eGL_IMAGE_2D_ARRAY:
case eGL_IMAGE_CUBE_MAP_ARRAY:
case eGL_IMAGE_2D_MULTISAMPLE:
case eGL_IMAGE_2D_MULTISAMPLE_ARRAY:
case eGL_INT_IMAGE_1D:
case eGL_INT_IMAGE_2D:
case eGL_INT_IMAGE_3D:
case eGL_INT_IMAGE_2D_RECT:
case eGL_INT_IMAGE_CUBE:
case eGL_INT_IMAGE_BUFFER:
case eGL_INT_IMAGE_1D_ARRAY:
case eGL_INT_IMAGE_2D_ARRAY:
case eGL_INT_IMAGE_2D_MULTISAMPLE:
case eGL_INT_IMAGE_2D_MULTISAMPLE_ARRAY:
case eGL_UNSIGNED_INT_IMAGE_1D:
case eGL_UNSIGNED_INT_IMAGE_2D:
case eGL_UNSIGNED_INT_IMAGE_3D:
case eGL_UNSIGNED_INT_IMAGE_2D_RECT:
case eGL_UNSIGNED_INT_IMAGE_CUBE:
case eGL_UNSIGNED_INT_IMAGE_BUFFER:
case eGL_UNSIGNED_INT_IMAGE_1D_ARRAY:
case eGL_UNSIGNED_INT_IMAGE_2D_ARRAY:
case eGL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY:
case eGL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE:
case eGL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY:
case eGL_UNSIGNED_INT_ATOMIC_COUNTER:
if(IsGLES || IsDstProgramSPIRV)
// Image uniforms cannot be re-assigned in GLES or with SPIR-V programs.
break;
DELIBERATE_FALLTHROUGH();
// treat all samplers as just an int (since they just store their binding value)
case eGL_SAMPLER_1D:
case eGL_SAMPLER_2D:
case eGL_SAMPLER_3D:
case eGL_SAMPLER_CUBE:
case eGL_SAMPLER_CUBE_MAP_ARRAY:
case eGL_SAMPLER_1D_SHADOW:
case eGL_SAMPLER_2D_SHADOW:
case eGL_SAMPLER_1D_ARRAY:
case eGL_SAMPLER_2D_ARRAY:
case eGL_SAMPLER_1D_ARRAY_SHADOW:
case eGL_SAMPLER_2D_ARRAY_SHADOW:
case eGL_SAMPLER_2D_MULTISAMPLE:
case eGL_SAMPLER_2D_MULTISAMPLE_ARRAY:
case eGL_SAMPLER_CUBE_SHADOW:
case eGL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
case eGL_SAMPLER_BUFFER:
case eGL_SAMPLER_2D_RECT:
case eGL_SAMPLER_2D_RECT_SHADOW:
case eGL_INT_SAMPLER_1D:
case eGL_INT_SAMPLER_2D:
case eGL_INT_SAMPLER_3D:
case eGL_INT_SAMPLER_CUBE:
case eGL_INT_SAMPLER_CUBE_MAP_ARRAY:
case eGL_INT_SAMPLER_1D_ARRAY:
case eGL_INT_SAMPLER_2D_ARRAY:
case eGL_INT_SAMPLER_2D_MULTISAMPLE:
case eGL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
case eGL_INT_SAMPLER_BUFFER:
case eGL_INT_SAMPLER_2D_RECT:
case eGL_UNSIGNED_INT_SAMPLER_1D:
case eGL_UNSIGNED_INT_SAMPLER_2D:
case eGL_UNSIGNED_INT_SAMPLER_3D:
case eGL_UNSIGNED_INT_SAMPLER_CUBE:
case eGL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY:
case eGL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
case eGL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
case eGL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
case eGL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
case eGL_UNSIGNED_INT_SAMPLER_BUFFER:
case eGL_UNSIGNED_INT_SAMPLER_2D_RECT:
if(!IsDstProgramSPIRV) // SPIR-V shaders treat samplers as immutable
GL.glProgramUniform1iv(progDst, dstLocation, 1, iv);
break;
default: RDCERR("Unhandled uniform type '%s'", ToStr(val.Type).c_str());
}
}
}
if(!IsDstProgramSPIRV)
{
// apply UBO bindings
for(const ProgramBinding &bind : serialisedUniforms.UBOBindings)
{
GLuint idx = GL.glGetUniformBlockIndex(progDst, bind.Name.c_str());
if(idx != GL_INVALID_INDEX)
GL.glUniformBlockBinding(progDst, idx, bind.Binding);
}
}
// apply SSBO bindings
// GLES does not allow modification of SSBO bindings - which is good as we don't need to restore
// them, since they're immutable.
if(!IsDstProgramSPIRV && !IsGLES)
{
for(const ProgramBinding &bind : serialisedUniforms.SSBOBindings)
{
GLuint idx =
GL.glGetProgramResourceIndex(progDst, eGL_SHADER_STORAGE_BLOCK, bind.Name.c_str());
if(idx != GL_INVALID_INDEX)
{
if(GL.glShaderStorageBlockBinding)
{
GL.glShaderStorageBlockBinding(progDst, idx, bind.Binding);
}
else
{
RDCERR("glShaderStorageBlockBinding is not supported!");
}
}
}
}
}
}