Commit b18609b9 by Kimmo Kinnunen Committed by Jamie Madill

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/287580Reviewed-by: 's avatarZhenyao Mo <zmo@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Tested-by: 's avatarKimmo Kinnunen <kkinnunen@nvidia.com>
parent 3574c614
......@@ -48,7 +48,7 @@ typedef unsigned int GLenum;
// Version number for shader translation API.
// It is incremented every time the API changes.
#define ANGLE_SH_VERSION 138
#define ANGLE_SH_VERSION 139
typedef enum {
SH_GLES2_SPEC = 0x8B40,
......@@ -247,6 +247,7 @@ typedef struct
int OES_standard_derivatives;
int OES_EGL_image_external;
int ARB_texture_rectangle;
int EXT_blend_func_extended;
int EXT_draw_buffers;
int EXT_frag_depth;
int EXT_shader_texture_lod;
......@@ -271,6 +272,13 @@ typedef struct
int MinProgramTexelOffset;
int MaxProgramTexelOffset;
// Extension constants.
// Value of GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT for OpenGL ES output context.
// Value of GL_MAX_DUAL_SOURCE_DRAW_BUFFERS for OpenGL output context.
// GLES SL version 100 gl_MaxDualSourceDrawBuffersEXT value for EXT_blend_func_extended.
int MaxDualSourceDrawBuffers;
// Name Hashing.
// Set a 64 bit hash function to enable user-defined name hashing.
// Default is NULL.
......
......@@ -11,6 +11,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sstream>
#include <vector>
#include "angle_gl.h"
......@@ -39,7 +40,8 @@ typedef std::vector<char *> ShaderSource;
static bool ReadShaderSource(const char *fileName, ShaderSource &source);
static void FreeShaderSource(ShaderSource &source);
static bool ParseGLSLOutputVersion(const char *num, ShShaderOutput *outResult);
static bool ParseGLSLOutputVersion(const std::string &, ShShaderOutput *outResult);
static bool ParseIntValue(const std::string &, int emptyDefault, int *outValue);
//
// Set up the per compile resources
......@@ -56,6 +58,7 @@ void GenerateResources(ShBuiltInResources *resources)
resources->MaxTextureImageUnits = 8;
resources->MaxFragmentUniformVectors = 16;
resources->MaxDrawBuffers = 1;
resources->MaxDualSourceDrawBuffers = 1;
resources->OES_standard_derivatives = 0;
resources->OES_EGL_image_external = 0;
......@@ -165,6 +168,31 @@ int main(int argc, char *argv[])
case 'i': resources.OES_EGL_image_external = 1; break;
case 'd': resources.OES_standard_derivatives = 1; break;
case 'r': resources.ARB_texture_rectangle = 1; break;
case 'b':
if (ParseIntValue(&argv[0][sizeof("-x=b") - 1], 1,
&resources.MaxDualSourceDrawBuffers))
{
resources.EXT_blend_func_extended = 1;
}
else
{
failCode = EFailUsage;
}
break;
case 'w':
if (ParseIntValue(&argv[0][sizeof("-x=w") - 1], 1,
&resources.MaxDrawBuffers))
{
resources.EXT_draw_buffers = 1;
}
else
{
failCode = EFailUsage;
}
break;
case 'g':
resources.EXT_frag_depth = 1;
break;
case 'l': resources.EXT_shader_texture_lod = 1; break;
case 'f': resources.EXT_shader_framebuffer_fetch = 1; break;
case 'n': resources.NV_shader_framebuffer_fetch = 1; break;
......@@ -283,6 +311,8 @@ void usage()
" -x=i : enable GL_OES_EGL_image_external\n"
" -x=d : enable GL_OES_EGL_standard_derivatives\n"
" -x=r : enable ARB_texture_rectangle\n"
" -x=b[NUM]: enable EXT_blend_func_extended (NUM default 1)\n"
" -x=w[NUM]: enable EXT_draw_buffers (NUM default 1)\n"
" -x=l : enable EXT_shader_texture_lod\n"
" -x=f : enable EXT_shader_framebuffer_fetch\n"
" -x=n : enable NV_shader_framebuffer_fetch\n"
......@@ -477,14 +507,20 @@ static void FreeShaderSource(ShaderSource &source)
source.clear();
}
static bool ParseGLSLOutputVersion(const char *num, ShShaderOutput *outResult)
static bool ParseGLSLOutputVersion(const std::string &num, ShShaderOutput *outResult)
{
if (*num == '\0')
if (num.length() == 0)
{
*outResult = SH_GLSL_COMPATIBILITY_OUTPUT;
return true;
}
long value = strtol(num, NULL, 10);
std::istringstream input(num);
int value;
if (!(input >> value && input.eof()))
{
return false;
}
switch (value)
{
case 130:
......@@ -522,3 +558,21 @@ static bool ParseGLSLOutputVersion(const char *num, ShShaderOutput *outResult)
}
return false;
}
static bool ParseIntValue(const std::string &num, int emptyDefault, int *outValue)
{
if (num.length() == 0)
{
*outValue = emptyDefault;
return true;
}
std::istringstream input(num);
int value;
if (!(input >> value && input.eof()))
{
return false;
}
*outValue = value;
return true;
}
......@@ -290,18 +290,18 @@ inline bool SupportsPrecision(TBasicType type)
//
enum TQualifier
{
EvqTemporary, // For temporaries (within a function), read/write
EvqGlobal, // For globals read/write
EvqConst, // User defined constants and non-output parameters in functions
EvqAttribute, // Readonly
EvqVaryingIn, // readonly, fragment shaders only
EvqVaryingOut, // vertex shaders only read/write
EvqUniform, // Readonly, vertex and fragment
EvqVertexIn, // Vertex shader input
EvqFragmentOut, // Fragment shader output
EvqVertexOut, // Vertex shader output
EvqFragmentIn, // Fragment shader input
EvqTemporary, // For temporaries (within a function), read/write
EvqGlobal, // For globals read/write
EvqConst, // User defined constants and non-output parameters in functions
EvqAttribute, // Readonly
EvqVaryingIn, // readonly, fragment shaders only
EvqVaryingOut, // vertex shaders only read/write
EvqUniform, // Readonly, vertex and fragment
EvqVertexIn, // Vertex shader input
EvqFragmentOut, // Fragment shader output
EvqVertexOut, // Vertex shader output
EvqFragmentIn, // Fragment shader input
// parameters
EvqIn,
......@@ -325,20 +325,22 @@ enum TQualifier
EvqFragColor,
EvqFragData,
EvqFragDepth,
EvqSecondaryFragColorEXT, // EXT_blend_func_extended
EvqSecondaryFragDataEXT, // EXT_blend_func_extended
// built-ins written by the shader_framebuffer_fetch extension(s)
EvqLastFragColor,
EvqLastFragData,
// GLSL ES 3.0 vertex output and fragment input
EvqSmooth, // Incomplete qualifier, smooth is the default
EvqFlat, // Incomplete qualifier
EvqSmooth, // Incomplete qualifier, smooth is the default
EvqFlat, // Incomplete qualifier
EvqSmoothOut = EvqSmooth,
EvqFlatOut = EvqFlat,
EvqCentroidOut, // Implies smooth
EvqFlatOut = EvqFlat,
EvqCentroidOut, // Implies smooth
EvqSmoothIn,
EvqFlatIn,
EvqCentroidIn, // Implies smooth
EvqCentroidIn, // Implies smooth
// end of list
EvqLast
......@@ -413,6 +415,12 @@ inline const char* getQualifierString(TQualifier q)
case EvqFragColor: return "FragColor"; break;
case EvqFragData: return "FragData"; break;
case EvqFragDepth: return "FragDepth"; break;
case EvqSecondaryFragColorEXT:
return "SecondaryFragColorEXT";
break;
case EvqSecondaryFragDataEXT:
return "SecondaryFragDataEXT";
break;
case EvqLastFragColor: return "LastFragColor"; break;
case EvqLastFragData: return "LastFragData"; break;
case EvqSmoothOut: return "smooth out"; break;
......
......@@ -461,6 +461,7 @@ void TCompiler::setResourceString()
<< ":FragmentPrecisionHigh:" << compileResources.FragmentPrecisionHigh
<< ":MaxExpressionComplexity:" << compileResources.MaxExpressionComplexity
<< ":MaxCallStackDepth:" << compileResources.MaxCallStackDepth
<< ":EXT_blend_func_extended:" << compileResources.EXT_blend_func_extended
<< ":EXT_frag_depth:" << compileResources.EXT_frag_depth
<< ":EXT_shader_texture_lod:" << compileResources.EXT_shader_texture_lod
<< ":EXT_shader_framebuffer_fetch:" << compileResources.EXT_shader_framebuffer_fetch
......@@ -470,6 +471,7 @@ void TCompiler::setResourceString()
<< ":MaxFragmentInputVectors:" << compileResources.MaxFragmentInputVectors
<< ":MinProgramTexelOffset:" << compileResources.MinProgramTexelOffset
<< ":MaxProgramTexelOffset:" << compileResources.MaxProgramTexelOffset
<< ":MaxDualSourceDrawBuffers:" << compileResources.MaxDualSourceDrawBuffers
<< ":NV_draw_buffers:" << compileResources.NV_draw_buffers
<< ":WEBGL_debug_shader_precision:" << compileResources.WEBGL_debug_shader_precision;
......@@ -660,9 +662,9 @@ bool TCompiler::pruneUnusedFunctions(TIntermNode *root)
bool TCompiler::validateOutputs(TIntermNode* root)
{
ValidateOutputs validateOutputs(infoSink.info, compileResources.MaxDrawBuffers);
ValidateOutputs validateOutputs(getExtensionBehavior(), compileResources.MaxDrawBuffers);
root->traverse(&validateOutputs);
return (validateOutputs.numErrors() == 0);
return (validateOutputs.validateAndCountErrors(infoSink.info) == 0);
}
void TCompiler::rewriteCSSShader(TIntermNode* root)
......
......@@ -34,4 +34,10 @@ inline const char* getBehaviorString(TBehavior b)
// Mapping between extension name and behavior.
typedef std::map<std::string, TBehavior> TExtensionBehavior;
inline bool IsExtensionEnabled(const TExtensionBehavior &extBehavior, const char *extension)
{
auto iter = extBehavior.find(extension);
return iter != extBehavior.end() && (iter->second == EBhEnable || iter->second == EBhRequire);
}
#endif // COMPILER_TRANSLATOR_EXTENSIONBEHAVIOR_H_
......@@ -464,6 +464,12 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR
if (spec != SH_CSS_SHADERS_SPEC)
{
symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxDrawBuffers", resources.MaxDrawBuffers);
if (resources.EXT_blend_func_extended)
{
symbolTable.insertConstIntExt(COMMON_BUILTINS, "GL_EXT_blend_func_extended",
"gl_MaxDualSourceDrawBuffersEXT",
resources.MaxDualSourceDrawBuffers);
}
}
symbolTable.insertConstInt(ESSL3_BUILTINS, "gl_MaxVertexOutputVectors", resources.MaxVertexOutputVectors);
......@@ -502,6 +508,19 @@ void IdentifyBuiltIns(sh::GLenum type, ShShaderSpec spec,
fragData.setArraySize(resources.MaxDrawBuffers);
symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("gl_FragData"), fragData));
if (resources.EXT_blend_func_extended)
{
symbolTable.insert(
ESSL1_BUILTINS, "GL_EXT_blend_func_extended",
new TVariable(NewPoolTString("gl_SecondaryFragColorEXT"),
TType(EbtFloat, EbpMedium, EvqSecondaryFragColorEXT, 4)));
TType secondaryFragData(EbtFloat, EbpMedium, EvqSecondaryFragDataEXT, 4, 1, true);
secondaryFragData.setArraySize(resources.MaxDualSourceDrawBuffers);
symbolTable.insert(
ESSL1_BUILTINS, "GL_EXT_blend_func_extended",
new TVariable(NewPoolTString("gl_SecondaryFragDataEXT"), secondaryFragData));
}
if (resources.EXT_frag_depth)
{
symbolTable.insert(ESSL1_BUILTINS, "GL_EXT_frag_depth", new TVariable(NewPoolTString("gl_FragDepthEXT"),
......@@ -567,6 +586,8 @@ void InitExtensionBehavior(const ShBuiltInResources& resources,
extBehavior["GL_OES_EGL_image_external"] = EBhUndefined;
if (resources.ARB_texture_rectangle)
extBehavior["GL_ARB_texture_rectangle"] = EBhUndefined;
if (resources.EXT_blend_func_extended)
extBehavior["GL_EXT_blend_func_extended"] = EBhUndefined;
if (resources.EXT_draw_buffers)
extBehavior["GL_EXT_draw_buffers"] = EBhUndefined;
if (resources.EXT_frag_depth)
......
......@@ -45,6 +45,14 @@ void TOutputGLSL::visitSymbol(TIntermSymbol *node)
{
out << "webgl_FragData";
}
else if (symbol == "gl_SecondaryFragColorEXT")
{
out << "angle_SecondaryFragColor";
}
else if (symbol == "gl_SecondaryFragDataEXT")
{
out << "angle_SecondaryFragData";
}
else
{
TOutputGLSLBase::visitSymbol(node);
......
......@@ -1143,15 +1143,7 @@ bool TParseContext::supportsExtension(const char *extension)
bool TParseContext::isExtensionEnabled(const char *extension) const
{
const TExtensionBehavior &extbehavior = extensionBehavior();
TExtensionBehavior::const_iterator iter = extbehavior.find(extension);
if (iter == extbehavior.end())
{
return false;
}
return (iter->second == EBhEnable || iter->second == EBhRequire);
return ::IsExtensionEnabled(extensionBehavior(), extension);
}
void TParseContext::handleExtensionDirective(const TSourceLoc &loc,
......@@ -1210,14 +1202,18 @@ const TVariable *TParseContext::getNamedVariable(const TSourceLoc &location,
// Reject shaders using both gl_FragData and gl_FragColor
TQualifier qualifier = variable->getType().getQualifier();
if (qualifier == EvqFragData)
if (qualifier == EvqFragData || qualifier == EvqSecondaryFragDataEXT)
{
mUsesFragData = true;
}
else if (qualifier == EvqFragColor)
else if (qualifier == EvqFragColor || qualifier == EvqSecondaryFragColorEXT)
{
mUsesFragColor = true;
}
if (qualifier == EvqSecondaryFragDataEXT || qualifier == EvqSecondaryFragColorEXT)
{
mUsesSecondaryOutputs = true;
}
// This validation is not quite correct - it's only an error to write to
// both FragData and FragColor. For simplicity, and because users shouldn't
......@@ -1225,7 +1221,14 @@ const TVariable *TParseContext::getNamedVariable(const TSourceLoc &location,
// if they are both referenced, rather than assigned.
if (mUsesFragData && mUsesFragColor)
{
error(location, "cannot use both gl_FragData and gl_FragColor", name->c_str());
const char *errorMessage = "cannot use both gl_FragData and gl_FragColor";
if (mUsesSecondaryOutputs)
{
errorMessage =
"cannot use both output variable sets (gl_FragData, gl_SecondaryFragDataEXT)"
" and (gl_FragColor, gl_SecondaryFragColorEXT)";
}
error(location, errorMessage, name->c_str());
recover();
}
}
......
......@@ -58,7 +58,8 @@ class TParseContext : angle::NonCopyable
mPreprocessor(&mDiagnostics, &mDirectiveHandler),
mScanner(nullptr),
mUsesFragData(false),
mUsesFragColor(false)
mUsesFragColor(false),
mUsesSecondaryOutputs(false)
{
}
......@@ -352,6 +353,8 @@ class TParseContext : angle::NonCopyable
void *mScanner;
bool mUsesFragData; // track if we are using both gl_FragData and gl_FragColor
bool mUsesFragColor;
bool mUsesSecondaryOutputs; // Track if we are using either gl_SecondaryFragData or
// gl_Secondary FragColor or both.
};
int PaParseStrings(
......
......@@ -154,6 +154,7 @@ void ShInitBuiltInResources(ShBuiltInResources* resources)
resources->OES_standard_derivatives = 0;
resources->OES_EGL_image_external = 0;
resources->ARB_texture_rectangle = 0;
resources->EXT_blend_func_extended = 0;
resources->EXT_draw_buffers = 0;
resources->EXT_frag_depth = 0;
resources->EXT_shader_texture_lod = 0;
......@@ -173,6 +174,9 @@ void ShInitBuiltInResources(ShBuiltInResources* resources)
resources->MinProgramTexelOffset = -8;
resources->MaxProgramTexelOffset = 7;
// Extensions constants.
resources->MaxDualSourceDrawBuffers = 0;
// Disable name hashing by default.
resources->HashFunction = NULL;
......
......@@ -412,6 +412,14 @@ class TSymbolTable : angle::NonCopyable
return insert(level, constant);
}
bool insertConstIntExt(ESymbolLevel level, const char *ext, const char *name, int value)
{
TVariable *constant =
new TVariable(NewPoolTString(name), TType(EbtInt, EbpUndefined, EvqConst, 1));
constant->getConstPointer()->setIConst(value);
return insert(level, ext, constant);
}
void insertBuiltIn(ESymbolLevel level, TOperator op, const char *ext, const TType *rvalue, const char *name,
const TType *ptype1, const TType *ptype2 = 0, const TType *ptype3 = 0, const TType *ptype4 = 0, const TType *ptype5 = 0);
......
......@@ -64,30 +64,70 @@ void TranslatorGLSL::translate(TIntermNode *root, int) {
// Declare gl_FragColor and glFragData as webgl_FragColor and webgl_FragData
// if it's core profile shaders and they are used.
if (getShaderType() == GL_FRAGMENT_SHADER && IsGLSL130OrNewer(getOutputType()))
if (getShaderType() == GL_FRAGMENT_SHADER)
{
bool usesGLFragColor = false;
bool usesGLFragData = false;
for (auto outputVar : outputVariables)
const bool mayHaveESSL1SecondaryOutputs =
IsExtensionEnabled(getExtensionBehavior(), "GL_EXT_blend_func_extended") &&
getShaderVersion() == 100;
const bool declareGLFragmentOutputs = IsGLSL130OrNewer(getOutputType());
bool hasGLFragColor = false;
bool hasGLFragData = false;
bool hasGLSecondaryFragColor = false;
bool hasGLSecondaryFragData = false;
for (const auto &outputVar : outputVariables)
{
if (outputVar.name == "gl_FragColor")
if (declareGLFragmentOutputs)
{
usesGLFragColor = true;
if (outputVar.name == "gl_FragColor")
{
ASSERT(!hasGLFragColor);
hasGLFragColor = true;
continue;
}
else if (outputVar.name == "gl_FragData")
{
ASSERT(!hasGLFragData);
hasGLFragData = true;
continue;
}
}
else if (outputVar.name == "gl_FragData")
if (mayHaveESSL1SecondaryOutputs)
{
usesGLFragData = true;
if (outputVar.name == "gl_SecondaryFragColorEXT")
{
ASSERT(!hasGLSecondaryFragColor);
hasGLSecondaryFragColor = true;
continue;
}
else if (outputVar.name == "gl_SecondaryFragDataEXT")
{
ASSERT(!hasGLSecondaryFragData);
hasGLSecondaryFragData = true;
continue;
}
}
}
ASSERT(!(usesGLFragColor && usesGLFragData));
if (usesGLFragColor)
ASSERT(!((hasGLFragColor || hasGLSecondaryFragColor) &&
(hasGLFragData || hasGLSecondaryFragData)));
if (hasGLFragColor)
{
sink << "out vec4 webgl_FragColor;\n";
}
if (usesGLFragData)
if (hasGLFragData)
{
sink << "out vec4 webgl_FragData[gl_MaxDrawBuffers];\n";
}
if (hasGLSecondaryFragColor)
{
sink << "out vec4 angle_SecondaryFragColor;\n";
}
if (hasGLSecondaryFragData)
{
sink << "out vec4 angle_SecondaryFragData[" << getResources().MaxDualSourceDrawBuffers
<< "];\n";
}
}
// Write translated shader.
......
......@@ -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 @@ void ValidateOutputs::visitSymbol(TIntermSymbol *symbol)
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());
mUnspecifiedLocationOutputs.push_back(symbol);
}
else if (isUnspecifiedOutputLocation)
else
{
mHasUnspecifiedOutputLocation = true;
mOutputs.push_back(symbol);
}
else
}
}
int ValidateOutputs::validateAndCountErrors(TInfoSinkBase &sink) const
{
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())
{
OutputMap::iterator mapEntry = mOutputMap.find(location);
if (mapEntry == mOutputMap.end())
for (size_t elementIndex = 0; elementIndex < elementCount; elementIndex++)
{
const int elementCount = type.isArray() ? type.getArraySize() : 1;
if (location + elementCount > mMaxDrawBuffers)
const size_t offsetLocation = location + elementIndex;
if (validOutputs[offsetLocation])
{
error(symbol->getLine(), "output location must be < MAX_DRAW_BUFFERS", name.c_str());
std::stringstream strstr;
strstr << "conflicting output locations with previously defined output '"
<< validOutputs[offsetLocation]->getSymbol() << "'";
error(&errorCount, sink, *symbol, strstr.str().c_str());
}
for (int elementIndex = 0; elementIndex < elementCount; elementIndex++)
else
{
const int offsetLocation = location + elementIndex;
mOutputMap[offsetLocation] = symbol;
validOutputs[offsetLocation] = symbol;
}
}
else
}
else
{
if (elementCount > 0)
{
std::stringstream strstr;
strstr << "conflicting output locations with previously defined output '"
<< mapEntry->second->getSymbol() << "'";
error(symbol->getLine(), strstr.str().c_str(), name.c_str());
error(&errorCount, sink, *symbol,
elementCount > 1 ? "output array locations would exceed MAX_DRAW_BUFFERS"
: "output location must be < MAX_DRAW_BUFFERS");
}
}
}
}
void ValidateOutputs::error(TSourceLoc loc, const char *reason, const char* token)
{
mSink.prefix(EPrefixError);
mSink.location(loc);
mSink << "'" << token << "' : " << reason << "\n";
mNumErrors++;
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;
}
......@@ -7,6 +7,7 @@
#ifndef COMPILER_TRANSLATOR_VALIDATEOUTPUTS_H_
#define COMPILER_TRANSLATOR_VALIDATEOUTPUTS_H_
#include "compiler/translator/ExtensionBehavior.h"
#include "compiler/translator/IntermNode.h"
#include <set>
......@@ -16,23 +17,20 @@ class TInfoSinkBase;
class ValidateOutputs : public TIntermTraverser
{
public:
ValidateOutputs(TInfoSinkBase& sink, int maxDrawBuffers);
ValidateOutputs(const TExtensionBehavior &extBehavior, int maxDrawBuffers);
int numErrors() const { return mNumErrors; }
int validateAndCountErrors(TInfoSinkBase &sink) const;
virtual void visitSymbol(TIntermSymbol*);
private:
TInfoSinkBase& mSink;
int mMaxDrawBuffers;
int mNumErrors;
bool mHasUnspecifiedOutputLocation;
bool mAllowUnspecifiedOutputLocationResolution;
typedef std::map<int, TIntermSymbol*> OutputMap;
OutputMap mOutputMap;
typedef std::vector<TIntermSymbol *> OutputVector;
OutputVector mOutputs;
OutputVector mUnspecifiedLocationOutputs;
std::set<TString> mVisitedSymbols;
void error(TSourceLoc loc, const char *reason, const char* token);
};
#endif // COMPILER_TRANSLATOR_VALIDATEOUTPUTS_H_
......@@ -151,6 +151,8 @@ CollectVariables::CollectVariables(std::vector<sh::Attribute> *attribs,
mFragColorAdded(false),
mFragDataAdded(false),
mFragDepthAdded(false),
mSecondaryFragColorEXTAdded(false),
mSecondaryFragDataEXTAdded(false),
mHashFunction(hashFunction),
mSymbolTable(symbolTable)
{
......@@ -420,6 +422,39 @@ void CollectVariables::visitSymbol(TIntermSymbol *symbol)
mFragDepthAdded = true;
}
return;
case EvqSecondaryFragColorEXT:
if (!mSecondaryFragColorEXTAdded)
{
Attribute info;
const char kName[] = "gl_SecondaryFragColorEXT";
info.name = kName;
info.mappedName = kName;
info.type = GL_FLOAT_VEC4;
info.arraySize = 0;
info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
info.staticUse = true;
mOutputVariables->push_back(info);
mSecondaryFragColorEXTAdded = true;
}
return;
case EvqSecondaryFragDataEXT:
if (!mSecondaryFragDataEXTAdded)
{
Attribute info;
const char kName[] = "gl_SecondaryFragDataEXT";
info.name = kName;
info.mappedName = kName;
info.type = GL_FLOAT_VEC4;
const TVariable *maxDualSourceDrawBuffersVar = static_cast<const TVariable *>(
mSymbolTable.findBuiltIn("gl_MaxDualSourceDrawBuffersEXT", 100));
info.arraySize = maxDualSourceDrawBuffersVar->getConstPointer()->getIConst();
info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
info.staticUse = true;
mOutputVariables->push_back(info);
mSecondaryFragDataEXTAdded = true;
}
return;
default:
break;
}
......
......@@ -59,6 +59,8 @@ class CollectVariables : public TIntermTraverser
bool mFragColorAdded;
bool mFragDataAdded;
bool mFragDepthAdded;
bool mSecondaryFragColorEXTAdded;
bool mSecondaryFragDataEXTAdded;
ShHashFunction64 mHashFunction;
......
......@@ -42,6 +42,7 @@
'<(angle_path)/src/tests/compiler_tests/ConstantFolding_test.cpp',
'<(angle_path)/src/tests/compiler_tests/DebugShaderPrecision_test.cpp',
'<(angle_path)/src/tests/compiler_tests/ExpressionLimit_test.cpp',
'<(angle_path)/src/tests/compiler_tests/EXT_blend_func_extended_test.cpp',
'<(angle_path)/src/tests/compiler_tests/MalformedShader_test.cpp',
'<(angle_path)/src/tests/compiler_tests/NV_draw_buffers_test.cpp',
'<(angle_path)/src/tests/compiler_tests/Pack_Unpack_test.cpp',
......
......@@ -96,6 +96,7 @@ class CollectVariablesTest : public testing::Test
// For use in tests for output varibles.
void validateOutputVariableForShader(const std::string &shaderString,
unsigned int varIndex,
const char *varName,
const sh::Attribute **outResult)
{
......@@ -104,8 +105,8 @@ class CollectVariablesTest : public testing::Test
<< mTranslator->getInfoSink().info.str();
const std::vector<sh::Attribute> &outputVariables = mTranslator->getOutputVariables();
ASSERT_LT(0u, outputVariables.size());
const sh::Attribute &outputVariable = outputVariables[0];
ASSERT_LT(varIndex, outputVariables.size());
const sh::Attribute &outputVariable = outputVariables[varIndex];
EXPECT_EQ(-1, outputVariable.location);
EXPECT_TRUE(outputVariable.staticUse);
EXPECT_EQ(varName, outputVariable.name);
......@@ -478,7 +479,7 @@ TEST_F(CollectFragmentVariablesTest, OutputVarESSL1FragColor)
"}\n";
const sh::Attribute *outputVariable = nullptr;
validateOutputVariableForShader(fragColorShader, "gl_FragColor", &outputVariable);
validateOutputVariableForShader(fragColorShader, 0u, "gl_FragColor", &outputVariable);
ASSERT_NE(outputVariable, nullptr);
EXPECT_EQ(0u, outputVariable->arraySize);
EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable->type);
......@@ -504,7 +505,7 @@ TEST_F(CollectFragmentVariablesTest, OutputVarESSL1FragData)
initTranslator(resources);
const sh::Attribute *outputVariable = nullptr;
validateOutputVariableForShader(fragDataShader, "gl_FragData", &outputVariable);
validateOutputVariableForShader(fragDataShader, 0u, "gl_FragData", &outputVariable);
ASSERT_NE(outputVariable, nullptr);
EXPECT_EQ(kMaxDrawBuffers, outputVariable->arraySize);
EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable->type);
......@@ -527,7 +528,7 @@ TEST_F(CollectFragmentVariablesTest, OutputVarESSL1FragDepthMediump)
initTranslator(resources);
const sh::Attribute *outputVariable = nullptr;
validateOutputVariableForShader(fragDepthShader, "gl_FragDepthEXT", &outputVariable);
validateOutputVariableForShader(fragDepthShader, 0u, "gl_FragDepthEXT", &outputVariable);
ASSERT_NE(outputVariable, nullptr);
EXPECT_EQ(0u, outputVariable->arraySize);
EXPECT_GLENUM_EQ(GL_FLOAT, outputVariable->type);
......@@ -550,9 +551,82 @@ TEST_F(CollectFragmentVariablesTest, OutputVarESSL1FragDepthHighp)
initTranslator(resources);
const sh::Attribute *outputVariable = nullptr;
validateOutputVariableForShader(fragDepthHighShader, "gl_FragDepthEXT", &outputVariable);
validateOutputVariableForShader(fragDepthHighShader, 0u, "gl_FragDepthEXT", &outputVariable);
ASSERT_NE(outputVariable, nullptr);
EXPECT_EQ(0u, outputVariable->arraySize);
EXPECT_GLENUM_EQ(GL_FLOAT, outputVariable->type);
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, outputVariable->precision);
}
// Test that gl_SecondaryFragColorEXT built-in usage in ESSL1 fragment shader is reflected in the
// output variables list.
TEST_F(CollectFragmentVariablesTest, OutputVarESSL1EXTBlendFuncExtendedSecondaryFragColor)
{
const char *secondaryFragColorShader =
"#extension GL_EXT_blend_func_extended : require\n"
"precision mediump float;\n"
"void main() {\n"
" gl_FragColor = vec4(1.0);\n"
" gl_SecondaryFragColorEXT = vec4(1.0);\n"
"}\n";
const unsigned int kMaxDrawBuffers = 3u;
ShBuiltInResources resources = mTranslator->getResources();
resources.EXT_blend_func_extended = 1;
resources.EXT_draw_buffers = 1;
resources.MaxDrawBuffers = kMaxDrawBuffers;
resources.MaxDualSourceDrawBuffers = resources.MaxDrawBuffers;
initTranslator(resources);
const sh::Attribute *outputVariable = nullptr;
validateOutputVariableForShader(secondaryFragColorShader, 0u, "gl_FragColor", &outputVariable);
ASSERT_NE(outputVariable, nullptr);
EXPECT_EQ(0u, outputVariable->arraySize);
EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable->type);
EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable->precision);
outputVariable = nullptr;
validateOutputVariableForShader(secondaryFragColorShader, 1u, "gl_SecondaryFragColorEXT",
&outputVariable);
ASSERT_NE(outputVariable, nullptr);
EXPECT_EQ(0u, outputVariable->arraySize);
EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable->type);
EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable->precision);
}
// Test that gl_SecondaryFragDataEXT built-in usage in ESSL1 fragment shader is reflected in the
// output variables list.
TEST_F(CollectFragmentVariablesTest, OutputVarESSL1EXTBlendFuncExtendedSecondaryFragData)
{
const char *secondaryFragDataShader =
"#extension GL_EXT_blend_func_extended : require\n"
"#extension GL_EXT_draw_buffers : require\n"
"precision mediump float;\n"
"void main() {\n"
" gl_FragData[0] = vec4(1.0);\n"
" gl_FragData[1] = vec4(0.5);\n"
" gl_SecondaryFragDataEXT[0] = vec4(1.0);\n"
" gl_SecondaryFragDataEXT[1] = vec4(0.8);\n"
"}\n";
const unsigned int kMaxDrawBuffers = 3u;
ShBuiltInResources resources = mTranslator->getResources();
resources.EXT_blend_func_extended = 1;
resources.EXT_draw_buffers = 1;
resources.MaxDrawBuffers = kMaxDrawBuffers;
resources.MaxDualSourceDrawBuffers = resources.MaxDrawBuffers;
initTranslator(resources);
const sh::Attribute *outputVariable = nullptr;
validateOutputVariableForShader(secondaryFragDataShader, 0u, "gl_FragData", &outputVariable);
ASSERT_NE(outputVariable, nullptr);
EXPECT_EQ(kMaxDrawBuffers, outputVariable->arraySize);
EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable->type);
EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable->precision);
outputVariable = nullptr;
validateOutputVariableForShader(secondaryFragDataShader, 1u, "gl_SecondaryFragDataEXT",
&outputVariable);
ASSERT_NE(outputVariable, nullptr);
EXPECT_EQ(kMaxDrawBuffers, outputVariable->arraySize);
EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable->type);
EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable->precision);
}
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