[Refactor] (ProgramInterface): build the program-resource model from the reflection snapshot, retiring GetReflection

This commit is contained in:
Swung0x48
2026-08-20 11:47:57 -04:00
parent 8329ab4264
commit 14744f117c
3 changed files with 69 additions and 59 deletions
@@ -81,19 +81,18 @@ namespace MobileGL::MG_Impl::GLImpl::ProgramInterface {
// The enumerated spelling of an array resource is "name[0]". glslang already applies // The enumerated spelling of an array resource is "name[0]". glslang already applies
// that to uniforms and buffer variables (EShReflectionBasicArraySuffix), but never to // that to uniforms and buffer variables (EShReflectionBasicArraySuffix), but never to
// stage inputs/outputs, so those get it here. // stage inputs/outputs, so those get it here.
String WithArraySuffix(const String& name, const glslang::TType* type) { String WithArraySuffix(const String& name, const ProgramObject::TypeFacts& type) {
if (type == nullptr || !type->isArray() || EndsWithZeroSubscript(name)) return name; if (!type.isArray || EndsWithZeroSubscript(name)) return name;
return name + "[0]"; return name + "[0]";
} }
// GL_ARRAY_SIZE: element count for a sized array, 0 for a runtime-sized one // GL_ARRAY_SIZE: element count for a sized array, 0 for a runtime-sized one
// (a shader storage block's unsized trailing member), 1 for a non-array. // (a shader storage block's unsized trailing member), 1 for a non-array.
GLint ArraySizeOf(const glslang::TType* type, GLint reflectedSize) { // `record.arraySize` is already the sized-array/reflected-size resolution; the only
if (type != nullptr && type->isArray()) { // extra rule here is GL's 0 for a runtime-sized array.
if (!type->isSizedArray()) return 0; GLint ArraySizeOf(const ProgramObject::ResourceReflection& record) {
return type->getOuterArraySize(); if (record.type.isArray && !record.type.isSizedArray) return 0;
} return record.arraySize;
return reflectedSize < 1 ? 1 : reflectedSize;
} }
// Two spellings name the same resource when they are equal, or differ only by the // Two spellings name the same resource when they are equal, or differ only by the
@@ -174,22 +173,21 @@ namespace MobileGL::MG_Impl::GLImpl::ProgramInterface {
return static_cast<GLint>(element); return static_cast<GLint>(element);
} }
BlockKind ClassifyBlock(const glslang::TObjectReflection& block) { BlockKind ClassifyBlock(const ProgramObject::BlockReflection& block) {
if (std::strstr(block.name.c_str(), MG_Util::ShaderTranspiler::GLOBAL_UBO_NAME) != nullptr) { if (std::strstr(block.name.c_str(), MG_Util::ShaderTranspiler::GLOBAL_UBO_NAME) != nullptr) {
return BlockKind::GlobalUbo; return BlockKind::GlobalUbo;
} }
if (IsAtomicCounterBlockName(block.name)) return BlockKind::AtomicCounter; if (IsAtomicCounterBlockName(block.name)) return BlockKind::AtomicCounter;
const glslang::TType* type = block.getType(); if (block.type.isBuffer) return BlockKind::Storage;
if (type != nullptr && type->getQualifier().storage == glslang::EvqBuffer) return BlockKind::Storage;
return BlockKind::Uniform; return BlockKind::Uniform;
} }
// std140/std430 column stride, the same vec4-rounded rule ProgramObject applies to // std140/std430 column stride, the same vec4-rounded rule ProgramObject applies to
// uniform matrices. 0 for a non-matrix. // uniform matrices. 0 for a non-matrix.
GLint MatrixStrideOf(const glslang::TType* type) { GLint MatrixStrideOf(const ProgramObject::TypeFacts& type) {
if (type == nullptr || !type->isMatrix()) return 0; if (!type.isMatrix) return 0;
const bool rowMajor = type->getQualifier().layoutMatrix == glslang::ElmRowMajor; const bool rowMajor = type.layoutMatrix == static_cast<Int>(glslang::ElmRowMajor);
const int strideVectorComponents = rowMajor ? type->getMatrixCols() : type->getMatrixRows(); const int strideVectorComponents = rowMajor ? type.matrixCols : type.matrixRows;
constexpr int scalarSize = 4; constexpr int scalarSize = 4;
const int vectorAlignment = (strideVectorComponents <= 1) ? scalarSize const int vectorAlignment = (strideVectorComponents <= 1) ? scalarSize
: (strideVectorComponents == 2) ? 2 * scalarSize : (strideVectorComponents == 2) ? 2 * scalarSize
@@ -197,9 +195,9 @@ namespace MobileGL::MG_Impl::GLImpl::ProgramInterface {
return (vectorAlignment + 15) & ~15; return (vectorAlignment + 15) & ~15;
} }
GLint IsRowMajorOf(const glslang::TType* type) { GLint IsRowMajorOf(const ProgramObject::TypeFacts& type) {
if (type == nullptr || !type->isMatrix()) return 0; if (!type.isMatrix) return 0;
return type->getQualifier().layoutMatrix == glslang::ElmRowMajor ? 1 : 0; return type.layoutMatrix == static_cast<Int>(glslang::ElmRowMajor) ? 1 : 0;
} }
GLint MappedLocation(Int rawLocation) { GLint MappedLocation(Int rawLocation) {
@@ -227,12 +225,12 @@ namespace MobileGL::MG_Impl::GLImpl::ProgramInterface {
// Note the union is used even when it is empty: an array element nobody dereferenced has // Note the union is used even when it is empty: an array element nobody dereferenced has
// no member bits and is genuinely referenced by nobody, which is the whole point - falling // no member bits and is genuinely referenced by nobody, which is the whole point - falling
// back to the block's own mask there would restore the over-approximation. // back to the block's own mask there would restore the over-approximation.
Vector<Uint32> BuildBlockStagesFromMembers(const glslang::TProgram& reflection, Int blockCount) { Vector<Uint32> BuildBlockStagesFromMembers(const ProgramObject::LinkArtifacts& reflection,
auto& mutableReflection = const_cast<glslang::TProgram&>(reflection); Int blockCount) {
Vector<Uint32> stagesByBlock(static_cast<SizeT>(blockCount < 0 ? 0 : blockCount), 0u); Vector<Uint32> stagesByBlock(static_cast<SizeT>(blockCount < 0 ? 0 : blockCount), 0u);
const Int uniformCount = mutableReflection.getNumUniformVariables(); const Int uniformCount = static_cast<Int>(reflection.uniformReflection.size());
for (Int index = 0; index < uniformCount; ++index) { for (Int index = 0; index < uniformCount; ++index) {
const auto& uniform = mutableReflection.getUniform(index); const auto& uniform = reflection.uniformReflection[index];
const Int owner = uniform.index; const Int owner = uniform.index;
if (owner < 0 || owner >= blockCount) continue; if (owner < 0 || owner >= blockCount) continue;
stagesByBlock[static_cast<SizeT>(owner)] |= static_cast<Uint32>(uniform.stages); stagesByBlock[static_cast<SizeT>(owner)] |= static_cast<Uint32>(uniform.stages);
@@ -250,7 +248,7 @@ namespace MobileGL::MG_Impl::GLImpl::ProgramInterface {
// ss[1] and requires both to report the fragment stage, which only glslang's own // ss[1] and requires both to report the fragment stage, which only glslang's own
// (deliberately over-approximating) block mask gets right. Storage and atomic-counter // (deliberately over-approximating) block mask gets right. Storage and atomic-counter
// blocks therefore keep that mask untouched. // blocks therefore keep that mask untouched.
Uint32 UniformBlockStages(const glslang::TObjectReflection& block, const Vector<Uint32>& stagesFromMembers, Uint32 UniformBlockStages(const ProgramObject::BlockReflection& block, const Vector<Uint32>& stagesFromMembers,
Int tIndex) { Int tIndex) {
String arrayBase; String arrayBase;
Uint element = 0; Uint element = 0;
@@ -264,15 +262,15 @@ namespace MobileGL::MG_Impl::GLImpl::ProgramInterface {
return stagesFromMembers[static_cast<SizeT>(tIndex)]; return stagesFromMembers[static_cast<SizeT>(tIndex)];
} }
void BuildBlocks(ProgramObject& program, const glslang::TProgram& reflection, Model& model, void BuildBlocks(ProgramObject& program, const ProgramObject::LinkArtifacts& reflection, Model& model,
Vector<BlockKind>& blockKind, Vector<Int>& blockInterfaceIndex) { Vector<BlockKind>& blockKind, Vector<Int>& blockInterfaceIndex) {
const Int blockCount = const_cast<glslang::TProgram&>(reflection).getNumUniformBlocks(); const Int blockCount = static_cast<Int>(reflection.blockReflection.size());
blockKind.assign(blockCount, BlockKind::Uniform); blockKind.assign(blockCount, BlockKind::Uniform);
blockInterfaceIndex.assign(blockCount, -1); blockInterfaceIndex.assign(blockCount, -1);
const Vector<Uint32> stagesFromMembers = BuildBlockStagesFromMembers(reflection, blockCount); const Vector<Uint32> stagesFromMembers = BuildBlockStagesFromMembers(reflection, blockCount);
for (Int tIndex = 0; tIndex < blockCount; ++tIndex) { for (Int tIndex = 0; tIndex < blockCount; ++tIndex) {
const auto& block = const_cast<glslang::TProgram&>(reflection).getUniformBlock(tIndex); const auto& block = reflection.blockReflection[tIndex];
const BlockKind kind = ClassifyBlock(block); const BlockKind kind = ClassifyBlock(block);
blockKind[tIndex] = kind; blockKind[tIndex] = kind;
if (kind == BlockKind::AtomicCounter) { if (kind == BlockKind::AtomicCounter) {
@@ -293,7 +291,7 @@ namespace MobileGL::MG_Impl::GLImpl::ProgramInterface {
// glShaderStorageBlockBinding wins over the declaration (GL 4.6 §7.6.2 - // glShaderStorageBlockBinding wins over the declaration (GL 4.6 §7.6.2 -
// exactly the same rule GL_UNIFORM_BLOCK follows through // exactly the same rule GL_UNIFORM_BLOCK follows through
// GetUniformBlockBinding below). // GetUniformBlockBinding below).
const GLint declared = block.getBinding(); const GLint declared = block.binding;
resource.bufferBinding = declared < 0 ? 0 : declared + BlockArrayElement(block.name); resource.bufferBinding = declared < 0 ? 0 : declared + BlockArrayElement(block.name);
const Int rebound = program.GetShaderStorageBlockBindingOverride(block.name); const Int rebound = program.GetShaderStorageBlockBindingOverride(block.name);
if (rebound >= 0) resource.bufferBinding = static_cast<GLint>(rebound); if (rebound >= 0) resource.bufferBinding = static_cast<GLint>(rebound);
@@ -315,21 +313,22 @@ namespace MobileGL::MG_Impl::GLImpl::ProgramInterface {
resource.bufferDataSize = static_cast<GLint>(program.GetUBOSizeAt(glIndex)); resource.bufferDataSize = static_cast<GLint>(program.GetUBOSizeAt(glIndex));
const Int tIndex = program.TProgramBlockIndex(static_cast<Uint>(glIndex)); const Int tIndex = program.TProgramBlockIndex(static_cast<Uint>(glIndex));
if (tIndex >= 0 && tIndex < blockCount) { if (tIndex >= 0 && tIndex < blockCount) {
resource.stages = UniformBlockStages(const_cast<glslang::TProgram&>(reflection).getUniformBlock(tIndex), resource.stages = UniformBlockStages(reflection.blockReflection[tIndex],
stagesFromMembers, tIndex); stagesFromMembers, tIndex);
} }
model.uniformBlocks.push_back(Move(resource)); model.uniformBlocks.push_back(Move(resource));
} }
} }
void BuildUniformsAndBufferVariables(ProgramObject& program, const glslang::TProgram& reflection, Model& model, void BuildUniformsAndBufferVariables(ProgramObject& program,
const ProgramObject::LinkArtifacts& reflection, Model& model,
const Vector<BlockKind>& blockKind, const Vector<BlockKind>& blockKind,
const Vector<Int>& blockInterfaceIndex) { const Vector<Int>& blockInterfaceIndex) {
const Uint uniformCount = program.GetUniformCount(); const Uint uniformCount = program.GetUniformCount();
for (Uint glIndex = 0; glIndex < uniformCount; ++glIndex) { for (Uint glIndex = 0; glIndex < uniformCount; ++glIndex) {
const Int tIndex = program.TProgramUniformIndex(glIndex); const Int tIndex = program.TProgramUniformIndex(glIndex);
const auto& refl = const_cast<glslang::TProgram&>(reflection).getUniform(tIndex); const auto& refl = ProgramObject::UniformAtIn(reflection, tIndex);
const glslang::TType* type = refl.getType(); const auto& type = refl.type;
const Int owner = refl.index; const Int owner = refl.index;
const BlockKind kind = (owner >= 0 && owner < static_cast<Int>(blockKind.size())) const BlockKind kind = (owner >= 0 && owner < static_cast<Int>(blockKind.size()))
? blockKind[owner] ? blockKind[owner]
@@ -338,7 +337,7 @@ namespace MobileGL::MG_Impl::GLImpl::ProgramInterface {
Resource resource; Resource resource;
resource.name = refl.name; resource.name = refl.name;
resource.type = static_cast<GLenum>(refl.glDefineType); resource.type = static_cast<GLenum>(refl.glDefineType);
resource.arraySize = ArraySizeOf(type, refl.size); resource.arraySize = ArraySizeOf(refl);
resource.stages = static_cast<Uint32>(refl.stages); resource.stages = static_cast<Uint32>(refl.stages);
if (kind == BlockKind::Storage) { if (kind == BlockKind::Storage) {
@@ -414,17 +413,13 @@ namespace MobileGL::MG_Impl::GLImpl::ProgramInterface {
// program that redeclares `out gl_PerVertex { vec4 gl_Position; }` still carries // program that redeclares `out gl_PerVertex { vec4 gl_Position; }` still carries
// gl_PointSize and gl_ClipDistance through the block-unwrapping reflection, and they // gl_PointSize and gl_ClipDistance through the block-unwrapping reflection, and they
// are not part of its output interface. // are not part of its output interface.
Bool IsHiddenBlockMember(const glslang::TType* type) { Bool IsHiddenBlockMember(const ProgramObject::TypeFacts& type) { return type.isVoid; }
return type != nullptr && type->getBasicType() == glslang::EbtVoid;
}
void BuildStageIO(ProgramObject& program, const glslang::TProgram& reflection, Model& model) { void BuildStageIO(ProgramObject& program, const ProgramObject::LinkArtifacts& reflection, Model& model) {
auto& mutableReflection = const_cast<glslang::TProgram&>(reflection); const Int inputCount = static_cast<Int>(reflection.pipeInputReflection.size());
const Int inputCount = mutableReflection.getNumPipeInputs();
for (Int index = 0; index < inputCount; ++index) { for (Int index = 0; index < inputCount; ++index) {
const auto& refl = mutableReflection.getPipeInput(index); const auto& refl = reflection.pipeInputReflection[index];
const glslang::TType* type = refl.getType(); const auto& type = refl.type;
if (IsHiddenBlockMember(type)) continue; if (IsHiddenBlockMember(type)) continue;
Resource resource; Resource resource;
// The Vulkan-semantics parse reflects the vertex builtins under their SPIR-V // The Vulkan-semantics parse reflects the vertex builtins under their SPIR-V
@@ -432,10 +427,10 @@ namespace MobileGL::MG_Impl::GLImpl::ProgramInterface {
const String& glName = ProgramObject::NormalizeBuiltinPipeInputName(refl.name); const String& glName = ProgramObject::NormalizeBuiltinPipeInputName(refl.name);
resource.name = WithArraySuffix(glName, type); resource.name = WithArraySuffix(glName, type);
resource.type = static_cast<GLenum>(refl.glDefineType); resource.type = static_cast<GLenum>(refl.glDefineType);
resource.arraySize = ArraySizeOf(type, refl.size); resource.arraySize = ArraySizeOf(refl);
resource.location = program.GetAttributeLocation(refl.name); resource.location = program.GetAttributeLocation(refl.name);
if (resource.location < 0) resource.location = MappedLocation(static_cast<Int>(refl.layoutLocation())); if (resource.location < 0) resource.location = MappedLocation(refl.location);
resource.isPerPatch = (type != nullptr && type->getQualifier().patch) ? 1 : 0; resource.isPerPatch = type.isPatch ? 1 : 0;
resource.stages = static_cast<Uint32>(refl.stages); resource.stages = static_cast<Uint32>(refl.stages);
model.programInputs.push_back(Move(resource)); model.programInputs.push_back(Move(resource));
} }
@@ -447,16 +442,16 @@ namespace MobileGL::MG_Impl::GLImpl::ProgramInterface {
// carries its own layout(location=N)), and a location then manufactures a color // carries its own layout(location=N)), and a location then manufactures a color
// index of 0 where GL requires -1 // index of 0 where GL requires -1
// (KHR-GL43.program_interface_query.separate-programs-tess-control). // (KHR-GL43.program_interface_query.separate-programs-tess-control).
const Bool lastStageIsFragment = mutableReflection.getIntermediate(EShLangFragment) != nullptr; const Bool lastStageIsFragment = reflection.lastStageIsFragment;
const Int outputCount = mutableReflection.getNumPipeOutputs(); const Int outputCount = static_cast<Int>(reflection.pipeOutputReflection.size());
for (Int index = 0; index < outputCount; ++index) { for (Int index = 0; index < outputCount; ++index) {
const auto& refl = mutableReflection.getPipeOutput(index); const auto& refl = reflection.pipeOutputReflection[index];
const glslang::TType* type = refl.getType(); const auto& type = refl.type;
if (IsHiddenBlockMember(type)) continue; if (IsHiddenBlockMember(type)) continue;
Resource resource; Resource resource;
resource.name = WithArraySuffix(refl.name, type); resource.name = WithArraySuffix(refl.name, type);
resource.type = static_cast<GLenum>(refl.glDefineType); resource.type = static_cast<GLenum>(refl.glDefineType);
resource.arraySize = ArraySizeOf(type, refl.size); resource.arraySize = ArraySizeOf(refl);
resource.location = MappedLocation(program.GetFragmentDataLocation(refl.name.c_str())); resource.location = MappedLocation(program.GetFragmentDataLocation(refl.name.c_str()));
if (resource.location < 0 || !lastStageIsFragment) { if (resource.location < 0 || !lastStageIsFragment) {
// A built-in output (gl_FragDepth, gl_SampleMask) has no location, and a // A built-in output (gl_FragDepth, gl_SampleMask) has no location, and a
@@ -467,11 +462,11 @@ namespace MobileGL::MG_Impl::GLImpl::ProgramInterface {
resource.locationIndex = program.GetFragmentDataIndex(refl.name.c_str()); resource.locationIndex = program.GetFragmentDataIndex(refl.name.c_str());
// glBindFragDataLocationIndexed wins; otherwise the shader's // glBindFragDataLocationIndexed wins; otherwise the shader's
// layout(index = N), which the frag-data maps never saw. // layout(index = N), which the frag-data maps never saw.
if (resource.locationIndex == 0 && type != nullptr && type->getQualifier().hasIndex()) { if (resource.locationIndex == 0 && type.hasIndex) {
resource.locationIndex = static_cast<GLint>(type->getQualifier().layoutIndex); resource.locationIndex = static_cast<GLint>(type.layoutIndex);
} }
} }
resource.isPerPatch = (type != nullptr && type->getQualifier().patch) ? 1 : 0; resource.isPerPatch = type.isPatch ? 1 : 0;
resource.stages = static_cast<Uint32>(refl.stages); resource.stages = static_cast<Uint32>(refl.stages);
model.programOutputs.push_back(Move(resource)); model.programOutputs.push_back(Move(resource));
} }
@@ -511,15 +506,14 @@ namespace MobileGL::MG_Impl::GLImpl::ProgramInterface {
Model BuildModel(ProgramObject& program) { Model BuildModel(ProgramObject& program) {
Model model; Model model;
if (!program.GetLinkStatus()) return model; if (!program.GetLinkStatus()) return model;
const glslang::TProgram* reflection = program.GetReflection(); const ProgramObject::LinkArtifacts& reflection = program.GetLinkReflection();
if (reflection == nullptr) return model;
model.valid = true; model.valid = true;
Vector<BlockKind> blockKind; Vector<BlockKind> blockKind;
Vector<Int> blockInterfaceIndex; Vector<Int> blockInterfaceIndex;
BuildBlocks(program, *reflection, model, blockKind, blockInterfaceIndex); BuildBlocks(program, reflection, model, blockKind, blockInterfaceIndex);
BuildUniformsAndBufferVariables(program, *reflection, model, blockKind, blockInterfaceIndex); BuildUniformsAndBufferVariables(program, reflection, model, blockKind, blockInterfaceIndex);
BuildStageIO(program, *reflection, model); BuildStageIO(program, reflection, model);
BuildXfb(program, model); BuildXfb(program, model);
return model; return model;
} }
@@ -48,6 +48,7 @@ namespace {
MobileGL::MG_State::GLState::ProgramObject::TypeFacts facts; MobileGL::MG_State::GLState::ProgramObject::TypeFacts facts;
if (type == nullptr) return facts; if (type == nullptr) return facts;
facts.isArray = type->isArray(); facts.isArray = type->isArray();
facts.isSizedArray = type->isSizedArray();
facts.isMatrix = type->isMatrix(); facts.isMatrix = type->isMatrix();
facts.isVector = type->isVector(); facts.isVector = type->isVector();
facts.isOpaque = type->isOpaque(); facts.isOpaque = type->isOpaque();
@@ -1166,6 +1167,7 @@ namespace MobileGL::MG_State::GLState {
artifacts.pipeOutputReflection.push_back(MakeResourceReflection(program.getPipeOutput(i))); artifacts.pipeOutputReflection.push_back(MakeResourceReflection(program.getPipeOutput(i)));
} }
artifacts.lastStageIsFragment = program.getIntermediate(EShLangFragment) != nullptr;
artifacts.atomicCounterCount = program.getNumAtomicCounters(); artifacts.atomicCounterCount = program.getNumAtomicCounters();
for (Uint dim = 0; dim < 3u; ++dim) { for (Uint dim = 0; dim < 3u; ++dim) {
artifacts.computeLocalSize[dim] = program.getLocalSize(static_cast<Int>(dim)); artifacts.computeLocalSize[dim] = program.getLocalSize(static_cast<Int>(dim));
@@ -29,6 +29,9 @@ namespace MobileGL::MG_State::GLState {
// AST, so a POD covers the whole surface exactly. // AST, so a POD covers the whole surface exactly.
struct TypeFacts { struct TypeFacts {
Bool isArray = false; Bool isArray = false;
// A runtime-sized array (a storage block's unsized trailing member) is an array
// that is NOT sized; GL_ARRAY_SIZE reports 0 for it.
Bool isSizedArray = false;
Bool isMatrix = false; Bool isMatrix = false;
Bool isVector = false; Bool isVector = false;
Bool isOpaque = false; Bool isOpaque = false;
@@ -908,8 +911,6 @@ namespace MobileGL::MG_State::GLState {
// (MG_Impl/GLImpl/Program/ProgramInterface.cpp), which has to enumerate buffer // (MG_Impl/GLImpl/Program/ProgramInterface.cpp), which has to enumerate buffer
// blocks, buffer variables, atomic counters and per-stage reference masks. Null // blocks, buffer variables, atomic counters and per-stage reference masks. Null
// until a link has succeeded. Read through the join gate like everything else. // until a link has succeeded. Read through the join gate like everything else.
const glslang::TProgram* GetReflection() const { return Artifacts().program.get(); }
Int GetShaderIndexByStage(ShaderStage stage) const { Int GetShaderIndexByStage(ShaderStage stage) const {
auto it = std::find_if(m_shaders.begin(), m_shaders.end(), [stage](const SharedPtr<ShaderObject>& shader) { auto it = std::find_if(m_shaders.begin(), m_shaders.end(), [stage](const SharedPtr<ShaderObject>& shader) {
return shader->GetShaderStage() == stage; return shader->GetShaderStage() == stage;
@@ -996,6 +997,11 @@ namespace MobileGL::MG_State::GLState {
Vector<PipeInputReflection> pipeInputReflection; Vector<PipeInputReflection> pipeInputReflection;
Vector<PipeOutputReflection> pipeOutputReflection; Vector<PipeOutputReflection> pipeOutputReflection;
// Program-level scalars glslang answers off the linked intermediates. // Program-level scalars glslang answers off the linked intermediates.
// Whether the program's LAST stage is the fragment stage. A color number - and so a
// color index - exists only there; a separable tess/geometry/vertex program's
// outputs are varyings and must report -1 (KHR-GL43.program_interface_query.
// separate-programs-tess-control).
Bool lastStageIsFragment = false;
Int atomicCounterCount = 0; Int atomicCounterCount = 0;
Array<GLuint, 3> computeLocalSize{}; Array<GLuint, 3> computeLocalSize{};
// Replaces program->getUniformIndex(name). Maps the reflected name to its // Replaces program->getUniformIndex(name). Maps the reflected name to its
@@ -1132,6 +1138,14 @@ namespace MobileGL::MG_State::GLState {
// ordering is explicit and nothing is exempt. // ordering is explicit and nothing is exempt.
static void ResetLinkArtifacts(LinkArtifacts& artifacts); static void ResetLinkArtifacts(LinkArtifacts& artifacts);
// The owned reflection snapshot, for the program-interface query layer. Replaces
// GetReflection(), which handed out the live glslang::TProgram - the last thing that
// forced a linked program to keep its parse alive.
const LinkArtifacts& GetLinkReflection() const {
EnsureLinkJoined();
return Artifacts();
}
static Bool IsValidUniformLocation(const LinkArtifacts& artifacts, Int location) { static Bool IsValidUniformLocation(const LinkArtifacts& artifacts, Int location) {
if (location < 0 || location > static_cast<Int>(artifacts.maxUniformLocation)) return false; if (location < 0 || location > static_cast<Int>(artifacts.maxUniformLocation)) return false;
if (static_cast<SizeT>(location) >= artifacts.uniformIndexInTProgram.size()) return false; if (static_cast<SizeT>(location) >= artifacts.uniformIndexInTProgram.size()) return false;