Implement ES 2,3 parts of EXT_blend_func_extended for shader translation
Exposes gl_SecondaryFragColor, glSecondaryFragData[] and
gl_MaxDualSourceDrawBuffers to GLES SL 1.0.
Relaxes rules for undefined output locations for GLES SL 3.0
and exposes gl_MaxDualSourceDrawBuffers.
If the output GL context is GL ES 2.0 or 3.0:
The emulation layer is expected to turn on EXT_blend_func_extended
if the output GL context supports it.
If the output GL context is GL:
The emulation layer is expected to turn on EXT_blend_func_extended
if the output GL context supports ARB_blend_func_extended or if GL
context is 3.2 or later.
If the source shader spec is GLES SL 2.0: The emulation layer is
expected to inspect the shader compilation output variables upon
linking. If output target is GL SL, the emulation layer should bind
color location 0, index 1 to "angle_SecondaryFragColor" if variable
"gl_SecondaryFragColorEXT" is used. Alternatively, emulation layer
should bind "angle_SecondaryFragData" to locations 0,1,2,3,..., all
color index 1, if "gl_SecondaryFragData" array is used.
(The caller can not bind the locations or specify output variables.)
If the source shader spec is GLES SL 3.0:
The emulation layer is expected to do location auto-resolve of the
the output variables that have undefined output locations that have
not been bound by the caller.
(The caller can not use gl_ built-ins, so nothing to do with those.)
BUG=angleproject:1085
TEST=angle_unittest
Change-Id: I5cafe205b0c29478b0dcd24aa89a7b0000f5d046
Reviewed-on: https://chromium-review.googlesource.com/287580
Reviewed-by: Zhenyao Mo <zmo@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Tested-by: Kimmo Kinnunen <kkinnunen@nvidia.com>
diff --git a/src/compiler/translator/ValidateOutputs.cpp b/src/compiler/translator/ValidateOutputs.cpp
index 6976309..cd37aea 100644
--- a/src/compiler/translator/ValidateOutputs.cpp
+++ b/src/compiler/translator/ValidateOutputs.cpp
@@ -9,12 +9,23 @@
#include "compiler/translator/InitializeParseContext.h"
#include "compiler/translator/ParseContext.h"
-ValidateOutputs::ValidateOutputs(TInfoSinkBase& sink, int maxDrawBuffers)
+namespace
+{
+void error(int *errorCount, TInfoSinkBase &sink, const TIntermSymbol &symbol, const char *reason)
+{
+ sink.prefix(EPrefixError);
+ sink.location(symbol.getLine());
+ sink << "'" << symbol.getSymbol() << "' : " << reason << "\n";
+ (*errorCount)++;
+}
+
+} // namespace
+
+ValidateOutputs::ValidateOutputs(const TExtensionBehavior &extBehavior, int maxDrawBuffers)
: TIntermTraverser(true, false, false),
- mSink(sink),
mMaxDrawBuffers(maxDrawBuffers),
- mNumErrors(0),
- mHasUnspecifiedOutputLocation(false)
+ mAllowUnspecifiedOutputLocationResolution(
+ IsExtensionEnabled(extBehavior, "GL_EXT_blend_func_extended"))
{
}
@@ -30,51 +41,68 @@
if (qualifier == EvqFragmentOut)
{
- const TType &type = symbol->getType();
- const int location = type.getLayoutQualifier().location;
- const bool isUnspecifiedOutputLocation = location == -1;
-
- if (mHasUnspecifiedOutputLocation || (isUnspecifiedOutputLocation && !mOutputMap.empty()))
+ if (symbol->getType().getLayoutQualifier().location == -1)
{
- error(symbol->getLine(), "must explicitly specify all locations when using multiple fragment outputs", name.c_str());
- }
- else if (isUnspecifiedOutputLocation)
- {
- mHasUnspecifiedOutputLocation = true;
+ mUnspecifiedLocationOutputs.push_back(symbol);
}
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());
- }
+ mOutputs.push_back(symbol);
}
}
}
-void ValidateOutputs::error(TSourceLoc loc, const char *reason, const char* token)
+int ValidateOutputs::validateAndCountErrors(TInfoSinkBase &sink) const
{
- mSink.prefix(EPrefixError);
- mSink.location(loc);
- mSink << "'" << token << "' : " << reason << "\n";
- mNumErrors++;
+ OutputVector validOutputs(mMaxDrawBuffers);
+ int errorCount = 0;
+
+ for (const auto &symbol : mOutputs)
+ {
+ const TType &type = symbol->getType();
+ const size_t elementCount = static_cast<size_t>(type.isArray() ? type.getArraySize() : 1);
+ const size_t location = static_cast<size_t>(type.getLayoutQualifier().location);
+
+ ASSERT(type.getLayoutQualifier().location != -1);
+
+ if (location + elementCount <= validOutputs.size())
+ {
+ for (size_t elementIndex = 0; elementIndex < elementCount; elementIndex++)
+ {
+ const size_t offsetLocation = location + elementIndex;
+ if (validOutputs[offsetLocation])
+ {
+ std::stringstream strstr;
+ strstr << "conflicting output locations with previously defined output '"
+ << validOutputs[offsetLocation]->getSymbol() << "'";
+ error(&errorCount, sink, *symbol, strstr.str().c_str());
+ }
+ else
+ {
+ validOutputs[offsetLocation] = symbol;
+ }
+ }
+ }
+ else
+ {
+ if (elementCount > 0)
+ {
+ error(&errorCount, sink, *symbol,
+ elementCount > 1 ? "output array locations would exceed MAX_DRAW_BUFFERS"
+ : "output location must be < MAX_DRAW_BUFFERS");
+ }
+ }
+ }
+
+ if (!mAllowUnspecifiedOutputLocationResolution &&
+ ((!mOutputs.empty() && !mUnspecifiedLocationOutputs.empty()) ||
+ mUnspecifiedLocationOutputs.size() > 1))
+ {
+ for (const auto &symbol : mUnspecifiedLocationOutputs)
+ {
+ error(&errorCount, sink, *symbol,
+ "must explicitly specify all locations when using multiple fragment outputs");
+ }
+ }
+ return errorCount;
}