mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
[Fix] (ShaderTranspiler): lower gl_NumSamples onto a reserved global-UBO uniform, restore ES preamble extension macros, tolerate a repeated #version
This commit is contained in:
@@ -1136,6 +1136,20 @@ namespace MobileGL::MG_State::GLState {
|
||||
in.externalIndex, uniform.name.c_str());
|
||||
continue;
|
||||
}
|
||||
// The gl_NumSamples stand-in InjectNumSamplesBuiltinShim declared. It is a driver
|
||||
// uniform, not the application's: gl_NumSamples is a BUILT-IN, so a conformant
|
||||
// implementation reports nothing for it in GL_ACTIVE_UNIFORMS, glGetActiveUniform or
|
||||
// glGetUniformLocation, and nothing may write it through glUniform* either. Filtering
|
||||
// it here does both, and costs it no storage: BuildGlobalUboRouting takes its offset
|
||||
// from the SPIR-V metadata by name, not from the GL location space.
|
||||
if (isGlobalUboMember(uniform) &&
|
||||
uniform.name == MG_Util::ShaderTranspiler::NUM_SAMPLES_UNIFORM_NAME) {
|
||||
artifacts.usesReservedNumSamples = true;
|
||||
MGLOG_D("ProgramObject %u: Reflection - reserved gl_NumSamples stand-in '%s' hidden from the GL "
|
||||
"uniform surface",
|
||||
in.externalIndex, uniform.name.c_str());
|
||||
continue;
|
||||
}
|
||||
if (isBufferVariable(uniform)) {
|
||||
MGLOG_D("ProgramObject %u: Reflection - buffer variable '%s' filtered from the GL uniform "
|
||||
"surface",
|
||||
|
||||
@@ -787,6 +787,33 @@ namespace MobileGL::MG_State::GLState {
|
||||
void MarkUBOContentDirty() const {
|
||||
if (++m_uboContentVersion == ~0u) m_uboContentVersion = 0;
|
||||
}
|
||||
|
||||
// ---- the reserved gl_NumSamples stand-in (ShaderTranspiler::NUM_SAMPLES_UNIFORM_NAME) ----
|
||||
//
|
||||
// PHASE A: answerable without joining the SPIR-V job, which is what lets the draw path ask
|
||||
// every program this question and pay nothing for the overwhelming majority that say no.
|
||||
Bool UsesReservedNumSamples() const { return Artifacts().usesReservedNumSamples; }
|
||||
|
||||
// Publishes `samples` into the global-UBO shadow. Returns false when there is nowhere to
|
||||
// put it - no shim in this program, no SPIR-V (a cancelled phase B), or the optimizer
|
||||
// dropped the member because nothing read it after all - all of which are ordinary states,
|
||||
// not errors. A value-identical write is dropped without bumping the content version, so a
|
||||
// steady stream of draws into one framebuffer does not force a re-upload per draw.
|
||||
Bool WriteReservedNumSamples(Int samples) {
|
||||
if (!UsesReservedNumSamples()) return false;
|
||||
SpirvArtifacts& spirv = Spirv();
|
||||
const Uint offset = spirv.reservedNumSamplesOffset;
|
||||
if (offset == kInvalidUniformOffset) return false;
|
||||
if (static_cast<SizeT>(offset) + sizeof(Int) > spirv.globalUboScratch.size()) return false;
|
||||
|
||||
Uint8* const slot = spirv.globalUboScratch.data() + offset;
|
||||
Int current = 0;
|
||||
Memcpy(¤t, slot, sizeof(Int));
|
||||
if (current == samples) return true;
|
||||
Memcpy(slot, &samples, sizeof(Int));
|
||||
MarkUBOContentDirty();
|
||||
return true;
|
||||
}
|
||||
// ---- glUniform* inside the phase-A -> phase-B window ----
|
||||
//
|
||||
// True while the program is fully linked and fully queryable but its uniform shadow's
|
||||
@@ -1296,6 +1323,14 @@ namespace MobileGL::MG_State::GLState {
|
||||
std::set<String> uniformBlocksWithoutBinding;
|
||||
|
||||
Uint activeUniformCount = 0;
|
||||
// This program's fragment stage read gl_NumSamples, so the source pipeline lowered it
|
||||
// onto the reserved default-block uniform (ShaderTranspiler::NUM_SAMPLES_UNIFORM_NAME)
|
||||
// and the draw path owes it the draw framebuffer's sample count before every draw.
|
||||
//
|
||||
// PHASE A on purpose, even though the byte offset it needs is phase-B output: the
|
||||
// gate has to be answerable without joining the SPIR-V job, or every draw of every
|
||||
// program would pay a join to discover it has nothing to write.
|
||||
Bool usesReservedNumSamples = false;
|
||||
Uint maxUniformLocation = 0;
|
||||
Int uniformNameMaxLength = 0;
|
||||
Int attribInNameMaxLength = 0;
|
||||
@@ -1348,6 +1383,11 @@ namespace MobileGL::MG_State::GLState {
|
||||
// kInvalidUniformOffset. Sized maxUniformLocation + 1 by the routing pass.
|
||||
Vector<Uint> uniformOffsets;
|
||||
Vector<Uint8> globalUboScratch;
|
||||
// Byte offset of the reserved gl_NumSamples stand-in inside globalUboScratch, or
|
||||
// kInvalidUniformOffset. Taken by NAME from the SPIR-V metadata rather than through
|
||||
// uniformOffsets, because the member has no GL location at all: the link task keeps
|
||||
// it out of the GL-visible uniform index space so no application can see or write it.
|
||||
Uint reservedNumSamplesOffset = kInvalidUniformOffset;
|
||||
// False for a program whose SPIR-V was never produced (phase B cancelled at
|
||||
// teardown or by a relink) or whose optimizer run failed. GL has no way to
|
||||
// retract a LINK_STATUS it already reported true, so such a program stays
|
||||
|
||||
@@ -278,6 +278,7 @@ namespace MobileGL::MG_State::GLState {
|
||||
|
||||
artifacts.uniformOffsets.clear();
|
||||
artifacts.globalUboScratch.clear();
|
||||
artifacts.reservedNumSamplesOffset = ProgramObject::kInvalidUniformOffset;
|
||||
// kInvalidUniformOffset marks locations that end up without global-UBO backing
|
||||
// (e.g. the optimizer eliminated every use of the uniform); the fallback pass
|
||||
// below gives those locations tail storage so glUniform* always has a target.
|
||||
@@ -311,6 +312,18 @@ namespace MobileGL::MG_State::GLState {
|
||||
artifacts.globalUboScratch.resize(size);
|
||||
}
|
||||
for (const auto& [name, offset] : meta.plainUniformOffsetsInUBO) {
|
||||
// The gl_NumSamples stand-in is routed by NAME and nothing else. It has no GL
|
||||
// location to look up - DoReflection hides it from the GL uniform index space
|
||||
// precisely so no application can address it - so the lookup below would find
|
||||
// nothing and log it as unbacked. Only the fragment stage declares it, and
|
||||
// every stage's copy sits at the same offset in the one shared global UBO.
|
||||
if (name == NUM_SAMPLES_UNIFORM_NAME) {
|
||||
artifacts.reservedNumSamplesOffset = offset;
|
||||
MGLOG_D("ProgramObject %u: BuildGlobalUboRouting - reserved gl_NumSamples stand-in '%s' "
|
||||
"backed at UBO offset %u",
|
||||
externalIndex, name.c_str(), offset);
|
||||
continue;
|
||||
}
|
||||
// SPIRV-Reflect leaf names never carry a "[0]" suffix; frontend
|
||||
// reflection keys arrays as "arr[0]" (GL naming), so retry with the
|
||||
// suffix before declaring the uniform unbacked.
|
||||
|
||||
Reference in New Issue
Block a user