[Fix] (MG_Impl/GLImpl, MG_State/GLState): validate frag data link locations [skip ci]

This commit is contained in:
2026-07-02 16:47:13 +08:00
parent b9de562491
commit 633a25b456
3 changed files with 54 additions and 0 deletions
@@ -589,6 +589,10 @@ namespace MobileGL::MG_Impl::GLImpl {
const auto& rendererInfo = activeBackendObject->GetRendererInfo();
allowVSOnlyPrograms = (Int)rendererInfo.StaticBackendCapability.AllowVSOnlyPrograms;
}
const auto& activeBackendObject = MG_Backend::pActiveBackendObject;
if (activeBackendObject) {
programObject->SetMaxFragmentOutputColorNumber(activeBackendObject->GetDynamicParameters().MaxDrawBuffers);
}
programObject->Link(!allowVSOnlyPrograms);
}
@@ -20,6 +20,11 @@ void main() {}
)";
namespace {
static MobileGL::String StripArrayElementSuffix(const MobileGL::String& name) {
const MobileGL::SizeT bracket = name.find('[');
return bracket == MobileGL::String::npos ? name : name.substr(0, bracket);
}
static int GetVertexInputLocationSpan(GLenum glType) {
switch (glType) {
case GL_FLOAT_MAT2:
@@ -225,6 +230,9 @@ namespace MobileGL::MG_State::GLState {
MGLOG_D("ProgramObject %u: Starting reflection", m_externalIndex);
DoReflection();
MGLOG_D("ProgramObject %u: Reflection done (linkStatus=%d)", m_externalIndex, (int)m_linkStatus);
if (!ValidateFragmentOutputLocations()) {
return;
}
MGLOG_D("ProgramObject %u: Starting binary generation", m_externalIndex);
GenerateBinary();
@@ -590,6 +598,43 @@ namespace MobileGL::MG_State::GLState {
m_externalIndex, name, index);
}
Bool ProgramObject::ValidateFragmentOutputLocations() {
if (!m_program) return false;
UnorderedMap<Int, String> colorNumberOwners;
const Int outputCount = m_program->getNumPipeOutputs();
for (Int index = 0; index < outputCount; ++index) {
const auto& output = m_program->getPipeOutput(index);
const String outputName = StripArrayElementSuffix(output.name);
const auto explicitLocation = m_explicitFragDataLocation.find(outputName);
const Int location = explicitLocation != m_explicitFragDataLocation.end()
? static_cast<Int>(explicitLocation->second)
: static_cast<Int>(output.layoutLocation());
const Int span = std::max<Int>(output.size, 1);
if (location < 0 || location + span > m_maxFragmentOutputColorNumber) {
m_infoLog = std::format("Fragment output '{}' location range [{}, {}) exceeds GL_MAX_DRAW_BUFFERS {}.",
outputName, location, location + span, m_maxFragmentOutputColorNumber);
MGLOG_E("ProgramObject %u: Link failed - %s", m_externalIndex, m_infoLog.c_str());
ResetLinkArtifacts();
return false;
}
for (Int colorNumber = location; colorNumber < location + span; ++colorNumber) {
auto [owner, inserted] = colorNumberOwners.emplace(colorNumber, outputName);
if (!inserted) {
m_infoLog = std::format("Fragment outputs '{}' and '{}' alias color number {}.",
owner->second, outputName, colorNumber);
MGLOG_E("ProgramObject %u: Link failed - %s", m_externalIndex, m_infoLog.c_str());
ResetLinkArtifacts();
return false;
}
}
}
return true;
}
Int ProgramObject::GetFragmentDataLocation(const char* name) {
if (!m_program || !name) return -1;
@@ -26,6 +26,9 @@ namespace MobileGL::MG_State::GLState {
void SetExplicitVertexInLocation(Uint index, const char* name);
void SetExplicitFragmentOutLocation(Uint index, const char* name);
void SetMaxFragmentOutputColorNumber(Int maxDrawBuffers) {
m_maxFragmentOutputColorNumber = maxDrawBuffers;
}
Int GetFragmentDataLocation(const char* name);
Vector<SharedPtr<ShaderObject>>& GetAttachedShaders();
@@ -241,6 +244,7 @@ namespace MobileGL::MG_State::GLState {
void GenerateBinary();
void WaitUntilGenerationCompleted() const;
void AddDefaultFragmentShaderIfMissing();
Bool ValidateFragmentOutputLocations();
const Uint m_externalIndex = 0;
Vector<SharedPtr<ShaderObject>> m_shaders;
@@ -258,6 +262,7 @@ namespace MobileGL::MG_State::GLState {
// FragData (Frag out)
UnorderedMap<String, Uint> m_explicitFragDataLocation;
UnorderedMap<String, Uint> m_linkedFragDataLocation;
Int m_maxFragmentOutputColorNumber = 8;
// Uniforms
UnorderedMap<String, Uint> m_uniformLocations;