blob: b65ec00247207f34115ef782dce8e0000ba29046 [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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 de::UniquePtr;
using std::map;
using std::ostringstream;
using std::pair;
using std::string;
using std::vector;
// 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 uint32_t vtxFragMask = (1u << SHADERTYPE_VERTEX) | (1u << SHADERTYPE_FRAGMENT);
const uint32_t tessCtrlEvalMask =
(1u << SHADERTYPE_TESSELLATION_CONTROL) | (1u << SHADERTYPE_TESSELLATION_EVALUATION);
const uint32_t 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)
{
uint32_t 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 uint32_t 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 bool isWhitespace(char c)
{
return (c == ' ') || (c == '\t') || (c == '\r') || (c == '\n');
}
DE_INLINE bool isEOL(char c)
{
return (c == '\r') || (c == '\n');
}
DE_INLINE bool isNumeric(char c)
{
return deInRange32(c, '0', '9');
}
DE_INLINE bool isAlpha(char c)
{
return deInRange32(c, 'a', 'z') || deInRange32(c, 'A', 'Z');
}
DE_INLINE bool 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);
uint32_t getShaderStageLiteralFlag(void);
uint32_t getGLEnumFromName(const std::string &enumName);
void parseValueElement(DataType dataType, Value &result);
void parseValue(ValueBlock &valueBlock);
void parseValueBlock(ValueBlock &valueBlock);
uint32_t 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(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(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>";
}
}
uint32_t 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;
}
}
uint32_t ShaderParser::getGLEnumFromName(const std::string &enumName)
{
static const struct
{
const char *name;
uint32_t 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
{
advanceToken(TOKEN_LEFT_BRACKET);
for (;;)
{
parseValueElement(basicType, dstBlock->back());
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);
}
uint32_t ShaderParser::parseShaderStageList(void)
{
uint32_t 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)
{
uint32_t 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;
uint32_t 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")
{
uint32_t 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> unusedCaps;
size_t size = program.requiredExtensions.size();
parseRequirement(unusedCaps, 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:
DE_FATAL("Unreachable");
}
}
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:
DE_FATAL("Unreachable");
}
}
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((uint8_t *)&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 << (uint32_t)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 << (uint32_t)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";
}
} // namespace sl
} // namespace glu