1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES Utilities
3 * ------------------------------------------------
4 *
5 * Copyright 2015 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Shader .test file utilities.
22 *//*--------------------------------------------------------------------*/
23
24#include "gluShaderLibrary.hpp"
25
26#include "tcuStringTemplate.hpp"
27#include "tcuResource.hpp"
28#include "tcuTestLog.hpp"
29
30#include "deStringUtil.hpp"
31#include "deUniquePtr.hpp"
32#include "deFilePath.hpp"
33
34#include "glwEnums.hpp"
35
36#include <sstream>
37#include <map>
38#include <cstdlib>
39
40#if 0
41#	define PARSE_DBG(X) printf X
42#else
43#	define PARSE_DBG(X) DE_NULL_STATEMENT
44#endif
45
46namespace glu
47{
48namespace sl
49{
50
51using namespace tcu;
52
53using std::vector;
54using std::string;
55using std::map;
56using std::ostringstream;
57using std::pair;
58using de::UniquePtr;
59
60// Specification
61
62bool isValid (const ValueBlock& block)
63{
64	for (size_t storageNdx = 0; storageNdx < 3; ++storageNdx)
65	{
66		const vector<Value>&	values		= storageNdx == 0 ? block.inputs	:
67											  storageNdx == 1 ? block.outputs	:
68																block.uniforms;
69		const size_t			refArrayLen	= values.empty() ? 0 : (values[0].elements.size() / (size_t)values[0].type.getScalarSize());
70
71		for (size_t valNdx = 0; valNdx < values.size(); ++valNdx)
72		{
73			const Value&	value	= values[valNdx];
74
75			if (!value.type.isBasicType())
76			{
77				print("ERROR: Value '%s' is of unsupported type!\n", value.name.c_str());
78				return false;
79			}
80
81			if (value.elements.size() != refArrayLen*(size_t)value.type.getScalarSize())
82			{
83				print("ERROR: Value '%s' has invalid number of scalars!\n", value.name.c_str());
84				return false;
85			}
86		}
87	}
88
89	return true;
90}
91
92bool isValid (const ShaderCaseSpecification& spec)
93{
94	const deUint32	vtxFragMask			= (1u << SHADERTYPE_VERTEX)
95										| (1u << SHADERTYPE_FRAGMENT);
96	const deUint32	tessCtrlEvalMask	= (1u << SHADERTYPE_TESSELLATION_CONTROL)
97										| (1u << SHADERTYPE_TESSELLATION_EVALUATION);
98	const deUint32	supportedStageMask	= vtxFragMask | tessCtrlEvalMask
99										| (1u << SHADERTYPE_GEOMETRY);
100	const bool		isSeparable			= !spec.programs.empty() && spec.programs[0].sources.separable;
101
102	if (spec.programs.empty())
103	{
104		print("ERROR: No programs specified!\n");
105		return false;
106	}
107
108	if (isCapabilityRequired(CAPABILITY_FULL_GLSL_ES_100_SUPPORT, spec))
109	{
110		if (spec.targetVersion != GLSL_VERSION_100_ES)
111		{
112			print("ERROR: Full GLSL ES 1.00 support requested for other GLSL version!\n");
113			return false;
114		}
115
116		if (spec.expectResult != EXPECT_PASS			&&
117			spec.expectResult != EXPECT_VALIDATION_FAIL	&&
118			spec.expectResult != EXPECT_BUILD_SUCCESSFUL)
119		{
120			print("ERROR: Full GLSL ES 1.00 support doesn't make sense when expecting compile/link failure!\n");
121			return false;
122		}
123	}
124
125	if (!de::inBounds(spec.caseType, (CaseType)0, CASETYPE_LAST))
126	{
127		print("ERROR: Invalid case type!\n");
128		return false;
129	}
130
131	if (!de::inBounds(spec.expectResult, (ExpectResult)0, EXPECT_LAST))
132	{
133		print("ERROR: Invalid expected result!\n");
134		return false;
135	}
136
137	if (!isValid(spec.values))
138		return false;
139
140	if (!spec.values.inputs.empty() && !spec.values.outputs.empty() &&
141		spec.values.inputs[0].elements.size() / spec.values.inputs[0].type.getScalarSize() != spec.values.outputs[0].elements.size() / spec.values.outputs[0].type.getScalarSize())
142	{
143		print("ERROR: Number of input and output elements don't match!\n");
144		return false;
145	}
146
147	if (isSeparable)
148	{
149		deUint32	usedStageMask	= 0u;
150
151		if (spec.caseType != CASETYPE_COMPLETE)
152		{
153			print("ERROR: Separable shaders supported only for complete cases!\n");
154			return false;
155		}
156
157		for (size_t progNdx = 0; progNdx < spec.programs.size(); ++progNdx)
158		{
159			for (int shaderStageNdx = 0; shaderStageNdx < SHADERTYPE_LAST; ++shaderStageNdx)
160			{
161				const deUint32	curStageMask	= (1u << shaderStageNdx);
162
163				if (supportedStageMask & curStageMask)
164				{
165					const bool		hasShader	= !spec.programs[progNdx].sources.sources[shaderStageNdx].empty();
166					const bool		isEnabled	= (spec.programs[progNdx].activeStages & curStageMask) != 0;
167
168					if (hasShader != isEnabled)
169					{
170						print("ERROR: Inconsistent source/enable for shader stage %s!\n", getShaderTypeName((ShaderType)shaderStageNdx));
171						return false;
172					}
173
174					if (hasShader && (usedStageMask & curStageMask) != 0)
175					{
176						print("ERROR: Stage %s enabled on multiple programs!\n", getShaderTypeName((ShaderType)shaderStageNdx));
177						return false;
178					}
179
180					if (isEnabled)
181						usedStageMask |= curStageMask;
182				}
183				else if (!spec.programs[progNdx].sources.sources[shaderStageNdx].empty())
184				{
185					print("ERROR: Source specified for unsupported shader stage %s!\n", getShaderTypeName((ShaderType)shaderStageNdx));
186					return false;
187				}
188			}
189		}
190
191		if ((usedStageMask & vtxFragMask) != vtxFragMask)
192		{
193			print("ERROR: Vertex and fragment shaders are mandatory!\n");
194			return false;
195		}
196
197		if ((usedStageMask & tessCtrlEvalMask) != 0 && (usedStageMask & tessCtrlEvalMask) != tessCtrlEvalMask)
198		{
199			print("ERROR: Both tessellation control and eval shaders must be either enabled or disabled!\n");
200			return false;
201		}
202	}
203	else
204	{
205		const bool	hasVertex		= !spec.programs[0].sources.sources[SHADERTYPE_VERTEX].empty();
206		const bool	hasFragment		= !spec.programs[0].sources.sources[SHADERTYPE_FRAGMENT].empty();
207
208		if (spec.programs.size() != 1)
209		{
210			print("ERROR: Only cases using separable programs can have multiple programs!\n");
211			return false;
212		}
213
214		if (spec.caseType == CASETYPE_VERTEX_ONLY && (!hasVertex || hasFragment))
215		{
216			print("ERROR: Vertex-only case must have only vertex shader!\n");
217			return false;
218		}
219
220		if (spec.caseType == CASETYPE_FRAGMENT_ONLY && (hasVertex || !hasFragment))
221		{
222			print("ERROR: Fragment-only case must have only fragment shader!\n");
223			return false;
224		}
225
226		if (spec.caseType == CASETYPE_COMPLETE && (!hasVertex || !hasFragment))
227		{
228			print("ERROR: Complete case must have at least vertex and fragment shaders\n");
229			return false;
230		}
231	}
232
233	return true;
234}
235
236bool isCapabilityRequired(CapabilityFlag capabilityFlag, const ShaderCaseSpecification& spec)
237{
238	std::vector<RequiredCapability>::const_iterator currRequirement = spec.requiredCaps.begin();
239	while (currRequirement != spec.requiredCaps.end())
240	{
241		if ((currRequirement->type == CAPABILITY_FLAG) && (currRequirement->flagName == capabilityFlag))
242			return true;
243		++currRequirement;
244	}
245
246	return false;
247}
248
249// Parser
250
251static const glu::GLSLVersion DEFAULT_GLSL_VERSION = glu::GLSL_VERSION_100_ES;
252
253DE_INLINE deBool isWhitespace (char c)
254{
255	return (c == ' ') || (c == '\t') || (c == '\r') || (c == '\n');
256}
257
258DE_INLINE deBool isEOL (char c)
259{
260	return (c == '\r') || (c == '\n');
261}
262
263DE_INLINE deBool isNumeric (char c)
264{
265	return deInRange32(c, '0', '9');
266}
267
268DE_INLINE deBool isAlpha (char c)
269{
270	return deInRange32(c, 'a', 'z') || deInRange32(c, 'A', 'Z');
271}
272
273DE_INLINE deBool isCaseNameChar (char c)
274{
275	return deInRange32(c, 'a', 'z') || deInRange32(c, 'A', 'Z') || deInRange32(c, '0', '9') || (c == '_') || (c == '-') || (c == '.');
276}
277
278class ShaderParser
279{
280public:
281							ShaderParser			(const tcu::Archive& archive, const std::string& filename, ShaderCaseFactory* caseFactory);
282							~ShaderParser			(void);
283
284	vector<tcu::TestNode*>	parse					(void);
285
286private:
287	enum Token
288	{
289		TOKEN_INVALID = 0,
290		TOKEN_EOF,
291		TOKEN_STRING,
292		TOKEN_SHADER_SOURCE,
293
294		TOKEN_INT_LITERAL,
295		TOKEN_FLOAT_LITERAL,
296
297		// identifiers
298		TOKEN_IDENTIFIER,
299		TOKEN_TRUE,
300		TOKEN_FALSE,
301		TOKEN_DESC,
302		TOKEN_EXPECT,
303		TOKEN_GROUP,
304		TOKEN_CASE,
305		TOKEN_END,
306		TOKEN_OUTPUT_COLOR,
307		TOKEN_FORMAT,
308		TOKEN_VALUES,
309		TOKEN_BOTH,
310		TOKEN_VERTEX,
311		TOKEN_FRAGMENT,
312		TOKEN_UNIFORM,
313		TOKEN_INPUT,
314		TOKEN_OUTPUT,
315		TOKEN_FLOAT,
316		TOKEN_FLOAT_VEC2,
317		TOKEN_FLOAT_VEC3,
318		TOKEN_FLOAT_VEC4,
319		TOKEN_FLOAT_MAT2,
320		TOKEN_FLOAT_MAT2X3,
321		TOKEN_FLOAT_MAT2X4,
322		TOKEN_FLOAT_MAT3X2,
323		TOKEN_FLOAT_MAT3,
324		TOKEN_FLOAT_MAT3X4,
325		TOKEN_FLOAT_MAT4X2,
326		TOKEN_FLOAT_MAT4X3,
327		TOKEN_FLOAT_MAT4,
328		TOKEN_INT,
329		TOKEN_INT_VEC2,
330		TOKEN_INT_VEC3,
331		TOKEN_INT_VEC4,
332		TOKEN_UINT,
333		TOKEN_UINT_VEC2,
334		TOKEN_UINT_VEC3,
335		TOKEN_UINT_VEC4,
336		TOKEN_BOOL,
337		TOKEN_BOOL_VEC2,
338		TOKEN_BOOL_VEC3,
339		TOKEN_BOOL_VEC4,
340		TOKEN_VERSION,
341		TOKEN_TESSELLATION_CONTROL,
342		TOKEN_TESSELLATION_EVALUATION,
343		TOKEN_GEOMETRY,
344		TOKEN_REQUIRE,
345		TOKEN_IN,
346		TOKEN_IMPORT,
347		TOKEN_PIPELINE_PROGRAM,
348		TOKEN_ACTIVE_STAGES,
349
350		// symbols
351		TOKEN_ASSIGN,
352		TOKEN_PLUS,
353		TOKEN_MINUS,
354		TOKEN_COMMA,
355		TOKEN_VERTICAL_BAR,
356		TOKEN_SEMI_COLON,
357		TOKEN_LEFT_PAREN,
358		TOKEN_RIGHT_PAREN,
359		TOKEN_LEFT_BRACKET,
360		TOKEN_RIGHT_BRACKET,
361		TOKEN_LEFT_BRACE,
362		TOKEN_RIGHT_BRACE,
363		TOKEN_GREATER,
364
365		TOKEN_LAST
366	};
367
368	void						parseError					(const std::string& errorStr);
369	float						parseFloatLiteral			(const char* str);
370	int							parseIntLiteral				(const char* str);
371	string						parseStringLiteral			(const char* str);
372	string						parseShaderSource			(const char* str);
373	void						advanceToken				(void);
374	void						advanceToken				(Token assumed);
375	void						assumeToken					(Token token);
376	DataType					mapDataTypeToken			(Token token);
377	const char*					getTokenName				(Token token);
378	deUint32					getShaderStageLiteralFlag	(void);
379	deUint32					getGLEnumFromName			(const std::string& enumName);
380
381	void						parseValueElement			(DataType dataType, Value& result);
382	void						parseValue					(ValueBlock& valueBlock);
383	void						parseValueBlock				(ValueBlock& valueBlock);
384	deUint32					parseShaderStageList		(void);
385	void						parseRequirement			(vector<RequiredCapability> &requiredCaps, vector<RequiredExtension> &requiredExts);
386	void						parseExpectResult			(ExpectResult& expectResult);
387	void						parseFormat					(DataType& format);
388	void						parseGLSLVersion			(glu::GLSLVersion& version);
389	void						parsePipelineProgram		(ProgramSpecification& program);
390	void						parseShaderCase				(vector<tcu::TestNode*>& shaderNodeList);
391	void						parseShaderGroup			(vector<tcu::TestNode*>& shaderNodeList);
392	void						parseImport					(vector<tcu::TestNode*>& shaderNodeList);
393
394	const tcu::Archive&			m_archive;
395	const string				m_filename;
396	ShaderCaseFactory* const	m_caseFactory;
397
398	UniquePtr<tcu::Resource>	m_resource;
399	vector<char>				m_input;
400
401	const char*					m_curPtr;
402	Token						m_curToken;
403	std::string					m_curTokenStr;
404};
405
406ShaderParser::ShaderParser (const tcu::Archive& archive, const string& filename, ShaderCaseFactory* caseFactroy)
407	: m_archive			(archive)
408	, m_filename		(filename)
409	, m_caseFactory		(caseFactroy)
410	, m_resource		(archive.getResource(m_filename.c_str()))
411	, m_curPtr			(DE_NULL)
412	, m_curToken		(TOKEN_LAST)
413{
414}
415
416ShaderParser::~ShaderParser (void)
417{
418}
419
420void ShaderParser::parseError (const std::string& errorStr)
421{
422	string atStr = string(m_curPtr, 80);
423	throw tcu::InternalError((string("Parser error: ") + errorStr + " near '" + atStr + " ...'").c_str(), DE_NULL, __FILE__, __LINE__);
424}
425
426float ShaderParser::parseFloatLiteral (const char* str)
427{
428	return (float)atof(str);
429}
430
431int ShaderParser::parseIntLiteral (const char* str)
432{
433	return atoi(str);
434}
435
436string ShaderParser::parseStringLiteral (const char* str)
437{
438	const char*		p		= str;
439	char			endChar = *p++;
440	ostringstream	o;
441
442	while (*p != endChar && *p)
443	{
444		if (*p == '\\')
445		{
446			switch (p[1])
447			{
448				case 0:		DE_ASSERT(DE_FALSE);	break;
449				case 'n':	o << '\n';				break;
450				case 't':	o << '\t';				break;
451				default:	o << p[1];				break;
452			}
453
454			p += 2;
455		}
456		else
457			o << *p++;
458	}
459
460	return o.str();
461}
462
463static string removeExtraIndentation (const string& source)
464{
465	// Detect indentation from first line.
466	int numIndentChars = 0;
467	for (int ndx = 0; ndx < (int)source.length() && isWhitespace(source[ndx]); ndx++)
468		numIndentChars += source[ndx] == '\t' ? 4 : 1;
469
470	// Process all lines and remove preceding indentation.
471	ostringstream processed;
472	{
473		bool	atLineStart			= true;
474		int		indentCharsOmitted	= 0;
475
476		for (int pos = 0; pos < (int)source.length(); pos++)
477		{
478			char c = source[pos];
479
480			if (atLineStart && indentCharsOmitted < numIndentChars && (c == ' ' || c == '\t'))
481			{
482				indentCharsOmitted += c == '\t' ? 4 : 1;
483			}
484			else if (isEOL(c))
485			{
486				if (source[pos] == '\r' && source[pos+1] == '\n')
487				{
488					pos += 1;
489					processed << '\n';
490				}
491				else
492					processed << c;
493
494				atLineStart			= true;
495				indentCharsOmitted	= 0;
496			}
497			else
498			{
499				processed << c;
500				atLineStart = false;
501			}
502		}
503	}
504
505	return processed.str();
506}
507
508string ShaderParser::parseShaderSource (const char* str)
509{
510	const char*		p = str+2;
511	ostringstream	o;
512
513	// Eat first empty line from beginning.
514	while (*p == ' ') p++;
515	if (*p == '\r') p++;
516	if (*p == '\n') p++;
517
518	while ((p[0] != '"') || (p[1] != '"'))
519	{
520		if (*p == '\\')
521		{
522			switch (p[1])
523			{
524				case 0:		DE_ASSERT(DE_FALSE);	break;
525				case 'n':	o << '\n';				break;
526				case 't':	o << '\t';				break;
527				default:	o << p[1];				break;
528			}
529
530			p += 2;
531		}
532		else
533			o << *p++;
534	}
535
536	return removeExtraIndentation(o.str());
537}
538
539void ShaderParser::advanceToken (void)
540{
541	// Skip old token.
542	m_curPtr += m_curTokenStr.length();
543
544	// Reset token (for safety).
545	m_curToken		= TOKEN_INVALID;
546	m_curTokenStr	= "";
547
548	// Eat whitespace & comments while they last.
549	for (;;)
550	{
551		while (isWhitespace(*m_curPtr))
552			m_curPtr++;
553
554		// Check for EOL comment.
555		if (*m_curPtr == '#')
556		{
557			while (*m_curPtr && !isEOL(*m_curPtr))
558				m_curPtr++;
559		}
560		else
561			break;
562	}
563
564	if (!*m_curPtr)
565	{
566		m_curToken = TOKEN_EOF;
567		m_curTokenStr = "<EOF>";
568	}
569	else if (isAlpha(*m_curPtr))
570	{
571		struct Named
572		{
573			const char*		str;
574			Token			token;
575		};
576
577		static const Named s_named[] =
578		{
579			{ "true",						TOKEN_TRUE						},
580			{ "false",						TOKEN_FALSE						},
581			{ "desc",						TOKEN_DESC						},
582			{ "expect",						TOKEN_EXPECT					},
583			{ "group",						TOKEN_GROUP						},
584			{ "case",						TOKEN_CASE						},
585			{ "end",						TOKEN_END						},
586			{ "output_color",				TOKEN_OUTPUT_COLOR				},
587			{ "format",						TOKEN_FORMAT					},
588			{ "values",						TOKEN_VALUES					},
589			{ "both",						TOKEN_BOTH						},
590			{ "vertex",						TOKEN_VERTEX					},
591			{ "fragment",					TOKEN_FRAGMENT					},
592			{ "uniform",					TOKEN_UNIFORM					},
593			{ "input",						TOKEN_INPUT						},
594			{ "output",						TOKEN_OUTPUT					},
595			{ "float",						TOKEN_FLOAT						},
596			{ "vec2",						TOKEN_FLOAT_VEC2				},
597			{ "vec3",						TOKEN_FLOAT_VEC3				},
598			{ "vec4",						TOKEN_FLOAT_VEC4				},
599			{ "mat2",						TOKEN_FLOAT_MAT2				},
600			{ "mat2x3",						TOKEN_FLOAT_MAT2X3				},
601			{ "mat2x4",						TOKEN_FLOAT_MAT2X4				},
602			{ "mat3x2",						TOKEN_FLOAT_MAT3X2				},
603			{ "mat3",						TOKEN_FLOAT_MAT3				},
604			{ "mat3x4",						TOKEN_FLOAT_MAT3X4				},
605			{ "mat4x2",						TOKEN_FLOAT_MAT4X2				},
606			{ "mat4x3",						TOKEN_FLOAT_MAT4X3				},
607			{ "mat4",						TOKEN_FLOAT_MAT4				},
608			{ "int",						TOKEN_INT						},
609			{ "ivec2",						TOKEN_INT_VEC2					},
610			{ "ivec3",						TOKEN_INT_VEC3					},
611			{ "ivec4",						TOKEN_INT_VEC4					},
612			{ "uint",						TOKEN_UINT						},
613			{ "uvec2",						TOKEN_UINT_VEC2					},
614			{ "uvec3",						TOKEN_UINT_VEC3					},
615			{ "uvec4",						TOKEN_UINT_VEC4					},
616			{ "bool",						TOKEN_BOOL						},
617			{ "bvec2",						TOKEN_BOOL_VEC2					},
618			{ "bvec3",						TOKEN_BOOL_VEC3					},
619			{ "bvec4",						TOKEN_BOOL_VEC4					},
620			{ "version",					TOKEN_VERSION					},
621			{ "tessellation_control",		TOKEN_TESSELLATION_CONTROL		},
622			{ "tessellation_evaluation",	TOKEN_TESSELLATION_EVALUATION	},
623			{ "geometry",					TOKEN_GEOMETRY					},
624			{ "require",					TOKEN_REQUIRE					},
625			{ "in",							TOKEN_IN						},
626			{ "import",						TOKEN_IMPORT					},
627			{ "pipeline_program",			TOKEN_PIPELINE_PROGRAM			},
628			{ "active_stages",				TOKEN_ACTIVE_STAGES				},
629		};
630
631		const char* end = m_curPtr + 1;
632		while (isCaseNameChar(*end))
633			end++;
634		m_curTokenStr = string(m_curPtr, end - m_curPtr);
635
636		m_curToken = TOKEN_IDENTIFIER;
637
638		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_named); ndx++)
639		{
640			if (m_curTokenStr == s_named[ndx].str)
641			{
642				m_curToken = s_named[ndx].token;
643				break;
644			}
645		}
646	}
647	else if (isNumeric(*m_curPtr))
648	{
649		/* \todo [2010-03-31 petri] Hex? */
650		const char* p = m_curPtr;
651		while (isNumeric(*p))
652			p++;
653		if (*p == '.')
654		{
655			p++;
656			while (isNumeric(*p))
657				p++;
658
659			if (*p == 'e' || *p == 'E')
660			{
661				p++;
662				if (*p == '+' || *p == '-')
663					p++;
664				DE_ASSERT(isNumeric(*p));
665				while (isNumeric(*p))
666					p++;
667			}
668
669			m_curToken = TOKEN_FLOAT_LITERAL;
670			m_curTokenStr = string(m_curPtr, p - m_curPtr);
671		}
672		else
673		{
674			m_curToken = TOKEN_INT_LITERAL;
675			m_curTokenStr = string(m_curPtr, p - m_curPtr);
676		}
677	}
678	else if (*m_curPtr == '"' && m_curPtr[1] == '"')
679	{
680		const char*	p = m_curPtr + 2;
681
682		while ((p[0] != '"') || (p[1] != '"'))
683		{
684			DE_ASSERT(*p);
685			if (*p == '\\')
686			{
687				DE_ASSERT(p[1] != 0);
688				p += 2;
689			}
690			else
691				p++;
692		}
693		p += 2;
694
695		m_curToken		= TOKEN_SHADER_SOURCE;
696		m_curTokenStr	= string(m_curPtr, (int)(p - m_curPtr));
697	}
698	else if (*m_curPtr == '"' || *m_curPtr == '\'')
699	{
700		char		endChar = *m_curPtr;
701		const char*	p		= m_curPtr + 1;
702
703		while (*p != endChar)
704		{
705			DE_ASSERT(*p);
706			if (*p == '\\')
707			{
708				DE_ASSERT(p[1] != 0);
709				p += 2;
710			}
711			else
712				p++;
713		}
714		p++;
715
716		m_curToken		= TOKEN_STRING;
717		m_curTokenStr	= string(m_curPtr, (int)(p - m_curPtr));
718	}
719	else
720	{
721		struct SimpleToken
722		{
723			const char*		str;
724			Token			token;
725		};
726
727		static const SimpleToken s_simple[] =
728		{
729			{ "=",			TOKEN_ASSIGN		},
730			{ "+",			TOKEN_PLUS			},
731			{ "-",			TOKEN_MINUS			},
732			{ ",",			TOKEN_COMMA			},
733			{ "|",			TOKEN_VERTICAL_BAR	},
734			{ ";",			TOKEN_SEMI_COLON	},
735			{ "(",			TOKEN_LEFT_PAREN	},
736			{ ")",			TOKEN_RIGHT_PAREN	},
737			{ "[",			TOKEN_LEFT_BRACKET	},
738			{ "]",			TOKEN_RIGHT_BRACKET },
739			{ "{",			TOKEN_LEFT_BRACE	},
740			{ "}",			TOKEN_RIGHT_BRACE	},
741			{ ">",			TOKEN_GREATER		},
742		};
743
744		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_simple); ndx++)
745		{
746			if (strncmp(s_simple[ndx].str, m_curPtr, strlen(s_simple[ndx].str)) == 0)
747			{
748				m_curToken		= s_simple[ndx].token;
749				m_curTokenStr	= s_simple[ndx].str;
750				return;
751			}
752		}
753
754		// Otherwise invalid token.
755		m_curToken = TOKEN_INVALID;
756		m_curTokenStr = *m_curPtr;
757	}
758}
759
760void ShaderParser::advanceToken (Token assumed)
761{
762	assumeToken(assumed);
763	advanceToken();
764}
765
766void ShaderParser::assumeToken (Token token)
767{
768	if (m_curToken != token)
769		parseError((string("unexpected token '") + m_curTokenStr + "', expecting '" + getTokenName(token) + "'").c_str());
770	DE_TEST_ASSERT(m_curToken == token);
771}
772
773DataType ShaderParser::mapDataTypeToken (Token token)
774{
775	switch (token)
776	{
777		case TOKEN_FLOAT:			return TYPE_FLOAT;
778		case TOKEN_FLOAT_VEC2:		return TYPE_FLOAT_VEC2;
779		case TOKEN_FLOAT_VEC3:		return TYPE_FLOAT_VEC3;
780		case TOKEN_FLOAT_VEC4:		return TYPE_FLOAT_VEC4;
781		case TOKEN_FLOAT_MAT2:		return TYPE_FLOAT_MAT2;
782		case TOKEN_FLOAT_MAT2X3:	return TYPE_FLOAT_MAT2X3;
783		case TOKEN_FLOAT_MAT2X4:	return TYPE_FLOAT_MAT2X4;
784		case TOKEN_FLOAT_MAT3X2:	return TYPE_FLOAT_MAT3X2;
785		case TOKEN_FLOAT_MAT3:		return TYPE_FLOAT_MAT3;
786		case TOKEN_FLOAT_MAT3X4:	return TYPE_FLOAT_MAT3X4;
787		case TOKEN_FLOAT_MAT4X2:	return TYPE_FLOAT_MAT4X2;
788		case TOKEN_FLOAT_MAT4X3:	return TYPE_FLOAT_MAT4X3;
789		case TOKEN_FLOAT_MAT4:		return TYPE_FLOAT_MAT4;
790		case TOKEN_INT:				return TYPE_INT;
791		case TOKEN_INT_VEC2:		return TYPE_INT_VEC2;
792		case TOKEN_INT_VEC3:		return TYPE_INT_VEC3;
793		case TOKEN_INT_VEC4:		return TYPE_INT_VEC4;
794		case TOKEN_UINT:			return TYPE_UINT;
795		case TOKEN_UINT_VEC2:		return TYPE_UINT_VEC2;
796		case TOKEN_UINT_VEC3:		return TYPE_UINT_VEC3;
797		case TOKEN_UINT_VEC4:		return TYPE_UINT_VEC4;
798		case TOKEN_BOOL:			return TYPE_BOOL;
799		case TOKEN_BOOL_VEC2:		return TYPE_BOOL_VEC2;
800		case TOKEN_BOOL_VEC3:		return TYPE_BOOL_VEC3;
801		case TOKEN_BOOL_VEC4:		return TYPE_BOOL_VEC4;
802		default:					return TYPE_INVALID;
803	}
804}
805
806const char* ShaderParser::getTokenName (Token token)
807{
808	switch (token)
809	{
810		case TOKEN_INVALID:					return "<invalid>";
811		case TOKEN_EOF:						return "<eof>";
812		case TOKEN_STRING:					return "<string>";
813		case TOKEN_SHADER_SOURCE:			return "source";
814
815		case TOKEN_INT_LITERAL:				return "<int>";
816		case TOKEN_FLOAT_LITERAL:			return "<float>";
817
818		// identifiers
819		case TOKEN_IDENTIFIER:				return "<identifier>";
820		case TOKEN_TRUE:					return "true";
821		case TOKEN_FALSE:					return "false";
822		case TOKEN_DESC:					return "desc";
823		case TOKEN_EXPECT:					return "expect";
824		case TOKEN_GROUP:					return "group";
825		case TOKEN_CASE:					return "case";
826		case TOKEN_END:						return "end";
827		case TOKEN_VALUES:					return "values";
828		case TOKEN_BOTH:					return "both";
829		case TOKEN_VERTEX:					return "vertex";
830		case TOKEN_FRAGMENT:				return "fragment";
831		case TOKEN_TESSELLATION_CONTROL:	return "tessellation_control";
832		case TOKEN_TESSELLATION_EVALUATION:	return "tessellation_evaluation";
833		case TOKEN_GEOMETRY:				return "geometry";
834		case TOKEN_REQUIRE:					return "require";
835		case TOKEN_UNIFORM:					return "uniform";
836		case TOKEN_INPUT:					return "input";
837		case TOKEN_OUTPUT:					return "output";
838		case TOKEN_FLOAT:					return "float";
839		case TOKEN_FLOAT_VEC2:				return "vec2";
840		case TOKEN_FLOAT_VEC3:				return "vec3";
841		case TOKEN_FLOAT_VEC4:				return "vec4";
842		case TOKEN_FLOAT_MAT2:				return "mat2";
843		case TOKEN_FLOAT_MAT2X3:			return "mat2x3";
844		case TOKEN_FLOAT_MAT2X4:			return "mat2x4";
845		case TOKEN_FLOAT_MAT3X2:			return "mat3x2";
846		case TOKEN_FLOAT_MAT3:				return "mat3";
847		case TOKEN_FLOAT_MAT3X4:			return "mat3x4";
848		case TOKEN_FLOAT_MAT4X2:			return "mat4x2";
849		case TOKEN_FLOAT_MAT4X3:			return "mat4x3";
850		case TOKEN_FLOAT_MAT4:				return "mat4";
851		case TOKEN_INT:						return "int";
852		case TOKEN_INT_VEC2:				return "ivec2";
853		case TOKEN_INT_VEC3:				return "ivec3";
854		case TOKEN_INT_VEC4:				return "ivec4";
855		case TOKEN_UINT:					return "uint";
856		case TOKEN_UINT_VEC2:				return "uvec2";
857		case TOKEN_UINT_VEC3:				return "uvec3";
858		case TOKEN_UINT_VEC4:				return "uvec4";
859		case TOKEN_BOOL:					return "bool";
860		case TOKEN_BOOL_VEC2:				return "bvec2";
861		case TOKEN_BOOL_VEC3:				return "bvec3";
862		case TOKEN_BOOL_VEC4:				return "bvec4";
863		case TOKEN_IN:						return "in";
864		case TOKEN_IMPORT:					return "import";
865		case TOKEN_PIPELINE_PROGRAM:		return "pipeline_program";
866		case TOKEN_ACTIVE_STAGES:			return "active_stages";
867
868		case TOKEN_ASSIGN:					return "=";
869		case TOKEN_PLUS:					return "+";
870		case TOKEN_MINUS:					return "-";
871		case TOKEN_COMMA:					return ",";
872		case TOKEN_VERTICAL_BAR:			return "|";
873		case TOKEN_SEMI_COLON:				return ";";
874		case TOKEN_LEFT_PAREN:				return "(";
875		case TOKEN_RIGHT_PAREN:				return ")";
876		case TOKEN_LEFT_BRACKET:			return "[";
877		case TOKEN_RIGHT_BRACKET:			return "]";
878		case TOKEN_LEFT_BRACE:				return "{";
879		case TOKEN_RIGHT_BRACE:				return "}";
880		case TOKEN_GREATER:					return ">";
881
882		default:							return "<unknown>";
883	}
884}
885
886deUint32 ShaderParser::getShaderStageLiteralFlag (void)
887{
888	switch (m_curToken)
889	{
890		case TOKEN_VERTEX:					return (1 << glu::SHADERTYPE_VERTEX);
891		case TOKEN_FRAGMENT:				return (1 << glu::SHADERTYPE_FRAGMENT);
892		case TOKEN_GEOMETRY:				return (1 << glu::SHADERTYPE_GEOMETRY);
893		case TOKEN_TESSELLATION_CONTROL:	return (1 << glu::SHADERTYPE_TESSELLATION_CONTROL);
894		case TOKEN_TESSELLATION_EVALUATION:	return (1 << glu::SHADERTYPE_TESSELLATION_EVALUATION);
895
896		default:
897			parseError(std::string() + "invalid shader stage name, got " + m_curTokenStr);
898			return 0;
899	}
900}
901
902deUint32 ShaderParser::getGLEnumFromName (const std::string& enumName)
903{
904	static const struct
905	{
906		const char*	name;
907		deUint32	value;
908	} names[] =
909	{
910		{ "GL_MAX_VERTEX_IMAGE_UNIFORMS",			GL_MAX_VERTEX_IMAGE_UNIFORMS			},
911		{ "GL_MAX_VERTEX_ATOMIC_COUNTERS",			GL_MAX_VERTEX_ATOMIC_COUNTERS			},
912		{ "GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS",	GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS		},
913		{ "GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS",	GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS	},
914	};
915
916	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(names); ++ndx)
917		if (names[ndx].name == enumName)
918			return names[ndx].value;
919
920	parseError(std::string() + "unknown enum name, got " + enumName);
921	return 0;
922}
923
924void ShaderParser::parseValueElement (DataType expectedDataType, Value& result)
925{
926	DataType	scalarType	= getDataTypeScalarType(expectedDataType);
927	int			scalarSize	= getDataTypeScalarSize(expectedDataType);
928
929	/* \todo [2010-04-19 petri] Support arrays. */
930	Value::Element elems[16];
931
932	if (scalarSize > 1)
933	{
934		DE_ASSERT(mapDataTypeToken(m_curToken) == expectedDataType);
935		advanceToken(); // data type (float, vec2, etc.)
936		advanceToken(TOKEN_LEFT_PAREN);
937	}
938
939	for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
940	{
941		if (scalarType == TYPE_FLOAT)
942		{
943			float signMult = 1.0f;
944			if (m_curToken == TOKEN_MINUS)
945			{
946				signMult = -1.0f;
947				advanceToken();
948			}
949
950			assumeToken(TOKEN_FLOAT_LITERAL);
951			elems[scalarNdx].float32 = signMult * parseFloatLiteral(m_curTokenStr.c_str());
952			advanceToken(TOKEN_FLOAT_LITERAL);
953		}
954		else if (scalarType == TYPE_INT || scalarType == TYPE_UINT)
955		{
956			int signMult = 1;
957			if (m_curToken == TOKEN_MINUS)
958			{
959				signMult = -1;
960				advanceToken();
961			}
962
963			assumeToken(TOKEN_INT_LITERAL);
964			elems[scalarNdx].int32 = signMult * parseIntLiteral(m_curTokenStr.c_str());
965			advanceToken(TOKEN_INT_LITERAL);
966		}
967		else
968		{
969			DE_ASSERT(scalarType == TYPE_BOOL);
970			elems[scalarNdx].bool32 = (m_curToken == TOKEN_TRUE);
971			if (m_curToken != TOKEN_TRUE && m_curToken != TOKEN_FALSE)
972				parseError(string("unexpected token, expecting bool: " + m_curTokenStr));
973			advanceToken(); // true/false
974		}
975
976		if (scalarNdx != (scalarSize - 1))
977			advanceToken(TOKEN_COMMA);
978	}
979
980	if (scalarSize > 1)
981		advanceToken(TOKEN_RIGHT_PAREN);
982
983	// Store results.
984	for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
985		result.elements.push_back(elems[scalarNdx]);
986}
987
988void ShaderParser::parseValue (ValueBlock& valueBlock)
989{
990	PARSE_DBG(("      parseValue()\n"));
991
992	// Parsed results.
993	vector<Value>*	dstBlock	= DE_NULL;
994	DataType		basicType	= TYPE_LAST;
995	std::string		valueName;
996
997	// Parse storage.
998	if (m_curToken == TOKEN_UNIFORM)
999		dstBlock = &valueBlock.uniforms;
1000	else if (m_curToken == TOKEN_INPUT)
1001		dstBlock = &valueBlock.inputs;
1002	else if (m_curToken == TOKEN_OUTPUT)
1003		dstBlock = &valueBlock.outputs;
1004	else
1005		parseError(string("unexpected token encountered when parsing value classifier"));
1006	advanceToken();
1007
1008	// Parse data type.
1009	basicType = mapDataTypeToken(m_curToken);
1010	if (basicType == TYPE_INVALID)
1011		parseError(string("unexpected token when parsing value data type: " + m_curTokenStr));
1012	advanceToken();
1013
1014	// Parse value name.
1015	if (m_curToken == TOKEN_IDENTIFIER || m_curToken == TOKEN_STRING)
1016	{
1017		if (m_curToken == TOKEN_IDENTIFIER)
1018			valueName = m_curTokenStr;
1019		else
1020			valueName = parseStringLiteral(m_curTokenStr.c_str());
1021	}
1022	else
1023		parseError(string("unexpected token when parsing value name: " + m_curTokenStr));
1024	advanceToken();
1025
1026	// Parse assignment operator.
1027	advanceToken(TOKEN_ASSIGN);
1028
1029	{
1030		Value value;
1031		value.name	= valueName;
1032		value.type	= VarType(basicType, PRECISION_LAST);
1033		dstBlock->push_back(value);
1034	}
1035
1036	// Parse actual value.
1037	if (m_curToken == TOKEN_LEFT_BRACKET) // value list
1038	{
1039		advanceToken(TOKEN_LEFT_BRACKET);
1040
1041		for (;;)
1042		{
1043			parseValueElement(basicType, dstBlock->back());
1044
1045			if (m_curToken == TOKEN_RIGHT_BRACKET)
1046				break;
1047			else if (m_curToken == TOKEN_VERTICAL_BAR)
1048			{
1049				advanceToken();
1050				continue;
1051			}
1052			else
1053				parseError(string("unexpected token in value element array: " + m_curTokenStr));
1054		}
1055
1056		advanceToken(TOKEN_RIGHT_BRACKET);
1057	}
1058	else //  single elements
1059	{
1060		parseValueElement(basicType, dstBlock->back());
1061	}
1062
1063	advanceToken(TOKEN_SEMI_COLON); // end of declaration
1064}
1065
1066void ShaderParser::parseValueBlock (ValueBlock& valueBlock)
1067{
1068	PARSE_DBG(("    parseValueBlock()\n"));
1069	advanceToken(TOKEN_VALUES);
1070	advanceToken(TOKEN_LEFT_BRACE);
1071
1072	for (;;)
1073	{
1074		if (m_curToken == TOKEN_UNIFORM || m_curToken == TOKEN_INPUT || m_curToken == TOKEN_OUTPUT)
1075			parseValue(valueBlock);
1076		else if (m_curToken == TOKEN_RIGHT_BRACE)
1077			break;
1078		else
1079			parseError(string("unexpected token when parsing a value block: " + m_curTokenStr));
1080	}
1081
1082	advanceToken(TOKEN_RIGHT_BRACE);
1083}
1084
1085deUint32 ShaderParser::parseShaderStageList (void)
1086{
1087	deUint32 mask = 0;
1088
1089	assumeToken(TOKEN_LEFT_BRACE);
1090
1091	// don't allow 0-sized lists
1092	advanceToken();
1093	mask |= getShaderStageLiteralFlag();
1094	advanceToken();
1095
1096	for (;;)
1097	{
1098		if (m_curToken == TOKEN_RIGHT_BRACE)
1099			break;
1100		else if (m_curToken == TOKEN_COMMA)
1101		{
1102			deUint32 stageFlag;
1103			advanceToken();
1104
1105			stageFlag = getShaderStageLiteralFlag();
1106			if (stageFlag & mask)
1107				parseError(string("stage already set in the shader stage set: " + m_curTokenStr));
1108
1109			mask |= stageFlag;
1110			advanceToken();
1111		}
1112		else
1113			parseError(string("invalid shader stage set token: " + m_curTokenStr));
1114	}
1115	advanceToken(TOKEN_RIGHT_BRACE);
1116
1117	return mask;
1118}
1119
1120void ShaderParser::parseRequirement (vector<RequiredCapability>& requiredCaps, vector<RequiredExtension>& requiredExts)
1121{
1122	PARSE_DBG(("    parseRequirement()\n"));
1123
1124	advanceToken();
1125	assumeToken(TOKEN_IDENTIFIER);
1126
1127	if (m_curTokenStr == "extension")
1128	{
1129		std::vector<std::string>	anyExtensionStringList;
1130		deUint32					affectedCasesFlags		= -1; // by default all stages
1131
1132		advanceToken();
1133		assumeToken(TOKEN_LEFT_BRACE);
1134
1135		advanceToken();
1136		assumeToken(TOKEN_STRING);
1137
1138		anyExtensionStringList.push_back(parseStringLiteral(m_curTokenStr.c_str()));
1139		advanceToken();
1140
1141		for (;;)
1142		{
1143			if (m_curToken == TOKEN_RIGHT_BRACE)
1144				break;
1145			else if (m_curToken == TOKEN_VERTICAL_BAR)
1146			{
1147				advanceToken();
1148				assumeToken(TOKEN_STRING);
1149
1150				anyExtensionStringList.push_back(parseStringLiteral(m_curTokenStr.c_str()));
1151				advanceToken();
1152			}
1153			else
1154				parseError(string("invalid extension list token: " + m_curTokenStr));
1155		}
1156		advanceToken(TOKEN_RIGHT_BRACE);
1157
1158		if (m_curToken == TOKEN_IN)
1159		{
1160			advanceToken();
1161			affectedCasesFlags = parseShaderStageList();
1162		}
1163
1164		requiredExts.push_back(RequiredExtension(anyExtensionStringList, affectedCasesFlags));
1165	}
1166	else if (m_curTokenStr == "limit")
1167	{
1168		deUint32	limitEnum;
1169		int			limitValue;
1170
1171		advanceToken();
1172
1173		assumeToken(TOKEN_STRING);
1174		limitEnum = getGLEnumFromName(parseStringLiteral(m_curTokenStr.c_str()));
1175		advanceToken();
1176
1177		assumeToken(TOKEN_GREATER);
1178		advanceToken();
1179
1180		assumeToken(TOKEN_INT_LITERAL);
1181		limitValue = parseIntLiteral(m_curTokenStr.c_str());
1182		advanceToken();
1183
1184		requiredCaps.push_back(RequiredCapability(limitEnum, limitValue));
1185	}
1186	else if (m_curTokenStr == "full_glsl_es_100_support")
1187	{
1188		advanceToken();
1189
1190		requiredCaps.push_back(RequiredCapability(CAPABILITY_FULL_GLSL_ES_100_SUPPORT));
1191	}
1192	else if (m_curTokenStr == "only_glsl_es_100_support")
1193	{
1194		advanceToken();
1195
1196		requiredCaps.push_back(RequiredCapability(CAPABILITY_ONLY_GLSL_ES_100_SUPPORT));
1197	}
1198	else if (m_curTokenStr == "exactly_one_draw_buffer")
1199	{
1200		advanceToken();
1201
1202		requiredCaps.push_back(RequiredCapability(CAPABILITY_EXACTLY_ONE_DRAW_BUFFER));
1203	}
1204	else
1205		parseError(string("invalid requirement value: " + m_curTokenStr));
1206}
1207
1208void ShaderParser::parseExpectResult (ExpectResult& expectResult)
1209{
1210	assumeToken(TOKEN_IDENTIFIER);
1211
1212	if (m_curTokenStr == "pass")
1213		expectResult = EXPECT_PASS;
1214	else if (m_curTokenStr == "compile_fail")
1215		expectResult = EXPECT_COMPILE_FAIL;
1216	else if (m_curTokenStr == "link_fail")
1217		expectResult = EXPECT_LINK_FAIL;
1218	else if (m_curTokenStr == "compile_or_link_fail")
1219		expectResult = EXPECT_COMPILE_LINK_FAIL;
1220	else if (m_curTokenStr == "validation_fail")
1221		expectResult = EXPECT_VALIDATION_FAIL;
1222	else if (m_curTokenStr == "build_successful")
1223		expectResult = EXPECT_BUILD_SUCCESSFUL;
1224	else
1225		parseError(string("invalid expected result value: " + m_curTokenStr));
1226
1227	advanceToken();
1228}
1229
1230void ShaderParser::parseFormat (DataType& format)
1231{
1232	format = mapDataTypeToken(m_curToken);
1233	advanceToken();
1234}
1235
1236void ShaderParser::parseGLSLVersion (glu::GLSLVersion& version)
1237{
1238	int			versionNum		= 0;
1239	std::string	postfix			= "";
1240
1241	assumeToken(TOKEN_INT_LITERAL);
1242	versionNum = parseIntLiteral(m_curTokenStr.c_str());
1243	advanceToken();
1244
1245	if (m_curToken == TOKEN_IDENTIFIER)
1246	{
1247		postfix = m_curTokenStr;
1248		advanceToken();
1249	}
1250
1251	DE_STATIC_ASSERT(glu::GLSL_VERSION_LAST == 15);
1252
1253	if		(versionNum == 100 && postfix == "es")	version = glu::GLSL_VERSION_100_ES;
1254	else if (versionNum == 300 && postfix == "es")	version = glu::GLSL_VERSION_300_ES;
1255	else if (versionNum == 310 && postfix == "es")	version = glu::GLSL_VERSION_310_ES;
1256	else if (versionNum == 320 && postfix == "es")	version = glu::GLSL_VERSION_320_ES;
1257	else if (versionNum == 130)						version = glu::GLSL_VERSION_130;
1258	else if (versionNum == 140)						version = glu::GLSL_VERSION_140;
1259	else if (versionNum == 150)						version = glu::GLSL_VERSION_150;
1260	else if (versionNum == 330)						version = glu::GLSL_VERSION_330;
1261	else if (versionNum == 400)						version = glu::GLSL_VERSION_400;
1262	else if (versionNum == 410)						version = glu::GLSL_VERSION_410;
1263	else if (versionNum == 420)						version = glu::GLSL_VERSION_420;
1264	else if (versionNum == 430)						version = glu::GLSL_VERSION_430;
1265	else if (versionNum == 440)						version = glu::GLSL_VERSION_440;
1266	else if (versionNum == 450)						version = glu::GLSL_VERSION_450;
1267	else if (versionNum == 460)						version = glu::GLSL_VERSION_460;
1268	else
1269		parseError("Unknown GLSL version");
1270}
1271
1272void ShaderParser::parsePipelineProgram (ProgramSpecification& program)
1273{
1274	advanceToken(TOKEN_PIPELINE_PROGRAM);
1275
1276	for (;;)
1277	{
1278		if (m_curToken == TOKEN_END)
1279			break;
1280		else if (m_curToken == TOKEN_ACTIVE_STAGES)
1281		{
1282			advanceToken();
1283			program.activeStages = parseShaderStageList();
1284		}
1285		else if (m_curToken == TOKEN_REQUIRE)
1286		{
1287			vector<RequiredCapability> unusedCaps;
1288			size_t size = program.requiredExtensions.size();
1289			parseRequirement(unusedCaps, program.requiredExtensions);
1290
1291			if (size == program.requiredExtensions.size())
1292				parseError("only extension requirements are allowed inside pipeline program");
1293		}
1294		else if (m_curToken == TOKEN_VERTEX						||
1295				 m_curToken == TOKEN_FRAGMENT					||
1296				 m_curToken == TOKEN_TESSELLATION_CONTROL		||
1297				 m_curToken == TOKEN_TESSELLATION_EVALUATION	||
1298				 m_curToken == TOKEN_GEOMETRY)
1299		{
1300			const Token	token = m_curToken;
1301			string		source;
1302
1303			advanceToken();
1304			assumeToken(TOKEN_SHADER_SOURCE);
1305			source = parseShaderSource(m_curTokenStr.c_str());
1306			advanceToken();
1307
1308			switch (token)
1309			{
1310				case TOKEN_VERTEX:					program.sources.sources[SHADERTYPE_VERTEX].push_back(source);					break;
1311				case TOKEN_FRAGMENT:				program.sources.sources[SHADERTYPE_FRAGMENT].push_back(source);					break;
1312				case TOKEN_TESSELLATION_CONTROL:	program.sources.sources[SHADERTYPE_TESSELLATION_CONTROL].push_back(source);		break;
1313				case TOKEN_TESSELLATION_EVALUATION:	program.sources.sources[SHADERTYPE_TESSELLATION_EVALUATION].push_back(source);	break;
1314				case TOKEN_GEOMETRY:				program.sources.sources[SHADERTYPE_GEOMETRY].push_back(source);					break;
1315				default:
1316					DE_FATAL("Unreachable");
1317			}
1318		}
1319		else
1320			parseError(string("invalid pipeline program value: " + m_curTokenStr));
1321	}
1322	advanceToken(TOKEN_END);
1323
1324	if (program.activeStages == 0)
1325		parseError("program pipeline object must have active stages");
1326}
1327
1328void ShaderParser::parseShaderCase (vector<tcu::TestNode*>& shaderNodeList)
1329{
1330	// Parse 'case'.
1331	PARSE_DBG(("  parseShaderCase()\n"));
1332	advanceToken(TOKEN_CASE);
1333
1334	// Parse case name.
1335	string caseName = m_curTokenStr;
1336	advanceToken(); // \note [pyry] All token types are allowed here.
1337
1338	// \todo [pyry] Optimize by parsing most stuff directly to ShaderCaseSpecification
1339
1340	// Setup case.
1341	GLSLVersion						version			= DEFAULT_GLSL_VERSION;
1342	ExpectResult					expectResult	= EXPECT_PASS;
1343	OutputType						outputType		= OUTPUT_RESULT;
1344	DataType						format			= TYPE_LAST;
1345	string							description;
1346	string							bothSource;
1347	vector<string>					vertexSources;
1348	vector<string>					fragmentSources;
1349	vector<string>					tessellationCtrlSources;
1350	vector<string>					tessellationEvalSources;
1351	vector<string>					geometrySources;
1352	ValueBlock						valueBlock;
1353	bool							valueBlockSeen	= false;
1354	vector<RequiredCapability>		requiredCaps;
1355	vector<RequiredExtension>		requiredExts;
1356	vector<ProgramSpecification>	pipelinePrograms;
1357
1358	for (;;)
1359	{
1360		if (m_curToken == TOKEN_END)
1361			break;
1362		else if (m_curToken == TOKEN_DESC)
1363		{
1364			advanceToken();
1365			assumeToken(TOKEN_STRING);
1366			description = parseStringLiteral(m_curTokenStr.c_str());
1367			advanceToken();
1368		}
1369		else if (m_curToken == TOKEN_EXPECT)
1370		{
1371			advanceToken();
1372			parseExpectResult(expectResult);
1373		}
1374		else if (m_curToken == TOKEN_OUTPUT_COLOR)
1375		{
1376			outputType = OUTPUT_COLOR;
1377			advanceToken();
1378			parseFormat(format);
1379		}
1380		else if (m_curToken == TOKEN_VALUES)
1381		{
1382			if (valueBlockSeen)
1383				parseError("multiple value blocks");
1384			parseValueBlock(valueBlock);
1385			valueBlockSeen = true;
1386		}
1387		else if (m_curToken == TOKEN_BOTH						||
1388				 m_curToken == TOKEN_VERTEX						||
1389				 m_curToken == TOKEN_FRAGMENT					||
1390				 m_curToken == TOKEN_TESSELLATION_CONTROL		||
1391				 m_curToken == TOKEN_TESSELLATION_EVALUATION	||
1392				 m_curToken == TOKEN_GEOMETRY)
1393		{
1394			const Token	token = m_curToken;
1395			string		source;
1396
1397			advanceToken();
1398			assumeToken(TOKEN_SHADER_SOURCE);
1399			source = parseShaderSource(m_curTokenStr.c_str());
1400			advanceToken();
1401
1402			switch (token)
1403			{
1404				case TOKEN_VERTEX:					vertexSources.push_back(source);			break;
1405				case TOKEN_FRAGMENT:				fragmentSources.push_back(source);			break;
1406				case TOKEN_TESSELLATION_CONTROL:	tessellationCtrlSources.push_back(source);	break;
1407				case TOKEN_TESSELLATION_EVALUATION:	tessellationEvalSources.push_back(source);	break;
1408				case TOKEN_GEOMETRY:				geometrySources.push_back(source);			break;
1409				case TOKEN_BOTH:
1410				{
1411					if (!bothSource.empty())
1412						parseError("multiple 'both' blocks");
1413					bothSource = source;
1414					break;
1415				}
1416
1417				default:
1418					DE_FATAL("Unreachable");
1419			}
1420		}
1421		else if (m_curToken == TOKEN_VERSION)
1422		{
1423			advanceToken();
1424			parseGLSLVersion(version);
1425		}
1426		else if (m_curToken == TOKEN_REQUIRE)
1427		{
1428			parseRequirement(requiredCaps, requiredExts);
1429		}
1430		else if (m_curToken == TOKEN_PIPELINE_PROGRAM)
1431		{
1432			ProgramSpecification pipelineProgram;
1433			parsePipelineProgram(pipelineProgram);
1434			pipelineProgram.sources.separable = true;
1435			pipelinePrograms.push_back(pipelineProgram);
1436		}
1437		else
1438			parseError(string("unexpected token while parsing shader case: " + m_curTokenStr));
1439	}
1440
1441	advanceToken(TOKEN_END); // case end
1442
1443	if (!bothSource.empty())
1444	{
1445		if (!vertexSources.empty()				||
1446			!fragmentSources.empty()			||
1447			!tessellationCtrlSources.empty()	||
1448			!tessellationEvalSources.empty()	||
1449			!geometrySources.empty()			||
1450			!pipelinePrograms.empty())
1451		{
1452			parseError("'both' cannot be mixed with other shader stages");
1453		}
1454
1455		// vertex
1456		{
1457			ShaderCaseSpecification	spec;
1458			spec.caseType				= CASETYPE_VERTEX_ONLY;
1459			spec.expectResult			= expectResult;
1460			spec.targetVersion			= version;
1461			spec.requiredCaps			= requiredCaps;
1462			spec.values					= valueBlock;
1463
1464			spec.programs.resize(1);
1465			spec.programs[0].sources << VertexSource(bothSource);
1466			spec.programs[0].requiredExtensions	= requiredExts;
1467
1468			shaderNodeList.push_back(m_caseFactory->createCase(caseName + "_vertex", description, ShaderCaseSpecification(spec)));
1469		}
1470
1471		// fragment
1472		{
1473			ShaderCaseSpecification	spec;
1474			spec.caseType				= CASETYPE_FRAGMENT_ONLY;
1475			spec.expectResult			= expectResult;
1476			spec.targetVersion			= version;
1477			spec.requiredCaps			= requiredCaps;
1478			spec.values					= valueBlock;
1479
1480			spec.programs.resize(1);
1481			spec.programs[0].sources << FragmentSource(bothSource);
1482			spec.programs[0].requiredExtensions	= requiredExts;
1483
1484			shaderNodeList.push_back(m_caseFactory->createCase(caseName + "_fragment", description, ShaderCaseSpecification(spec)));
1485		}
1486	}
1487	else if (pipelinePrograms.empty())
1488	{
1489		ShaderCaseSpecification	spec;
1490		spec.caseType				= CASETYPE_COMPLETE;
1491		spec.expectResult			= expectResult;
1492		spec.outputType				= outputType;
1493		spec.outputFormat			= format;
1494		spec.targetVersion			= version;
1495		spec.requiredCaps			= requiredCaps;
1496		spec.values					= valueBlock;
1497
1498		spec.programs.resize(1);
1499		spec.programs[0].sources.sources[SHADERTYPE_VERTEX].swap(vertexSources);
1500		spec.programs[0].sources.sources[SHADERTYPE_FRAGMENT].swap(fragmentSources);
1501		spec.programs[0].sources.sources[SHADERTYPE_TESSELLATION_CONTROL].swap(tessellationCtrlSources);
1502		spec.programs[0].sources.sources[SHADERTYPE_TESSELLATION_EVALUATION].swap(tessellationEvalSources);
1503		spec.programs[0].sources.sources[SHADERTYPE_GEOMETRY].swap(geometrySources);
1504		spec.programs[0].requiredExtensions.swap(requiredExts);
1505
1506		shaderNodeList.push_back(m_caseFactory->createCase(caseName, description, ShaderCaseSpecification(spec)));
1507	}
1508	else
1509	{
1510		if (!vertexSources.empty()				||
1511			!fragmentSources.empty()			||
1512			!tessellationCtrlSources.empty()	||
1513			!tessellationEvalSources.empty()	||
1514			!geometrySources.empty())
1515		{
1516			parseError("pipeline programs cannot be mixed with complete programs");
1517		}
1518
1519		if (!requiredExts.empty())
1520			parseError("global extension requirements cannot be mixed with pipeline programs");
1521
1522		// Pipeline case, multiple programs
1523		{
1524			ShaderCaseSpecification	spec;
1525			spec.caseType				= CASETYPE_COMPLETE;
1526			spec.expectResult			= expectResult;
1527			spec.targetVersion			= version;
1528			spec.requiredCaps			= requiredCaps;
1529			spec.values					= valueBlock;
1530
1531			spec.programs.swap(pipelinePrograms);
1532
1533			shaderNodeList.push_back(m_caseFactory->createCase(caseName, description, ShaderCaseSpecification(spec)));
1534		}
1535	}
1536}
1537
1538void ShaderParser::parseShaderGroup (vector<tcu::TestNode*>& shaderNodeList)
1539{
1540	// Parse 'case'.
1541	PARSE_DBG(("  parseShaderGroup()\n"));
1542	advanceToken(TOKEN_GROUP);
1543
1544	// Parse case name.
1545	string name = m_curTokenStr;
1546	advanceToken(); // \note [pyry] We don't want to check token type here (for instance to allow "uniform") group.
1547
1548	// Parse description.
1549	assumeToken(TOKEN_STRING);
1550	string description = parseStringLiteral(m_curTokenStr.c_str());
1551	advanceToken(TOKEN_STRING);
1552
1553	std::vector<tcu::TestNode*> children;
1554
1555	// Parse group children.
1556	for (;;)
1557	{
1558		if (m_curToken == TOKEN_END)
1559			break;
1560		else if (m_curToken == TOKEN_GROUP)
1561			parseShaderGroup(children);
1562		else if (m_curToken == TOKEN_CASE)
1563			parseShaderCase(children);
1564		else if (m_curToken == TOKEN_IMPORT)
1565			parseImport(children);
1566		else
1567			parseError(string("unexpected token while parsing shader group: " + m_curTokenStr));
1568	}
1569
1570	advanceToken(TOKEN_END); // group end
1571
1572	// Create group node.
1573	tcu::TestCaseGroup* groupNode = m_caseFactory->createGroup(name, description, children);
1574	shaderNodeList.push_back(groupNode);
1575}
1576
1577void ShaderParser::parseImport (vector<tcu::TestNode*>& shaderNodeList)
1578{
1579	std::string	importFileName;
1580
1581	advanceToken(TOKEN_IMPORT);
1582
1583	assumeToken(TOKEN_STRING);
1584	importFileName = parseStringLiteral(m_curTokenStr.c_str());
1585	advanceToken(TOKEN_STRING);
1586
1587	{
1588		ShaderParser					subParser		(m_archive, de::FilePath::join(de::FilePath(m_filename).getDirName(), importFileName).getPath(), m_caseFactory);
1589		const vector<tcu::TestNode*>	importedCases = subParser.parse();
1590
1591		// \todo [2015-08-03 pyry] Not exception safe
1592		shaderNodeList.insert(shaderNodeList.end(), importedCases.begin(), importedCases.end());
1593	}
1594}
1595
1596vector<tcu::TestNode*> ShaderParser::parse (void)
1597{
1598	const int	dataLen		= m_resource->getSize();
1599
1600	m_input.resize(dataLen+1);
1601	m_resource->setPosition(0);
1602	m_resource->read((deUint8*)&m_input[0], dataLen);
1603	m_input[dataLen] = '\0';
1604
1605	// Initialize parser.
1606	m_curPtr		= &m_input[0];
1607	m_curToken		= TOKEN_INVALID;
1608	m_curTokenStr	= "";
1609	advanceToken();
1610
1611	vector<tcu::TestNode*> nodeList;
1612
1613	// Parse all cases.
1614	PARSE_DBG(("parse()\n"));
1615	for (;;)
1616	{
1617		if (m_curToken == TOKEN_CASE)
1618			parseShaderCase(nodeList);
1619		else if (m_curToken == TOKEN_GROUP)
1620			parseShaderGroup(nodeList);
1621		else if (m_curToken == TOKEN_IMPORT)
1622			parseImport(nodeList);
1623		else if (m_curToken == TOKEN_EOF)
1624			break;
1625		else
1626			parseError(string("invalid token encountered at main level: '") + m_curTokenStr + "'");
1627	}
1628
1629	assumeToken(TOKEN_EOF);
1630//	printf("  parsed %d test cases.\n", caseList.size());
1631	return nodeList;
1632}
1633
1634std::vector<tcu::TestNode*> parseFile (const tcu::Archive& archive, const std::string& filename, ShaderCaseFactory* caseFactory)
1635{
1636	sl::ShaderParser	parser	(archive, filename, caseFactory);
1637
1638	return parser.parse();
1639}
1640
1641// Execution utilities
1642
1643static void dumpValue (tcu::TestLog& log, const Value& val, const char* storageName, int arrayNdx)
1644{
1645	const char* const	valueName		= val.name.c_str();
1646	const DataType		dataType		= val.type.getBasicType();
1647	int					scalarSize		= getDataTypeScalarSize(dataType);
1648	ostringstream		result;
1649
1650	result << "    " << storageName << " ";
1651
1652	result << getDataTypeName(dataType) << " " << valueName << ":";
1653
1654	if (isDataTypeScalar(dataType))
1655		result << " ";
1656	if (isDataTypeVector(dataType))
1657		result << " [ ";
1658	else if (isDataTypeMatrix(dataType))
1659		result << "\n";
1660
1661	if (isDataTypeScalarOrVector(dataType))
1662	{
1663		for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
1664		{
1665			int						elemNdx	= arrayNdx;
1666			const Value::Element&	e		= val.elements[elemNdx*scalarSize + scalarNdx];
1667			result << ((scalarNdx != 0) ? ", " : "");
1668
1669			if (isDataTypeFloatOrVec(dataType))
1670				result << e.float32;
1671			else if (isDataTypeIntOrIVec(dataType))
1672				result << e.int32;
1673			else if (isDataTypeUintOrUVec(dataType))
1674				result << (deUint32)e.int32;
1675			else if (isDataTypeBoolOrBVec(dataType))
1676				result << (e.bool32 ? "true" : "false");
1677		}
1678	}
1679	else if (isDataTypeMatrix(dataType))
1680	{
1681		int numRows = getDataTypeMatrixNumRows(dataType);
1682		int numCols = getDataTypeMatrixNumColumns(dataType);
1683		for (int rowNdx = 0; rowNdx < numRows; rowNdx++)
1684		{
1685			result << "       [ ";
1686			for (int colNdx = 0; colNdx < numCols; colNdx++)
1687			{
1688				int		elemNdx = arrayNdx;
1689				float	v		= val.elements[elemNdx*scalarSize + rowNdx*numCols + colNdx].float32;
1690				result << ((colNdx==0) ? "" : ", ") << v;
1691			}
1692			result << " ]\n";
1693		}
1694	}
1695
1696	if (isDataTypeScalar(dataType))
1697		result << "\n";
1698	else if (isDataTypeVector(dataType))
1699		result << " ]\n";
1700
1701	log << TestLog::Message << result.str() << TestLog::EndMessage;
1702}
1703
1704static void dumpValues (tcu::TestLog& log, const vector<Value>& values, const char* storageName, int arrayNdx)
1705{
1706	for (size_t valNdx = 0; valNdx < values.size(); valNdx++)
1707		dumpValue(log, values[valNdx], storageName, arrayNdx);
1708}
1709
1710void dumpValues (tcu::TestLog& log, const ValueBlock& values, int arrayNdx)
1711{
1712	dumpValues(log, values.inputs,		"input",	arrayNdx);
1713	dumpValues(log, values.outputs,		"expected",	arrayNdx);
1714	dumpValues(log, values.uniforms,	"uniform",	arrayNdx);
1715}
1716
1717static void generateExtensionStatements (std::ostringstream& buf, const std::vector<RequiredExtension>& extensions, glu::ShaderType type)
1718{
1719	for (size_t ndx = 0; ndx < extensions.size(); ++ndx)
1720	{
1721		DE_ASSERT(extensions[ndx].effectiveStages != 0u &&
1722				  extensions[ndx].alternatives.size() == 1);
1723
1724		if ((extensions[ndx].effectiveStages & (1u << (deUint32)type)) != 0)
1725			buf << "#extension " << extensions[ndx].alternatives[0] << " : require\n";
1726	}
1727}
1728
1729// Injects #extension XXX : require lines after the last preprocessor directive in the shader code. Does not support line continuations
1730std::string injectExtensionRequirements (const std::string& baseCode, const std::vector<RequiredExtension>& extensions, glu::ShaderType shaderType)
1731{
1732	std::istringstream	baseCodeBuf					(baseCode);
1733	std::ostringstream	resultBuf;
1734	std::string			line;
1735	bool				firstNonPreprocessorLine	= true;
1736	std::ostringstream	extStr;
1737
1738	generateExtensionStatements(extStr, extensions, shaderType);
1739
1740	// skip if no requirements
1741	if (extStr.str().empty())
1742		return baseCode;
1743
1744	while (std::getline(baseCodeBuf, line))
1745	{
1746		// begins with '#'?
1747		const std::string::size_type	firstNonWhitespace		= line.find_first_not_of("\t ");
1748		const bool						isPreprocessorDirective	= (firstNonWhitespace != std::string::npos && line.at(firstNonWhitespace) == '#');
1749
1750		// Inject #extensions
1751		if (!isPreprocessorDirective && firstNonPreprocessorLine)
1752		{
1753			firstNonPreprocessorLine = false;
1754			resultBuf << extStr.str();
1755		}
1756
1757		resultBuf << line << "\n";
1758	}
1759
1760	return resultBuf.str();
1761}
1762
1763void genCompareFunctions (ostringstream& stream, const ValueBlock& valueBlock, bool useFloatTypes)
1764{
1765	bool cmpTypeFound[TYPE_LAST];
1766	for (int i = 0; i < TYPE_LAST; i++)
1767		cmpTypeFound[i] = false;
1768
1769	for (size_t valueNdx = 0; valueNdx < valueBlock.outputs.size(); valueNdx++)
1770	{
1771		const Value& val = valueBlock.outputs[valueNdx];
1772		cmpTypeFound[(size_t)val.type.getBasicType()] = true;
1773	}
1774
1775	if (useFloatTypes)
1776	{
1777		if (cmpTypeFound[TYPE_BOOL])		stream << "bool isOk (float a, bool b) { return ((a > 0.5) == b); }\n";
1778		if (cmpTypeFound[TYPE_BOOL_VEC2])	stream << "bool isOk (vec2 a, bvec2 b) { return (greaterThan(a, vec2(0.5)) == b); }\n";
1779		if (cmpTypeFound[TYPE_BOOL_VEC3])	stream << "bool isOk (vec3 a, bvec3 b) { return (greaterThan(a, vec3(0.5)) == b); }\n";
1780		if (cmpTypeFound[TYPE_BOOL_VEC4])	stream << "bool isOk (vec4 a, bvec4 b) { return (greaterThan(a, vec4(0.5)) == b); }\n";
1781		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";
1782		if (cmpTypeFound[TYPE_INT_VEC2])	stream << "bool isOk (vec2 a, ivec2 b) { return (ivec2(floor(a + 0.5)) == b); }\n";
1783		if (cmpTypeFound[TYPE_INT_VEC3])	stream << "bool isOk (vec3 a, ivec3 b) { return (ivec3(floor(a + 0.5)) == b); }\n";
1784		if (cmpTypeFound[TYPE_INT_VEC4])	stream << "bool isOk (vec4 a, ivec4 b) { return (ivec4(floor(a + 0.5)) == b); }\n";
1785		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";
1786		if (cmpTypeFound[TYPE_UINT_VEC2])	stream << "bool isOk (vec2 a, uvec2 b) { return (uvec2(floor(a + 0.5)) == b); }\n";
1787		if (cmpTypeFound[TYPE_UINT_VEC3])	stream << "bool isOk (vec3 a, uvec3 b) { return (uvec3(floor(a + 0.5)) == b); }\n";
1788		if (cmpTypeFound[TYPE_UINT_VEC4])	stream << "bool isOk (vec4 a, uvec4 b) { return (uvec4(floor(a + 0.5)) == b); }\n";
1789	}
1790	else
1791	{
1792		if (cmpTypeFound[TYPE_BOOL])		stream << "bool isOk (bool a, bool b)   { return (a == b); }\n";
1793		if (cmpTypeFound[TYPE_BOOL_VEC2])	stream << "bool isOk (bvec2 a, bvec2 b) { return (a == b); }\n";
1794		if (cmpTypeFound[TYPE_BOOL_VEC3])	stream << "bool isOk (bvec3 a, bvec3 b) { return (a == b); }\n";
1795		if (cmpTypeFound[TYPE_BOOL_VEC4])	stream << "bool isOk (bvec4 a, bvec4 b) { return (a == b); }\n";
1796		if (cmpTypeFound[TYPE_INT])			stream << "bool isOk (int a, int b)     { return (a == b); }\n";
1797		if (cmpTypeFound[TYPE_INT_VEC2])	stream << "bool isOk (ivec2 a, ivec2 b) { return (a == b); }\n";
1798		if (cmpTypeFound[TYPE_INT_VEC3])	stream << "bool isOk (ivec3 a, ivec3 b) { return (a == b); }\n";
1799		if (cmpTypeFound[TYPE_INT_VEC4])	stream << "bool isOk (ivec4 a, ivec4 b) { return (a == b); }\n";
1800		if (cmpTypeFound[TYPE_UINT])		stream << "bool isOk (uint a, uint b)   { return (a == b); }\n";
1801		if (cmpTypeFound[TYPE_UINT_VEC2])	stream << "bool isOk (uvec2 a, uvec2 b) { return (a == b); }\n";
1802		if (cmpTypeFound[TYPE_UINT_VEC3])	stream << "bool isOk (uvec3 a, uvec3 b) { return (a == b); }\n";
1803		if (cmpTypeFound[TYPE_UINT_VEC4])	stream << "bool isOk (uvec4 a, uvec4 b) { return (a == b); }\n";
1804	}
1805
1806	if (cmpTypeFound[TYPE_FLOAT])		stream << "bool isOk (float a, float b, float eps) { return (abs(a-b) <= (eps*abs(b) + eps)); }\n";
1807	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";
1808	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";
1809	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";
1810
1811	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";
1812	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";
1813	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";
1814	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";
1815	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";
1816	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";
1817	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";
1818	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";
1819	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";
1820}
1821
1822} // sl
1823} // glu
1824