diff --git a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp index 251520b1..beab9679 100644 --- a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp +++ b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp @@ -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); } diff --git a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp index 56e120ef..75b3b092 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp +++ b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp @@ -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 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(explicitLocation->second) + : static_cast(output.layoutLocation()); + const Int span = std::max(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; diff --git a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h index 432f6e4f..2ba112e7 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h +++ b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h @@ -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>& 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> m_shaders; @@ -258,6 +262,7 @@ namespace MobileGL::MG_State::GLState { // FragData (Frag out) UnorderedMap m_explicitFragDataLocation; UnorderedMap m_linkedFragDataLocation; + Int m_maxFragmentOutputColorNumber = 8; // Uniforms UnorderedMap m_uniformLocations;