1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 2.0 Module
3 * -------------------------------------------------
4 *
5 * Copyright 2014 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 matrix arithmetic tests.
22 *
23 * Variables:
24 *  + operation
25 *    - mat OP mat
26 *    - mat OP vec
27 *    - vec OP mat
28 *    - mat OP scalar
29 *    - OP mat
30 *  + matrix source
31 *    - constant (ctor)
32 *    - uniform
33 *    - vertex input
34 *    - fragment input
35 *  + other operand: always dynamic data?
36 *  + how to reduce to vec3?
37 *//*--------------------------------------------------------------------*/
38
39#include "es2fShaderMatrixTests.hpp"
40#include "glsShaderRenderCase.hpp"
41#include "gluShaderUtil.hpp"
42#include "tcuVector.hpp"
43#include "tcuMatrix.hpp"
44#include "tcuMatrixUtil.hpp"
45#include "deStringUtil.hpp"
46
47#include "glwEnums.hpp"
48#include "glwFunctions.hpp"
49
50namespace deqp
51{
52namespace gles2
53{
54namespace Functional
55{
56
57using std::string;
58using std::vector;
59using namespace glu;
60using namespace deqp::gls;
61
62using tcu::Vec2;
63using tcu::Vec3;
64using tcu::Vec4;
65using tcu::Mat2;
66using tcu::Mat3;
67using tcu::Mat4;
68
69// Uniform / constant values for tests.
70// \note Input1 should not contain 0 components as it is used as divisor in div cases.
71// \todo [2012-02-14 pyry] Make these dynamic.
72static const float	s_constInFloat[2]	= { 0.5f, -0.2f };
73static const Vec2	s_constInVec2[2]	= { Vec2(1.2f, 0.5f), Vec2(0.5f, 1.0f) };
74static const Vec3	s_constInVec3[2]	= { Vec3(1.1f, 0.1f, 0.5f), Vec3(-0.2f, 0.5f, 0.8f) };
75static const Vec4	s_constInVec4[2]	= { Vec4(1.4f, 0.2f, -0.5f, 0.7f), Vec4(0.2f, -1.0f, 0.5f, 0.8f) };
76
77static const float s_constInMat20[] = { 0.6f, -1.0f, 0.7f, 0.4f };
78static const float s_constInMat21[] = { -0.5f, -0.4f, 0.7f, -0.8f };
79
80static const float s_constInMat31[] =
81{
82	1.2f,  0.1f, -0.1f,
83	0.1f,  0.9f,  0.2f,
84	0.2f, -0.1f,  0.7f
85};
86static const float s_constInMat41[] =
87{
88	 1.2f, -0.2f,  0.4f,  0.1f,
89	 0.1f,  0.8f, -0.1f, -0.2f,
90	-0.2f,  0.1f, -1.1f,  0.3f,
91	 0.1f,  0.2f,  0.3f,  0.9f
92};
93
94static const Mat2	s_constInMat2[2]	= { tcu::Mat2(s_constInMat20), tcu::Mat2(s_constInMat21) };
95static const Mat3	s_constInMat3[2]	= { tcu::translationMatrix(tcu::Vec2(0.2f, -0.3f)), tcu::Mat3(s_constInMat31) };
96static const Mat4	s_constInMat4[2]	= { tcu::translationMatrix(tcu::Vec3(0.2f, -0.3f, 0.15f)), tcu::Mat4(s_constInMat41) };
97
98namespace MatrixCaseUtils
99{
100
101enum InputType
102{
103	INPUTTYPE_CONST = 0,
104	INPUTTYPE_UNIFORM,
105	INPUTTYPE_DYNAMIC,
106
107	INPUTTYPE_LAST
108};
109
110struct ShaderInput
111{
112	ShaderInput (InputType inputType_, DataType dataType_, Precision precision_)
113		: inputType	(inputType_)
114		, dataType	(dataType_)
115		, precision	(precision_)
116	{
117	}
118
119	InputType		inputType;
120	DataType		dataType;
121	Precision		precision;
122};
123
124enum MatrixOp
125{
126	OP_ADD = 0,
127	OP_SUB,
128	OP_MUL,
129	OP_DIV,
130	OP_COMP_MUL,
131	OP_UNARY_PLUS,
132	OP_NEGATION,
133	OP_PRE_INCREMENT,
134	OP_PRE_DECREMENT,
135	OP_POST_INCREMENT,
136	OP_POST_DECREMENT,
137	OP_ADD_INTO,
138	OP_SUBTRACT_FROM,
139	OP_MULTIPLY_INTO,
140	OP_DIVIDE_INTO,
141
142	OP_LAST
143};
144
145// Type traits.
146
147template <int DataT>
148struct TypeTraits;
149
150#define DECLARE_TYPE_TRAIT(DATATYPE, TYPE)	\
151template<>									\
152struct TypeTraits<DATATYPE> {				\
153	typedef TYPE Type;						\
154}
155
156DECLARE_TYPE_TRAIT(TYPE_FLOAT,		float);
157DECLARE_TYPE_TRAIT(TYPE_FLOAT_VEC2,	tcu::Vec2);
158DECLARE_TYPE_TRAIT(TYPE_FLOAT_VEC3,	tcu::Vec3);
159DECLARE_TYPE_TRAIT(TYPE_FLOAT_VEC4,	tcu::Vec4);
160DECLARE_TYPE_TRAIT(TYPE_FLOAT_MAT2, tcu::Mat2);
161DECLARE_TYPE_TRAIT(TYPE_FLOAT_MAT3, tcu::Mat3);
162DECLARE_TYPE_TRAIT(TYPE_FLOAT_MAT4, tcu::Mat4);
163
164// Operation info
165
166enum OperationType
167{
168	OPERATIONTYPE_BINARY_OPERATOR = 0,
169	OPERATIONTYPE_BINARY_FUNCTION,
170	OPERATIONTYPE_UNARY_PREFIX_OPERATOR,
171	OPERATIONTYPE_UNARY_POSTFIX_OPERATOR,
172	OPERATIONTYPE_ASSIGNMENT,
173
174	OPERATIONTYPE_LAST
175};
176
177static const char* getOperationName (MatrixOp op)
178{
179	switch (op)
180	{
181		case OP_ADD:			return "+";
182		case OP_SUB:			return "-";
183		case OP_MUL:			return "*";
184		case OP_DIV:			return "/";
185		case OP_COMP_MUL:		return "matrixCompMult";
186		case OP_UNARY_PLUS:		return "+";
187		case OP_NEGATION:		return "-";
188		case OP_PRE_INCREMENT:	return "++";
189		case OP_PRE_DECREMENT:	return "--";
190		case OP_POST_INCREMENT:	return "++";
191		case OP_POST_DECREMENT:	return "--";
192		case OP_ADD_INTO:		return "+=";
193		case OP_SUBTRACT_FROM:	return "-=";
194		case OP_MULTIPLY_INTO:	return "*=";
195		case OP_DIVIDE_INTO:	return "/=";
196		default:
197			DE_ASSERT(DE_FALSE);
198			return "";
199	}
200}
201
202static OperationType getOperationType (MatrixOp op)
203{
204	switch (op)
205	{
206		case OP_ADD:			return OPERATIONTYPE_BINARY_OPERATOR;
207		case OP_SUB:			return OPERATIONTYPE_BINARY_OPERATOR;
208		case OP_MUL:			return OPERATIONTYPE_BINARY_OPERATOR;
209		case OP_DIV:			return OPERATIONTYPE_BINARY_OPERATOR;
210		case OP_COMP_MUL:		return OPERATIONTYPE_BINARY_FUNCTION;
211		case OP_UNARY_PLUS:		return OPERATIONTYPE_UNARY_PREFIX_OPERATOR;
212		case OP_NEGATION:		return OPERATIONTYPE_UNARY_PREFIX_OPERATOR;
213		case OP_PRE_INCREMENT:	return OPERATIONTYPE_UNARY_PREFIX_OPERATOR;
214		case OP_PRE_DECREMENT:	return OPERATIONTYPE_UNARY_PREFIX_OPERATOR;
215		case OP_POST_INCREMENT:	return OPERATIONTYPE_UNARY_POSTFIX_OPERATOR;
216		case OP_POST_DECREMENT:	return OPERATIONTYPE_UNARY_POSTFIX_OPERATOR;
217		case OP_ADD_INTO:		return OPERATIONTYPE_ASSIGNMENT;
218		case OP_SUBTRACT_FROM:	return OPERATIONTYPE_ASSIGNMENT;
219		case OP_MULTIPLY_INTO:	return OPERATIONTYPE_ASSIGNMENT;
220		case OP_DIVIDE_INTO:	return OPERATIONTYPE_ASSIGNMENT;
221		default:
222			DE_ASSERT(DE_FALSE);
223			return OPERATIONTYPE_LAST;
224	}
225}
226
227enum TestMatrixType
228{
229	TESTMATRIXTYPE_DEFAULT = 0,
230	TESTMATRIXTYPE_NEGATED,
231	TESTMATRIXTYPE_INCREMENTED,
232	TESTMATRIXTYPE_DECREMENTED,
233
234	TESTMATRIXTYPE_LAST
235};
236
237static TestMatrixType getOperationTestMatrixType (MatrixOp op)
238{
239	switch(op)
240	{
241		case OP_ADD:			return TESTMATRIXTYPE_DEFAULT;
242		case OP_SUB:			return TESTMATRIXTYPE_DEFAULT;
243		case OP_MUL:			return TESTMATRIXTYPE_DEFAULT;
244		case OP_DIV:			return TESTMATRIXTYPE_DEFAULT;
245		case OP_COMP_MUL:		return TESTMATRIXTYPE_DEFAULT;
246		case OP_UNARY_PLUS:		return TESTMATRIXTYPE_DEFAULT;
247		case OP_NEGATION:		return TESTMATRIXTYPE_NEGATED;
248		case OP_PRE_INCREMENT:	return TESTMATRIXTYPE_NEGATED;
249		case OP_PRE_DECREMENT:	return TESTMATRIXTYPE_INCREMENTED;
250		case OP_POST_INCREMENT:	return TESTMATRIXTYPE_NEGATED;
251		case OP_POST_DECREMENT:	return TESTMATRIXTYPE_DEFAULT;
252		case OP_ADD_INTO:		return TESTMATRIXTYPE_DECREMENTED;
253		case OP_SUBTRACT_FROM:	return TESTMATRIXTYPE_DEFAULT;
254		case OP_MULTIPLY_INTO:	return TESTMATRIXTYPE_DEFAULT;
255		case OP_DIVIDE_INTO:	return TESTMATRIXTYPE_DEFAULT;
256
257		default:
258			DE_ASSERT(DE_FALSE);
259			return TESTMATRIXTYPE_LAST;
260	}
261}
262
263static bool isOperationBinary (MatrixOp op)
264{
265	return getOperationType(op) == OPERATIONTYPE_BINARY_OPERATOR ||
266	       getOperationType(op) == OPERATIONTYPE_BINARY_FUNCTION ||
267	       getOperationType(op) == OPERATIONTYPE_ASSIGNMENT;
268}
269
270static bool isOperationMatrixScalar (MatrixOp op)
271{
272	return op == OP_ADD || op == OP_SUB || op == OP_MUL || op == OP_DIV;
273}
274
275static bool isOperationMatrixVector (MatrixOp op)
276{
277	return op == OP_MUL;
278}
279
280static bool isOperationMatrixMatrix (MatrixOp op)
281{
282	return op == OP_ADD || op == OP_SUB || op == OP_MUL || op == OP_DIV || op == OP_COMP_MUL;
283}
284
285static bool isOperationUnary (MatrixOp op)
286{
287	return  op == OP_UNARY_PLUS			||
288			op == OP_NEGATION			||
289			op == OP_PRE_INCREMENT		||
290			op == OP_PRE_DECREMENT		||
291			op == OP_POST_INCREMENT		||
292			op == OP_POST_DECREMENT;
293}
294
295static bool isOperationValueModifying (MatrixOp op)
296{
297	return  op == OP_PRE_INCREMENT		||
298			op == OP_PRE_DECREMENT		||
299			op == OP_POST_INCREMENT		||
300			op == OP_POST_DECREMENT;
301}
302
303static bool isOperationAssignment (MatrixOp op)
304{
305	return  op == OP_ADD_INTO		 ||
306			op == OP_SUBTRACT_FROM	 ||
307			op == OP_MULTIPLY_INTO	 ||
308			op == OP_DIVIDE_INTO;
309}
310
311// Operation nature
312
313enum OperationNature
314{
315	OPERATIONNATURE_PURE = 0,
316	OPERATIONNATURE_MUTATING,
317	OPERATIONNATURE_ASSIGNMENT,
318
319	OPERATIONNATURE_LAST
320};
321
322static OperationNature getOperationNature (MatrixOp op)
323{
324	if (isOperationAssignment(op))
325		return OPERATIONNATURE_ASSIGNMENT;
326
327	if (isOperationValueModifying(op))
328		return OPERATIONNATURE_MUTATING;
329
330	return OPERATIONNATURE_PURE;
331}
332
333// Input value loader.
334
335template <int InputT, int DataT>
336typename TypeTraits<DataT>::Type getInputValue (const ShaderEvalContext& evalCtx, int inputNdx);
337
338template <> inline float		getInputValue<INPUTTYPE_CONST,		TYPE_FLOAT>			(const ShaderEvalContext& evalCtx, int inputNdx) { DE_UNREF(evalCtx); return s_constInFloat[inputNdx];	}
339template <> inline tcu::Vec2	getInputValue<INPUTTYPE_CONST,		TYPE_FLOAT_VEC2>	(const ShaderEvalContext& evalCtx, int inputNdx) { DE_UNREF(evalCtx); return s_constInVec2[inputNdx];	}
340template <> inline tcu::Vec3	getInputValue<INPUTTYPE_CONST,		TYPE_FLOAT_VEC3>	(const ShaderEvalContext& evalCtx, int inputNdx) { DE_UNREF(evalCtx); return s_constInVec3[inputNdx];	}
341template <> inline tcu::Vec4	getInputValue<INPUTTYPE_CONST,		TYPE_FLOAT_VEC4>	(const ShaderEvalContext& evalCtx, int inputNdx) { DE_UNREF(evalCtx); return s_constInVec4[inputNdx];	}
342template <> inline tcu::Mat2	getInputValue<INPUTTYPE_CONST,		TYPE_FLOAT_MAT2>	(const ShaderEvalContext& evalCtx, int inputNdx) { DE_UNREF(evalCtx); return s_constInMat2[inputNdx];	}
343template <> inline tcu::Mat3	getInputValue<INPUTTYPE_CONST,		TYPE_FLOAT_MAT3>	(const ShaderEvalContext& evalCtx, int inputNdx) { DE_UNREF(evalCtx); return s_constInMat3[inputNdx];	}
344template <> inline tcu::Mat4	getInputValue<INPUTTYPE_CONST,		TYPE_FLOAT_MAT4>	(const ShaderEvalContext& evalCtx, int inputNdx) { DE_UNREF(evalCtx); return s_constInMat4[inputNdx];	}
345
346template <> inline float		getInputValue<INPUTTYPE_DYNAMIC,	TYPE_FLOAT>			(const ShaderEvalContext& evalCtx, int inputNdx) { DE_UNREF(inputNdx); return evalCtx.coords.x();					}
347template <> inline tcu::Vec2	getInputValue<INPUTTYPE_DYNAMIC,	TYPE_FLOAT_VEC2>	(const ShaderEvalContext& evalCtx, int inputNdx) { DE_UNREF(inputNdx); return evalCtx.coords.swizzle(0, 1);			}
348template <> inline tcu::Vec3	getInputValue<INPUTTYPE_DYNAMIC,	TYPE_FLOAT_VEC3>	(const ShaderEvalContext& evalCtx, int inputNdx) { DE_UNREF(inputNdx); return evalCtx.coords.swizzle(0, 1, 2);		}
349template <> inline tcu::Vec4	getInputValue<INPUTTYPE_DYNAMIC,	TYPE_FLOAT_VEC4>	(const ShaderEvalContext& evalCtx, int inputNdx) { DE_UNREF(inputNdx); return evalCtx.coords.swizzle(0, 1, 2, 3);	}
350
351template <> inline tcu::Mat2 getInputValue<INPUTTYPE_DYNAMIC, TYPE_FLOAT_MAT2> (const ShaderEvalContext& evalCtx, int inputNdx)
352{
353	DE_UNREF(inputNdx); // Not used.
354	tcu::Mat2 m;
355	m.setColumn(0, evalCtx.in[0].swizzle(0,1));
356	m.setColumn(1, evalCtx.in[1].swizzle(0,1));
357	return m;
358}
359
360template <> inline tcu::Mat3 getInputValue<INPUTTYPE_DYNAMIC, TYPE_FLOAT_MAT3> (const ShaderEvalContext& evalCtx, int inputNdx)
361{
362	DE_UNREF(inputNdx); // Not used.
363	tcu::Mat3 m;
364	m.setColumn(0, evalCtx.in[0].swizzle(0,1,2));
365	m.setColumn(1, evalCtx.in[1].swizzle(0,1,2));
366	m.setColumn(2, evalCtx.in[2].swizzle(0,1,2));
367	return m;
368}
369
370template <> inline tcu::Mat4 getInputValue<INPUTTYPE_DYNAMIC, TYPE_FLOAT_MAT4> (const ShaderEvalContext& evalCtx, int inputNdx)
371{
372	DE_UNREF(inputNdx); // Not used.
373	tcu::Mat4 m;
374	m.setColumn(0, evalCtx.in[0]);
375	m.setColumn(1, evalCtx.in[1]);
376	m.setColumn(2, evalCtx.in[2]);
377	m.setColumn(3, evalCtx.in[3]);
378	return m;
379}
380
381// Reduction from expression result to vec3.
382
383inline tcu::Vec3 reduceToVec3 (const tcu::Vec2& value) { return value.swizzle(0,1,0); }
384inline tcu::Vec3 reduceToVec3 (const tcu::Vec3& value) { return value; }
385inline tcu::Vec3 reduceToVec3 (const tcu::Vec4& value) { return tcu::Vec3(value.x(), value.y(), value.z()+value.w()); }
386inline tcu::Vec3 reduceToVec3 (const tcu::Mat2& value) { return tcu::Vec3(value(0, 0), value(0, 1), value(1, 0)+value(1, 1)); }
387inline tcu::Vec3 reduceToVec3 (const tcu::Mat3& value) { return value.getColumn(0) + value.getColumn(1) + value.getColumn(2); }
388inline tcu::Vec3 reduceToVec3 (const tcu::Mat4& value) { return value.getColumn(0).swizzle(0,1,2) + value.getColumn(1).swizzle(1,2,3) + value.getColumn(2).swizzle(2,3,0) + value.getColumn(3).swizzle(3,0,1); }
389
390// matrixCompMult
391
392template <typename T, int Rows, int Cols>
393tcu::Matrix<T, Rows, Cols> matrixCompMult (const tcu::Matrix<T, Rows, Cols>& a, const tcu::Matrix<T, Rows, Cols>& b)
394{
395	tcu::Matrix<T, Rows, Cols> retVal;
396
397	for (int r = 0; r < Rows; ++r)
398		for (int c = 0; c < Cols; ++c)
399			retVal(r,c) = a(r,c) * b(r, c);
400
401	return retVal;
402}
403
404// negate
405
406template <typename T, int Rows, int Cols>
407tcu::Matrix<T, Rows, Cols> negate (const tcu::Matrix<T, Rows, Cols>& mat)
408{
409	tcu::Matrix<T, Rows, Cols> retVal;
410
411	for (int r = 0; r < Rows; ++r)
412		for (int c = 0; c < Cols; ++c)
413			retVal(r,c) = -mat(r, c);
414
415	return retVal;
416}
417
418// increment/decrement
419
420template <typename T, int Rows, int Cols>
421tcu::Matrix<T, Rows, Cols> increment (const tcu::Matrix<T, Rows, Cols>& mat)
422{
423	tcu::Matrix<T, Rows, Cols> retVal;
424
425	for (int r = 0; r < Rows; ++r)
426		for (int c = 0; c < Cols; ++c)
427			retVal(r,c) = mat(r, c) + 1.0f;
428
429	return retVal;
430}
431
432template <typename T, int Rows, int Cols>
433tcu::Matrix<T, Rows, Cols> decrement (const tcu::Matrix<T, Rows, Cols>& mat)
434{
435	tcu::Matrix<T, Rows, Cols> retVal;
436
437	for (int r = 0; r < Rows; ++r)
438		for (int c = 0; c < Cols; ++c)
439			retVal(r,c) = mat(r, c) - 1.0f;
440
441	return retVal;
442}
443
444// Evaluator template.
445
446template <int Op, int In0Type, int In0DataType, int In1Type, int In1DataType>
447struct Evaluator;
448
449template <int In0Type, int In0DataType, int In1Type, int In1DataType>
450struct Evaluator<OP_ADD, In0Type, In0DataType, In1Type, In1DataType>
451{
452	static void evaluate (ShaderEvalContext& evalCtx)
453	{
454		evalCtx.color.xyz() = reduceToVec3(getInputValue<In0Type, In0DataType>(evalCtx, 0) + getInputValue<In1Type, In1DataType>(evalCtx, 1));
455	}
456};
457
458template <int In0Type, int In0DataType, int In1Type, int In1DataType>
459struct Evaluator<OP_SUB, In0Type, In0DataType, In1Type, In1DataType>
460{
461	static void evaluate (ShaderEvalContext& evalCtx)
462	{
463		evalCtx.color.xyz() = reduceToVec3(getInputValue<In0Type, In0DataType>(evalCtx, 0) - getInputValue<In1Type, In1DataType>(evalCtx, 1));
464	}
465};
466
467template <int In0Type, int In0DataType, int In1Type, int In1DataType>
468struct Evaluator<OP_MUL, In0Type, In0DataType, In1Type, In1DataType>
469{
470	static void evaluate (ShaderEvalContext& evalCtx)
471	{
472		evalCtx.color.xyz() = reduceToVec3(getInputValue<In0Type, In0DataType>(evalCtx, 0) * getInputValue<In1Type, In1DataType>(evalCtx, 1));
473	}
474};
475
476template <int In0Type, int In0DataType, int In1Type, int In1DataType>
477struct Evaluator<OP_DIV, In0Type, In0DataType, In1Type, In1DataType>
478{
479	static void evaluate (ShaderEvalContext& evalCtx)
480	{
481		evalCtx.color.xyz() = reduceToVec3(getInputValue<In0Type, In0DataType>(evalCtx, 0) / getInputValue<In1Type, In1DataType>(evalCtx, 1));
482	}
483};
484
485template <int In0Type, int In0DataType, int In1Type, int In1DataType>
486struct Evaluator<OP_COMP_MUL, In0Type, In0DataType, In1Type, In1DataType>
487{
488	static void evaluate (ShaderEvalContext& evalCtx)
489	{
490		evalCtx.color.xyz() = reduceToVec3(matrixCompMult(getInputValue<In0Type, In0DataType>(evalCtx, 0), getInputValue<In1Type, In1DataType>(evalCtx, 1)));
491	}
492};
493
494template <int In0Type, int In0DataType, int In1Type, int In1DataType>
495struct Evaluator<OP_UNARY_PLUS, In0Type, In0DataType, In1Type, In1DataType>
496{
497	static void evaluate (ShaderEvalContext& evalCtx)
498	{
499		evalCtx.color.xyz() = reduceToVec3(getInputValue<In0Type, In0DataType>(evalCtx, 0));
500	}
501};
502
503template <int In0Type, int In0DataType, int In1Type, int In1DataType>
504struct Evaluator<OP_NEGATION, In0Type, In0DataType, In1Type, In1DataType>
505{
506	static void evaluate (ShaderEvalContext& evalCtx)
507	{
508		evalCtx.color.xyz() = reduceToVec3(negate(getInputValue<In0Type, In0DataType>(evalCtx, 0)));
509	}
510};
511
512template <int In0Type, int In0DataType, int In1Type, int In1DataType>
513struct Evaluator<OP_PRE_INCREMENT, In0Type, In0DataType, In1Type, In1DataType>
514{
515	static void evaluate (ShaderEvalContext& evalCtx)
516	{
517		// modifying reduction: sum modified value too
518		evalCtx.color.xyz() = reduceToVec3(increment(getInputValue<In0Type, In0DataType>(evalCtx, 0))) + reduceToVec3(increment(getInputValue<In0Type, In0DataType>(evalCtx, 0)));
519	}
520};
521
522template <int In0Type, int In0DataType, int In1Type, int In1DataType>
523struct Evaluator<OP_PRE_DECREMENT, In0Type, In0DataType, In1Type, In1DataType>
524{
525	static void evaluate (ShaderEvalContext& evalCtx)
526	{
527		// modifying reduction: sum modified value too
528		evalCtx.color.xyz() = reduceToVec3(decrement(getInputValue<In0Type, In0DataType>(evalCtx, 0))) + reduceToVec3(decrement(getInputValue<In0Type, In0DataType>(evalCtx, 0)));
529	}
530};
531
532template <int In0Type, int In0DataType, int In1Type, int In1DataType>
533struct Evaluator<OP_POST_INCREMENT, In0Type, In0DataType, In1Type, In1DataType>
534{
535	static void evaluate (ShaderEvalContext& evalCtx)
536	{
537		// modifying reduction: sum modified value too
538		evalCtx.color.xyz() = reduceToVec3(getInputValue<In0Type, In0DataType>(evalCtx, 0)) + reduceToVec3(increment(getInputValue<In0Type, In0DataType>(evalCtx, 0)));
539	}
540};
541
542template <int In0Type, int In0DataType, int In1Type, int In1DataType>
543struct Evaluator<OP_POST_DECREMENT, In0Type, In0DataType, In1Type, In1DataType>
544{
545	static void evaluate (ShaderEvalContext& evalCtx)
546	{
547		// modifying reduction: sum modified value too
548		evalCtx.color.xyz() = reduceToVec3(getInputValue<In0Type, In0DataType>(evalCtx, 0)) + reduceToVec3(decrement(getInputValue<In0Type, In0DataType>(evalCtx, 0)));
549	}
550};
551
552template <int In0Type, int In0DataType, int In1Type, int In1DataType>
553struct Evaluator<OP_ADD_INTO, In0Type, In0DataType, In1Type, In1DataType>
554{
555	static void evaluate (ShaderEvalContext& evalCtx)
556	{
557		evalCtx.color.xyz() = reduceToVec3(getInputValue<In0Type, In0DataType>(evalCtx, 0) + getInputValue<In1Type, In1DataType>(evalCtx, 1));
558	}
559};
560
561template <int In0Type, int In0DataType, int In1Type, int In1DataType>
562struct Evaluator<OP_SUBTRACT_FROM, In0Type, In0DataType, In1Type, In1DataType>
563{
564	static void evaluate (ShaderEvalContext& evalCtx)
565	{
566		evalCtx.color.xyz() = reduceToVec3(getInputValue<In0Type, In0DataType>(evalCtx, 0) - getInputValue<In1Type, In1DataType>(evalCtx, 1));
567	}
568};
569
570template <int In0Type, int In0DataType, int In1Type, int In1DataType>
571struct Evaluator<OP_MULTIPLY_INTO, In0Type, In0DataType, In1Type, In1DataType>
572{
573	static void evaluate (ShaderEvalContext& evalCtx)
574	{
575		evalCtx.color.xyz() = reduceToVec3(getInputValue<In0Type, In0DataType>(evalCtx, 0) * getInputValue<In1Type, In1DataType>(evalCtx, 1));
576	}
577};
578
579template <int In0Type, int In0DataType, int In1Type, int In1DataType>
580struct Evaluator<OP_DIVIDE_INTO, In0Type, In0DataType, In1Type, In1DataType>
581{
582	static void evaluate (ShaderEvalContext& evalCtx)
583	{
584		evalCtx.color.xyz() = reduceToVec3(getInputValue<In0Type, In0DataType>(evalCtx, 0) / getInputValue<In1Type, In1DataType>(evalCtx, 1));
585	}
586};
587
588ShaderEvalFunc getEvalFunc (const ShaderInput& in0, const ShaderInput& in1, MatrixOp op)
589{
590	DE_STATIC_ASSERT(TYPE_LAST		<= (1<<7));
591	DE_STATIC_ASSERT(OP_LAST		<= (1<<4));
592	DE_STATIC_ASSERT(INPUTTYPE_LAST	<= (1<<2));
593
594#define PACK_EVAL_CASE(OP, IN0TYPE, IN0DATATYPE, IN1TYPE, IN1DATATYPE)	(((OP) << 18) | ((IN0TYPE) << 16) | ((IN0DATATYPE) << 9) | ((IN1TYPE) << 7) | (IN1DATATYPE))
595
596#define MAKE_EVAL_CASE(OP, IN0TYPE, IN0DATATYPE, IN1TYPE, IN1DATATYPE)		\
597	case PACK_EVAL_CASE(OP, IN0TYPE, IN0DATATYPE, IN1TYPE, IN1DATATYPE):	\
598		return Evaluator<OP, IN0TYPE, IN0DATATYPE, IN1TYPE, IN1DATATYPE>::evaluate
599
600#define SCALAR_OPS(IN0TYPE, IN0DATATYPE, IN1TYPE, IN1DATATYPE)	\
601	MAKE_EVAL_CASE(OP_ADD,		IN0TYPE, IN0DATATYPE, IN1TYPE, IN1DATATYPE);	\
602	MAKE_EVAL_CASE(OP_SUB,		IN0TYPE, IN0DATATYPE, IN1TYPE, IN1DATATYPE);	\
603	MAKE_EVAL_CASE(OP_MUL,		IN0TYPE, IN0DATATYPE, IN1TYPE, IN1DATATYPE);	\
604	MAKE_EVAL_CASE(OP_DIV,		IN0TYPE, IN0DATATYPE, IN1TYPE, IN1DATATYPE)
605
606#define ALL_OPS(IN0TYPE, IN0DATATYPE, IN1TYPE, IN1DATATYPE)	\
607	MAKE_EVAL_CASE(OP_ADD,			IN0TYPE, IN0DATATYPE, IN1TYPE, IN1DATATYPE);	\
608	MAKE_EVAL_CASE(OP_SUB,			IN0TYPE, IN0DATATYPE, IN1TYPE, IN1DATATYPE);	\
609	MAKE_EVAL_CASE(OP_MUL,			IN0TYPE, IN0DATATYPE, IN1TYPE, IN1DATATYPE);	\
610	MAKE_EVAL_CASE(OP_DIV,			IN0TYPE, IN0DATATYPE, IN1TYPE, IN1DATATYPE);	\
611	MAKE_EVAL_CASE(OP_COMP_MUL,		IN0TYPE, IN0DATATYPE, IN1TYPE, IN1DATATYPE);
612
613#define MUL_OP(IN0TYPE, IN0DATATYPE, IN1TYPE, IN1DATATYPE)	\
614	MAKE_EVAL_CASE(OP_MUL, IN0TYPE, IN0DATATYPE, IN1TYPE, IN1DATATYPE)
615
616#define MAKE_MAT_SCALAR_VEC_CASES(OP, TYPE0, TYPE1)				\
617	OP(INPUTTYPE_CONST,		TYPE0, INPUTTYPE_CONST,		TYPE1);	\
618	OP(INPUTTYPE_DYNAMIC,	TYPE0, INPUTTYPE_CONST,		TYPE1);	\
619	OP(INPUTTYPE_CONST,		TYPE0, INPUTTYPE_DYNAMIC,	TYPE1);	\
620	OP(INPUTTYPE_DYNAMIC,	TYPE0, INPUTTYPE_DYNAMIC,	TYPE1)
621
622#define MAKE_MAT_MAT_CASES(OP, MATTYPE)								\
623	OP(INPUTTYPE_CONST,		MATTYPE, INPUTTYPE_CONST,	MATTYPE);	\
624	OP(INPUTTYPE_DYNAMIC,	MATTYPE, INPUTTYPE_CONST,	MATTYPE)
625
626#define UNARY_OP(IN0TYPE, IN0DATATYPE)														\
627	MAKE_EVAL_CASE(OP_UNARY_PLUS,		IN0TYPE, IN0DATATYPE, INPUTTYPE_CONST, TYPE_LAST);	\
628	MAKE_EVAL_CASE(OP_NEGATION,			IN0TYPE, IN0DATATYPE, INPUTTYPE_CONST, TYPE_LAST);	\
629	MAKE_EVAL_CASE(OP_PRE_INCREMENT,	IN0TYPE, IN0DATATYPE, INPUTTYPE_CONST, TYPE_LAST);	\
630	MAKE_EVAL_CASE(OP_PRE_DECREMENT,	IN0TYPE, IN0DATATYPE, INPUTTYPE_CONST, TYPE_LAST);	\
631	MAKE_EVAL_CASE(OP_POST_INCREMENT,	IN0TYPE, IN0DATATYPE, INPUTTYPE_CONST, TYPE_LAST);	\
632	MAKE_EVAL_CASE(OP_POST_DECREMENT,	IN0TYPE, IN0DATATYPE, INPUTTYPE_CONST, TYPE_LAST)
633
634#define MAKE_UNARY_CASES(OP, MATTYPE)	\
635	OP(INPUTTYPE_CONST,		MATTYPE);	\
636	OP(INPUTTYPE_DYNAMIC,	MATTYPE)
637
638#define ASSIGN_OP(IN0TYPE, IN0DATATYPE, IN1TYPE, IN1DATATYPE)							\
639	MAKE_EVAL_CASE(OP_ADD_INTO,			IN0TYPE, IN0DATATYPE, IN1TYPE, IN1DATATYPE);	\
640	MAKE_EVAL_CASE(OP_SUBTRACT_FROM,	IN0TYPE, IN0DATATYPE, IN1TYPE, IN1DATATYPE);	\
641	MAKE_EVAL_CASE(OP_MULTIPLY_INTO,	IN0TYPE, IN0DATATYPE, IN1TYPE, IN1DATATYPE);	\
642	MAKE_EVAL_CASE(OP_DIVIDE_INTO,		IN0TYPE, IN0DATATYPE, IN1TYPE, IN1DATATYPE)
643
644#define MAKE_ASSIGNMENT_CASES(OP, MATTYPE)						\
645	OP(INPUTTYPE_CONST,		MATTYPE, INPUTTYPE_CONST,	MATTYPE);	\
646	OP(INPUTTYPE_DYNAMIC,	MATTYPE, INPUTTYPE_CONST,	MATTYPE);	\
647	OP(INPUTTYPE_CONST,		MATTYPE, INPUTTYPE_DYNAMIC,	MATTYPE);	\
648	OP(INPUTTYPE_DYNAMIC,	MATTYPE, INPUTTYPE_DYNAMIC,	MATTYPE)
649
650	// \note At the moment there is no difference between uniform and const inputs. This saves binary size.
651	InputType in0Type = in0.inputType == INPUTTYPE_DYNAMIC ? INPUTTYPE_DYNAMIC : INPUTTYPE_CONST;
652	InputType in1Type = in1.inputType == INPUTTYPE_DYNAMIC ? INPUTTYPE_DYNAMIC : INPUTTYPE_CONST;
653
654	switch (PACK_EVAL_CASE(op, in0Type, in0.dataType, in1Type, in1.dataType))
655	{
656		// Matrix-scalar.
657		MAKE_MAT_SCALAR_VEC_CASES(SCALAR_OPS,	TYPE_FLOAT_MAT2, TYPE_FLOAT);
658		MAKE_MAT_SCALAR_VEC_CASES(SCALAR_OPS,	TYPE_FLOAT_MAT3, TYPE_FLOAT);
659		MAKE_MAT_SCALAR_VEC_CASES(SCALAR_OPS,	TYPE_FLOAT_MAT4, TYPE_FLOAT);
660
661		// Matrix-vector.
662		MAKE_MAT_SCALAR_VEC_CASES(MUL_OP,		TYPE_FLOAT_MAT2, TYPE_FLOAT_VEC2);
663		MAKE_MAT_SCALAR_VEC_CASES(MUL_OP,		TYPE_FLOAT_MAT3, TYPE_FLOAT_VEC3);
664		MAKE_MAT_SCALAR_VEC_CASES(MUL_OP,		TYPE_FLOAT_MAT4, TYPE_FLOAT_VEC4);
665
666		// Vector-matrix.
667		MAKE_MAT_SCALAR_VEC_CASES(MUL_OP,		TYPE_FLOAT_VEC2, TYPE_FLOAT_MAT2);
668		MAKE_MAT_SCALAR_VEC_CASES(MUL_OP,		TYPE_FLOAT_VEC3, TYPE_FLOAT_MAT3);
669		MAKE_MAT_SCALAR_VEC_CASES(MUL_OP,		TYPE_FLOAT_VEC4, TYPE_FLOAT_MAT4);
670
671		// Matrix-matrix.
672		MAKE_MAT_MAT_CASES(ALL_OPS,	TYPE_FLOAT_MAT2);
673		MAKE_MAT_MAT_CASES(ALL_OPS,	TYPE_FLOAT_MAT3);
674		MAKE_MAT_MAT_CASES(ALL_OPS,	TYPE_FLOAT_MAT4);
675
676		// Unary matrix
677		MAKE_UNARY_CASES(UNARY_OP, TYPE_FLOAT_MAT2);
678		MAKE_UNARY_CASES(UNARY_OP, TYPE_FLOAT_MAT3);
679		MAKE_UNARY_CASES(UNARY_OP, TYPE_FLOAT_MAT4);
680
681		// Assignment matrix
682		MAKE_ASSIGNMENT_CASES(ASSIGN_OP, TYPE_FLOAT_MAT2);
683		MAKE_ASSIGNMENT_CASES(ASSIGN_OP, TYPE_FLOAT_MAT3);
684		MAKE_ASSIGNMENT_CASES(ASSIGN_OP, TYPE_FLOAT_MAT4);
685
686		default:
687			DE_ASSERT(DE_FALSE);
688			return DE_NULL;
689	}
690
691#undef PACK_EVAL_CASE
692#undef MAKE_EVAL_CASE
693#undef MUL_OP
694#undef ALL_OPS
695#undef MAKE_MAT_SCALAR_VEC_CASES
696#undef MAKE_MAT_MAT_CASES
697}
698
699// Shader source format utilities.
700
701template <int Size>
702void writeVectorConstructor (std::ostream& str, const tcu::Vector<float, Size>& v)
703{
704	str << "vec" << Size << "(";
705	for (int ndx = 0; ndx < Size; ndx++)
706	{
707		if (ndx != 0)
708			str << ", ";
709		str << de::floatToString(v[ndx], 1);
710	}
711	str << ")";
712}
713
714template <int Cols, int Rows>
715void writeMatrixConstructor (std::ostream& str, const tcu::Matrix<float, Rows, Cols>& m)
716{
717	if (Rows == Cols)
718		str << "mat" << Cols;
719	else
720		str << "mat" << Cols << "x" << Rows;
721
722	str << "(";
723	for (int colNdx = 0; colNdx < Cols; colNdx++)
724	{
725		for (int rowNdx = 0; rowNdx < Rows; rowNdx++)
726		{
727			if (rowNdx > 0 || colNdx > 0)
728				str << ", ";
729			str << de::floatToString(m(rowNdx, colNdx), 1);
730		}
731	}
732	str << ")";
733}
734
735} // MatrixCaseUtils
736
737using namespace MatrixCaseUtils;
738
739class ShaderMatrixCase : public ShaderRenderCase
740{
741public:
742					ShaderMatrixCase			(Context& context, const char* name, const char* desc, const ShaderInput& in0, const ShaderInput& in1, MatrixOp op, bool isVertexCase);
743					~ShaderMatrixCase			(void);
744
745	void			init						(void);
746
747protected:
748	std::string		genGLSLMatToVec3Reduction	(const glu::DataType& matType, const char* varName);
749	void			setupUniforms				(int programID, const tcu::Vec4& constCoords);
750
751private:
752	ShaderInput		m_in0;
753	ShaderInput		m_in1;
754	MatrixOp		m_op;
755};
756
757ShaderMatrixCase::ShaderMatrixCase (Context& context, const char* name, const char* desc, const ShaderInput& in0, const ShaderInput& in1, MatrixOp op, bool isVertexCase)
758	: ShaderRenderCase	(context.getTestContext(), context.getRenderContext(), context.getContextInfo(), name, desc, isVertexCase, getEvalFunc(in0, in1, op))
759	, m_in0				(in0)
760	, m_in1				(in1)
761	, m_op				(op)
762{
763}
764
765ShaderMatrixCase::~ShaderMatrixCase (void)
766{
767}
768
769void ShaderMatrixCase::init (void)
770{
771	std::ostringstream	vtx;
772	std::ostringstream	frag;
773	std::ostringstream&	op				= m_isVertexCase ? vtx : frag;
774
775	bool				isInDynMat0		= isDataTypeMatrix(m_in0.dataType) && m_in0.inputType == INPUTTYPE_DYNAMIC;
776	bool				isInDynMat1		= isDataTypeMatrix(m_in1.dataType) && m_in1.inputType == INPUTTYPE_DYNAMIC;
777	string				inValue0;
778	string				inValue1;
779	DataType			resultType		= TYPE_LAST;
780	Precision			resultPrec		= m_in0.precision;
781	vector<string>		passVars;
782	int					numInputs		= (isOperationBinary(m_op)) ? (2) : (1);
783
784	std::string			operationValue0;
785	std::string			operationValue1;
786
787	DE_ASSERT(!isInDynMat0 || !isInDynMat1); // Only single dynamic matrix input is allowed.
788	DE_UNREF(isInDynMat0 && isInDynMat1);
789
790	// Compute result type.
791	if (isDataTypeMatrix(m_in0.dataType) && isDataTypeMatrix(m_in1.dataType))
792	{
793		DE_ASSERT(m_in0.dataType == m_in1.dataType);
794		resultType = m_in0.dataType;
795	}
796	else if (getOperationType(m_op) == OPERATIONTYPE_UNARY_PREFIX_OPERATOR ||
797			 getOperationType(m_op) == OPERATIONTYPE_UNARY_POSTFIX_OPERATOR)
798	{
799		resultType = m_in0.dataType;
800	}
801	else
802	{
803		int			matNdx		= isDataTypeMatrix(m_in0.dataType) ? 0 : 1;
804		DataType	matrixType	= matNdx == 0 ? m_in0.dataType : m_in1.dataType;
805		DataType	otherType	= matNdx == 0 ? m_in1.dataType : m_in0.dataType;
806
807		if (otherType == TYPE_FLOAT)
808			resultType = matrixType;
809		else
810		{
811			DE_ASSERT(isDataTypeVector(otherType));
812			resultType = otherType;
813		}
814	}
815
816	vtx << "attribute highp vec4 a_position;\n";
817	if (m_isVertexCase)
818	{
819		vtx << "varying mediump vec4 v_color;\n";
820		frag << "varying mediump vec4 v_color;\n";
821	}
822
823	// Input declarations.
824	for (int inNdx = 0; inNdx < numInputs; inNdx++)
825	{
826		const ShaderInput&	in			= inNdx > 0 ? m_in1 : m_in0;
827		const char*			precName	= getPrecisionName(in.precision);
828		const char*			typeName	= getDataTypeName(in.dataType);
829		string&				inValue		= inNdx > 0 ? inValue1 : inValue0;
830
831		if (in.inputType == INPUTTYPE_DYNAMIC)
832		{
833			vtx << "attribute " << precName << " " << typeName << " a_";
834
835			if (isDataTypeMatrix(in.dataType))
836			{
837				// a_matN, v_matN
838				vtx << typeName << ";\n";
839				if (!m_isVertexCase)
840				{
841					vtx << "varying " << precName << " " << typeName << " v_" << typeName << ";\n";
842					frag << "varying " << precName << " " << typeName << " v_" << typeName << ";\n";
843					passVars.push_back(typeName);
844				}
845
846				inValue = string(m_isVertexCase ? "a_" : "v_") + getDataTypeName(in.dataType);
847			}
848			else
849			{
850				// a_coords, v_coords
851				vtx << "coords;\n";
852				if (!m_isVertexCase)
853				{
854					vtx << "varying " << precName << " " << typeName << " v_coords;\n";
855					frag << "varying " << precName << " " << typeName << " v_coords;\n";
856					passVars.push_back("coords");
857				}
858
859				inValue = m_isVertexCase ? "a_coords" : "v_coords";
860			}
861		}
862		else if (in.inputType == INPUTTYPE_UNIFORM)
863		{
864			op << "uniform " << precName << " " << typeName << " u_in" << inNdx << ";\n";
865			inValue = string("u_in") + de::toString(inNdx);
866		}
867		else if (in.inputType == INPUTTYPE_CONST)
868		{
869			op << "const " << precName << " " << typeName << " in" << inNdx << " = ";
870
871			// Generate declaration.
872			switch (in.dataType)
873			{
874				case TYPE_FLOAT:		op << de::floatToString(s_constInFloat[inNdx], 1);					break;
875				case TYPE_FLOAT_VEC2:	writeVectorConstructor<2>(op, s_constInVec2[inNdx]);				break;
876				case TYPE_FLOAT_VEC3:	writeVectorConstructor<3>(op, s_constInVec3[inNdx]);				break;
877				case TYPE_FLOAT_VEC4:	writeVectorConstructor<4>(op, s_constInVec4[inNdx]);				break;
878				case TYPE_FLOAT_MAT2:	writeMatrixConstructor<2, 2>(op, Mat2(s_constInMat2[inNdx]));		break;
879				case TYPE_FLOAT_MAT3:	writeMatrixConstructor<3, 3>(op, Mat3(s_constInMat3[inNdx]));		break;
880				case TYPE_FLOAT_MAT4:	writeMatrixConstructor<4, 4>(op, Mat4(s_constInMat4[inNdx]));		break;
881
882				default:
883					DE_ASSERT(DE_FALSE);
884			}
885
886			op << ";\n";
887
888			inValue = string("in") + de::toString(inNdx);
889		}
890	}
891
892	vtx << "\n"
893		<< "void main (void)\n"
894		<< "{\n"
895		<< "	gl_Position = a_position;\n";
896	frag << "\n"
897		 << "void main (void)\n"
898		 << "{\n";
899
900	if (m_isVertexCase)
901	{
902		frag << "	gl_FragColor = v_color;\n";
903	}
904	else
905	{
906		for (vector<string>::const_iterator copyIter = passVars.begin(); copyIter != passVars.end(); copyIter++)
907			vtx << "	v_" << *copyIter << " = " << "a_" << *copyIter << ";\n";
908	}
909
910	// Operation.
911
912	switch (getOperationNature(m_op))
913	{
914		case OPERATIONNATURE_PURE:
915			DE_ASSERT(getOperationType(m_op) != OPERATIONTYPE_ASSIGNMENT);
916
917			operationValue0 = inValue0;
918			operationValue1 = inValue1;
919			break;
920
921		case OPERATIONNATURE_MUTATING:
922			DE_ASSERT(getOperationType(m_op) != OPERATIONTYPE_ASSIGNMENT);
923
924			op << "	" << getPrecisionName(resultPrec) << " " << getDataTypeName(resultType) << " tmpValue = " << inValue0 << ";\n";
925
926			operationValue0 = "tmpValue";
927			operationValue1 = inValue1;
928			break;
929
930		case OPERATIONNATURE_ASSIGNMENT:
931			DE_ASSERT(getOperationType(m_op) == OPERATIONTYPE_ASSIGNMENT);
932
933			operationValue0 = inValue0;
934			operationValue1 = inValue1;
935			break;
936
937		default:
938			DE_ASSERT(DE_FALSE);
939	}
940
941	switch (getOperationType(m_op))
942	{
943		case OPERATIONTYPE_BINARY_OPERATOR:
944			op << "	" << getPrecisionName(resultPrec) << " " << getDataTypeName(resultType) << " res = " << operationValue0 << " " << getOperationName(m_op) << " " << operationValue1 << ";\n";
945			break;
946
947		case OPERATIONTYPE_UNARY_PREFIX_OPERATOR:
948			op << "	" << getPrecisionName(resultPrec) << " " << getDataTypeName(resultType) << " res = " << getOperationName(m_op) << operationValue0 << ";\n";
949			break;
950
951		case OPERATIONTYPE_UNARY_POSTFIX_OPERATOR:
952			op << "	" << getPrecisionName(resultPrec) << " " << getDataTypeName(resultType) << " res = " << operationValue0 << getOperationName(m_op) << ";\n";
953			break;
954
955		case OPERATIONTYPE_BINARY_FUNCTION:
956			op << "	" << getPrecisionName(resultPrec) << " " << getDataTypeName(resultType) << " res = " << getOperationName(m_op) << "(" << operationValue0 << ", " << operationValue1 << ");\n";
957			break;
958
959		case OPERATIONTYPE_ASSIGNMENT:
960			op << "	" << getPrecisionName(resultPrec) << " " << getDataTypeName(resultType) << " res = " << operationValue0 << ";\n";
961			op << "	res " << getOperationName(m_op) << " " << operationValue1 << ";\n";
962			break;
963
964		default:
965			DE_ASSERT(DE_FALSE);
966	}
967
968	// Reduction to vec3 (rgb). Check the used value too if it was modified.
969	op << "	" << (m_isVertexCase ? "v_color" : "gl_FragColor") << " = ";
970
971	if (isOperationValueModifying(m_op))
972		op << "vec4(" << genGLSLMatToVec3Reduction(resultType, "res") << ", 1.0) + vec4(" << genGLSLMatToVec3Reduction(resultType, "tmpValue") << ", 0.0);\n";
973	else
974		op << "vec4(" << genGLSLMatToVec3Reduction(resultType, "res") << ", 1.0);\n";
975
976	vtx << "}\n";
977	frag << "}\n";
978
979	m_vertShaderSource	= vtx.str();
980	m_fragShaderSource	= frag.str();
981
982	// \todo [2012-02-14 pyry] Compute better values for matrix tests.
983	m_userAttribTransforms.resize(4);
984	for (int attribNdx = 0; attribNdx < 4; attribNdx++)
985	{
986		m_userAttribTransforms[attribNdx] = Mat4(0.0f);
987		m_userAttribTransforms[attribNdx]((0 + attribNdx) % 4, 0) = 1.0f;
988		m_userAttribTransforms[attribNdx]((1 + attribNdx) % 4, 1) = 1.0f;
989		m_userAttribTransforms[attribNdx]((2 + attribNdx) % 4, 2) = 1.0f;
990		m_userAttribTransforms[attribNdx]((3 + attribNdx) % 4, 3) = 1.0f;
991	}
992
993	// prevent bad reference cases such as black result images by fine-tuning used matrices
994	if (getOperationTestMatrixType(m_op) != TESTMATRIXTYPE_DEFAULT)
995	{
996		for (int attribNdx = 0; attribNdx < 4; attribNdx++)
997		{
998			for (int row = 0; row < 4; row++)
999			for (int col = 0; col < 4; col++)
1000			{
1001				switch (getOperationTestMatrixType(m_op))
1002				{
1003					case TESTMATRIXTYPE_NEGATED:
1004						m_userAttribTransforms[attribNdx](row, col) = -m_userAttribTransforms[attribNdx](row, col);
1005						break;
1006					case TESTMATRIXTYPE_INCREMENTED:
1007						m_userAttribTransforms[attribNdx](row, col) += 0.3f;
1008						break;
1009					case TESTMATRIXTYPE_DECREMENTED:
1010						m_userAttribTransforms[attribNdx](row, col) -= 0.1f;
1011						break;
1012
1013					default:
1014						DE_ASSERT(DE_FALSE);
1015						break;
1016				}
1017			}
1018		}
1019	}
1020
1021	ShaderRenderCase::init();
1022}
1023
1024std::string ShaderMatrixCase::genGLSLMatToVec3Reduction (const glu::DataType& matType, const char* varName)
1025{
1026	std::ostringstream op;
1027
1028	switch (matType)
1029	{
1030		case TYPE_FLOAT:		op << varName << ", "		<< varName << ", "			<< varName << "";										break;
1031		case TYPE_FLOAT_VEC2:	op << varName << ".x, "		<< varName << ".y, "		<< varName << ".x";										break;
1032		case TYPE_FLOAT_VEC3:	op << varName << "";																							break;
1033		case TYPE_FLOAT_VEC4:	op << varName << ".x, "		<< varName << ".y, "		<< varName << ".z+"			<< varName << ".w";			break;
1034		case TYPE_FLOAT_MAT2:	op << varName << "[0][0], "	<< varName << "[1][0], "	<< varName << "[0][1]+"		<< varName << "[1][1]";		break;
1035		case TYPE_FLOAT_MAT3:	op << varName << "[0]+"		<< varName << "[1]+"		<< varName << "[2]";									break;
1036		case TYPE_FLOAT_MAT4:	op << varName << "[0].xyz+"	<< varName << "[1].yzw+"	<< varName << "[2].zwx+"	<< varName << "[3].wxy";	break;
1037
1038		default:
1039			DE_ASSERT(DE_FALSE);
1040	}
1041
1042	return op.str();
1043}
1044
1045void ShaderMatrixCase::setupUniforms (int programID, const tcu::Vec4& constCoords)
1046{
1047	const glw::Functions& gl = m_renderCtx.getFunctions();
1048
1049	DE_UNREF(constCoords);
1050
1051	for (int inNdx = 0; inNdx < 2; inNdx++)
1052	{
1053		const ShaderInput& in = inNdx > 0 ? m_in1 : m_in0;
1054
1055		if (in.inputType == INPUTTYPE_UNIFORM)
1056		{
1057			int loc = gl.getUniformLocation(programID, (string("u_in") + de::toString(inNdx)).c_str());
1058
1059			if (loc < 0)
1060				continue;
1061
1062			switch (in.dataType)
1063			{
1064				case TYPE_FLOAT:		gl.uniform1f(loc, s_constInFloat[inNdx]);													break;
1065				case TYPE_FLOAT_VEC2:	gl.uniform2fv(loc, 1, s_constInVec2[inNdx].getPtr());										break;
1066				case TYPE_FLOAT_VEC3:	gl.uniform3fv(loc, 1, s_constInVec3[inNdx].getPtr());										break;
1067				case TYPE_FLOAT_VEC4:	gl.uniform4fv(loc, 1, s_constInVec4[inNdx].getPtr());										break;
1068				case TYPE_FLOAT_MAT2:	gl.uniformMatrix2fv(loc, 1, GL_FALSE, s_constInMat2[inNdx].getColumnMajorData().getPtr());	break;
1069				case TYPE_FLOAT_MAT3:	gl.uniformMatrix3fv(loc, 1, GL_FALSE, s_constInMat3[inNdx].getColumnMajorData().getPtr());	break;
1070				case TYPE_FLOAT_MAT4:	gl.uniformMatrix4fv(loc, 1, GL_FALSE, s_constInMat4[inNdx].getColumnMajorData().getPtr());	break;
1071				default:
1072					DE_ASSERT(false);
1073			}
1074		}
1075	}
1076}
1077
1078ShaderMatrixTests::ShaderMatrixTests (Context& context)
1079	: TestCaseGroup(context, "matrix", "Matrix Tests")
1080{
1081}
1082
1083ShaderMatrixTests::~ShaderMatrixTests (void)
1084{
1085}
1086
1087void ShaderMatrixTests::init (void)
1088{
1089	static const struct
1090	{
1091		const char*		name;
1092		const char*		desc;
1093		MatrixOp		op;
1094		bool			extendedInputTypeCases; // !< test with const and uniform types too
1095	} ops[] =
1096	{
1097		{ "add",			"Matrix addition tests",						OP_ADD,				true	},
1098		{ "sub",			"Matrix subtraction tests",						OP_SUB,				true	},
1099		{ "mul",			"Matrix multiplication tests",					OP_MUL,				true	},
1100		{ "div",			"Matrix division tests",						OP_DIV,				true	},
1101		{ "matrixcompmult",	"Matrix component-wise multiplication tests",	OP_COMP_MUL,		false	},
1102		{ "unary_addition",	"Matrix unary addition tests",					OP_UNARY_PLUS,		false	},
1103		{ "negation",		"Matrix negation tests",						OP_NEGATION,		false	},
1104		{ "pre_increment",	"Matrix prefix increment tests",				OP_PRE_INCREMENT,	false	},
1105		{ "pre_decrement",	"Matrix prefix decrement tests",				OP_PRE_DECREMENT,	false	},
1106		{ "post_increment",	"Matrix postfix increment tests",				OP_POST_INCREMENT,	false	},
1107		{ "post_decrement",	"Matrix postfix decrement tests",				OP_POST_DECREMENT,	false	},
1108		{ "add_assign",		"Matrix add into tests",						OP_ADD_INTO,		false	},
1109		{ "sub_assign",		"Matrix subtract from tests",					OP_SUBTRACT_FROM,	false	},
1110		{ "mul_assign",		"Matrix multiply into tests",					OP_MULTIPLY_INTO,	false	},
1111		{ "div_assign",		"Matrix divide into tests",						OP_DIVIDE_INTO,		false	},
1112	};
1113
1114	struct InputTypeSpec
1115	{
1116		const char*		name;
1117		const char*		desc;
1118		InputType		type;
1119	};
1120	static const InputTypeSpec extendedInputTypes[] =
1121	{
1122		{ "const",		"Constant matrix input",	INPUTTYPE_CONST		},
1123		{ "uniform",	"Uniform matrix input",		INPUTTYPE_UNIFORM	},
1124		{ "dynamic",	"Dynamic matrix input",		INPUTTYPE_DYNAMIC	}
1125	};
1126	static const InputTypeSpec reducedInputTypes[] =
1127	{
1128		{ "dynamic",	"Dynamic matrix input",		INPUTTYPE_DYNAMIC	}
1129	};
1130
1131	static const DataType matrixTypes[] =
1132	{
1133		TYPE_FLOAT_MAT2,
1134		TYPE_FLOAT_MAT3,
1135		TYPE_FLOAT_MAT4
1136	};
1137
1138	static const Precision precisions[] =
1139	{
1140		PRECISION_LOWP,
1141		PRECISION_MEDIUMP,
1142		PRECISION_HIGHP
1143	};
1144
1145	for (int opNdx = 0; opNdx < DE_LENGTH_OF_ARRAY(ops); opNdx++)
1146	{
1147		const InputTypeSpec*	inTypeList		= (ops[opNdx].extendedInputTypeCases) ? (extendedInputTypes) : (reducedInputTypes);
1148		const int				inTypeListSize	= (ops[opNdx].extendedInputTypeCases) ? (DE_LENGTH_OF_ARRAY(extendedInputTypes)) : (DE_LENGTH_OF_ARRAY(reducedInputTypes));
1149		const MatrixOp			op				= ops[opNdx].op;
1150		tcu::TestCaseGroup*		opGroup			= new tcu::TestCaseGroup(m_testCtx, ops[opNdx].name, ops[opNdx].desc);
1151
1152		addChild(opGroup);
1153
1154		for (int inTypeNdx = 0; inTypeNdx < inTypeListSize; inTypeNdx++)
1155		{
1156			const InputType		inputType	= inTypeList[inTypeNdx].type;
1157
1158			for (int matTypeNdx = 0; matTypeNdx < DE_LENGTH_OF_ARRAY(matrixTypes); matTypeNdx++)
1159			{
1160				DataType	matType		= matrixTypes[matTypeNdx];
1161				const char*	matTypeName	= getDataTypeName(matType);
1162
1163				for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
1164				{
1165					Precision	precision	= precisions[precNdx];
1166					const char*	precName	= getPrecisionName(precision);
1167					string		baseName	= string(inTypeList[inTypeNdx].name) + "_" + precName + "_" + matTypeName + "_";
1168					ShaderInput	matIn		(inputType, matType, precision);
1169
1170					if (isOperationMatrixScalar(op))
1171					{
1172						// Matrix-scalar \note For div cases we use uniform input.
1173						ShaderInput scalarIn(op == OP_DIV ? INPUTTYPE_UNIFORM : INPUTTYPE_DYNAMIC, TYPE_FLOAT, precision);
1174						opGroup->addChild(new ShaderMatrixCase(m_context, (baseName + "float_vertex").c_str(),		"Matrix-scalar case", matIn, scalarIn, op, true));
1175						opGroup->addChild(new ShaderMatrixCase(m_context, (baseName + "float_fragment").c_str(),	"Matrix-scalar case", matIn, scalarIn, op, false));
1176					}
1177
1178					if (isOperationMatrixVector(op))
1179					{
1180						// Matrix-vector.
1181						DataType	vecType	= getDataTypeFloatVec(getDataTypeMatrixNumColumns(matType));
1182						ShaderInput vecIn	(op == OP_DIV ? INPUTTYPE_UNIFORM : INPUTTYPE_DYNAMIC, vecType, precision);
1183
1184						opGroup->addChild(new ShaderMatrixCase(m_context, (baseName + getDataTypeName(vecType) + "_vertex").c_str(),	"Matrix-vector case", matIn, vecIn, op, true));
1185						opGroup->addChild(new ShaderMatrixCase(m_context, (baseName + getDataTypeName(vecType) + "_fragment").c_str(),	"Matrix-vector case", matIn, vecIn, op, false));
1186
1187						// Vector-matrix.
1188						string vecMatName = string(inTypeList[inTypeNdx].name) + "_" + precName + "_" + getDataTypeName(vecType) + "_" + matTypeName;
1189						opGroup->addChild(new ShaderMatrixCase(m_context, (vecMatName + "_vertex").c_str(),		"Vector-matrix case", vecIn, matIn, op, true));
1190						opGroup->addChild(new ShaderMatrixCase(m_context, (vecMatName + "_fragment").c_str(),	"Vector-matrix case", vecIn, matIn, op, false));
1191					}
1192
1193					if (isOperationMatrixMatrix(op))
1194					{
1195						// Matrix-matrix.
1196						ShaderInput otherMatIn(inputType == INPUTTYPE_DYNAMIC ? INPUTTYPE_UNIFORM : inputType, matType, precision);
1197						opGroup->addChild(new ShaderMatrixCase(m_context, (baseName + matTypeName + "_vertex").c_str(),		"Matrix-matrix case", matIn, otherMatIn, op, true));
1198						opGroup->addChild(new ShaderMatrixCase(m_context, (baseName + matTypeName + "_fragment").c_str(),	"Matrix-matrix case", matIn, otherMatIn, op, false));
1199					}
1200
1201					if (isOperationUnary(op))
1202					{
1203						// op matrix
1204						ShaderInput voidInput(INPUTTYPE_LAST, TYPE_LAST, PRECISION_LAST);
1205						opGroup->addChild(new ShaderMatrixCase(m_context, (baseName + "vertex").c_str(),		"Matrix case", matIn, voidInput, op, true));
1206						opGroup->addChild(new ShaderMatrixCase(m_context, (baseName + "fragment").c_str(),	"Matrix case", matIn, voidInput, op, false));
1207					}
1208
1209					if (isOperationAssignment(op))
1210					{
1211						ShaderInput otherMatIn(inputType == INPUTTYPE_DYNAMIC ? INPUTTYPE_UNIFORM : inputType, matType, precision);
1212						opGroup->addChild(new ShaderMatrixCase(m_context, (baseName + "vertex").c_str(),		"Matrix assignment case", matIn, otherMatIn, op, true));
1213						opGroup->addChild(new ShaderMatrixCase(m_context, (baseName + "fragment").c_str(),	"Matrix assignment case", matIn, otherMatIn, op, false));
1214					}
1215				}
1216			}
1217		}
1218	}
1219}
1220
1221} // Functional
1222} // gles2
1223} // deqp
1224