Commit 05a80ceb by Jamie Madill Committed by Shannon Woods

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
parent 975af378
...@@ -124,6 +124,8 @@ ...@@ -124,6 +124,8 @@
'compiler/util.h', 'compiler/util.h',
'compiler/ValidateLimitations.cpp', 'compiler/ValidateLimitations.cpp',
'compiler/ValidateLimitations.h', 'compiler/ValidateLimitations.h',
'compiler/ValidateOutput.cpp',
'compiler/ValidateOutput.h',
'compiler/VariableInfo.cpp', 'compiler/VariableInfo.cpp',
'compiler/VariableInfo.h', 'compiler/VariableInfo.h',
'compiler/VariablePacker.cpp', 'compiler/VariablePacker.cpp',
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "compiler/RenameFunction.h" #include "compiler/RenameFunction.h"
#include "compiler/ShHandle.h" #include "compiler/ShHandle.h"
#include "compiler/ValidateLimitations.h" #include "compiler/ValidateLimitations.h"
#include "compiler/ValidateOutputs.h"
#include "compiler/VariablePacker.h" #include "compiler/VariablePacker.h"
#include "compiler/depgraph/DependencyGraph.h" #include "compiler/depgraph/DependencyGraph.h"
#include "compiler/depgraph/DependencyGraphOutput.h" #include "compiler/depgraph/DependencyGraphOutput.h"
...@@ -143,6 +144,9 @@ bool TCompiler::compile(const char* const shaderStrings[], ...@@ -143,6 +144,9 @@ bool TCompiler::compile(const char* const shaderStrings[],
if (success) if (success)
success = detectRecursion(root); success = detectRecursion(root);
if (success && shaderVersion == 300 && shaderType == SH_FRAGMENT_SHADER)
success = validateOutputs(root);
if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING)) if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING))
success = validateLimitations(root); success = validateLimitations(root);
...@@ -272,6 +276,13 @@ bool TCompiler::detectRecursion(TIntermNode* root) ...@@ -272,6 +276,13 @@ bool TCompiler::detectRecursion(TIntermNode* root)
} }
} }
bool TCompiler::validateOutputs(TIntermNode* root)
{
ValidateOutputs validateOutputs(infoSink.info, compileResources.MaxDrawBuffers);
root->traverse(&validateOutputs);
return (validateOutputs.numErrors() == 0);
}
void TCompiler::rewriteCSSShader(TIntermNode* root) void TCompiler::rewriteCSSShader(TIntermNode* root)
{ {
RenameFunction renamer("main(", "css_main("); RenameFunction renamer("main(", "css_main(");
......
...@@ -2320,17 +2320,16 @@ TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierTyp ...@@ -2320,17 +2320,16 @@ TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierTyp
} }
else else
{ {
// must check that location is non-negative
if (intValue < 0) 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(); recover();
} }
else else
{ {
qualifier.location = intValue; qualifier.location = intValue;
} }
// TODO: must check that location is < MAX_DRAW_BUFFERS
} }
return qualifier; return qualifier;
......
...@@ -86,6 +86,8 @@ protected: ...@@ -86,6 +86,8 @@ protected:
void clearResults(); void clearResults();
// Return true if function recursion is detected. // Return true if function recursion is detected.
bool detectRecursion(TIntermNode* root); 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. // Rewrites a shader's intermediate tree according to the CSS Shaders spec.
void rewriteCSSShader(TIntermNode* root); void rewriteCSSShader(TIntermNode* root);
// Returns true if the given shader does not exceed the minimum // Returns true if the given shader does not exceed the minimum
......
//
// 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++;
}
//
// 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);
};
...@@ -164,6 +164,7 @@ ...@@ -164,6 +164,7 @@
<ClCompile Include="SymbolTable.cpp" /> <ClCompile Include="SymbolTable.cpp" />
<ClCompile Include="util.cpp" /> <ClCompile Include="util.cpp" />
<ClCompile Include="ValidateLimitations.cpp" /> <ClCompile Include="ValidateLimitations.cpp" />
<ClCompile Include="ValidateOutputs.cpp" />
<ClCompile Include="VariableInfo.cpp" /> <ClCompile Include="VariableInfo.cpp" />
<ClCompile Include="VariablePacker.cpp" /> <ClCompile Include="VariablePacker.cpp" />
<ClCompile Include="glslang_lex.cpp" /> <ClCompile Include="glslang_lex.cpp" />
...@@ -258,6 +259,7 @@ ...@@ -258,6 +259,7 @@
<ClInclude Include="Types.h" /> <ClInclude Include="Types.h" />
<ClInclude Include="util.h" /> <ClInclude Include="util.h" />
<ClInclude Include="ValidateLimitations.h" /> <ClInclude Include="ValidateLimitations.h" />
<ClInclude Include="ValidateOutputs.h" />
<ClInclude Include="VariableInfo.h" /> <ClInclude Include="VariableInfo.h" />
<ClInclude Include="VariablePacker.h" /> <ClInclude Include="VariablePacker.h" />
<ClInclude Include="glslang_tab.h" /> <ClInclude Include="glslang_tab.h" />
......
...@@ -140,6 +140,9 @@ ...@@ -140,6 +140,9 @@
<ClCompile Include="..\common\utilities.cpp"> <ClCompile Include="..\common\utilities.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="ValidateOutputs.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="BaseTypes.h"> <ClInclude Include="BaseTypes.h">
...@@ -262,6 +265,9 @@ ...@@ -262,6 +265,9 @@
<ClInclude Include="..\third_party\compiler\ArrayBoundsClamper.h"> <ClInclude Include="..\third_party\compiler\ArrayBoundsClamper.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="ValidateOutputs.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<CustomBuild Include="glslang.l"> <CustomBuild Include="glslang.l">
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment