Validate fragment shader outputs and produce a compile error on missing or conflicting assignments.
TRAC #22704
Signed-off-by: Geoff Lang
Signed-off-by: Nicolas Capens
Authored-by: Jamie Madill
diff --git a/src/compiler/Compiler.cpp b/src/compiler/Compiler.cpp
index 8aef072..7b3c646 100644
--- a/src/compiler/Compiler.cpp
+++ b/src/compiler/Compiler.cpp
@@ -14,6 +14,7 @@
#include "compiler/RenameFunction.h"
#include "compiler/ShHandle.h"
#include "compiler/ValidateLimitations.h"
+#include "compiler/ValidateOutputs.h"
#include "compiler/VariablePacker.h"
#include "compiler/depgraph/DependencyGraph.h"
#include "compiler/depgraph/DependencyGraphOutput.h"
@@ -143,6 +144,9 @@
if (success)
success = detectRecursion(root);
+ if (success && shaderVersion == 300 && shaderType == SH_FRAGMENT_SHADER)
+ success = validateOutputs(root);
+
if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING))
success = validateLimitations(root);
@@ -272,6 +276,13 @@
}
}
+bool TCompiler::validateOutputs(TIntermNode* root)
+{
+ ValidateOutputs validateOutputs(infoSink.info, compileResources.MaxDrawBuffers);
+ root->traverse(&validateOutputs);
+ return (validateOutputs.numErrors() == 0);
+}
+
void TCompiler::rewriteCSSShader(TIntermNode* root)
{
RenameFunction renamer("main(", "css_main(");
diff --git a/src/compiler/ParseHelper.cpp b/src/compiler/ParseHelper.cpp
index a30bd27..31af70d 100644
--- a/src/compiler/ParseHelper.cpp
+++ b/src/compiler/ParseHelper.cpp
@@ -2320,17 +2320,16 @@
}
else
{
+ // must check that location is non-negative
if (intValue < 0)
{
- error(intValueLine, "out of range", intValueString.c_str(), "value must be non-negative and < MAX_DRAW_BUFFERS");
+ error(intValueLine, "out of range:", intValueString.c_str(), "location must be non-negative");
recover();
}
else
{
qualifier.location = intValue;
}
-
- // TODO: must check that location is < MAX_DRAW_BUFFERS
}
return qualifier;
diff --git a/src/compiler/ShHandle.h b/src/compiler/ShHandle.h
index 5b70307..5f8d9d0 100644
--- a/src/compiler/ShHandle.h
+++ b/src/compiler/ShHandle.h
@@ -86,6 +86,8 @@
void clearResults();
// Return true if function recursion is detected.
bool detectRecursion(TIntermNode* root);
+ // Returns true if a program has no conflicting or missing fragment outputs
+ bool validateOutputs(TIntermNode* root);
// Rewrites a shader's intermediate tree according to the CSS Shaders spec.
void rewriteCSSShader(TIntermNode* root);
// Returns true if the given shader does not exceed the minimum
diff --git a/src/compiler/ValidateOutputs.cpp b/src/compiler/ValidateOutputs.cpp
new file mode 100644
index 0000000..d7f294c
--- /dev/null
+++ b/src/compiler/ValidateOutputs.cpp
@@ -0,0 +1,78 @@
+//
+// Copyright (c) 2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/ValidateOutputs.h"
+#include "compiler/InfoSink.h"
+#include "compiler/InitializeParseContext.h"
+#include "compiler/ParseHelper.h"
+
+ValidateOutputs::ValidateOutputs(TInfoSinkBase& sink, int maxDrawBuffers)
+ : mSink(sink),
+ mMaxDrawBuffers(maxDrawBuffers),
+ mNumErrors(0),
+ mHasUnspecifiedOutputLocation(false)
+{
+}
+
+void ValidateOutputs::visitSymbol(TIntermSymbol *symbol)
+{
+ TString name = symbol->getSymbol();
+ TQualifier qualifier = symbol->getQualifier();
+
+ if (mVisitedSymbols.count(name) == 1)
+ return;
+
+ mVisitedSymbols.insert(name);
+
+ if (qualifier == EvqFragmentOutput)
+ {
+ const TType &type = symbol->getType();
+ const int location = type.getLayoutQualifier().location;
+
+ if (mHasUnspecifiedOutputLocation)
+ {
+ error(symbol->getLine(), "must explicitly specify all locations when using multiple fragment outputs", name.c_str());
+ }
+ else if (location == -1)
+ {
+ mHasUnspecifiedOutputLocation = true;
+ }
+ else
+ {
+ OutputMap::iterator mapEntry = mOutputMap.find(location);
+ if (mapEntry == mOutputMap.end())
+ {
+ const int elementCount = type.isArray() ? type.getArraySize() : 1;
+ if (location + elementCount > mMaxDrawBuffers)
+ {
+ error(symbol->getLine(), "output location must be < MAX_DRAW_BUFFERS", name.c_str());
+ }
+
+ for (int elementIndex = 0; elementIndex < elementCount; elementIndex++)
+ {
+ const int offsetLocation = location + elementIndex;
+ mOutputMap[offsetLocation] = symbol;
+ }
+ }
+ else
+ {
+ std::stringstream strstr;
+ strstr << "conflicting output locations with previously defined output '"
+ << mapEntry->second->getSymbol() << "'";
+
+ error(symbol->getLine(), strstr.str().c_str(), name.c_str());
+ }
+ }
+ }
+}
+
+void ValidateOutputs::error(TSourceLoc loc, const char *reason, const char* token)
+{
+ mSink.prefix(EPrefixError);
+ mSink.location(loc);
+ mSink << "'" << token << "' : " << reason << "\n";
+ mNumErrors++;
+}
diff --git a/src/compiler/ValidateOutputs.h b/src/compiler/ValidateOutputs.h
new file mode 100644
index 0000000..1f81124
--- /dev/null
+++ b/src/compiler/ValidateOutputs.h
@@ -0,0 +1,34 @@
+//
+// Copyright (c) 2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "GLSLANG/ShaderLang.h"
+#include "compiler/intermediate.h"
+
+#include <set>
+
+class TInfoSinkBase;
+
+class ValidateOutputs : public TIntermTraverser
+{
+ public:
+ ValidateOutputs(TInfoSinkBase& sink, int maxDrawBuffers);
+
+ int numErrors() const { return mNumErrors; }
+
+ virtual void visitSymbol(TIntermSymbol*);
+
+ private:
+ TInfoSinkBase& mSink;
+ int mMaxDrawBuffers;
+ int mNumErrors;
+ bool mHasUnspecifiedOutputLocation;
+
+ typedef std::map<int, TIntermSymbol*> OutputMap;
+ OutputMap mOutputMap;
+ std::set<TString> mVisitedSymbols;
+
+ void error(TSourceLoc loc, const char *reason, const char* token);
+};
diff --git a/src/compiler/translator_common.vcxproj b/src/compiler/translator_common.vcxproj
index ac4d4c5..2899354 100644
--- a/src/compiler/translator_common.vcxproj
+++ b/src/compiler/translator_common.vcxproj
@@ -164,6 +164,7 @@
<ClCompile Include="SymbolTable.cpp" />
<ClCompile Include="util.cpp" />
<ClCompile Include="ValidateLimitations.cpp" />
+ <ClCompile Include="ValidateOutputs.cpp" />
<ClCompile Include="VariableInfo.cpp" />
<ClCompile Include="VariablePacker.cpp" />
<ClCompile Include="glslang_lex.cpp" />
@@ -258,6 +259,7 @@
<ClInclude Include="Types.h" />
<ClInclude Include="util.h" />
<ClInclude Include="ValidateLimitations.h" />
+ <ClInclude Include="ValidateOutputs.h" />
<ClInclude Include="VariableInfo.h" />
<ClInclude Include="VariablePacker.h" />
<ClInclude Include="glslang_tab.h" />
diff --git a/src/compiler/translator_common.vcxproj.filters b/src/compiler/translator_common.vcxproj.filters
index 7dc64e4..93b9df6 100644
--- a/src/compiler/translator_common.vcxproj.filters
+++ b/src/compiler/translator_common.vcxproj.filters
@@ -140,6 +140,9 @@
<ClCompile Include="..\common\utilities.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="ValidateOutputs.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="BaseTypes.h">
@@ -262,6 +265,9 @@
<ClInclude Include="..\third_party\compiler\ArrayBoundsClamper.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="ValidateOutputs.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="glslang.l">