mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 22:28:32 +09:00
[Refactor] (ProgramState): answer the GL query surface from an owned reflection snapshot, not the live TProgram
This commit is contained in:
@@ -4670,12 +4670,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
const auto& name = stateProgramObject.GetUniformName(loc);
|
const auto& name = stateProgramObject.GetUniformName(loc);
|
||||||
if (name.empty()) continue;
|
if (name.empty()) continue;
|
||||||
if (!IsImageUniformType(stateProgramObject.GetUniformType(loc))) continue;
|
if (!IsImageUniformType(stateProgramObject.GetUniformType(loc))) continue;
|
||||||
const glslang::TType* type = stateProgramObject.GetUniformTType(loc);
|
const auto& type = stateProgramObject.GetUniformTypeFacts(loc);
|
||||||
if (type == nullptr) continue;
|
if (type.hasFormat) {
|
||||||
if (type->getQualifier().hasFormat()) {
|
|
||||||
// Declared, and therefore left exactly as written - but a non-core spelling
|
// Declared, and therefore left exactly as written - but a non-core spelling
|
||||||
// still needs the extension directive to survive the ES compiler.
|
// still needs the extension directive to survive the ES compiler.
|
||||||
if (!IsCoreEsslLayoutFormat(type->getQualifier().getFormat())) {
|
if (!IsCoreEsslLayoutFormat(static_cast<glslang::TLayoutFormat>(type.layoutFormat))) {
|
||||||
inputs.needsExtendedImageFormats = true;
|
inputs.needsExtendedImageFormats = true;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -21,6 +21,9 @@
|
|||||||
#include <MG_Backend/BackendObjects.h>
|
#include <MG_Backend/BackendObjects.h>
|
||||||
|
|
||||||
namespace MobileGL::MG_Impl::GLImpl {
|
namespace MobileGL::MG_Impl::GLImpl {
|
||||||
|
// The flattened uniform type these helpers used to take as a raw glslang::TType*
|
||||||
|
// pointing into the TProgram's pool allocator. See ProgramObject::TypeFacts.
|
||||||
|
using TypeFactsRef = const MG_State::GLState::ProgramObject::TypeFacts&;
|
||||||
static GLint BoolToGLInt(bool value) {
|
static GLint BoolToGLInt(bool value) {
|
||||||
return value ? GL_TRUE : GL_FALSE;
|
return value ? GL_TRUE : GL_FALSE;
|
||||||
}
|
}
|
||||||
@@ -223,14 +226,14 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLint GetOpaqueUniformUnitLimit(const glslang::TType* type) {
|
GLint GetOpaqueUniformUnitLimit(const TypeFactsRef type) {
|
||||||
const auto& dynamicParameters = MG_Backend::pActiveBackendObject->GetDynamicParameters();
|
const auto& dynamicParameters = MG_Backend::pActiveBackendObject->GetDynamicParameters();
|
||||||
if (type && type->isImage()) return dynamicParameters.MaxImageUnits;
|
if (type.isImage) return dynamicParameters.MaxImageUnits;
|
||||||
if (type && type->isTexture()) return dynamicParameters.MaxCombinedTextureImageUnits;
|
if (type.isTexture) return dynamicParameters.MaxCombinedTextureImageUnits;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ValidateOpaqueUniformUnit(const char* functionName, const glslang::TType* type, GLint unit) {
|
bool ValidateOpaqueUniformUnit(const char* functionName, const TypeFactsRef type, GLint unit) {
|
||||||
const GLint limit = GetOpaqueUniformUnitLimit(type);
|
const GLint limit = GetOpaqueUniformUnitLimit(type);
|
||||||
if (unit < 0 || unit >= limit) {
|
if (unit < 0 || unit >= limit) {
|
||||||
MG_State::pGLContext->RecordError(
|
MG_State::pGLContext->RecordError(
|
||||||
@@ -856,10 +859,10 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
// demotion makes a dmat4 a mat4 in the shader and a mat4-shaped slot here - but because it
|
// demotion makes a dmat4 a mat4 in the shader and a mat4-shaped slot here - but because it
|
||||||
// is ROUTED differently: the caller's component-by-component EbtDouble branch has to widen
|
// is ROUTED differently: the caller's component-by-component EbtDouble branch has to widen
|
||||||
// each float back to the queried type, and it undoes the same padding itself.
|
// each float back to the queried type, and it undoes the same padding itself.
|
||||||
Bool TryGatherFloatMatrixColumns(const glslang::TType* ttype, const char* pBase, void* params) {
|
Bool TryGatherFloatMatrixColumns(const TypeFactsRef ttype, const char* pBase, void* params) {
|
||||||
if (ttype == nullptr || !ttype->isMatrix() || ttype->getBasicType() == glslang::EbtDouble) return false;
|
if (!ttype.isMatrix || ttype.isDouble) return false;
|
||||||
const Int columns = ttype->getMatrixCols();
|
const Int columns = ttype.matrixCols;
|
||||||
const Int rows = ttype->getMatrixRows();
|
const Int rows = ttype.matrixRows;
|
||||||
for (Int column = 0; column < columns; ++column) {
|
for (Int column = 0; column < columns; ++column) {
|
||||||
Memcpy(static_cast<char*>(params) + static_cast<SizeT>(column) * rows * sizeof(GLfloat),
|
Memcpy(static_cast<char*>(params) + static_cast<SizeT>(column) * rows * sizeof(GLfloat),
|
||||||
pBase + static_cast<SizeT>(column) * 4 * sizeof(GLfloat), rows * sizeof(GLfloat));
|
pBase + static_cast<SizeT>(column) * 4 * sizeof(GLfloat), rows * sizeof(GLfloat));
|
||||||
@@ -871,7 +874,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
// everything except a float matrix, whose padded columns make it wider. The rule itself
|
// everything except a float matrix, whose padded columns make it wider. The rule itself
|
||||||
// lives on ProgramObject, because the pipeline composite's uniform refresh needs the same
|
// lives on ProgramObject, because the pipeline composite's uniform refresh needs the same
|
||||||
// one and two copies of a layout rule is one too many.
|
// one and two copies of a layout rule is one too many.
|
||||||
SizeT UniformStorageSpanInBytes(const glslang::TType* ttype, SizeT tightSize) {
|
SizeT UniformStorageSpanInBytes(const TypeFactsRef ttype, SizeT tightSize) {
|
||||||
return MG_State::GLState::ProgramObject::UniformStorageSpanInBytes(ttype, tightSize);
|
return MG_State::GLState::ProgramObject::UniformStorageSpanInBytes(ttype, tightSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -904,7 +907,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
auto offset = programObject->GetUniformOffset(location);
|
auto offset = programObject->GetUniformOffset(location);
|
||||||
auto size = programObject->GetUniformSizesInBytes(location);
|
auto size = programObject->GetUniformSizesInBytes(location);
|
||||||
char* pUBO = (char*)programObject->MapUBO();
|
char* pUBO = (char*)programObject->MapUBO();
|
||||||
auto* ttype = programObject->GetUniformTType(location);
|
const auto& ttype = programObject->GetUniformTypeFacts(location);
|
||||||
const SizeT span = UniformStorageSpanInBytes(ttype, size);
|
const SizeT span = UniformStorageSpanInBytes(ttype, size);
|
||||||
if (pUBO == nullptr || offset == MG_State::GLState::ProgramObject::kInvalidUniformOffset ||
|
if (pUBO == nullptr || offset == MG_State::GLState::ProgramObject::kInvalidUniformOffset ||
|
||||||
offset + span > programObject->GetUBOSize()) {
|
offset + span > programObject->GetUBOSize()) {
|
||||||
@@ -958,7 +961,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
auto offset = programObject->GetUniformOffset(location);
|
auto offset = programObject->GetUniformOffset(location);
|
||||||
auto size = programObject->GetUniformSizesInBytes(location);
|
auto size = programObject->GetUniformSizesInBytes(location);
|
||||||
char* pUBO = static_cast<char*>(programObject->MapUBO());
|
char* pUBO = static_cast<char*>(programObject->MapUBO());
|
||||||
auto* ttype = programObject->GetUniformTType(location);
|
const auto& ttype = programObject->GetUniformTypeFacts(location);
|
||||||
const SizeT span = UniformStorageSpanInBytes(ttype, size);
|
const SizeT span = UniformStorageSpanInBytes(ttype, size);
|
||||||
if (pUBO == nullptr || offset == MG_State::GLState::ProgramObject::kInvalidUniformOffset ||
|
if (pUBO == nullptr || offset == MG_State::GLState::ProgramObject::kInvalidUniformOffset ||
|
||||||
offset + span > programObject->GetUBOSize()) {
|
offset + span > programObject->GetUBOSize()) {
|
||||||
@@ -981,10 +984,10 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
// conversion rules (7.6: round to nearest for the integer queries) apply; the value
|
// conversion rules (7.6: round to nearest for the integer queries) apply; the value
|
||||||
// widens back to the queried type, having lost precision at the glUniform*d that
|
// widens back to the queried type, having lost precision at the glUniform*d that
|
||||||
// stored it and not here.
|
// stored it and not here.
|
||||||
if (ttype->getBasicType() == glslang::EbtDouble) {
|
if (ttype.isDouble) {
|
||||||
const Int columns = ttype->isMatrix() ? ttype->getMatrixCols() : 1;
|
const Int columns = ttype.isMatrix ? ttype.matrixCols : 1;
|
||||||
const Int rows = ttype->isMatrix() ? ttype->getMatrixRows()
|
const Int rows = ttype.isMatrix ? ttype.matrixRows
|
||||||
: (ttype->isVector() ? ttype->getVectorSize() : 1);
|
: (ttype.isVector ? ttype.vectorSize : 1);
|
||||||
// std140 gives every matrix column its own 16-byte slot; a non-matrix is one
|
// std140 gives every matrix column its own 16-byte slot; a non-matrix is one
|
||||||
// tightly packed run and never reaches the stride at all.
|
// tightly packed run and never reaches the stride at all.
|
||||||
const SizeT columnStride = 4 * sizeof(GLfloat);
|
const SizeT columnStride = 4 * sizeof(GLfloat);
|
||||||
@@ -1191,8 +1194,8 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
Memcpy(pUBO + offset + byteOffsetInsideUniform, value, writeSize);
|
Memcpy(pUBO + offset + byteOffsetInsideUniform, value, writeSize);
|
||||||
programObject.MarkUBOContentDirty();
|
programObject.MarkUBOContentDirty();
|
||||||
} else {
|
} else {
|
||||||
auto* ttype = programObject.GetUniformTType(location);
|
const auto& ttype = programObject.GetUniformTypeFacts(location);
|
||||||
if (!ttype->isTexture() && !ttype->isImage()) return;
|
if (!ttype.isTexture && !ttype.isImage) return;
|
||||||
if constexpr (!std::is_same_v<std::remove_cv_t<T>, GLint> || ItemCount != 1) {
|
if constexpr (!std::is_same_v<std::remove_cv_t<T>, GLint> || ItemCount != 1) {
|
||||||
MG_State::pGLContext->RecordError(
|
MG_State::pGLContext->RecordError(
|
||||||
ErrorCode::InvalidOperation,
|
ErrorCode::InvalidOperation,
|
||||||
|
|||||||
@@ -36,6 +36,69 @@ namespace {
|
|||||||
return std::min(backendLimit, capacity);
|
return std::min(backendLimit, capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Everything the post-link query surface ever asks a glslang::TType, flattened into a
|
||||||
|
// POD. The list is closed and was audited call site by call site: nothing after the link
|
||||||
|
// walks a struct, a type name or the AST, so there is no recursion to mirror.
|
||||||
|
//
|
||||||
|
// Why it has to be flattened at all: TObjectReflection::type points into the TProgram's
|
||||||
|
// OWN TPoolAllocator (reflection.cpp clones each TType into it), so every one of these
|
||||||
|
// pointers dangles the moment the TProgram is released - and releasing it is exactly what
|
||||||
|
// lets a link be served from the L1 translation memo without a parse.
|
||||||
|
static MobileGL::MG_State::GLState::ProgramObject::TypeFacts MakeTypeFacts(const glslang::TType* type) {
|
||||||
|
MobileGL::MG_State::GLState::ProgramObject::TypeFacts facts;
|
||||||
|
if (type == nullptr) return facts;
|
||||||
|
facts.isArray = type->isArray();
|
||||||
|
facts.isMatrix = type->isMatrix();
|
||||||
|
facts.isVector = type->isVector();
|
||||||
|
facts.isOpaque = type->isOpaque();
|
||||||
|
facts.isTexture = type->isTexture();
|
||||||
|
facts.isImage = type->isImage();
|
||||||
|
facts.isDouble = type->getBasicType() == glslang::EbtDouble;
|
||||||
|
facts.isVoid = type->getBasicType() == glslang::EbtVoid;
|
||||||
|
facts.basicType = static_cast<MobileGL::Int>(type->getBasicType());
|
||||||
|
// Stored RAW, exactly as glslang reports them (0 for a non-matrix, 1 for a scalar),
|
||||||
|
// because the callers already gate on isMatrix()/isVector() themselves.
|
||||||
|
facts.vectorSize = type->getVectorSize();
|
||||||
|
facts.matrixCols = type->getMatrixCols();
|
||||||
|
facts.matrixRows = type->getMatrixRows();
|
||||||
|
const glslang::TQualifier& qualifier = type->getQualifier();
|
||||||
|
facts.isBuffer = qualifier.storage == glslang::EvqBuffer;
|
||||||
|
facts.isPatch = qualifier.patch;
|
||||||
|
facts.hasIndex = qualifier.hasIndex();
|
||||||
|
facts.layoutIndex = static_cast<MobileGL::Int>(qualifier.layoutIndex);
|
||||||
|
facts.hasFormat = qualifier.hasFormat();
|
||||||
|
facts.layoutFormat = static_cast<MobileGL::Uint>(qualifier.getFormat());
|
||||||
|
facts.layoutMatrix = static_cast<MobileGL::Int>(qualifier.layoutMatrix);
|
||||||
|
return facts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// One glslang::TObjectReflection, flattened. Shared by uniforms, blocks, pipe inputs and
|
||||||
|
// pipe outputs, because glslang reflects all four as TObjectReflection.
|
||||||
|
static MobileGL::MG_State::GLState::ProgramObject::ResourceReflection MakeResourceReflection(
|
||||||
|
const glslang::TObjectReflection& object) {
|
||||||
|
MobileGL::MG_State::GLState::ProgramObject::ResourceReflection record;
|
||||||
|
record.name = object.name;
|
||||||
|
record.glDefineType = object.glDefineType;
|
||||||
|
record.offset = object.offset;
|
||||||
|
record.size = object.size;
|
||||||
|
record.index = object.index;
|
||||||
|
record.counterIndex = object.counterIndex;
|
||||||
|
record.arrayStride = object.arrayStride;
|
||||||
|
record.topLevelArraySize = object.topLevelArraySize;
|
||||||
|
record.topLevelArrayStride = object.topLevelArrayStride;
|
||||||
|
record.binding = object.getBinding();
|
||||||
|
record.location = object.layoutLocation();
|
||||||
|
record.stages = static_cast<MobileGL::Uint32>(object.stages);
|
||||||
|
record.type = MakeTypeFacts(object.getType());
|
||||||
|
// GL_UNIFORM_SIZE / GL_ARRAY_SIZE, resolved here so no caller needs the TType:
|
||||||
|
// TObjectReflection::size carries the element count only for a NON-block array, so
|
||||||
|
// the sized-array outer count wins whenever it exists.
|
||||||
|
const glslang::TType* type = object.getType();
|
||||||
|
record.arraySize = (type != nullptr && type->isSizedArray()) ? type->getOuterArraySize()
|
||||||
|
: (object.size < 1 ? 1 : object.size);
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
static MobileGL::String StripArrayElementSuffix(const MobileGL::String& name) {
|
static MobileGL::String StripArrayElementSuffix(const MobileGL::String& name) {
|
||||||
const MobileGL::SizeT bracket = name.find('[');
|
const MobileGL::SizeT bracket = name.find('[');
|
||||||
return bracket == MobileGL::String::npos ? name : name.substr(0, bracket);
|
return bracket == MobileGL::String::npos ? name : name.substr(0, bracket);
|
||||||
@@ -497,6 +560,15 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
spirvHandoff.reflection.uniformIndexInTProgram = artifacts.uniformIndexInTProgram;
|
spirvHandoff.reflection.uniformIndexInTProgram = artifacts.uniformIndexInTProgram;
|
||||||
spirvHandoff.reflection.tProgramUniformIndexToGl = artifacts.tProgramUniformIndexToGl;
|
spirvHandoff.reflection.tProgramUniformIndexToGl = artifacts.tProgramUniformIndexToGl;
|
||||||
spirvHandoff.reflection.maxUniformLocation = artifacts.maxUniformLocation;
|
spirvHandoff.reflection.maxUniformLocation = artifacts.maxUniformLocation;
|
||||||
|
// The owned reflection mirror, and the block index space its global-UBO test needs.
|
||||||
|
// BuildGlobalUboRouting reads BOTH - per-uniform array size, opaqueness, GL type and
|
||||||
|
// matrix shape, plus "is this a member of a GL-visible block". Leaving them out of the
|
||||||
|
// handoff is not a compile error, it is a SILENT one: every array collapses to a
|
||||||
|
// single element and every element past the first falls through to the fallback tail
|
||||||
|
// allocator (ProgramTest.NestedStructArrayUniformElementWrites catches exactly that).
|
||||||
|
spirvHandoff.reflection.uniformReflection = artifacts.uniformReflection;
|
||||||
|
spirvHandoff.reflection.blockReflection = artifacts.blockReflection;
|
||||||
|
spirvHandoff.reflection.tProgramBlockIndexToGl = artifacts.tProgramBlockIndexToGl;
|
||||||
spirvHandoff.spirvCacheKey = BuildSpirvCacheKey(env);
|
spirvHandoff.spirvCacheKey = BuildSpirvCacheKey(env);
|
||||||
spirvHandoff.ready = true;
|
spirvHandoff.ready = true;
|
||||||
MGLOG_D("ProgramObject %u: phase A done, %zu module(s) handed to the SPIR-V job", in.externalIndex,
|
MGLOG_D("ProgramObject %u: phase A done, %zu module(s) handed to the SPIR-V job", in.externalIndex,
|
||||||
@@ -1032,9 +1104,78 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
MGLOG_D("ProgramObject %u: Reflection - UBO[%d] name='%s' size=%u binding=%d", in.externalIndex, i,
|
MGLOG_D("ProgramObject %u: Reflection - UBO[%d] name='%s' size=%u binding=%d", in.externalIndex, i,
|
||||||
ubo.name.c_str(), ubo.size, ubo.getBinding());
|
ubo.name.c_str(), ubo.size, ubo.getBinding());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SnapshotGlslangReflection();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The last thing DoReflection does, and the thing that lets everything after it stop
|
||||||
|
// caring that a glslang::TProgram ever existed: copy every reflection record the GL query
|
||||||
|
// surface reads into LinkArtifacts' own owned tables.
|
||||||
|
//
|
||||||
|
// Indexed by TPROGRAM index throughout - the same space glUniformIndexToTProgram,
|
||||||
|
// tProgramUniformIndexToGl and uniformIndexInTProgram already speak - so the accessors
|
||||||
|
// that used to call program->getUniform(i) index uniformReflection[i] and are otherwise
|
||||||
|
// unchanged.
|
||||||
|
void ProgramLinkTask::SnapshotGlslangReflection() {
|
||||||
|
glslang::TProgram& program = *artifacts.program;
|
||||||
|
|
||||||
|
// Blocks FIRST: a uniform's effective layoutMatrix is resolved against its owning
|
||||||
|
// block below, which needs the block records to already exist.
|
||||||
|
const Int blockCount = program.getNumUniformBlocks();
|
||||||
|
artifacts.blockReflection.clear();
|
||||||
|
artifacts.blockReflection.reserve(static_cast<SizeT>(blockCount));
|
||||||
|
for (Int i = 0; i < blockCount; ++i) {
|
||||||
|
artifacts.blockReflection.push_back(MakeResourceReflection(program.getUniformBlock(i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
const Int uniformCount = program.getNumUniformVariables();
|
||||||
|
artifacts.uniformReflection.clear();
|
||||||
|
artifacts.uniformReflection.reserve(static_cast<SizeT>(uniformCount));
|
||||||
|
artifacts.uniformIndexByName.clear();
|
||||||
|
artifacts.uniformIndexByName.reserve(static_cast<SizeT>(uniformCount));
|
||||||
|
for (Int i = 0; i < uniformCount; ++i) {
|
||||||
|
ProgramObject::UniformReflection record = MakeResourceReflection(program.getUniform(i));
|
||||||
|
// A block-level layout(row_major)/(column_major) that the member did not inherit
|
||||||
|
// in its own qualifier. Resolved once HERE rather than at every GL_UNIFORM_* query,
|
||||||
|
// which is what the getUniformBlock() fallback in the old accessors was doing.
|
||||||
|
if (record.type.layoutMatrix == static_cast<Int>(glslang::ElmNone) && record.index >= 0 &&
|
||||||
|
record.index < static_cast<Int>(artifacts.blockReflection.size())) {
|
||||||
|
record.type.layoutMatrix = artifacts.blockReflection[record.index].type.layoutMatrix;
|
||||||
|
}
|
||||||
|
// Keyed on the REFLECTED name and on uniforms only. That is deliberate and is the
|
||||||
|
// filtered semantics the old code hand-rolled: glslang's TReflection::nameToIndex
|
||||||
|
// also holds block and function entries, which is exactly why every
|
||||||
|
// getUniformIndex() call site re-checked getUniform(idx).name == name afterwards.
|
||||||
|
// First writer wins, so a duplicated name resolves the way a forward scan would.
|
||||||
|
artifacts.uniformIndexByName.emplace(record.name, i);
|
||||||
|
artifacts.uniformReflection.push_back(Move(record));
|
||||||
|
}
|
||||||
|
|
||||||
|
const Int pipeInputCount = program.getNumPipeInputs();
|
||||||
|
artifacts.pipeInputReflection.clear();
|
||||||
|
artifacts.pipeInputReflection.reserve(static_cast<SizeT>(pipeInputCount));
|
||||||
|
for (Int i = 0; i < pipeInputCount; ++i) {
|
||||||
|
artifacts.pipeInputReflection.push_back(MakeResourceReflection(program.getPipeInput(i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
const Int pipeOutputCount = program.getNumPipeOutputs();
|
||||||
|
artifacts.pipeOutputReflection.clear();
|
||||||
|
artifacts.pipeOutputReflection.reserve(static_cast<SizeT>(pipeOutputCount));
|
||||||
|
for (Int i = 0; i < pipeOutputCount; ++i) {
|
||||||
|
artifacts.pipeOutputReflection.push_back(MakeResourceReflection(program.getPipeOutput(i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
artifacts.atomicCounterCount = program.getNumAtomicCounters();
|
||||||
|
for (Uint dim = 0; dim < 3u; ++dim) {
|
||||||
|
artifacts.computeLocalSize[dim] = program.getLocalSize(static_cast<Int>(dim));
|
||||||
|
}
|
||||||
|
MGLOG_D("ProgramObject %u: Reflection - snapshot: %zu uniform(s), %zu block(s), %zu input(s), "
|
||||||
|
"%zu output(s)",
|
||||||
|
in.externalIndex, artifacts.uniformReflection.size(), artifacts.blockReflection.size(),
|
||||||
|
artifacts.pipeInputReflection.size(), artifacts.pipeOutputReflection.size());
|
||||||
|
}
|
||||||
|
|
||||||
Bool ProgramLinkTask::ValidateFragmentOutputLocations() {
|
Bool ProgramLinkTask::ValidateFragmentOutputLocations() {
|
||||||
if (!artifacts.program) return false;
|
if (!artifacts.program) return false;
|
||||||
// The pipe-output list is the output interface of the program's LAST stage. Only a
|
// The pipe-output list is the output interface of the program's LAST stage. Only a
|
||||||
|
|||||||
@@ -165,6 +165,9 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
MG_Util::ShaderTranspiler::TranslationCacheKey BuildSpirvCacheKey(
|
MG_Util::ShaderTranspiler::TranslationCacheKey BuildSpirvCacheKey(
|
||||||
const MG_Util::ShaderTranspiler::CompileEnv& env) const;
|
const MG_Util::ShaderTranspiler::CompileEnv& env) const;
|
||||||
Bool DoReflection(const MG_Util::ShaderTranspiler::CompileEnv& env);
|
Bool DoReflection(const MG_Util::ShaderTranspiler::CompileEnv& env);
|
||||||
|
// Copies every reflection record the GL query surface reads out of the glslang
|
||||||
|
// TProgram into LinkArtifacts own owned tables. Runs at the tail of DoReflection.
|
||||||
|
void SnapshotGlslangReflection();
|
||||||
Bool ValidateFragmentOutputLocations();
|
Bool ValidateFragmentOutputLocations();
|
||||||
Bool ResolveTransformFeedbackVaryings();
|
Bool ResolveTransformFeedbackVaryings();
|
||||||
void ResolveGsTriangleStripCapture(const glslang::TIntermediate* captureIntermediate);
|
void ResolveGsTriangleStripCapture(const glslang::TIntermediate* captureIntermediate);
|
||||||
|
|||||||
@@ -24,6 +24,68 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
|
|
||||||
class ProgramObject {
|
class ProgramObject {
|
||||||
public:
|
public:
|
||||||
|
// Everything the query surface ever asked a glslang::TType, flattened. Twenty
|
||||||
|
// predicates, no recursion: nothing post-link ever walks a struct, a type name or the
|
||||||
|
// AST, so a POD covers the whole surface exactly.
|
||||||
|
struct TypeFacts {
|
||||||
|
Bool isArray = false;
|
||||||
|
Bool isMatrix = false;
|
||||||
|
Bool isVector = false;
|
||||||
|
Bool isOpaque = false;
|
||||||
|
Bool isTexture = false;
|
||||||
|
Bool isImage = false;
|
||||||
|
Bool isDouble = false; // getBasicType() == EbtDouble
|
||||||
|
Bool isVoid = false; // getBasicType() == EbtVoid (hidden block members)
|
||||||
|
Bool isBuffer = false; // getQualifier().storage == EvqBuffer
|
||||||
|
Bool isPatch = false; // getQualifier().patch
|
||||||
|
Bool hasIndex = false; // getQualifier().hasIndex()
|
||||||
|
Bool hasFormat = false; // getQualifier().hasFormat()
|
||||||
|
Int vectorSize = 0;
|
||||||
|
Int matrixCols = 0;
|
||||||
|
Int matrixRows = 0;
|
||||||
|
Int layoutIndex = 0; // getQualifier().layoutIndex
|
||||||
|
Uint layoutFormat = 0; // getQualifier().getFormat()
|
||||||
|
// glslang::TLayoutMatrix, widened. For a uniform this is already RESOLVED against
|
||||||
|
// the owning block's qualifier, so the getUniformBlock() fallback the old
|
||||||
|
// accessors carried is gone.
|
||||||
|
Int layoutMatrix = 0;
|
||||||
|
// glslang::TBasicType, widened - ApplyUniformInitialValues and the typed
|
||||||
|
// glGetUniform* paths compare against a handful of enumerators.
|
||||||
|
Int basicType = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// One glslang::TObjectReflection, flattened. Used for uniforms, blocks, pipe inputs
|
||||||
|
// and pipe outputs alike, because glslang reflects all four as TObjectReflection.
|
||||||
|
struct ResourceReflection {
|
||||||
|
String name;
|
||||||
|
GLenum glDefineType = 0;
|
||||||
|
Int offset = -1;
|
||||||
|
// TObjectReflection::size, RAW. For a uniform prefer `arraySize` below, which is
|
||||||
|
// the resolved GL_UNIFORM_SIZE answer.
|
||||||
|
Int size = 0;
|
||||||
|
// TObjectReflection::index - for a uniform, the TPROGRAM block index owning it
|
||||||
|
// (-1 for a default-block one; translate with GlBlockIndexFromTProgram).
|
||||||
|
Int index = -1;
|
||||||
|
Int counterIndex = -1;
|
||||||
|
Int arrayStride = 0;
|
||||||
|
Int topLevelArraySize = 0;
|
||||||
|
Int topLevelArrayStride = 0;
|
||||||
|
Int binding = -1;
|
||||||
|
Int location = -1; // layoutLocation()
|
||||||
|
// EShLanguageMask of the stages that reference it; 0 means "declared but read by
|
||||||
|
// nobody", which is what the dead-default-block-uniform filter tests.
|
||||||
|
Uint32 stages = 0;
|
||||||
|
// GL_UNIFORM_SIZE / GL_ARRAY_SIZE, already resolved through the
|
||||||
|
// isSizedArray()/getOuterArraySize()/size fallback.
|
||||||
|
GLint arraySize = 1;
|
||||||
|
TypeFacts type;
|
||||||
|
};
|
||||||
|
|
||||||
|
using UniformReflection = ResourceReflection;
|
||||||
|
using BlockReflection = ResourceReflection;
|
||||||
|
using PipeInputReflection = ResourceReflection;
|
||||||
|
using PipeOutputReflection = ResourceReflection;
|
||||||
|
|
||||||
ProgramObject(Uint externalIndex) : m_externalIndex(externalIndex), m_lifetimeId(AllocateLifetimeId()) {}
|
ProgramObject(Uint externalIndex) : m_externalIndex(externalIndex), m_lifetimeId(AllocateLifetimeId()) {}
|
||||||
// Cancel-not-join, exactly like ~ShaderObject: the link job owns its inputs, so an
|
// Cancel-not-join, exactly like ~ShaderObject: the link job owns its inputs, so an
|
||||||
// in-flight link whose program just went away is safe to abandon where it stands.
|
// in-flight link whose program just went away is safe to abandon where it stands.
|
||||||
@@ -134,8 +196,7 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
const Int index = Artifacts().uniformIndexInTProgram[base];
|
const Int index = Artifacts().uniformIndexInTProgram[base];
|
||||||
// "[k]" only addresses arrays ("scalar[0]" is not a uniform name), and only
|
// "[k]" only addresses arrays ("scalar[0]" is not a uniform name), and only
|
||||||
// in-range elements.
|
// in-range elements.
|
||||||
const glslang::TType* type = Artifacts().program->getUniform(index).getType();
|
if (!UniformAt(index).type.isArray) return -1;
|
||||||
if (type == nullptr || !type->isArray()) return -1;
|
|
||||||
if (static_cast<GLint>(element) >= GetUniformArraySizeByTIndex(index)) return -1;
|
if (static_cast<GLint>(element) >= GetUniformArraySizeByTIndex(index)) return -1;
|
||||||
const Int location = base + (Int)element;
|
const Int location = base + (Int)element;
|
||||||
if (!UniformLocationsAliasSameUniform(base, location)) return -1;
|
if (!UniformLocationsAliasSameUniform(base, location)) return -1;
|
||||||
@@ -175,44 +236,35 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Int GetActiveUniformIndex(const String& name) const {
|
Int GetActiveUniformIndex(const String& name) const {
|
||||||
const Int tProgramCount = static_cast<Int>(Artifacts().tProgramUniformIndexToGl.size());
|
// uniformIndexByName is keyed by the REFLECTED name, so a lookup that hits is
|
||||||
const Int uniformIndex = Artifacts().program->getUniformIndex(name.c_str());
|
// already the exact-match the old code re-verified with a string compare after
|
||||||
if (uniformIndex >= 0 && uniformIndex < tProgramCount &&
|
// glslang's getUniformIndex(); a lookup that misses needs no bounds check.
|
||||||
Artifacts().program->getUniform(uniformIndex).name == name) {
|
const auto& byName = Artifacts().uniformIndexByName;
|
||||||
return GlUniformIndexFromTProgram(uniformIndex);
|
if (const auto direct = byName.find(name); direct != byName.end()) {
|
||||||
|
return GlUniformIndexFromTProgram(direct->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reflection stores an array uniform under "arr[0]"; accept the bare "arr"
|
// Reflection stores an array uniform under "arr[0]"; accept the bare "arr"
|
||||||
// spelling too. The reverse ("arr[0]" against a bare "arr" entry) is kept for
|
// spelling too. The reverse ("arr[0]" against a bare "arr" entry) is kept for
|
||||||
// robustness against non-suffixed reflection entries.
|
// robustness against non-suffixed reflection entries.
|
||||||
if (!name.empty() && name.back() != ']') {
|
if (!name.empty() && name.back() != ']') {
|
||||||
const String suffixedName = name + "[0]";
|
const auto suffixed = byName.find(name + "[0]");
|
||||||
const Int suffixedIndex = Artifacts().program->getUniformIndex(suffixedName.c_str());
|
return suffixed != byName.end() ? GlUniformIndexFromTProgram(suffixed->second) : -1;
|
||||||
if (suffixedIndex >= 0 && suffixedIndex < tProgramCount &&
|
|
||||||
Artifacts().program->getUniform(suffixedIndex).name == suffixedName) {
|
|
||||||
return GlUniformIndexFromTProgram(suffixedIndex);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name.length() <= 3 || name.compare(name.length() - 3, 3, "[0]") != 0) return -1;
|
if (name.length() <= 3 || name.compare(name.length() - 3, 3, "[0]") != 0) return -1;
|
||||||
const String baseName = name.substr(0, name.length() - 3);
|
const auto base = byName.find(name.substr(0, name.length() - 3));
|
||||||
const Int baseIndex = Artifacts().program->getUniformIndex(baseName.c_str());
|
return base != byName.end() ? GlUniformIndexFromTProgram(base->second) : -1;
|
||||||
if (baseIndex < 0 || baseIndex >= tProgramCount) return -1;
|
|
||||||
return Artifacts().program->getUniform(baseIndex).name == baseName ? GlUniformIndexFromTProgram(baseIndex)
|
|
||||||
: -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool IsValidUniformLocation(Int location) const { return IsValidUniformLocation(Artifacts(), location); }
|
Bool IsValidUniformLocation(Int location) const { return IsValidUniformLocation(Artifacts(), location); }
|
||||||
|
|
||||||
GLenum GetUniformType(Uint location) const {
|
GLenum GetUniformType(Uint location) const {
|
||||||
auto& uniform = Artifacts().program->getUniform(Artifacts().uniformIndexInTProgram[location]);
|
return UniformAt(Artifacts().uniformIndexInTProgram[location]).glDefineType;
|
||||||
return uniform.glDefineType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GLenum GetActiveUniformType(Uint index) const {
|
GLenum GetActiveUniformType(Uint index) const {
|
||||||
auto& uniform = Artifacts().program->getUniform(TProgramUniformIndex(index));
|
return UniformAt(TProgramUniformIndex(index)).glDefineType;
|
||||||
return uniform.glDefineType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Number of active array elements (GL_UNIFORM_SIZE / GL_ARRAY_SIZE); 1 for a non-array.
|
// Number of active array elements (GL_UNIFORM_SIZE / GL_ARRAY_SIZE); 1 for a non-array.
|
||||||
@@ -229,16 +281,15 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Int GetActiveUniformBlockIndex(Uint index) const {
|
Int GetActiveUniformBlockIndex(Uint index) const {
|
||||||
auto& uniform = Artifacts().program->getUniform(TProgramUniformIndex(index));
|
|
||||||
// Members of the synthesized global UBO are default-block uniforms to GL: -1.
|
// Members of the synthesized global UBO are default-block uniforms to GL: -1.
|
||||||
return GlBlockIndexFromTProgram(uniform.index);
|
return GlBlockIndexFromTProgram(UniformAt(TProgramUniformIndex(index)).index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// GL_UNIFORM_OFFSET: byte offset within the owning named block; -1 for a default-block
|
// GL_UNIFORM_OFFSET: byte offset within the owning named block; -1 for a default-block
|
||||||
// uniform. The relaxed parse gives global-UBO members real byte offsets, but GL must keep
|
// uniform. The relaxed parse gives global-UBO members real byte offsets, but GL must keep
|
||||||
// seeing them as default-block uniforms, so gate on the GL-visible block index.
|
// seeing them as default-block uniforms, so gate on the GL-visible block index.
|
||||||
GLint GetActiveUniformOffset(Uint index) const {
|
GLint GetActiveUniformOffset(Uint index) const {
|
||||||
const auto& uniform = Artifacts().program->getUniform(TProgramUniformIndex(index));
|
const auto& uniform = UniformAt(TProgramUniformIndex(index));
|
||||||
if (GlBlockIndexFromTProgram(uniform.index) < 0) return -1;
|
if (GlBlockIndexFromTProgram(uniform.index) < 0) return -1;
|
||||||
return uniform.offset;
|
return uniform.offset;
|
||||||
}
|
}
|
||||||
@@ -252,13 +303,12 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
// generated SPIR-V lay the array out with std140 16-byte-rounded strides. MobileGL's UBO
|
// generated SPIR-V lay the array out with std140 16-byte-rounded strides. MobileGL's UBO
|
||||||
// layout is always std140, where every array element stride rounds up to a vec4.
|
// layout is always std140, where every array element stride rounds up to a vec4.
|
||||||
GLint GetActiveUniformArrayStride(Uint index) const {
|
GLint GetActiveUniformArrayStride(Uint index) const {
|
||||||
const auto& uniform = Artifacts().program->getUniform(TProgramUniformIndex(index));
|
const auto& uniform = UniformAt(TProgramUniformIndex(index));
|
||||||
if (GlBlockIndexFromTProgram(uniform.index) < 0) return -1;
|
if (GlBlockIndexFromTProgram(uniform.index) < 0) return -1;
|
||||||
const glslang::TType* type = uniform.getType();
|
if (!uniform.type.isArray) return 0;
|
||||||
if (type == nullptr || !type->isArray()) return 0;
|
if (uniform.type.isMatrix) {
|
||||||
if (type->isMatrix()) {
|
|
||||||
const bool rowMajor = GetActiveUniformIsRowMajor(index) != 0;
|
const bool rowMajor = GetActiveUniformIsRowMajor(index) != 0;
|
||||||
const int vectors = rowMajor ? type->getMatrixRows() : type->getMatrixCols();
|
const int vectors = rowMajor ? uniform.type.matrixRows : uniform.type.matrixCols;
|
||||||
return GetActiveUniformMatrixStride(index) * vectors;
|
return GetActiveUniformMatrixStride(index) * vectors;
|
||||||
}
|
}
|
||||||
return 16; // scalars and vectors: std140 rounds the element stride up to a vec4
|
return 16; // scalars and vectors: std140 rounds the element stride up to a vec4
|
||||||
@@ -272,15 +322,12 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
// check suffices; the getUniformBlock() fallback is defensive for a config that instead leaves
|
// check suffices; the getUniformBlock() fallback is defensive for a config that instead leaves
|
||||||
// an inheriting member's layoutMatrix == ElmNone.
|
// an inheriting member's layoutMatrix == ElmNone.
|
||||||
GLint GetActiveUniformIsRowMajor(Uint index) const {
|
GLint GetActiveUniformIsRowMajor(Uint index) const {
|
||||||
const auto& uniform = Artifacts().program->getUniform(TProgramUniformIndex(index));
|
const auto& uniform = UniformAt(TProgramUniformIndex(index));
|
||||||
if (GlBlockIndexFromTProgram(uniform.index) < 0) return 0;
|
if (GlBlockIndexFromTProgram(uniform.index) < 0) return 0;
|
||||||
const glslang::TType* type = uniform.getType();
|
if (!uniform.type.isMatrix) return 0;
|
||||||
if (type == nullptr || !type->isMatrix()) return 0;
|
// layoutMatrix is already resolved against the owning block's qualifier at
|
||||||
glslang::TLayoutMatrix layoutMatrix = type->getQualifier().layoutMatrix;
|
// snapshot time, so the getUniformBlock() fallback this used to carry is gone.
|
||||||
if (layoutMatrix == glslang::ElmNone) {
|
return (uniform.type.layoutMatrix == static_cast<Int>(glslang::ElmRowMajor)) ? 1 : 0;
|
||||||
layoutMatrix = Artifacts().program->getUniformBlock(uniform.index).getType()->getQualifier().layoutMatrix;
|
|
||||||
}
|
|
||||||
return (layoutMatrix == glslang::ElmRowMajor) ? 1 : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GL_UNIFORM_MATRIX_STRIDE: byte stride between columns (col-major) / rows (row-major) of a
|
// GL_UNIFORM_MATRIX_STRIDE: byte stride between columns (col-major) / rows (row-major) of a
|
||||||
@@ -290,16 +337,11 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
// out as std140 (packed/shared are coerced), so this matches the offsets glslang reports. For
|
// out as std140 (packed/shared are coerced), so this matches the offsets glslang reports. For
|
||||||
// every GL 3.3 float matrix this evaluates to 16, independent of majorness.
|
// every GL 3.3 float matrix this evaluates to 16, independent of majorness.
|
||||||
GLint GetActiveUniformMatrixStride(Uint index) const {
|
GLint GetActiveUniformMatrixStride(Uint index) const {
|
||||||
const auto& uniform = Artifacts().program->getUniform(TProgramUniformIndex(index));
|
const auto& uniform = UniformAt(TProgramUniformIndex(index));
|
||||||
if (GlBlockIndexFromTProgram(uniform.index) < 0) return -1;
|
if (GlBlockIndexFromTProgram(uniform.index) < 0) return -1;
|
||||||
const glslang::TType* type = uniform.getType();
|
if (!uniform.type.isMatrix) return 0;
|
||||||
if (type == nullptr || !type->isMatrix()) return 0;
|
const bool rowMajor = (uniform.type.layoutMatrix == static_cast<Int>(glslang::ElmRowMajor));
|
||||||
glslang::TLayoutMatrix layoutMatrix = type->getQualifier().layoutMatrix;
|
const int strideVectorComponents = rowMajor ? uniform.type.matrixCols : uniform.type.matrixRows;
|
||||||
if (layoutMatrix == glslang::ElmNone) {
|
|
||||||
layoutMatrix = Artifacts().program->getUniformBlock(uniform.index).getType()->getQualifier().layoutMatrix;
|
|
||||||
}
|
|
||||||
const bool rowMajor = (layoutMatrix == glslang::ElmRowMajor);
|
|
||||||
const int strideVectorComponents = rowMajor ? type->getMatrixCols() : type->getMatrixRows();
|
|
||||||
constexpr int scalarSize = 4; // GL 3.3 core uniform matrices are float
|
constexpr int scalarSize = 4; // GL 3.3 core uniform matrices are float
|
||||||
const int vectorAlignment = (strideVectorComponents <= 1) ? scalarSize
|
const int vectorAlignment = (strideVectorComponents <= 1) ? scalarSize
|
||||||
: (strideVectorComponents == 2) ? 2 * scalarSize
|
: (strideVectorComponents == 2) ? 2 * scalarSize
|
||||||
@@ -307,21 +349,39 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
return (vectorAlignment + 15) & ~15; // std140 round-up to a vec4
|
return (vectorAlignment + 15) & ~15; // std140 round-up to a vec4
|
||||||
}
|
}
|
||||||
|
|
||||||
const glslang::TType* GetUniformTType(Uint location) const {
|
// The flattened type of the uniform at `location`. This is what replaced
|
||||||
auto& uniform = Artifacts().program->getUniform(Artifacts().uniformIndexInTProgram[location]);
|
// GetUniformTType(): the same information, owned by the program instead of by a
|
||||||
return uniform.getType();
|
// glslang pool, so it stays valid for a link served from the L1 translation memo.
|
||||||
|
const TypeFacts& GetUniformTypeFacts(Uint location) const {
|
||||||
|
return UniformAt(Artifacts().uniformIndexInTProgram[location]).type;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool IsUniformOpaqueAtLocation(Uint location) const { return GetUniformTType(location)->isOpaque(); }
|
// Replaces GetUniformTType(), which used to hand a raw glslang::TType* - into a
|
||||||
|
// pool the program no longer necessarily owns - out to the DirectGLES image-format
|
||||||
|
// bake. These are the only three things any caller ever read off it.
|
||||||
|
Bool UniformHasDeclaredImageFormat(Uint location) const {
|
||||||
|
return UniformAt(Artifacts().uniformIndexInTProgram[location]).type.hasFormat;
|
||||||
|
}
|
||||||
|
Uint GetUniformDeclaredImageFormat(Uint location) const {
|
||||||
|
return UniformAt(Artifacts().uniformIndexInTProgram[location]).type.layoutFormat;
|
||||||
|
}
|
||||||
|
// Matrix column count, 0 for a non-matrix. The global-UBO fallback allocator sizes a
|
||||||
|
// matrix slot from it.
|
||||||
|
Int GetUniformMatrixColumns(Uint location) const {
|
||||||
|
const auto& uniform = UniformAt(Artifacts().uniformIndexInTProgram[location]);
|
||||||
|
return uniform.type.isMatrix ? uniform.type.matrixCols : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool IsUniformOpaqueAtLocation(Uint location) const {
|
||||||
|
return UniformAt(Artifacts().uniformIndexInTProgram[location]).type.isOpaque;
|
||||||
|
}
|
||||||
|
|
||||||
const String& GetUniformName(Uint location) const {
|
const String& GetUniformName(Uint location) const {
|
||||||
auto& uniform = Artifacts().program->getUniform(Artifacts().uniformIndexInTProgram[location]);
|
return UniformAt(Artifacts().uniformIndexInTProgram[location]).name;
|
||||||
return uniform.name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const String& GetActiveUniformName(Uint index) const {
|
const String& GetActiveUniformName(Uint index) const {
|
||||||
auto& uniform = Artifacts().program->getUniform(TProgramUniformIndex(index));
|
return UniformAt(TProgramUniformIndex(index)).name;
|
||||||
return uniform.name;
|
|
||||||
}
|
}
|
||||||
// Sentinel for a uniform location without global-UBO backing storage (should not
|
// Sentinel for a uniform location without global-UBO backing storage (should not
|
||||||
// survive linking: GenerateBinary falls back to tail-allocated scratch storage).
|
// survive linking: GenerateBinary falls back to tail-allocated scratch storage).
|
||||||
@@ -355,17 +415,17 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
// would, half its GL type size, and a `dmat4` is padded like any other matrix. Anything
|
// would, half its GL type size, and a `dmat4` is padded like any other matrix. Anything
|
||||||
// reading or writing a whole uniform's storage - a bounds check, a copy between two
|
// reading or writing a whole uniform's storage - a bounds check, a copy between two
|
||||||
// programs' shadows - wants this rather than GetUniformSizesInBytes.
|
// programs' shadows - wants this rather than GetUniformSizesInBytes.
|
||||||
static SizeT UniformStorageSpanInBytes(const glslang::TType* type, SizeT tightSize) {
|
static SizeT UniformStorageSpanInBytes(const TypeFacts& type, SizeT tightSize) {
|
||||||
if (type != nullptr && type->isMatrix()) {
|
if (type.isMatrix) {
|
||||||
return static_cast<SizeT>(type->getMatrixCols()) * 4 * sizeof(Float);
|
return static_cast<SizeT>(type.matrixCols) * 4 * sizeof(Float);
|
||||||
}
|
}
|
||||||
if (type != nullptr && type->getBasicType() == glslang::EbtDouble) {
|
if (type.isDouble) {
|
||||||
return tightSize / 2;
|
return tightSize / 2;
|
||||||
}
|
}
|
||||||
return tightSize;
|
return tightSize;
|
||||||
}
|
}
|
||||||
SizeT GetUniformStorageSpanInBytes(Uint location) const {
|
SizeT GetUniformStorageSpanInBytes(Uint location) const {
|
||||||
return UniformStorageSpanInBytes(GetUniformTType(location), GetUniformSizesInBytes(location));
|
return UniformStorageSpanInBytes(GetUniformTypeFacts(location), GetUniformSizesInBytes(location));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- "written since link": the per-location dirty set the pipeline composite mirrors from ----
|
// ---- "written since link": the per-location dirty set the pipeline composite mirrors from ----
|
||||||
@@ -476,14 +536,14 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
Uint32 GetActiveFragmentOutputLocationMask() const {
|
Uint32 GetActiveFragmentOutputLocationMask() const {
|
||||||
if (!Artifacts().program) {
|
if (Artifacts().pipeOutputReflection.empty()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Uint32 mask = 0;
|
Uint32 mask = 0;
|
||||||
const Int outputCount = Artifacts().program->getNumPipeOutputs();
|
const Int outputCount = static_cast<Int>(Artifacts().pipeOutputReflection.size());
|
||||||
for (Int index = 0; index < outputCount; ++index) {
|
for (Int index = 0; index < outputCount; ++index) {
|
||||||
const Int location = static_cast<Int>(Artifacts().program->getPipeOutput(index).layoutLocation());
|
const Int location = Artifacts().pipeOutputReflection[index].location;
|
||||||
if (location >= 0 && location < 32) {
|
if (location >= 0 && location < 32) {
|
||||||
mask |= (1u << location);
|
mask |= (1u << location);
|
||||||
}
|
}
|
||||||
@@ -491,38 +551,34 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
Int GetActiveFragmentOutputCount() const {
|
Int GetActiveFragmentOutputCount() const {
|
||||||
return Artifacts().program ? Artifacts().program->getNumPipeOutputs() : 0;
|
return static_cast<Int>(Artifacts().pipeOutputReflection.size());
|
||||||
}
|
}
|
||||||
const String& GetActiveFragmentOutputName(Uint index) const {
|
const String& GetActiveFragmentOutputName(Uint index) const {
|
||||||
MOBILEGL_ASSERT(Artifacts().program != nullptr, "ProgramObject::GetActiveFragmentOutputName: program is null");
|
MOBILEGL_ASSERT(index < static_cast<Uint>(Artifacts().pipeOutputReflection.size()),
|
||||||
MOBILEGL_ASSERT(index < static_cast<Uint>(Artifacts().program->getNumPipeOutputs()),
|
|
||||||
"ProgramObject::GetActiveFragmentOutputName: index=%u out of range", index);
|
"ProgramObject::GetActiveFragmentOutputName: index=%u out of range", index);
|
||||||
return Artifacts().program->getPipeOutput(static_cast<Int>(index)).name;
|
return Artifacts().pipeOutputReflection[index].name;
|
||||||
}
|
}
|
||||||
Int GetFragmentOutputLocation(Uint index) const {
|
Int GetFragmentOutputLocation(Uint index) const {
|
||||||
MOBILEGL_ASSERT(Artifacts().program != nullptr, "ProgramObject::GetFragmentOutputLocation: program is null");
|
MOBILEGL_ASSERT(index < static_cast<Uint>(Artifacts().pipeOutputReflection.size()),
|
||||||
MOBILEGL_ASSERT(index < static_cast<Uint>(Artifacts().program->getNumPipeOutputs()),
|
|
||||||
"ProgramObject::GetFragmentOutputLocation: index=%u out of range",
|
"ProgramObject::GetFragmentOutputLocation: index=%u out of range",
|
||||||
index);
|
index);
|
||||||
return static_cast<Int>(Artifacts().program->getPipeOutput(static_cast<Int>(index)).layoutLocation());
|
return Artifacts().pipeOutputReflection[index].location;
|
||||||
}
|
}
|
||||||
GLint GetActiveFragmentOutputArraySize(Uint index) const {
|
GLint GetActiveFragmentOutputArraySize(Uint index) const {
|
||||||
MOBILEGL_ASSERT(Artifacts().program != nullptr, "ProgramObject::GetActiveFragmentOutputArraySize: program is null");
|
MOBILEGL_ASSERT(index < static_cast<Uint>(Artifacts().pipeOutputReflection.size()),
|
||||||
MOBILEGL_ASSERT(index < static_cast<Uint>(Artifacts().program->getNumPipeOutputs()),
|
|
||||||
"ProgramObject::GetActiveFragmentOutputArraySize: index=%u out of range", index);
|
"ProgramObject::GetActiveFragmentOutputArraySize: index=%u out of range", index);
|
||||||
return Artifacts().program->getPipeOutput(static_cast<Int>(index)).size;
|
return Artifacts().pipeOutputReflection[index].size;
|
||||||
}
|
}
|
||||||
GLenum GetFragmentOutputType(Uint index) const {
|
GLenum GetFragmentOutputType(Uint index) const {
|
||||||
MOBILEGL_ASSERT(Artifacts().program != nullptr, "ProgramObject::GetFragmentOutputType: program is null");
|
MOBILEGL_ASSERT(index < static_cast<Uint>(Artifacts().pipeOutputReflection.size()),
|
||||||
MOBILEGL_ASSERT(index < static_cast<Uint>(Artifacts().program->getNumPipeOutputs()),
|
|
||||||
"ProgramObject::GetFragmentOutputType: index=%u out of range",
|
"ProgramObject::GetFragmentOutputType: index=%u out of range",
|
||||||
index);
|
index);
|
||||||
return Artifacts().program->getPipeOutput(static_cast<Int>(index)).glDefineType;
|
return Artifacts().pipeOutputReflection[index].glDefineType;
|
||||||
}
|
}
|
||||||
GLenum GetAttribType(Uint index) const { return Artifacts().attribTypes[index]; }
|
GLenum GetAttribType(Uint index) const { return Artifacts().attribTypes[index]; }
|
||||||
const String& GetAttribName(Uint index) const { return Artifacts().attribs[index]; }
|
const String& GetAttribName(Uint index) const { return Artifacts().attribs[index]; }
|
||||||
GLenum GetActiveAttribType(Uint index) const { return Artifacts().program->getPipeInput(static_cast<Int>(index)).glDefineType; }
|
GLenum GetActiveAttribType(Uint index) const { return Artifacts().pipeInputReflection[index].glDefineType; }
|
||||||
GLint GetActiveAttribArraySize(Uint index) const { return Artifacts().program->getPipeInput(static_cast<Int>(index)).size; }
|
GLint GetActiveAttribArraySize(Uint index) const { return Artifacts().pipeInputReflection[index].size; }
|
||||||
// The Vulkan-semantics parse reflects the vertex builtins under their SPIR-V names;
|
// The Vulkan-semantics parse reflects the vertex builtins under their SPIR-V names;
|
||||||
// GL must keep reporting the GL spellings (glGetActiveAttrib and the program-input
|
// GL must keep reporting the GL spellings (glGetActiveAttrib and the program-input
|
||||||
// resource queries enumerate builtins).
|
// resource queries enumerate builtins).
|
||||||
@@ -534,7 +590,7 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
const String& GetActiveAttribName(Uint index) const {
|
const String& GetActiveAttribName(Uint index) const {
|
||||||
return NormalizeBuiltinPipeInputName(Artifacts().program->getPipeInput(static_cast<Int>(index)).name);
|
return NormalizeBuiltinPipeInputName(Artifacts().pipeInputReflection[index].name);
|
||||||
}
|
}
|
||||||
// PHASE B, all three (see EnsureSpirvJoined): the shadow buffer's layout is decided
|
// PHASE B, all three (see EnsureSpirvJoined): the shadow buffer's layout is decided
|
||||||
// by the OPTIMIZED SPIR-V, so it does not exist until the SPIR-V job has settled - and
|
// by the OPTIMIZED SPIR-V, so it does not exist until the SPIR-V job has settled - and
|
||||||
@@ -627,7 +683,7 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
// which means the change is only honoured by regenerating the program. That
|
// which means the change is only honoured by regenerating the program. That
|
||||||
// regeneration is gated on link-shaped versions, so without a counter that moves
|
// regeneration is gated on link-shaped versions, so without a counter that moves
|
||||||
// here the new unit would never reach the driver.
|
// here the new unit would never reach the driver.
|
||||||
if (const glslang::TType* type = GetUniformTType(location); type != nullptr && type->isImage()) {
|
if (GetUniformTypeFacts(location).isImage) {
|
||||||
++m_imageUnitVersion;
|
++m_imageUnitVersion;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -703,19 +759,16 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
// this after a failed glGetAttribLocation, and reached it as soon as the CopyTexImage2D
|
// this after a failed glGetAttribLocation, and reached it as soon as the CopyTexImage2D
|
||||||
// throw ahead of it stopped killing the run first.
|
// throw ahead of it stopped killing the run first.
|
||||||
Int GetActiveAtomicCounterCount() const {
|
Int GetActiveAtomicCounterCount() const {
|
||||||
const auto& program = Artifacts().program;
|
return Artifacts().atomicCounterCount;
|
||||||
return program ? program->getNumAtomicCounters() : 0;
|
|
||||||
}
|
}
|
||||||
Int GetActiveAttributesCount() const {
|
Int GetActiveAttributesCount() const {
|
||||||
const auto& program = Artifacts().program;
|
return static_cast<Int>(Artifacts().pipeInputReflection.size());
|
||||||
return program ? program->getNumPipeInputs() : 0;
|
|
||||||
}
|
}
|
||||||
// GL-visible uniform blocks only: the synthesized MGL_GLOBAL_UBO the relaxed parse
|
// GL-visible uniform blocks only: the synthesized MGL_GLOBAL_UBO the relaxed parse
|
||||||
// materializes for default-block uniforms is filtered out by DoReflection.
|
// materializes for default-block uniforms is filtered out by DoReflection.
|
||||||
Int GetActiveUniformBlocksCount() const { return static_cast<Int>(Artifacts().glBlockIndexToTProgram.size()); }
|
Int GetActiveUniformBlocksCount() const { return static_cast<Int>(Artifacts().glBlockIndexToTProgram.size()); }
|
||||||
GLuint GetComputeLocalSize(Uint dim) const {
|
GLuint GetComputeLocalSize(Uint dim) const {
|
||||||
const auto& program = Artifacts().program;
|
return dim < 3u ? Artifacts().computeLocalSize[dim] : 0u;
|
||||||
return program ? program->getLocalSize(static_cast<Int>(dim)) : 0;
|
|
||||||
}
|
}
|
||||||
Int GetActiveAttributesMaxLength() const { return Artifacts().attribInNameMaxLength; }
|
Int GetActiveAttributesMaxLength() const { return Artifacts().attribInNameMaxLength; }
|
||||||
Int GetActiveUniformBlocksMaxNameLength() const { return Artifacts().uniformBlockNameMaxLength; }
|
Int GetActiveUniformBlocksMaxNameLength() const { return Artifacts().uniformBlockNameMaxLength; }
|
||||||
@@ -739,11 +792,11 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
// (like a std140 struct) occupies a vec4-rounded size, and that is what the
|
// (like a std140 struct) occupies a vec4-rounded size, and that is what the
|
||||||
// backend compiles: ES drivers reject draws whose bound UBO range is smaller
|
// backend compiles: ES drivers reject draws whose bound UBO range is smaller
|
||||||
// than the block (a block ending in ivec3 reported 12 while the driver needs 16).
|
// than the block (a block ending in ivec3 reported 12 while the driver needs 16).
|
||||||
return (Artifacts().program->getUniformBlock(Artifacts().glBlockIndexToTProgram[index]).size + 15u) & ~15u;
|
return (static_cast<Uint>(BlockAt(Artifacts().glBlockIndexToTProgram[index]).size) + 15u) & ~15u;
|
||||||
}
|
}
|
||||||
|
|
||||||
const String& GetUniformBlockName(Uint index) const {
|
const String& GetUniformBlockName(Uint index) const {
|
||||||
auto& ubo = Artifacts().program->getUniformBlock(Artifacts().glBlockIndexToTProgram[index]);
|
const auto& ubo = BlockAt(Artifacts().glBlockIndexToTProgram[index]);
|
||||||
return ubo.name;
|
return ubo.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -774,7 +827,7 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Bool IsUniformBlockReferencedByStage(Uint index, EShLanguage stage) const {
|
Bool IsUniformBlockReferencedByStage(Uint index, EShLanguage stage) const {
|
||||||
const auto& ubo = Artifacts().program->getUniformBlock(Artifacts().glBlockIndexToTProgram[index]);
|
const auto& ubo = BlockAt(Artifacts().glBlockIndexToTProgram[index]);
|
||||||
const auto stageMask = static_cast<EShLanguageMask>(1 << stage);
|
const auto stageMask = static_cast<EShLanguageMask>(1 << stage);
|
||||||
return (ubo.stages & stageMask) != 0;
|
return (ubo.stages & stageMask) != 0;
|
||||||
}
|
}
|
||||||
@@ -909,9 +962,46 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
// what makes "every read of link output joins the pending link" a property the
|
// what makes "every read of link output joins the pending link" a property the
|
||||||
// compiler checks rather than a review item - a new reader cannot spell the field
|
// compiler checks rather than a review item - a new reader cannot spell the field
|
||||||
// without going through the gate.
|
// without going through the gate.
|
||||||
|
// ---- the owned mirror of glslang's reflection ----
|
||||||
|
//
|
||||||
|
// WHY THIS EXISTS. Every GL query about a linked program used to be answered by
|
||||||
|
// asking the live glslang::TProgram - program->getUniform(i).getType()->isMatrix()
|
||||||
|
// and friends. That made the TProgram part of the program's PERMANENT state, which
|
||||||
|
// in turn made the whole front end (parse + link) unskippable: the L1 shader
|
||||||
|
// translation memo could hand back the SPIR-V but the reflection still had to be
|
||||||
|
// rebuilt from a freshly parsed AST.
|
||||||
|
//
|
||||||
|
// These three tables are a snapshot of everything the query surface ever reads off
|
||||||
|
// the TProgram, in PLAIN OWNED VALUES - no TType*, no TString, nothing pointing into
|
||||||
|
// a glslang pool. Taken once at the tail of DoReflection (SnapshotGlslangReflection),
|
||||||
|
// they are copyable, immutable after the link, and safe to memoize and share between
|
||||||
|
// ProgramObjects and threads. Once they are filled, `program` is dead weight to
|
||||||
|
// everything except DoReflection itself.
|
||||||
|
//
|
||||||
|
// INDEXED BY TPROGRAM INDEX, deliberately: that is the space uniformIndexInTProgram,
|
||||||
|
// glUniformIndexToTProgram and tProgramUniformIndexToGl already speak, so every
|
||||||
|
// accessor that used to call program->getUniform(i) indexes uniformReflection[i]
|
||||||
|
// instead, unchanged in every other respect.
|
||||||
|
|
||||||
struct LinkArtifacts {
|
struct LinkArtifacts {
|
||||||
|
// Live only between LinkProgram() and the end of DoReflection. Everything after
|
||||||
|
// that reads the owned mirror below; a link served from the L1 memo never
|
||||||
|
// constructs one at all, so this is null for such a program and MUST NOT be
|
||||||
|
// dereferenced outside DoReflection.
|
||||||
SharedPtr<glslang::TProgram> program;
|
SharedPtr<glslang::TProgram> program;
|
||||||
|
|
||||||
|
// The owned reflection snapshot. Indexed by TProgram index; see the structs above.
|
||||||
|
Vector<UniformReflection> uniformReflection;
|
||||||
|
Vector<BlockReflection> blockReflection;
|
||||||
|
Vector<PipeInputReflection> pipeInputReflection;
|
||||||
|
Vector<PipeOutputReflection> pipeOutputReflection;
|
||||||
|
// Program-level scalars glslang answers off the linked intermediates.
|
||||||
|
Int atomicCounterCount = 0;
|
||||||
|
Array<GLuint, 3> computeLocalSize{};
|
||||||
|
// Replaces program->getUniformIndex(name). Maps the reflected name to its
|
||||||
|
// TProgram uniform index.
|
||||||
|
UnorderedMap<String, Int> uniformIndexByName;
|
||||||
|
|
||||||
// Attributes (Vertex in)
|
// Attributes (Vertex in)
|
||||||
Vector<String> attribs;
|
Vector<String> attribs;
|
||||||
Vector<GLenum> attribTypes;
|
Vector<GLenum> attribTypes;
|
||||||
@@ -1057,12 +1147,24 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
// for both. GL 3.3 core uniforms are always sized. Takes a TProgram uniform index (the space
|
// for both. GL 3.3 core uniforms are always sized. Takes a TProgram uniform index (the space
|
||||||
// the artifacts' uniformIndexInTProgram stores).
|
// the artifacts' uniformIndexInTProgram stores).
|
||||||
static GLint GetUniformArraySizeByTIndex(const LinkArtifacts& artifacts, Int tIndex) {
|
static GLint GetUniformArraySizeByTIndex(const LinkArtifacts& artifacts, Int tIndex) {
|
||||||
const auto& uniform = artifacts.program->getUniform(tIndex);
|
return UniformAtIn(artifacts, tIndex).arraySize;
|
||||||
const glslang::TType* type = uniform.getType();
|
|
||||||
if (type != nullptr && type->isSizedArray()) {
|
|
||||||
return type->getOuterArraySize();
|
|
||||||
}
|
}
|
||||||
return uniform.size < 1 ? 1 : uniform.size;
|
|
||||||
|
// Bounds-checked mirror lookup. Out of range yields a default-constructed entry
|
||||||
|
// rather than UB, which is the same shape the phase-B getters use: a program whose
|
||||||
|
// reflection is missing must stay answerable, not crash the query surface.
|
||||||
|
static const UniformReflection& UniformAtIn(const LinkArtifacts& artifacts, Int tIndex) {
|
||||||
|
static const UniformReflection kEmpty;
|
||||||
|
if (tIndex < 0 || static_cast<SizeT>(tIndex) >= artifacts.uniformReflection.size()) return kEmpty;
|
||||||
|
return artifacts.uniformReflection[tIndex];
|
||||||
|
}
|
||||||
|
const UniformReflection& UniformAt(Int tIndex) const { return UniformAtIn(Artifacts(), tIndex); }
|
||||||
|
const BlockReflection& BlockAt(Int tBlockIndex) const {
|
||||||
|
static const BlockReflection kEmpty;
|
||||||
|
if (tBlockIndex < 0 || static_cast<SizeT>(tBlockIndex) >= Artifacts().blockReflection.size()) {
|
||||||
|
return kEmpty;
|
||||||
|
}
|
||||||
|
return Artifacts().blockReflection[tBlockIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blocks until a pending link has published its artifacts. Public because a few call
|
// Blocks until a pending link has published its artifacts. Public because a few call
|
||||||
|
|||||||
@@ -329,22 +329,25 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
for (Uint location = 0; location <= reflection.maxUniformLocation; ++location) {
|
for (Uint location = 0; location <= reflection.maxUniformLocation; ++location) {
|
||||||
if (artifacts.uniformOffsets[location] != ProgramObject::kInvalidUniformOffset) continue;
|
if (artifacts.uniformOffsets[location] != ProgramObject::kInvalidUniformOffset) continue;
|
||||||
if (!ProgramObject::IsValidUniformLocation(reflection, static_cast<Int>(location))) continue;
|
if (!ProgramObject::IsValidUniformLocation(reflection, static_cast<Int>(location))) continue;
|
||||||
const auto& uniform = reflection.program->getUniform(reflection.uniformIndexInTProgram[location]);
|
const auto& uniform =
|
||||||
const glslang::TType* type = uniform.getType();
|
ProgramObject::UniformAtIn(reflection, reflection.uniformIndexInTProgram[location]);
|
||||||
if (type != nullptr && type->isOpaque()) continue;
|
if (uniform.type.isOpaque) continue;
|
||||||
if (uniform.index >= 0 && uniform.index < reflection.program->getNumUniformBlocks() &&
|
// Member of a named uniform block: not settable through glUniform*, so it needs
|
||||||
std::strstr(reflection.program->getUniformBlock(uniform.index).name.c_str(),
|
// no global-UBO shadow storage. tProgramBlockIndexToGl[i] >= 0 means block i is
|
||||||
MG_Util::ShaderTranspiler::GLOBAL_UBO_NAME) == nullptr) {
|
// GL-visible, i.e. NOT the synthesized MGL_GLOBAL_UBO - which is exactly what the
|
||||||
// Member of a named uniform block: not settable through glUniform*, so it
|
// strstr(GLOBAL_UBO_NAME) test this replaced was asking, without needing the
|
||||||
// needs no global-UBO shadow storage.
|
// TProgram to spell the block name.
|
||||||
|
if (uniform.index >= 0 &&
|
||||||
|
uniform.index < static_cast<Int>(reflection.tProgramBlockIndexToGl.size()) &&
|
||||||
|
reflection.tProgramBlockIndexToGl[uniform.index] >= 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// std140-style slot: the matrix upload paths write column vectors at
|
// std140-style slot: the matrix upload paths write column vectors at
|
||||||
// 16-byte strides, so a matrix slot must cover cols * 16 bytes.
|
// 16-byte strides, so a matrix slot must cover cols * 16 bytes.
|
||||||
SizeT slotSize = MG_Util::GetGLTypeSize(uniform.glDefineType);
|
SizeT slotSize = MG_Util::GetGLTypeSize(uniform.glDefineType);
|
||||||
if (type != nullptr && type->isMatrix()) {
|
if (uniform.type.isMatrix) {
|
||||||
slotSize = static_cast<SizeT>(type->getMatrixCols()) * 16u;
|
slotSize = static_cast<SizeT>(uniform.type.matrixCols) * 16u;
|
||||||
}
|
}
|
||||||
slotSize = (slotSize + 15u) & ~static_cast<SizeT>(15u);
|
slotSize = (slotSize + 15u) & ~static_cast<SizeT>(15u);
|
||||||
const SizeT slotOffset = (artifacts.globalUboScratch.size() + 15u) & ~static_cast<SizeT>(15u);
|
const SizeT slotOffset = (artifacts.globalUboScratch.size() + 15u) & ~static_cast<SizeT>(15u);
|
||||||
|
|||||||
Reference in New Issue
Block a user