You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1827 lines
53 KiB
1827 lines
53 KiB
/*-------------------------------------------------------------------------
|
|
* drawElements Quality Program OpenGL ES Utilities
|
|
* ------------------------------------------------
|
|
*
|
|
* Copyright 2015 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*//*!
|
|
* \file
|
|
* \brief Shader .test file utilities.
|
|
*//*--------------------------------------------------------------------*/
|
|
|
|
#include "gluShaderLibrary.hpp"
|
|
|
|
#include "tcuStringTemplate.hpp"
|
|
#include "tcuResource.hpp"
|
|
#include "tcuTestLog.hpp"
|
|
|
|
#include "deStringUtil.hpp"
|
|
#include "deUniquePtr.hpp"
|
|
#include "deFilePath.hpp"
|
|
|
|
#include "glwEnums.hpp"
|
|
|
|
#include <sstream>
|
|
#include <map>
|
|
#include <cstdlib>
|
|
|
|
#if 0
|
|
# define PARSE_DBG(X) printf X
|
|
#else
|
|
# define PARSE_DBG(X) DE_NULL_STATEMENT
|
|
#endif
|
|
|
|
namespace glu
|
|
{
|
|
namespace sl
|
|
{
|
|
|
|
using namespace tcu;
|
|
|
|
using std::vector;
|
|
using std::string;
|
|
using std::map;
|
|
using std::ostringstream;
|
|
using std::pair;
|
|
using de::UniquePtr;
|
|
|
|
// Specification
|
|
|
|
bool isValid (const ValueBlock& block)
|
|
{
|
|
for (size_t storageNdx = 0; storageNdx < 3; ++storageNdx)
|
|
{
|
|
const vector<Value>& values = storageNdx == 0 ? block.inputs :
|
|
storageNdx == 1 ? block.outputs :
|
|
block.uniforms;
|
|
const size_t refArrayLen = values.empty() ? 0 : (values[0].elements.size() / (size_t)values[0].type.getScalarSize());
|
|
|
|
for (size_t valNdx = 0; valNdx < values.size(); ++valNdx)
|
|
{
|
|
const Value& value = values[valNdx];
|
|
|
|
if (!value.type.isBasicType())
|
|
{
|
|
print("ERROR: Value '%s' is of unsupported type!\n", value.name.c_str());
|
|
return false;
|
|
}
|
|
|
|
if (value.elements.size() != refArrayLen*(size_t)value.type.getScalarSize())
|
|
{
|
|
print("ERROR: Value '%s' has invalid number of scalars!\n", value.name.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool isValid (const ShaderCaseSpecification& spec)
|
|
{
|
|
const deUint32 vtxFragMask = (1u << SHADERTYPE_VERTEX)
|
|
| (1u << SHADERTYPE_FRAGMENT);
|
|
const deUint32 tessCtrlEvalMask = (1u << SHADERTYPE_TESSELLATION_CONTROL)
|
|
| (1u << SHADERTYPE_TESSELLATION_EVALUATION);
|
|
const deUint32 supportedStageMask = vtxFragMask | tessCtrlEvalMask
|
|
| (1u << SHADERTYPE_GEOMETRY);
|
|
const bool isSeparable = !spec.programs.empty() && spec.programs[0].sources.separable;
|
|
|
|
if (spec.programs.empty())
|
|
{
|
|
print("ERROR: No programs specified!\n");
|
|
return false;
|
|
}
|
|
|
|
if (isCapabilityRequired(CAPABILITY_FULL_GLSL_ES_100_SUPPORT, spec))
|
|
{
|
|
if (spec.targetVersion != GLSL_VERSION_100_ES)
|
|
{
|
|
print("ERROR: Full GLSL ES 1.00 support requested for other GLSL version!\n");
|
|
return false;
|
|
}
|
|
|
|
if (spec.expectResult != EXPECT_PASS &&
|
|
spec.expectResult != EXPECT_VALIDATION_FAIL &&
|
|
spec.expectResult != EXPECT_BUILD_SUCCESSFUL)
|
|
{
|
|
print("ERROR: Full GLSL ES 1.00 support doesn't make sense when expecting compile/link failure!\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!de::inBounds(spec.caseType, (CaseType)0, CASETYPE_LAST))
|
|
{
|
|
print("ERROR: Invalid case type!\n");
|
|
return false;
|
|
}
|
|
|
|
if (!de::inBounds(spec.expectResult, (ExpectResult)0, EXPECT_LAST))
|
|
{
|
|
print("ERROR: Invalid expected result!\n");
|
|
return false;
|
|
}
|
|
|
|
if (!isValid(spec.values))
|
|
return false;
|
|
|
|
if (!spec.values.inputs.empty() && !spec.values.outputs.empty() &&
|
|
spec.values.inputs[0].elements.size() / spec.values.inputs[0].type.getScalarSize() != spec.values.outputs[0].elements.size() / spec.values.outputs[0].type.getScalarSize())
|
|
{
|
|
print("ERROR: Number of input and output elements don't match!\n");
|
|
return false;
|
|
}
|
|
|
|
if (isSeparable)
|
|
{
|
|
deUint32 usedStageMask = 0u;
|
|
|
|
if (spec.caseType != CASETYPE_COMPLETE)
|
|
{
|
|
print("ERROR: Separable shaders supported only for complete cases!\n");
|
|
return false;
|
|
}
|
|
|
|
for (size_t progNdx = 0; progNdx < spec.programs.size(); ++progNdx)
|
|
{
|
|
for (int shaderStageNdx = 0; shaderStageNdx < SHADERTYPE_LAST; ++shaderStageNdx)
|
|
{
|
|
const deUint32 curStageMask = (1u << shaderStageNdx);
|
|
|
|
if (supportedStageMask & curStageMask)
|
|
{
|
|
const bool hasShader = !spec.programs[progNdx].sources.sources[shaderStageNdx].empty();
|
|
const bool isEnabled = (spec.programs[progNdx].activeStages & curStageMask) != 0;
|
|
|
|
if (hasShader != isEnabled)
|
|
{
|
|
print("ERROR: Inconsistent source/enable for shader stage %s!\n", getShaderTypeName((ShaderType)shaderStageNdx));
|
|
return false;
|
|
}
|
|
|
|
if (hasShader && (usedStageMask & curStageMask) != 0)
|
|
{
|
|
print("ERROR: Stage %s enabled on multiple programs!\n", getShaderTypeName((ShaderType)shaderStageNdx));
|
|
return false;
|
|
}
|
|
|
|
if (isEnabled)
|
|
usedStageMask |= curStageMask;
|
|
}
|
|
else if (!spec.programs[progNdx].sources.sources[shaderStageNdx].empty())
|
|
{
|
|
print("ERROR: Source specified for unsupported shader stage %s!\n", getShaderTypeName((ShaderType)shaderStageNdx));
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((usedStageMask & vtxFragMask) != vtxFragMask)
|
|
{
|
|
print("ERROR: Vertex and fragment shaders are mandatory!\n");
|
|
return false;
|
|
}
|
|
|
|
if ((usedStageMask & tessCtrlEvalMask) != 0 && (usedStageMask & tessCtrlEvalMask) != tessCtrlEvalMask)
|
|
{
|
|
print("ERROR: Both tessellation control and eval shaders must be either enabled or disabled!\n");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const bool hasVertex = !spec.programs[0].sources.sources[SHADERTYPE_VERTEX].empty();
|
|
const bool hasFragment = !spec.programs[0].sources.sources[SHADERTYPE_FRAGMENT].empty();
|
|
|
|
if (spec.programs.size() != 1)
|
|
{
|
|
print("ERROR: Only cases using separable programs can have multiple programs!\n");
|
|
return false;
|
|
}
|
|
|
|
if (spec.caseType == CASETYPE_VERTEX_ONLY && (!hasVertex || hasFragment))
|
|
{
|
|
print("ERROR: Vertex-only case must have only vertex shader!\n");
|
|
return false;
|
|
}
|
|
|
|
if (spec.caseType == CASETYPE_FRAGMENT_ONLY && (hasVertex || !hasFragment))
|
|
{
|
|
print("ERROR: Fragment-only case must have only fragment shader!\n");
|
|
return false;
|
|
}
|
|
|
|
if (spec.caseType == CASETYPE_COMPLETE && (!hasVertex || !hasFragment))
|
|
{
|
|
print("ERROR: Complete case must have at least vertex and fragment shaders\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool isCapabilityRequired(CapabilityFlag capabilityFlag, const ShaderCaseSpecification& spec)
|
|
{
|
|
std::vector<RequiredCapability>::const_iterator currRequirement = spec.requiredCaps.begin();
|
|
while (currRequirement != spec.requiredCaps.end())
|
|
{
|
|
if ((currRequirement->type == CAPABILITY_FLAG) && (currRequirement->flagName == capabilityFlag))
|
|
return true;
|
|
++currRequirement;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Parser
|
|
|
|
static const glu::GLSLVersion DEFAULT_GLSL_VERSION = glu::GLSL_VERSION_100_ES;
|
|
|
|
DE_INLINE deBool isWhitespace (char c)
|
|
{
|
|
return (c == ' ') || (c == '\t') || (c == '\r') || (c == '\n');
|
|
}
|
|
|
|
DE_INLINE deBool isEOL (char c)
|
|
{
|
|
return (c == '\r') || (c == '\n');
|
|
}
|
|
|
|
DE_INLINE deBool isNumeric (char c)
|
|
{
|
|
return deInRange32(c, '0', '9');
|
|
}
|
|
|
|
DE_INLINE deBool isAlpha (char c)
|
|
{
|
|
return deInRange32(c, 'a', 'z') || deInRange32(c, 'A', 'Z');
|
|
}
|
|
|
|
DE_INLINE deBool isCaseNameChar (char c)
|
|
{
|
|
return deInRange32(c, 'a', 'z') || deInRange32(c, 'A', 'Z') || deInRange32(c, '0', '9') || (c == '_') || (c == '-') || (c == '.');
|
|
}
|
|
|
|
class ShaderParser
|
|
{
|
|
public:
|
|
ShaderParser (const tcu::Archive& archive, const std::string& filename, ShaderCaseFactory* caseFactory);
|
|
~ShaderParser (void);
|
|
|
|
vector<tcu::TestNode*> parse (void);
|
|
|
|
private:
|
|
enum Token
|
|
{
|
|
TOKEN_INVALID = 0,
|
|
TOKEN_EOF,
|
|
TOKEN_STRING,
|
|
TOKEN_SHADER_SOURCE,
|
|
|
|
TOKEN_INT_LITERAL,
|
|
TOKEN_FLOAT_LITERAL,
|
|
|
|
// identifiers
|
|
TOKEN_IDENTIFIER,
|
|
TOKEN_TRUE,
|
|
TOKEN_FALSE,
|
|
TOKEN_DESC,
|
|
TOKEN_EXPECT,
|
|
TOKEN_GROUP,
|
|
TOKEN_CASE,
|
|
TOKEN_END,
|
|
TOKEN_OUTPUT_COLOR,
|
|
TOKEN_FORMAT,
|
|
TOKEN_VALUES,
|
|
TOKEN_BOTH,
|
|
TOKEN_VERTEX,
|
|
TOKEN_FRAGMENT,
|
|
TOKEN_UNIFORM,
|
|
TOKEN_INPUT,
|
|
TOKEN_OUTPUT,
|
|
TOKEN_FLOAT,
|
|
TOKEN_FLOAT_VEC2,
|
|
TOKEN_FLOAT_VEC3,
|
|
TOKEN_FLOAT_VEC4,
|
|
TOKEN_FLOAT_MAT2,
|
|
TOKEN_FLOAT_MAT2X3,
|
|
TOKEN_FLOAT_MAT2X4,
|
|
TOKEN_FLOAT_MAT3X2,
|
|
TOKEN_FLOAT_MAT3,
|
|
TOKEN_FLOAT_MAT3X4,
|
|
TOKEN_FLOAT_MAT4X2,
|
|
TOKEN_FLOAT_MAT4X3,
|
|
TOKEN_FLOAT_MAT4,
|
|
TOKEN_INT,
|
|
TOKEN_INT_VEC2,
|
|
TOKEN_INT_VEC3,
|
|
TOKEN_INT_VEC4,
|
|
TOKEN_UINT,
|
|
TOKEN_UINT_VEC2,
|
|
TOKEN_UINT_VEC3,
|
|
TOKEN_UINT_VEC4,
|
|
TOKEN_BOOL,
|
|
TOKEN_BOOL_VEC2,
|
|
TOKEN_BOOL_VEC3,
|
|
TOKEN_BOOL_VEC4,
|
|
TOKEN_VERSION,
|
|
TOKEN_TESSELLATION_CONTROL,
|
|
TOKEN_TESSELLATION_EVALUATION,
|
|
TOKEN_GEOMETRY,
|
|
TOKEN_REQUIRE,
|
|
TOKEN_IN,
|
|
TOKEN_IMPORT,
|
|
TOKEN_PIPELINE_PROGRAM,
|
|
TOKEN_ACTIVE_STAGES,
|
|
|
|
// symbols
|
|
TOKEN_ASSIGN,
|
|
TOKEN_PLUS,
|
|
TOKEN_MINUS,
|
|
TOKEN_COMMA,
|
|
TOKEN_VERTICAL_BAR,
|
|
TOKEN_SEMI_COLON,
|
|
TOKEN_LEFT_PAREN,
|
|
TOKEN_RIGHT_PAREN,
|
|
TOKEN_LEFT_BRACKET,
|
|
TOKEN_RIGHT_BRACKET,
|
|
TOKEN_LEFT_BRACE,
|
|
TOKEN_RIGHT_BRACE,
|
|
TOKEN_GREATER,
|
|
|
|
TOKEN_LAST
|
|
};
|
|
|
|
void parseError (const std::string& errorStr);
|
|
float parseFloatLiteral (const char* str);
|
|
int parseIntLiteral (const char* str);
|
|
string parseStringLiteral (const char* str);
|
|
string parseShaderSource (const char* str);
|
|
void advanceToken (void);
|
|
void advanceToken (Token assumed);
|
|
void assumeToken (Token token);
|
|
DataType mapDataTypeToken (Token token);
|
|
const char* getTokenName (Token token);
|
|
deUint32 getShaderStageLiteralFlag (void);
|
|
deUint32 getGLEnumFromName (const std::string& enumName);
|
|
|
|
void parseValueElement (DataType dataType, Value& result);
|
|
void parseValue (ValueBlock& valueBlock);
|
|
void parseValueBlock (ValueBlock& valueBlock);
|
|
deUint32 parseShaderStageList (void);
|
|
void parseRequirement (vector<RequiredCapability> &requiredCaps, vector<RequiredExtension> &requiredExts);
|
|
void parseExpectResult (ExpectResult& expectResult);
|
|
void parseFormat (DataType& format);
|
|
void parseGLSLVersion (glu::GLSLVersion& version);
|
|
void parsePipelineProgram (ProgramSpecification& program);
|
|
void parseShaderCase (vector<tcu::TestNode*>& shaderNodeList);
|
|
void parseShaderGroup (vector<tcu::TestNode*>& shaderNodeList);
|
|
void parseImport (vector<tcu::TestNode*>& shaderNodeList);
|
|
|
|
const tcu::Archive& m_archive;
|
|
const string m_filename;
|
|
ShaderCaseFactory* const m_caseFactory;
|
|
|
|
UniquePtr<tcu::Resource> m_resource;
|
|
vector<char> m_input;
|
|
|
|
const char* m_curPtr;
|
|
Token m_curToken;
|
|
std::string m_curTokenStr;
|
|
};
|
|
|
|
ShaderParser::ShaderParser (const tcu::Archive& archive, const string& filename, ShaderCaseFactory* caseFactroy)
|
|
: m_archive (archive)
|
|
, m_filename (filename)
|
|
, m_caseFactory (caseFactroy)
|
|
, m_resource (archive.getResource(m_filename.c_str()))
|
|
, m_curPtr (DE_NULL)
|
|
, m_curToken (TOKEN_LAST)
|
|
{
|
|
}
|
|
|
|
ShaderParser::~ShaderParser (void)
|
|
{
|
|
}
|
|
|
|
void ShaderParser::parseError (const std::string& errorStr)
|
|
{
|
|
string atStr = string(m_curPtr, 80);
|
|
throw tcu::InternalError((string("Parser error: ") + errorStr + " near '" + atStr + " ...'").c_str(), DE_NULL, __FILE__, __LINE__);
|
|
}
|
|
|
|
float ShaderParser::parseFloatLiteral (const char* str)
|
|
{
|
|
return (float)atof(str);
|
|
}
|
|
|
|
int ShaderParser::parseIntLiteral (const char* str)
|
|
{
|
|
return atoi(str);
|
|
}
|
|
|
|
string ShaderParser::parseStringLiteral (const char* str)
|
|
{
|
|
const char* p = str;
|
|
char endChar = *p++;
|
|
ostringstream o;
|
|
|
|
while (*p != endChar && *p)
|
|
{
|
|
if (*p == '\\')
|
|
{
|
|
switch (p[1])
|
|
{
|
|
case 0: DE_ASSERT(DE_FALSE); break;
|
|
case 'n': o << '\n'; break;
|
|
case 't': o << '\t'; break;
|
|
default: o << p[1]; break;
|
|
}
|
|
|
|
p += 2;
|
|
}
|
|
else
|
|
o << *p++;
|
|
}
|
|
|
|
return o.str();
|
|
}
|
|
|
|
static string removeExtraIndentation (const string& source)
|
|
{
|
|
// Detect indentation from first line.
|
|
int numIndentChars = 0;
|
|
for (int ndx = 0; ndx < (int)source.length() && isWhitespace(source[ndx]); ndx++)
|
|
numIndentChars += source[ndx] == '\t' ? 4 : 1;
|
|
|
|
// Process all lines and remove preceding indentation.
|
|
ostringstream processed;
|
|
{
|
|
bool atLineStart = true;
|
|
int indentCharsOmitted = 0;
|
|
|
|
for (int pos = 0; pos < (int)source.length(); pos++)
|
|
{
|
|
char c = source[pos];
|
|
|
|
if (atLineStart && indentCharsOmitted < numIndentChars && (c == ' ' || c == '\t'))
|
|
{
|
|
indentCharsOmitted += c == '\t' ? 4 : 1;
|
|
}
|
|
else if (isEOL(c))
|
|
{
|
|
if (source[pos] == '\r' && source[pos+1] == '\n')
|
|
{
|
|
pos += 1;
|
|
processed << '\n';
|
|
}
|
|
else
|
|
processed << c;
|
|
|
|
atLineStart = true;
|
|
indentCharsOmitted = 0;
|
|
}
|
|
else
|
|
{
|
|
processed << c;
|
|
atLineStart = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return processed.str();
|
|
}
|
|
|
|
string ShaderParser::parseShaderSource (const char* str)
|
|
{
|
|
const char* p = str+2;
|
|
ostringstream o;
|
|
|
|
// Eat first empty line from beginning.
|
|
while (*p == ' ') p++;
|
|
if (*p == '\r') p++;
|
|
if (*p == '\n') p++;
|
|
|
|
while ((p[0] != '"') || (p[1] != '"'))
|
|
{
|
|
if (*p == '\\')
|
|
{
|
|
switch (p[1])
|
|
{
|
|
case 0: DE_ASSERT(DE_FALSE); break;
|
|
case 'n': o << '\n'; break;
|
|
case 't': o << '\t'; break;
|
|
default: o << p[1]; break;
|
|
}
|
|
|
|
p += 2;
|
|
}
|
|
else
|
|
o << *p++;
|
|
}
|
|
|
|
return removeExtraIndentation(o.str());
|
|
}
|
|
|
|
void ShaderParser::advanceToken (void)
|
|
{
|
|
// Skip old token.
|
|
m_curPtr += m_curTokenStr.length();
|
|
|
|
// Reset token (for safety).
|
|
m_curToken = TOKEN_INVALID;
|
|
m_curTokenStr = "";
|
|
|
|
// Eat whitespace & comments while they last.
|
|
for (;;)
|
|
{
|
|
while (isWhitespace(*m_curPtr))
|
|
m_curPtr++;
|
|
|
|
// Check for EOL comment.
|
|
if (*m_curPtr == '#')
|
|
{
|
|
while (*m_curPtr && !isEOL(*m_curPtr))
|
|
m_curPtr++;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (!*m_curPtr)
|
|
{
|
|
m_curToken = TOKEN_EOF;
|
|
m_curTokenStr = "<EOF>";
|
|
}
|
|
else if (isAlpha(*m_curPtr))
|
|
{
|
|
struct Named
|
|
{
|
|
const char* str;
|
|
Token token;
|
|
};
|
|
|
|
static const Named s_named[] =
|
|
{
|
|
{ "true", TOKEN_TRUE },
|
|
{ "false", TOKEN_FALSE },
|
|
{ "desc", TOKEN_DESC },
|
|
{ "expect", TOKEN_EXPECT },
|
|
{ "group", TOKEN_GROUP },
|
|
{ "case", TOKEN_CASE },
|
|
{ "end", TOKEN_END },
|
|
{ "output_color", TOKEN_OUTPUT_COLOR },
|
|
{ "format", TOKEN_FORMAT },
|
|
{ "values", TOKEN_VALUES },
|
|
{ "both", TOKEN_BOTH },
|
|
{ "vertex", TOKEN_VERTEX },
|
|
{ "fragment", TOKEN_FRAGMENT },
|
|
{ "uniform", TOKEN_UNIFORM },
|
|
{ "input", TOKEN_INPUT },
|
|
{ "output", TOKEN_OUTPUT },
|
|
{ "float", TOKEN_FLOAT },
|
|
{ "vec2", TOKEN_FLOAT_VEC2 },
|
|
{ "vec3", TOKEN_FLOAT_VEC3 },
|
|
{ "vec4", TOKEN_FLOAT_VEC4 },
|
|
{ "mat2", TOKEN_FLOAT_MAT2 },
|
|
{ "mat2x3", TOKEN_FLOAT_MAT2X3 },
|
|
{ "mat2x4", TOKEN_FLOAT_MAT2X4 },
|
|
{ "mat3x2", TOKEN_FLOAT_MAT3X2 },
|
|
{ "mat3", TOKEN_FLOAT_MAT3 },
|
|
{ "mat3x4", TOKEN_FLOAT_MAT3X4 },
|
|
{ "mat4x2", TOKEN_FLOAT_MAT4X2 },
|
|
{ "mat4x3", TOKEN_FLOAT_MAT4X3 },
|
|
{ "mat4", TOKEN_FLOAT_MAT4 },
|
|
{ "int", TOKEN_INT },
|
|
{ "ivec2", TOKEN_INT_VEC2 },
|
|
{ "ivec3", TOKEN_INT_VEC3 },
|
|
{ "ivec4", TOKEN_INT_VEC4 },
|
|
{ "uint", TOKEN_UINT },
|
|
{ "uvec2", TOKEN_UINT_VEC2 },
|
|
{ "uvec3", TOKEN_UINT_VEC3 },
|
|
{ "uvec4", TOKEN_UINT_VEC4 },
|
|
{ "bool", TOKEN_BOOL },
|
|
{ "bvec2", TOKEN_BOOL_VEC2 },
|
|
{ "bvec3", TOKEN_BOOL_VEC3 },
|
|
{ "bvec4", TOKEN_BOOL_VEC4 },
|
|
{ "version", TOKEN_VERSION },
|
|
{ "tessellation_control", TOKEN_TESSELLATION_CONTROL },
|
|
{ "tessellation_evaluation", TOKEN_TESSELLATION_EVALUATION },
|
|
{ "geometry", TOKEN_GEOMETRY },
|
|
{ "require", TOKEN_REQUIRE },
|
|
{ "in", TOKEN_IN },
|
|
{ "import", TOKEN_IMPORT },
|
|
{ "pipeline_program", TOKEN_PIPELINE_PROGRAM },
|
|
{ "active_stages", TOKEN_ACTIVE_STAGES },
|
|
};
|
|
|
|
const char* end = m_curPtr + 1;
|
|
while (isCaseNameChar(*end))
|
|
end++;
|
|
m_curTokenStr = string(m_curPtr, end - m_curPtr);
|
|
|
|
m_curToken = TOKEN_IDENTIFIER;
|
|
|
|
for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_named); ndx++)
|
|
{
|
|
if (m_curTokenStr == s_named[ndx].str)
|
|
{
|
|
m_curToken = s_named[ndx].token;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (isNumeric(*m_curPtr))
|
|
{
|
|
/* \todo [2010-03-31 petri] Hex? */
|
|
const char* p = m_curPtr;
|
|
while (isNumeric(*p))
|
|
p++;
|
|
if (*p == '.')
|
|
{
|
|
p++;
|
|
while (isNumeric(*p))
|
|
p++;
|
|
|
|
if (*p == 'e' || *p == 'E')
|
|
{
|
|
p++;
|
|
if (*p == '+' || *p == '-')
|
|
p++;
|
|
DE_ASSERT(isNumeric(*p));
|
|
while (isNumeric(*p))
|
|
p++;
|
|
}
|
|
|
|
m_curToken = TOKEN_FLOAT_LITERAL;
|
|
m_curTokenStr = string(m_curPtr, p - m_curPtr);
|
|
}
|
|
else
|
|
{
|
|
m_curToken = TOKEN_INT_LITERAL;
|
|
m_curTokenStr = string(m_curPtr, p - m_curPtr);
|
|
}
|
|
}
|
|
else if (*m_curPtr == '"' && m_curPtr[1] == '"')
|
|
{
|
|
const char* p = m_curPtr + 2;
|
|
|
|
while ((p[0] != '"') || (p[1] != '"'))
|
|
{
|
|
DE_ASSERT(*p);
|
|
if (*p == '\\')
|
|
{
|
|
DE_ASSERT(p[1] != 0);
|
|
p += 2;
|
|
}
|
|
else
|
|
p++;
|
|
}
|
|
p += 2;
|
|
|
|
m_curToken = TOKEN_SHADER_SOURCE;
|
|
m_curTokenStr = string(m_curPtr, (int)(p - m_curPtr));
|
|
}
|
|
else if (*m_curPtr == '"' || *m_curPtr == '\'')
|
|
{
|
|
char endChar = *m_curPtr;
|
|
const char* p = m_curPtr + 1;
|
|
|
|
while (*p != endChar)
|
|
{
|
|
DE_ASSERT(*p);
|
|
if (*p == '\\')
|
|
{
|
|
DE_ASSERT(p[1] != 0);
|
|
p += 2;
|
|
}
|
|
else
|
|
p++;
|
|
}
|
|
p++;
|
|
|
|
m_curToken = TOKEN_STRING;
|
|
m_curTokenStr = string(m_curPtr, (int)(p - m_curPtr));
|
|
}
|
|
else
|
|
{
|
|
struct SimpleToken
|
|
{
|
|
const char* str;
|
|
Token token;
|
|
};
|
|
|
|
static const SimpleToken s_simple[] =
|
|
{
|
|
{ "=", TOKEN_ASSIGN },
|
|
{ "+", TOKEN_PLUS },
|
|
{ "-", TOKEN_MINUS },
|
|
{ ",", TOKEN_COMMA },
|
|
{ "|", TOKEN_VERTICAL_BAR },
|
|
{ ";", TOKEN_SEMI_COLON },
|
|
{ "(", TOKEN_LEFT_PAREN },
|
|
{ ")", TOKEN_RIGHT_PAREN },
|
|
{ "[", TOKEN_LEFT_BRACKET },
|
|
{ "]", TOKEN_RIGHT_BRACKET },
|
|
{ "{", TOKEN_LEFT_BRACE },
|
|
{ "}", TOKEN_RIGHT_BRACE },
|
|
{ ">", TOKEN_GREATER },
|
|
};
|
|
|
|
for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_simple); ndx++)
|
|
{
|
|
if (strncmp(s_simple[ndx].str, m_curPtr, strlen(s_simple[ndx].str)) == 0)
|
|
{
|
|
m_curToken = s_simple[ndx].token;
|
|
m_curTokenStr = s_simple[ndx].str;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Otherwise invalid token.
|
|
m_curToken = TOKEN_INVALID;
|
|
m_curTokenStr = *m_curPtr;
|
|
}
|
|
}
|
|
|
|
void ShaderParser::advanceToken (Token assumed)
|
|
{
|
|
assumeToken(assumed);
|
|
advanceToken();
|
|
}
|
|
|
|
void ShaderParser::assumeToken (Token token)
|
|
{
|
|
if (m_curToken != token)
|
|
parseError((string("unexpected token '") + m_curTokenStr + "', expecting '" + getTokenName(token) + "'").c_str());
|
|
DE_TEST_ASSERT(m_curToken == token);
|
|
}
|
|
|
|
DataType ShaderParser::mapDataTypeToken (Token token)
|
|
{
|
|
switch (token)
|
|
{
|
|
case TOKEN_FLOAT: return TYPE_FLOAT;
|
|
case TOKEN_FLOAT_VEC2: return TYPE_FLOAT_VEC2;
|
|
case TOKEN_FLOAT_VEC3: return TYPE_FLOAT_VEC3;
|
|
case TOKEN_FLOAT_VEC4: return TYPE_FLOAT_VEC4;
|
|
case TOKEN_FLOAT_MAT2: return TYPE_FLOAT_MAT2;
|
|
case TOKEN_FLOAT_MAT2X3: return TYPE_FLOAT_MAT2X3;
|
|
case TOKEN_FLOAT_MAT2X4: return TYPE_FLOAT_MAT2X4;
|
|
case TOKEN_FLOAT_MAT3X2: return TYPE_FLOAT_MAT3X2;
|
|
case TOKEN_FLOAT_MAT3: return TYPE_FLOAT_MAT3;
|
|
case TOKEN_FLOAT_MAT3X4: return TYPE_FLOAT_MAT3X4;
|
|
case TOKEN_FLOAT_MAT4X2: return TYPE_FLOAT_MAT4X2;
|
|
case TOKEN_FLOAT_MAT4X3: return TYPE_FLOAT_MAT4X3;
|
|
case TOKEN_FLOAT_MAT4: return TYPE_FLOAT_MAT4;
|
|
case TOKEN_INT: return TYPE_INT;
|
|
case TOKEN_INT_VEC2: return TYPE_INT_VEC2;
|
|
case TOKEN_INT_VEC3: return TYPE_INT_VEC3;
|
|
case TOKEN_INT_VEC4: return TYPE_INT_VEC4;
|
|
case TOKEN_UINT: return TYPE_UINT;
|
|
case TOKEN_UINT_VEC2: return TYPE_UINT_VEC2;
|
|
case TOKEN_UINT_VEC3: return TYPE_UINT_VEC3;
|
|
case TOKEN_UINT_VEC4: return TYPE_UINT_VEC4;
|
|
case TOKEN_BOOL: return TYPE_BOOL;
|
|
case TOKEN_BOOL_VEC2: return TYPE_BOOL_VEC2;
|
|
case TOKEN_BOOL_VEC3: return TYPE_BOOL_VEC3;
|
|
case TOKEN_BOOL_VEC4: return TYPE_BOOL_VEC4;
|
|
default: return TYPE_INVALID;
|
|
}
|
|
}
|
|
|
|
const char* ShaderParser::getTokenName (Token token)
|
|
{
|
|
switch (token)
|
|
{
|
|
case TOKEN_INVALID: return "<invalid>";
|
|
case TOKEN_EOF: return "<eof>";
|
|
case TOKEN_STRING: return "<string>";
|
|
case TOKEN_SHADER_SOURCE: return "source";
|
|
|
|
case TOKEN_INT_LITERAL: return "<int>";
|
|
case TOKEN_FLOAT_LITERAL: return "<float>";
|
|
|
|
// identifiers
|
|
case TOKEN_IDENTIFIER: return "<identifier>";
|
|
case TOKEN_TRUE: return "true";
|
|
case TOKEN_FALSE: return "false";
|
|
case TOKEN_DESC: return "desc";
|
|
case TOKEN_EXPECT: return "expect";
|
|
case TOKEN_GROUP: return "group";
|
|
case TOKEN_CASE: return "case";
|
|
case TOKEN_END: return "end";
|
|
case TOKEN_VALUES: return "values";
|
|
case TOKEN_BOTH: return "both";
|
|
case TOKEN_VERTEX: return "vertex";
|
|
case TOKEN_FRAGMENT: return "fragment";
|
|
case TOKEN_TESSELLATION_CONTROL: return "tessellation_control";
|
|
case TOKEN_TESSELLATION_EVALUATION: return "tessellation_evaluation";
|
|
case TOKEN_GEOMETRY: return "geometry";
|
|
case TOKEN_REQUIRE: return "require";
|
|
case TOKEN_UNIFORM: return "uniform";
|
|
case TOKEN_INPUT: return "input";
|
|
case TOKEN_OUTPUT: return "output";
|
|
case TOKEN_FLOAT: return "float";
|
|
case TOKEN_FLOAT_VEC2: return "vec2";
|
|
case TOKEN_FLOAT_VEC3: return "vec3";
|
|
case TOKEN_FLOAT_VEC4: return "vec4";
|
|
case TOKEN_FLOAT_MAT2: return "mat2";
|
|
case TOKEN_FLOAT_MAT2X3: return "mat2x3";
|
|
case TOKEN_FLOAT_MAT2X4: return "mat2x4";
|
|
case TOKEN_FLOAT_MAT3X2: return "mat3x2";
|
|
case TOKEN_FLOAT_MAT3: return "mat3";
|
|
case TOKEN_FLOAT_MAT3X4: return "mat3x4";
|
|
case TOKEN_FLOAT_MAT4X2: return "mat4x2";
|
|
case TOKEN_FLOAT_MAT4X3: return "mat4x3";
|
|
case TOKEN_FLOAT_MAT4: return "mat4";
|
|
case TOKEN_INT: return "int";
|
|
case TOKEN_INT_VEC2: return "ivec2";
|
|
case TOKEN_INT_VEC3: return "ivec3";
|
|
case TOKEN_INT_VEC4: return "ivec4";
|
|
case TOKEN_UINT: return "uint";
|
|
case TOKEN_UINT_VEC2: return "uvec2";
|
|
case TOKEN_UINT_VEC3: return "uvec3";
|
|
case TOKEN_UINT_VEC4: return "uvec4";
|
|
case TOKEN_BOOL: return "bool";
|
|
case TOKEN_BOOL_VEC2: return "bvec2";
|
|
case TOKEN_BOOL_VEC3: return "bvec3";
|
|
case TOKEN_BOOL_VEC4: return "bvec4";
|
|
case TOKEN_IN: return "in";
|
|
case TOKEN_IMPORT: return "import";
|
|
case TOKEN_PIPELINE_PROGRAM: return "pipeline_program";
|
|
case TOKEN_ACTIVE_STAGES: return "active_stages";
|
|
|
|
case TOKEN_ASSIGN: return "=";
|
|
case TOKEN_PLUS: return "+";
|
|
case TOKEN_MINUS: return "-";
|
|
case TOKEN_COMMA: return ",";
|
|
case TOKEN_VERTICAL_BAR: return "|";
|
|
case TOKEN_SEMI_COLON: return ";";
|
|
case TOKEN_LEFT_PAREN: return "(";
|
|
case TOKEN_RIGHT_PAREN: return ")";
|
|
case TOKEN_LEFT_BRACKET: return "[";
|
|
case TOKEN_RIGHT_BRACKET: return "]";
|
|
case TOKEN_LEFT_BRACE: return "{";
|
|
case TOKEN_RIGHT_BRACE: return "}";
|
|
case TOKEN_GREATER: return ">";
|
|
|
|
default: return "<unknown>";
|
|
}
|
|
}
|
|
|
|
deUint32 ShaderParser::getShaderStageLiteralFlag (void)
|
|
{
|
|
switch (m_curToken)
|
|
{
|
|
case TOKEN_VERTEX: return (1 << glu::SHADERTYPE_VERTEX);
|
|
case TOKEN_FRAGMENT: return (1 << glu::SHADERTYPE_FRAGMENT);
|
|
case TOKEN_GEOMETRY: return (1 << glu::SHADERTYPE_GEOMETRY);
|
|
case TOKEN_TESSELLATION_CONTROL: return (1 << glu::SHADERTYPE_TESSELLATION_CONTROL);
|
|
case TOKEN_TESSELLATION_EVALUATION: return (1 << glu::SHADERTYPE_TESSELLATION_EVALUATION);
|
|
|
|
default:
|
|
parseError(std::string() + "invalid shader stage name, got " + m_curTokenStr);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
deUint32 ShaderParser::getGLEnumFromName (const std::string& enumName)
|
|
{
|
|
static const struct
|
|
{
|
|
const char* name;
|
|
deUint32 value;
|
|
} names[] =
|
|
{
|
|
{ "GL_MAX_VERTEX_IMAGE_UNIFORMS", GL_MAX_VERTEX_IMAGE_UNIFORMS },
|
|
{ "GL_MAX_VERTEX_ATOMIC_COUNTERS", GL_MAX_VERTEX_ATOMIC_COUNTERS },
|
|
{ "GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS", GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS },
|
|
{ "GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS", GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS },
|
|
};
|
|
|
|
for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(names); ++ndx)
|
|
if (names[ndx].name == enumName)
|
|
return names[ndx].value;
|
|
|
|
parseError(std::string() + "unknown enum name, got " + enumName);
|
|
return 0;
|
|
}
|
|
|
|
void ShaderParser::parseValueElement (DataType expectedDataType, Value& result)
|
|
{
|
|
DataType scalarType = getDataTypeScalarType(expectedDataType);
|
|
int scalarSize = getDataTypeScalarSize(expectedDataType);
|
|
|
|
/* \todo [2010-04-19 petri] Support arrays. */
|
|
Value::Element elems[16];
|
|
|
|
if (scalarSize > 1)
|
|
{
|
|
DE_ASSERT(mapDataTypeToken(m_curToken) == expectedDataType);
|
|
advanceToken(); // data type (float, vec2, etc.)
|
|
advanceToken(TOKEN_LEFT_PAREN);
|
|
}
|
|
|
|
for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
|
|
{
|
|
if (scalarType == TYPE_FLOAT)
|
|
{
|
|
float signMult = 1.0f;
|
|
if (m_curToken == TOKEN_MINUS)
|
|
{
|
|
signMult = -1.0f;
|
|
advanceToken();
|
|
}
|
|
|
|
assumeToken(TOKEN_FLOAT_LITERAL);
|
|
elems[scalarNdx].float32 = signMult * parseFloatLiteral(m_curTokenStr.c_str());
|
|
advanceToken(TOKEN_FLOAT_LITERAL);
|
|
}
|
|
else if (scalarType == TYPE_INT || scalarType == TYPE_UINT)
|
|
{
|
|
int signMult = 1;
|
|
if (m_curToken == TOKEN_MINUS)
|
|
{
|
|
signMult = -1;
|
|
advanceToken();
|
|
}
|
|
|
|
assumeToken(TOKEN_INT_LITERAL);
|
|
elems[scalarNdx].int32 = signMult * parseIntLiteral(m_curTokenStr.c_str());
|
|
advanceToken(TOKEN_INT_LITERAL);
|
|
}
|
|
else
|
|
{
|
|
DE_ASSERT(scalarType == TYPE_BOOL);
|
|
elems[scalarNdx].bool32 = (m_curToken == TOKEN_TRUE);
|
|
if (m_curToken != TOKEN_TRUE && m_curToken != TOKEN_FALSE)
|
|
parseError(string("unexpected token, expecting bool: " + m_curTokenStr));
|
|
advanceToken(); // true/false
|
|
}
|
|
|
|
if (scalarNdx != (scalarSize - 1))
|
|
advanceToken(TOKEN_COMMA);
|
|
}
|
|
|
|
if (scalarSize > 1)
|
|
advanceToken(TOKEN_RIGHT_PAREN);
|
|
|
|
// Store results.
|
|
for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
|
|
result.elements.push_back(elems[scalarNdx]);
|
|
}
|
|
|
|
void ShaderParser::parseValue (ValueBlock& valueBlock)
|
|
{
|
|
PARSE_DBG((" parseValue()\n"));
|
|
|
|
// Parsed results.
|
|
vector<Value>* dstBlock = DE_NULL;
|
|
DataType basicType = TYPE_LAST;
|
|
std::string valueName;
|
|
|
|
// Parse storage.
|
|
if (m_curToken == TOKEN_UNIFORM)
|
|
dstBlock = &valueBlock.uniforms;
|
|
else if (m_curToken == TOKEN_INPUT)
|
|
dstBlock = &valueBlock.inputs;
|
|
else if (m_curToken == TOKEN_OUTPUT)
|
|
dstBlock = &valueBlock.outputs;
|
|
else
|
|
parseError(string("unexpected token encountered when parsing value classifier"));
|
|
advanceToken();
|
|
|
|
// Parse data type.
|
|
basicType = mapDataTypeToken(m_curToken);
|
|
if (basicType == TYPE_INVALID)
|
|
parseError(string("unexpected token when parsing value data type: " + m_curTokenStr));
|
|
advanceToken();
|
|
|
|
// Parse value name.
|
|
if (m_curToken == TOKEN_IDENTIFIER || m_curToken == TOKEN_STRING)
|
|
{
|
|
if (m_curToken == TOKEN_IDENTIFIER)
|
|
valueName = m_curTokenStr;
|
|
else
|
|
valueName = parseStringLiteral(m_curTokenStr.c_str());
|
|
}
|
|
else
|
|
parseError(string("unexpected token when parsing value name: " + m_curTokenStr));
|
|
advanceToken();
|
|
|
|
// Parse assignment operator.
|
|
advanceToken(TOKEN_ASSIGN);
|
|
|
|
{
|
|
Value value;
|
|
value.name = valueName;
|
|
value.type = VarType(basicType, PRECISION_LAST);
|
|
dstBlock->push_back(value);
|
|
}
|
|
|
|
// Parse actual value.
|
|
if (m_curToken == TOKEN_LEFT_BRACKET) // value list
|
|
{
|
|
int arrayLength = 0; // \todo [2015-08-03 pyry] Currently unused
|
|
|
|
advanceToken(TOKEN_LEFT_BRACKET);
|
|
|
|
for (;;)
|
|
{
|
|
parseValueElement(basicType, dstBlock->back());
|
|
arrayLength++;
|
|
|
|
if (m_curToken == TOKEN_RIGHT_BRACKET)
|
|
break;
|
|
else if (m_curToken == TOKEN_VERTICAL_BAR)
|
|
{
|
|
advanceToken();
|
|
continue;
|
|
}
|
|
else
|
|
parseError(string("unexpected token in value element array: " + m_curTokenStr));
|
|
}
|
|
|
|
advanceToken(TOKEN_RIGHT_BRACKET);
|
|
}
|
|
else // single elements
|
|
{
|
|
parseValueElement(basicType, dstBlock->back());
|
|
}
|
|
|
|
advanceToken(TOKEN_SEMI_COLON); // end of declaration
|
|
}
|
|
|
|
void ShaderParser::parseValueBlock (ValueBlock& valueBlock)
|
|
{
|
|
PARSE_DBG((" parseValueBlock()\n"));
|
|
advanceToken(TOKEN_VALUES);
|
|
advanceToken(TOKEN_LEFT_BRACE);
|
|
|
|
for (;;)
|
|
{
|
|
if (m_curToken == TOKEN_UNIFORM || m_curToken == TOKEN_INPUT || m_curToken == TOKEN_OUTPUT)
|
|
parseValue(valueBlock);
|
|
else if (m_curToken == TOKEN_RIGHT_BRACE)
|
|
break;
|
|
else
|
|
parseError(string("unexpected token when parsing a value block: " + m_curTokenStr));
|
|
}
|
|
|
|
advanceToken(TOKEN_RIGHT_BRACE);
|
|
}
|
|
|
|
deUint32 ShaderParser::parseShaderStageList (void)
|
|
{
|
|
deUint32 mask = 0;
|
|
|
|
assumeToken(TOKEN_LEFT_BRACE);
|
|
|
|
// don't allow 0-sized lists
|
|
advanceToken();
|
|
mask |= getShaderStageLiteralFlag();
|
|
advanceToken();
|
|
|
|
for (;;)
|
|
{
|
|
if (m_curToken == TOKEN_RIGHT_BRACE)
|
|
break;
|
|
else if (m_curToken == TOKEN_COMMA)
|
|
{
|
|
deUint32 stageFlag;
|
|
advanceToken();
|
|
|
|
stageFlag = getShaderStageLiteralFlag();
|
|
if (stageFlag & mask)
|
|
parseError(string("stage already set in the shader stage set: " + m_curTokenStr));
|
|
|
|
mask |= stageFlag;
|
|
advanceToken();
|
|
}
|
|
else
|
|
parseError(string("invalid shader stage set token: " + m_curTokenStr));
|
|
}
|
|
advanceToken(TOKEN_RIGHT_BRACE);
|
|
|
|
return mask;
|
|
}
|
|
|
|
void ShaderParser::parseRequirement (vector<RequiredCapability>& requiredCaps, vector<RequiredExtension>& requiredExts)
|
|
{
|
|
PARSE_DBG((" parseRequirement()\n"));
|
|
|
|
advanceToken();
|
|
assumeToken(TOKEN_IDENTIFIER);
|
|
|
|
if (m_curTokenStr == "extension")
|
|
{
|
|
std::vector<std::string> anyExtensionStringList;
|
|
deUint32 affectedCasesFlags = -1; // by default all stages
|
|
|
|
advanceToken();
|
|
assumeToken(TOKEN_LEFT_BRACE);
|
|
|
|
advanceToken();
|
|
assumeToken(TOKEN_STRING);
|
|
|
|
anyExtensionStringList.push_back(parseStringLiteral(m_curTokenStr.c_str()));
|
|
advanceToken();
|
|
|
|
for (;;)
|
|
{
|
|
if (m_curToken == TOKEN_RIGHT_BRACE)
|
|
break;
|
|
else if (m_curToken == TOKEN_VERTICAL_BAR)
|
|
{
|
|
advanceToken();
|
|
assumeToken(TOKEN_STRING);
|
|
|
|
anyExtensionStringList.push_back(parseStringLiteral(m_curTokenStr.c_str()));
|
|
advanceToken();
|
|
}
|
|
else
|
|
parseError(string("invalid extension list token: " + m_curTokenStr));
|
|
}
|
|
advanceToken(TOKEN_RIGHT_BRACE);
|
|
|
|
if (m_curToken == TOKEN_IN)
|
|
{
|
|
advanceToken();
|
|
affectedCasesFlags = parseShaderStageList();
|
|
}
|
|
|
|
requiredExts.push_back(RequiredExtension(anyExtensionStringList, affectedCasesFlags));
|
|
}
|
|
else if (m_curTokenStr == "limit")
|
|
{
|
|
deUint32 limitEnum;
|
|
int limitValue;
|
|
|
|
advanceToken();
|
|
|
|
assumeToken(TOKEN_STRING);
|
|
limitEnum = getGLEnumFromName(parseStringLiteral(m_curTokenStr.c_str()));
|
|
advanceToken();
|
|
|
|
assumeToken(TOKEN_GREATER);
|
|
advanceToken();
|
|
|
|
assumeToken(TOKEN_INT_LITERAL);
|
|
limitValue = parseIntLiteral(m_curTokenStr.c_str());
|
|
advanceToken();
|
|
|
|
requiredCaps.push_back(RequiredCapability(limitEnum, limitValue));
|
|
}
|
|
else if (m_curTokenStr == "full_glsl_es_100_support")
|
|
{
|
|
advanceToken();
|
|
|
|
requiredCaps.push_back(RequiredCapability(CAPABILITY_FULL_GLSL_ES_100_SUPPORT));
|
|
}
|
|
else if (m_curTokenStr == "only_glsl_es_100_support")
|
|
{
|
|
advanceToken();
|
|
|
|
requiredCaps.push_back(RequiredCapability(CAPABILITY_ONLY_GLSL_ES_100_SUPPORT));
|
|
}
|
|
else if (m_curTokenStr == "exactly_one_draw_buffer")
|
|
{
|
|
advanceToken();
|
|
|
|
requiredCaps.push_back(RequiredCapability(CAPABILITY_EXACTLY_ONE_DRAW_BUFFER));
|
|
}
|
|
else
|
|
parseError(string("invalid requirement value: " + m_curTokenStr));
|
|
}
|
|
|
|
void ShaderParser::parseExpectResult (ExpectResult& expectResult)
|
|
{
|
|
assumeToken(TOKEN_IDENTIFIER);
|
|
|
|
if (m_curTokenStr == "pass")
|
|
expectResult = EXPECT_PASS;
|
|
else if (m_curTokenStr == "compile_fail")
|
|
expectResult = EXPECT_COMPILE_FAIL;
|
|
else if (m_curTokenStr == "link_fail")
|
|
expectResult = EXPECT_LINK_FAIL;
|
|
else if (m_curTokenStr == "compile_or_link_fail")
|
|
expectResult = EXPECT_COMPILE_LINK_FAIL;
|
|
else if (m_curTokenStr == "validation_fail")
|
|
expectResult = EXPECT_VALIDATION_FAIL;
|
|
else if (m_curTokenStr == "build_successful")
|
|
expectResult = EXPECT_BUILD_SUCCESSFUL;
|
|
else
|
|
parseError(string("invalid expected result value: " + m_curTokenStr));
|
|
|
|
advanceToken();
|
|
}
|
|
|
|
void ShaderParser::parseFormat (DataType& format)
|
|
{
|
|
format = mapDataTypeToken(m_curToken);
|
|
advanceToken();
|
|
}
|
|
|
|
void ShaderParser::parseGLSLVersion (glu::GLSLVersion& version)
|
|
{
|
|
int versionNum = 0;
|
|
std::string postfix = "";
|
|
|
|
assumeToken(TOKEN_INT_LITERAL);
|
|
versionNum = parseIntLiteral(m_curTokenStr.c_str());
|
|
advanceToken();
|
|
|
|
if (m_curToken == TOKEN_IDENTIFIER)
|
|
{
|
|
postfix = m_curTokenStr;
|
|
advanceToken();
|
|
}
|
|
|
|
DE_STATIC_ASSERT(glu::GLSL_VERSION_LAST == 15);
|
|
|
|
if (versionNum == 100 && postfix == "es") version = glu::GLSL_VERSION_100_ES;
|
|
else if (versionNum == 300 && postfix == "es") version = glu::GLSL_VERSION_300_ES;
|
|
else if (versionNum == 310 && postfix == "es") version = glu::GLSL_VERSION_310_ES;
|
|
else if (versionNum == 320 && postfix == "es") version = glu::GLSL_VERSION_320_ES;
|
|
else if (versionNum == 130) version = glu::GLSL_VERSION_130;
|
|
else if (versionNum == 140) version = glu::GLSL_VERSION_140;
|
|
else if (versionNum == 150) version = glu::GLSL_VERSION_150;
|
|
else if (versionNum == 330) version = glu::GLSL_VERSION_330;
|
|
else if (versionNum == 400) version = glu::GLSL_VERSION_400;
|
|
else if (versionNum == 410) version = glu::GLSL_VERSION_410;
|
|
else if (versionNum == 420) version = glu::GLSL_VERSION_420;
|
|
else if (versionNum == 430) version = glu::GLSL_VERSION_430;
|
|
else if (versionNum == 440) version = glu::GLSL_VERSION_440;
|
|
else if (versionNum == 450) version = glu::GLSL_VERSION_450;
|
|
else if (versionNum == 460) version = glu::GLSL_VERSION_460;
|
|
else
|
|
parseError("Unknown GLSL version");
|
|
}
|
|
|
|
void ShaderParser::parsePipelineProgram (ProgramSpecification& program)
|
|
{
|
|
advanceToken(TOKEN_PIPELINE_PROGRAM);
|
|
|
|
for (;;)
|
|
{
|
|
if (m_curToken == TOKEN_END)
|
|
break;
|
|
else if (m_curToken == TOKEN_ACTIVE_STAGES)
|
|
{
|
|
advanceToken();
|
|
program.activeStages = parseShaderStageList();
|
|
}
|
|
else if (m_curToken == TOKEN_REQUIRE)
|
|
{
|
|
vector<RequiredCapability> dummyCaps;
|
|
size_t size = program.requiredExtensions.size();
|
|
parseRequirement(dummyCaps, program.requiredExtensions);
|
|
|
|
if (size == program.requiredExtensions.size())
|
|
parseError("only extension requirements are allowed inside pipeline program");
|
|
}
|
|
else if (m_curToken == TOKEN_VERTEX ||
|
|
m_curToken == TOKEN_FRAGMENT ||
|
|
m_curToken == TOKEN_TESSELLATION_CONTROL ||
|
|
m_curToken == TOKEN_TESSELLATION_EVALUATION ||
|
|
m_curToken == TOKEN_GEOMETRY)
|
|
{
|
|
const Token token = m_curToken;
|
|
string source;
|
|
|
|
advanceToken();
|
|
assumeToken(TOKEN_SHADER_SOURCE);
|
|
source = parseShaderSource(m_curTokenStr.c_str());
|
|
advanceToken();
|
|
|
|
switch (token)
|
|
{
|
|
case TOKEN_VERTEX: program.sources.sources[SHADERTYPE_VERTEX].push_back(source); break;
|
|
case TOKEN_FRAGMENT: program.sources.sources[SHADERTYPE_FRAGMENT].push_back(source); break;
|
|
case TOKEN_TESSELLATION_CONTROL: program.sources.sources[SHADERTYPE_TESSELLATION_CONTROL].push_back(source); break;
|
|
case TOKEN_TESSELLATION_EVALUATION: program.sources.sources[SHADERTYPE_TESSELLATION_EVALUATION].push_back(source); break;
|
|
case TOKEN_GEOMETRY: program.sources.sources[SHADERTYPE_GEOMETRY].push_back(source); break;
|
|
default:
|
|
parseError(DE_FALSE);
|
|
}
|
|
}
|
|
else
|
|
parseError(string("invalid pipeline program value: " + m_curTokenStr));
|
|
}
|
|
advanceToken(TOKEN_END);
|
|
|
|
if (program.activeStages == 0)
|
|
parseError("program pipeline object must have active stages");
|
|
}
|
|
|
|
void ShaderParser::parseShaderCase (vector<tcu::TestNode*>& shaderNodeList)
|
|
{
|
|
// Parse 'case'.
|
|
PARSE_DBG((" parseShaderCase()\n"));
|
|
advanceToken(TOKEN_CASE);
|
|
|
|
// Parse case name.
|
|
string caseName = m_curTokenStr;
|
|
advanceToken(); // \note [pyry] All token types are allowed here.
|
|
|
|
// \todo [pyry] Optimize by parsing most stuff directly to ShaderCaseSpecification
|
|
|
|
// Setup case.
|
|
GLSLVersion version = DEFAULT_GLSL_VERSION;
|
|
ExpectResult expectResult = EXPECT_PASS;
|
|
OutputType outputType = OUTPUT_RESULT;
|
|
DataType format = TYPE_LAST;
|
|
string description;
|
|
string bothSource;
|
|
vector<string> vertexSources;
|
|
vector<string> fragmentSources;
|
|
vector<string> tessellationCtrlSources;
|
|
vector<string> tessellationEvalSources;
|
|
vector<string> geometrySources;
|
|
ValueBlock valueBlock;
|
|
bool valueBlockSeen = false;
|
|
vector<RequiredCapability> requiredCaps;
|
|
vector<RequiredExtension> requiredExts;
|
|
vector<ProgramSpecification> pipelinePrograms;
|
|
|
|
for (;;)
|
|
{
|
|
if (m_curToken == TOKEN_END)
|
|
break;
|
|
else if (m_curToken == TOKEN_DESC)
|
|
{
|
|
advanceToken();
|
|
assumeToken(TOKEN_STRING);
|
|
description = parseStringLiteral(m_curTokenStr.c_str());
|
|
advanceToken();
|
|
}
|
|
else if (m_curToken == TOKEN_EXPECT)
|
|
{
|
|
advanceToken();
|
|
parseExpectResult(expectResult);
|
|
}
|
|
else if (m_curToken == TOKEN_OUTPUT_COLOR)
|
|
{
|
|
outputType = OUTPUT_COLOR;
|
|
advanceToken();
|
|
parseFormat(format);
|
|
}
|
|
else if (m_curToken == TOKEN_VALUES)
|
|
{
|
|
if (valueBlockSeen)
|
|
parseError("multiple value blocks");
|
|
parseValueBlock(valueBlock);
|
|
valueBlockSeen = true;
|
|
}
|
|
else if (m_curToken == TOKEN_BOTH ||
|
|
m_curToken == TOKEN_VERTEX ||
|
|
m_curToken == TOKEN_FRAGMENT ||
|
|
m_curToken == TOKEN_TESSELLATION_CONTROL ||
|
|
m_curToken == TOKEN_TESSELLATION_EVALUATION ||
|
|
m_curToken == TOKEN_GEOMETRY)
|
|
{
|
|
const Token token = m_curToken;
|
|
string source;
|
|
|
|
advanceToken();
|
|
assumeToken(TOKEN_SHADER_SOURCE);
|
|
source = parseShaderSource(m_curTokenStr.c_str());
|
|
advanceToken();
|
|
|
|
switch (token)
|
|
{
|
|
case TOKEN_VERTEX: vertexSources.push_back(source); break;
|
|
case TOKEN_FRAGMENT: fragmentSources.push_back(source); break;
|
|
case TOKEN_TESSELLATION_CONTROL: tessellationCtrlSources.push_back(source); break;
|
|
case TOKEN_TESSELLATION_EVALUATION: tessellationEvalSources.push_back(source); break;
|
|
case TOKEN_GEOMETRY: geometrySources.push_back(source); break;
|
|
case TOKEN_BOTH:
|
|
{
|
|
if (!bothSource.empty())
|
|
parseError("multiple 'both' blocks");
|
|
bothSource = source;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
parseError(DE_FALSE);
|
|
}
|
|
}
|
|
else if (m_curToken == TOKEN_VERSION)
|
|
{
|
|
advanceToken();
|
|
parseGLSLVersion(version);
|
|
}
|
|
else if (m_curToken == TOKEN_REQUIRE)
|
|
{
|
|
parseRequirement(requiredCaps, requiredExts);
|
|
}
|
|
else if (m_curToken == TOKEN_PIPELINE_PROGRAM)
|
|
{
|
|
ProgramSpecification pipelineProgram;
|
|
parsePipelineProgram(pipelineProgram);
|
|
pipelineProgram.sources.separable = true;
|
|
pipelinePrograms.push_back(pipelineProgram);
|
|
}
|
|
else
|
|
parseError(string("unexpected token while parsing shader case: " + m_curTokenStr));
|
|
}
|
|
|
|
advanceToken(TOKEN_END); // case end
|
|
|
|
if (!bothSource.empty())
|
|
{
|
|
if (!vertexSources.empty() ||
|
|
!fragmentSources.empty() ||
|
|
!tessellationCtrlSources.empty() ||
|
|
!tessellationEvalSources.empty() ||
|
|
!geometrySources.empty() ||
|
|
!pipelinePrograms.empty())
|
|
{
|
|
parseError("'both' cannot be mixed with other shader stages");
|
|
}
|
|
|
|
// vertex
|
|
{
|
|
ShaderCaseSpecification spec;
|
|
spec.caseType = CASETYPE_VERTEX_ONLY;
|
|
spec.expectResult = expectResult;
|
|
spec.targetVersion = version;
|
|
spec.requiredCaps = requiredCaps;
|
|
spec.values = valueBlock;
|
|
|
|
spec.programs.resize(1);
|
|
spec.programs[0].sources << VertexSource(bothSource);
|
|
spec.programs[0].requiredExtensions = requiredExts;
|
|
|
|
shaderNodeList.push_back(m_caseFactory->createCase(caseName + "_vertex", description, ShaderCaseSpecification(spec)));
|
|
}
|
|
|
|
// fragment
|
|
{
|
|
ShaderCaseSpecification spec;
|
|
spec.caseType = CASETYPE_FRAGMENT_ONLY;
|
|
spec.expectResult = expectResult;
|
|
spec.targetVersion = version;
|
|
spec.requiredCaps = requiredCaps;
|
|
spec.values = valueBlock;
|
|
|
|
spec.programs.resize(1);
|
|
spec.programs[0].sources << FragmentSource(bothSource);
|
|
spec.programs[0].requiredExtensions = requiredExts;
|
|
|
|
shaderNodeList.push_back(m_caseFactory->createCase(caseName + "_fragment", description, ShaderCaseSpecification(spec)));
|
|
}
|
|
}
|
|
else if (pipelinePrograms.empty())
|
|
{
|
|
ShaderCaseSpecification spec;
|
|
spec.caseType = CASETYPE_COMPLETE;
|
|
spec.expectResult = expectResult;
|
|
spec.outputType = outputType;
|
|
spec.outputFormat = format;
|
|
spec.targetVersion = version;
|
|
spec.requiredCaps = requiredCaps;
|
|
spec.values = valueBlock;
|
|
|
|
spec.programs.resize(1);
|
|
spec.programs[0].sources.sources[SHADERTYPE_VERTEX].swap(vertexSources);
|
|
spec.programs[0].sources.sources[SHADERTYPE_FRAGMENT].swap(fragmentSources);
|
|
spec.programs[0].sources.sources[SHADERTYPE_TESSELLATION_CONTROL].swap(tessellationCtrlSources);
|
|
spec.programs[0].sources.sources[SHADERTYPE_TESSELLATION_EVALUATION].swap(tessellationEvalSources);
|
|
spec.programs[0].sources.sources[SHADERTYPE_GEOMETRY].swap(geometrySources);
|
|
spec.programs[0].requiredExtensions.swap(requiredExts);
|
|
|
|
shaderNodeList.push_back(m_caseFactory->createCase(caseName, description, ShaderCaseSpecification(spec)));
|
|
}
|
|
else
|
|
{
|
|
if (!vertexSources.empty() ||
|
|
!fragmentSources.empty() ||
|
|
!tessellationCtrlSources.empty() ||
|
|
!tessellationEvalSources.empty() ||
|
|
!geometrySources.empty())
|
|
{
|
|
parseError("pipeline programs cannot be mixed with complete programs");
|
|
}
|
|
|
|
if (!requiredExts.empty())
|
|
parseError("global extension requirements cannot be mixed with pipeline programs");
|
|
|
|
// Pipeline case, multiple programs
|
|
{
|
|
ShaderCaseSpecification spec;
|
|
spec.caseType = CASETYPE_COMPLETE;
|
|
spec.expectResult = expectResult;
|
|
spec.targetVersion = version;
|
|
spec.requiredCaps = requiredCaps;
|
|
spec.values = valueBlock;
|
|
|
|
spec.programs.swap(pipelinePrograms);
|
|
|
|
shaderNodeList.push_back(m_caseFactory->createCase(caseName, description, ShaderCaseSpecification(spec)));
|
|
}
|
|
}
|
|
}
|
|
|
|
void ShaderParser::parseShaderGroup (vector<tcu::TestNode*>& shaderNodeList)
|
|
{
|
|
// Parse 'case'.
|
|
PARSE_DBG((" parseShaderGroup()\n"));
|
|
advanceToken(TOKEN_GROUP);
|
|
|
|
// Parse case name.
|
|
string name = m_curTokenStr;
|
|
advanceToken(); // \note [pyry] We don't want to check token type here (for instance to allow "uniform") group.
|
|
|
|
// Parse description.
|
|
assumeToken(TOKEN_STRING);
|
|
string description = parseStringLiteral(m_curTokenStr.c_str());
|
|
advanceToken(TOKEN_STRING);
|
|
|
|
std::vector<tcu::TestNode*> children;
|
|
|
|
// Parse group children.
|
|
for (;;)
|
|
{
|
|
if (m_curToken == TOKEN_END)
|
|
break;
|
|
else if (m_curToken == TOKEN_GROUP)
|
|
parseShaderGroup(children);
|
|
else if (m_curToken == TOKEN_CASE)
|
|
parseShaderCase(children);
|
|
else if (m_curToken == TOKEN_IMPORT)
|
|
parseImport(children);
|
|
else
|
|
parseError(string("unexpected token while parsing shader group: " + m_curTokenStr));
|
|
}
|
|
|
|
advanceToken(TOKEN_END); // group end
|
|
|
|
// Create group node.
|
|
tcu::TestCaseGroup* groupNode = m_caseFactory->createGroup(name, description, children);
|
|
shaderNodeList.push_back(groupNode);
|
|
}
|
|
|
|
void ShaderParser::parseImport (vector<tcu::TestNode*>& shaderNodeList)
|
|
{
|
|
std::string importFileName;
|
|
|
|
advanceToken(TOKEN_IMPORT);
|
|
|
|
assumeToken(TOKEN_STRING);
|
|
importFileName = parseStringLiteral(m_curTokenStr.c_str());
|
|
advanceToken(TOKEN_STRING);
|
|
|
|
{
|
|
ShaderParser subParser (m_archive, de::FilePath::join(de::FilePath(m_filename).getDirName(), importFileName).getPath(), m_caseFactory);
|
|
const vector<tcu::TestNode*> importedCases = subParser.parse();
|
|
|
|
// \todo [2015-08-03 pyry] Not exception safe
|
|
shaderNodeList.insert(shaderNodeList.end(), importedCases.begin(), importedCases.end());
|
|
}
|
|
}
|
|
|
|
vector<tcu::TestNode*> ShaderParser::parse (void)
|
|
{
|
|
const int dataLen = m_resource->getSize();
|
|
|
|
m_input.resize(dataLen+1);
|
|
m_resource->setPosition(0);
|
|
m_resource->read((deUint8*)&m_input[0], dataLen);
|
|
m_input[dataLen] = '\0';
|
|
|
|
// Initialize parser.
|
|
m_curPtr = &m_input[0];
|
|
m_curToken = TOKEN_INVALID;
|
|
m_curTokenStr = "";
|
|
advanceToken();
|
|
|
|
vector<tcu::TestNode*> nodeList;
|
|
|
|
// Parse all cases.
|
|
PARSE_DBG(("parse()\n"));
|
|
for (;;)
|
|
{
|
|
if (m_curToken == TOKEN_CASE)
|
|
parseShaderCase(nodeList);
|
|
else if (m_curToken == TOKEN_GROUP)
|
|
parseShaderGroup(nodeList);
|
|
else if (m_curToken == TOKEN_IMPORT)
|
|
parseImport(nodeList);
|
|
else if (m_curToken == TOKEN_EOF)
|
|
break;
|
|
else
|
|
parseError(string("invalid token encountered at main level: '") + m_curTokenStr + "'");
|
|
}
|
|
|
|
assumeToken(TOKEN_EOF);
|
|
// printf(" parsed %d test cases.\n", caseList.size());
|
|
return nodeList;
|
|
}
|
|
|
|
std::vector<tcu::TestNode*> parseFile (const tcu::Archive& archive, const std::string& filename, ShaderCaseFactory* caseFactory)
|
|
{
|
|
sl::ShaderParser parser (archive, filename, caseFactory);
|
|
|
|
return parser.parse();
|
|
}
|
|
|
|
// Execution utilities
|
|
|
|
static void dumpValue (tcu::TestLog& log, const Value& val, const char* storageName, int arrayNdx)
|
|
{
|
|
const char* const valueName = val.name.c_str();
|
|
const DataType dataType = val.type.getBasicType();
|
|
int scalarSize = getDataTypeScalarSize(dataType);
|
|
ostringstream result;
|
|
|
|
result << " " << storageName << " ";
|
|
|
|
result << getDataTypeName(dataType) << " " << valueName << ":";
|
|
|
|
if (isDataTypeScalar(dataType))
|
|
result << " ";
|
|
if (isDataTypeVector(dataType))
|
|
result << " [ ";
|
|
else if (isDataTypeMatrix(dataType))
|
|
result << "\n";
|
|
|
|
if (isDataTypeScalarOrVector(dataType))
|
|
{
|
|
for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
|
|
{
|
|
int elemNdx = arrayNdx;
|
|
const Value::Element& e = val.elements[elemNdx*scalarSize + scalarNdx];
|
|
result << ((scalarNdx != 0) ? ", " : "");
|
|
|
|
if (isDataTypeFloatOrVec(dataType))
|
|
result << e.float32;
|
|
else if (isDataTypeIntOrIVec(dataType))
|
|
result << e.int32;
|
|
else if (isDataTypeUintOrUVec(dataType))
|
|
result << (deUint32)e.int32;
|
|
else if (isDataTypeBoolOrBVec(dataType))
|
|
result << (e.bool32 ? "true" : "false");
|
|
}
|
|
}
|
|
else if (isDataTypeMatrix(dataType))
|
|
{
|
|
int numRows = getDataTypeMatrixNumRows(dataType);
|
|
int numCols = getDataTypeMatrixNumColumns(dataType);
|
|
for (int rowNdx = 0; rowNdx < numRows; rowNdx++)
|
|
{
|
|
result << " [ ";
|
|
for (int colNdx = 0; colNdx < numCols; colNdx++)
|
|
{
|
|
int elemNdx = arrayNdx;
|
|
float v = val.elements[elemNdx*scalarSize + rowNdx*numCols + colNdx].float32;
|
|
result << ((colNdx==0) ? "" : ", ") << v;
|
|
}
|
|
result << " ]\n";
|
|
}
|
|
}
|
|
|
|
if (isDataTypeScalar(dataType))
|
|
result << "\n";
|
|
else if (isDataTypeVector(dataType))
|
|
result << " ]\n";
|
|
|
|
log << TestLog::Message << result.str() << TestLog::EndMessage;
|
|
}
|
|
|
|
static void dumpValues (tcu::TestLog& log, const vector<Value>& values, const char* storageName, int arrayNdx)
|
|
{
|
|
for (size_t valNdx = 0; valNdx < values.size(); valNdx++)
|
|
dumpValue(log, values[valNdx], storageName, arrayNdx);
|
|
}
|
|
|
|
void dumpValues (tcu::TestLog& log, const ValueBlock& values, int arrayNdx)
|
|
{
|
|
dumpValues(log, values.inputs, "input", arrayNdx);
|
|
dumpValues(log, values.outputs, "expected", arrayNdx);
|
|
dumpValues(log, values.uniforms, "uniform", arrayNdx);
|
|
}
|
|
|
|
static void generateExtensionStatements (std::ostringstream& buf, const std::vector<RequiredExtension>& extensions, glu::ShaderType type)
|
|
{
|
|
for (size_t ndx = 0; ndx < extensions.size(); ++ndx)
|
|
{
|
|
DE_ASSERT(extensions[ndx].effectiveStages != 0u &&
|
|
extensions[ndx].alternatives.size() == 1);
|
|
|
|
if ((extensions[ndx].effectiveStages & (1u << (deUint32)type)) != 0)
|
|
buf << "#extension " << extensions[ndx].alternatives[0] << " : require\n";
|
|
}
|
|
}
|
|
|
|
// Injects #extension XXX : require lines after the last preprocessor directive in the shader code. Does not support line continuations
|
|
std::string injectExtensionRequirements (const std::string& baseCode, const std::vector<RequiredExtension>& extensions, glu::ShaderType shaderType)
|
|
{
|
|
std::istringstream baseCodeBuf (baseCode);
|
|
std::ostringstream resultBuf;
|
|
std::string line;
|
|
bool firstNonPreprocessorLine = true;
|
|
std::ostringstream extStr;
|
|
|
|
generateExtensionStatements(extStr, extensions, shaderType);
|
|
|
|
// skip if no requirements
|
|
if (extStr.str().empty())
|
|
return baseCode;
|
|
|
|
while (std::getline(baseCodeBuf, line))
|
|
{
|
|
// begins with '#'?
|
|
const std::string::size_type firstNonWhitespace = line.find_first_not_of("\t ");
|
|
const bool isPreprocessorDirective = (firstNonWhitespace != std::string::npos && line.at(firstNonWhitespace) == '#');
|
|
|
|
// Inject #extensions
|
|
if (!isPreprocessorDirective && firstNonPreprocessorLine)
|
|
{
|
|
firstNonPreprocessorLine = false;
|
|
resultBuf << extStr.str();
|
|
}
|
|
|
|
resultBuf << line << "\n";
|
|
}
|
|
|
|
return resultBuf.str();
|
|
}
|
|
|
|
void genCompareFunctions (ostringstream& stream, const ValueBlock& valueBlock, bool useFloatTypes)
|
|
{
|
|
bool cmpTypeFound[TYPE_LAST];
|
|
for (int i = 0; i < TYPE_LAST; i++)
|
|
cmpTypeFound[i] = false;
|
|
|
|
for (size_t valueNdx = 0; valueNdx < valueBlock.outputs.size(); valueNdx++)
|
|
{
|
|
const Value& val = valueBlock.outputs[valueNdx];
|
|
cmpTypeFound[(size_t)val.type.getBasicType()] = true;
|
|
}
|
|
|
|
if (useFloatTypes)
|
|
{
|
|
if (cmpTypeFound[TYPE_BOOL]) stream << "bool isOk (float a, bool b) { return ((a > 0.5) == b); }\n";
|
|
if (cmpTypeFound[TYPE_BOOL_VEC2]) stream << "bool isOk (vec2 a, bvec2 b) { return (greaterThan(a, vec2(0.5)) == b); }\n";
|
|
if (cmpTypeFound[TYPE_BOOL_VEC3]) stream << "bool isOk (vec3 a, bvec3 b) { return (greaterThan(a, vec3(0.5)) == b); }\n";
|
|
if (cmpTypeFound[TYPE_BOOL_VEC4]) stream << "bool isOk (vec4 a, bvec4 b) { return (greaterThan(a, vec4(0.5)) == b); }\n";
|
|
if (cmpTypeFound[TYPE_INT]) stream << "bool isOk (float a, int b) { float atemp = a+0.5; return (float(b) <= atemp && atemp <= float(b+1)); }\n";
|
|
if (cmpTypeFound[TYPE_INT_VEC2]) stream << "bool isOk (vec2 a, ivec2 b) { return (ivec2(floor(a + 0.5)) == b); }\n";
|
|
if (cmpTypeFound[TYPE_INT_VEC3]) stream << "bool isOk (vec3 a, ivec3 b) { return (ivec3(floor(a + 0.5)) == b); }\n";
|
|
if (cmpTypeFound[TYPE_INT_VEC4]) stream << "bool isOk (vec4 a, ivec4 b) { return (ivec4(floor(a + 0.5)) == b); }\n";
|
|
if (cmpTypeFound[TYPE_UINT]) stream << "bool isOk (float a, uint b) { float atemp = a+0.5; return (float(b) <= atemp && atemp <= float(b+1u)); }\n";
|
|
if (cmpTypeFound[TYPE_UINT_VEC2]) stream << "bool isOk (vec2 a, uvec2 b) { return (uvec2(floor(a + 0.5)) == b); }\n";
|
|
if (cmpTypeFound[TYPE_UINT_VEC3]) stream << "bool isOk (vec3 a, uvec3 b) { return (uvec3(floor(a + 0.5)) == b); }\n";
|
|
if (cmpTypeFound[TYPE_UINT_VEC4]) stream << "bool isOk (vec4 a, uvec4 b) { return (uvec4(floor(a + 0.5)) == b); }\n";
|
|
}
|
|
else
|
|
{
|
|
if (cmpTypeFound[TYPE_BOOL]) stream << "bool isOk (bool a, bool b) { return (a == b); }\n";
|
|
if (cmpTypeFound[TYPE_BOOL_VEC2]) stream << "bool isOk (bvec2 a, bvec2 b) { return (a == b); }\n";
|
|
if (cmpTypeFound[TYPE_BOOL_VEC3]) stream << "bool isOk (bvec3 a, bvec3 b) { return (a == b); }\n";
|
|
if (cmpTypeFound[TYPE_BOOL_VEC4]) stream << "bool isOk (bvec4 a, bvec4 b) { return (a == b); }\n";
|
|
if (cmpTypeFound[TYPE_INT]) stream << "bool isOk (int a, int b) { return (a == b); }\n";
|
|
if (cmpTypeFound[TYPE_INT_VEC2]) stream << "bool isOk (ivec2 a, ivec2 b) { return (a == b); }\n";
|
|
if (cmpTypeFound[TYPE_INT_VEC3]) stream << "bool isOk (ivec3 a, ivec3 b) { return (a == b); }\n";
|
|
if (cmpTypeFound[TYPE_INT_VEC4]) stream << "bool isOk (ivec4 a, ivec4 b) { return (a == b); }\n";
|
|
if (cmpTypeFound[TYPE_UINT]) stream << "bool isOk (uint a, uint b) { return (a == b); }\n";
|
|
if (cmpTypeFound[TYPE_UINT_VEC2]) stream << "bool isOk (uvec2 a, uvec2 b) { return (a == b); }\n";
|
|
if (cmpTypeFound[TYPE_UINT_VEC3]) stream << "bool isOk (uvec3 a, uvec3 b) { return (a == b); }\n";
|
|
if (cmpTypeFound[TYPE_UINT_VEC4]) stream << "bool isOk (uvec4 a, uvec4 b) { return (a == b); }\n";
|
|
}
|
|
|
|
if (cmpTypeFound[TYPE_FLOAT]) stream << "bool isOk (float a, float b, float eps) { return (abs(a-b) <= (eps*abs(b) + eps)); }\n";
|
|
if (cmpTypeFound[TYPE_FLOAT_VEC2]) stream << "bool isOk (vec2 a, vec2 b, float eps) { return all(lessThanEqual(abs(a-b), (eps*abs(b) + eps))); }\n";
|
|
if (cmpTypeFound[TYPE_FLOAT_VEC3]) stream << "bool isOk (vec3 a, vec3 b, float eps) { return all(lessThanEqual(abs(a-b), (eps*abs(b) + eps))); }\n";
|
|
if (cmpTypeFound[TYPE_FLOAT_VEC4]) stream << "bool isOk (vec4 a, vec4 b, float eps) { return all(lessThanEqual(abs(a-b), (eps*abs(b) + eps))); }\n";
|
|
|
|
if (cmpTypeFound[TYPE_FLOAT_MAT2]) stream << "bool isOk (mat2 a, mat2 b, float eps) { vec2 diff = max(abs(a[0]-b[0]), abs(a[1]-b[1])); return all(lessThanEqual(diff, vec2(eps))); }\n";
|
|
if (cmpTypeFound[TYPE_FLOAT_MAT2X3]) stream << "bool isOk (mat2x3 a, mat2x3 b, float eps) { vec3 diff = max(abs(a[0]-b[0]), abs(a[1]-b[1])); return all(lessThanEqual(diff, vec3(eps))); }\n";
|
|
if (cmpTypeFound[TYPE_FLOAT_MAT2X4]) stream << "bool isOk (mat2x4 a, mat2x4 b, float eps) { vec4 diff = max(abs(a[0]-b[0]), abs(a[1]-b[1])); return all(lessThanEqual(diff, vec4(eps))); }\n";
|
|
if (cmpTypeFound[TYPE_FLOAT_MAT3X2]) stream << "bool isOk (mat3x2 a, mat3x2 b, float eps) { vec2 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), abs(a[2]-b[2])); return all(lessThanEqual(diff, vec2(eps))); }\n";
|
|
if (cmpTypeFound[TYPE_FLOAT_MAT3]) stream << "bool isOk (mat3 a, mat3 b, float eps) { vec3 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), abs(a[2]-b[2])); return all(lessThanEqual(diff, vec3(eps))); }\n";
|
|
if (cmpTypeFound[TYPE_FLOAT_MAT3X4]) stream << "bool isOk (mat3x4 a, mat3x4 b, float eps) { vec4 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), abs(a[2]-b[2])); return all(lessThanEqual(diff, vec4(eps))); }\n";
|
|
if (cmpTypeFound[TYPE_FLOAT_MAT4X2]) stream << "bool isOk (mat4x2 a, mat4x2 b, float eps) { vec2 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), max(abs(a[2]-b[2]), abs(a[3]-b[3]))); return all(lessThanEqual(diff, vec2(eps))); }\n";
|
|
if (cmpTypeFound[TYPE_FLOAT_MAT4X3]) stream << "bool isOk (mat4x3 a, mat4x3 b, float eps) { vec3 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), max(abs(a[2]-b[2]), abs(a[3]-b[3]))); return all(lessThanEqual(diff, vec3(eps))); }\n";
|
|
if (cmpTypeFound[TYPE_FLOAT_MAT4]) stream << "bool isOk (mat4 a, mat4 b, float eps) { vec4 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), max(abs(a[2]-b[2]), abs(a[3]-b[3]))); return all(lessThanEqual(diff, vec4(eps))); }\n";
|
|
}
|
|
|
|
} // sl
|
|
} // glu
|