1 /*-------------------------------------------------------------------------
2  * OpenGL Conformance Test Suite
3  * -----------------------------
4  *
5  * Copyright (c) 2016 Google Inc.
6  * Copyright (c) 2016 The Khronos Group Inc.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */ /*!
21  * \file
22  * \brief Compiler test case.
23  */ /*-------------------------------------------------------------------*/
24 
25 #include "glcShaderLibraryCase.hpp"
26 
27 #include "tcuRenderTarget.hpp"
28 #include "tcuTestLog.hpp"
29 
30 #include "gluDrawUtil.hpp"
31 #include "gluPixelTransfer.hpp"
32 #include "gluShaderProgram.hpp"
33 #include "tcuStringTemplate.hpp"
34 
35 #include "glwEnums.hpp"
36 #include "glwFunctions.hpp"
37 
38 #include "deInt32.h"
39 #include "deMath.h"
40 #include "deRandom.hpp"
41 #include "deString.h"
42 
43 #include <map>
44 #include <sstream>
45 #include <string>
46 #include <vector>
47 
48 using namespace std;
49 using namespace tcu;
50 using namespace glu;
51 
52 namespace deqp
53 {
54 namespace sl
55 {
56 
57 enum
58 {
59 	VIEWPORT_WIDTH  = 128,
60 	VIEWPORT_HEIGHT = 128
61 };
62 
usesShaderInoutQualifiers(glu::GLSLVersion version)63 static inline bool usesShaderInoutQualifiers(glu::GLSLVersion version)
64 {
65 	switch (version)
66 	{
67 	case glu::GLSL_VERSION_100_ES:
68 	case glu::GLSL_VERSION_130:
69 	case glu::GLSL_VERSION_140:
70 	case glu::GLSL_VERSION_150:
71 		return false;
72 
73 	default:
74 		return true;
75 	}
76 }
77 
78 // ShaderCase.
79 
ShaderCase(tcu::TestContext & testCtx,RenderContext & renderCtx,const char * name,const char * description,ExpectResult expectResult,const std::vector<ValueBlock> & valueBlocks,GLSLVersion targetVersion,const char * vertexSource,const char * fragmentSource)80 ShaderCase::ShaderCase(tcu::TestContext& testCtx, RenderContext& renderCtx, const char* name, const char* description,
81 					   ExpectResult expectResult, const std::vector<ValueBlock>& valueBlocks, GLSLVersion targetVersion,
82 					   const char* vertexSource, const char* fragmentSource)
83 	: tcu::TestCase(testCtx, name, description)
84 	, m_renderCtx(renderCtx)
85 	, m_expectResult(expectResult)
86 	, m_valueBlocks(valueBlocks)
87 	, m_targetVersion(targetVersion)
88 {
89 	// If no value blocks given, use an empty one.
90 	if (m_valueBlocks.size() == 0)
91 		m_valueBlocks.push_back(ValueBlock());
92 
93 	// Use first value block to specialize shaders.
94 	const ValueBlock& valueBlock = m_valueBlocks[0];
95 
96 	// \todo [2010-04-01 petri] Check that all value blocks have matching values.
97 
98 	// Generate specialized shader sources.
99 	if (vertexSource && fragmentSource)
100 	{
101 		m_caseType = CASETYPE_COMPLETE;
102 		specializeShaders(vertexSource, fragmentSource, m_vertexSource, m_fragmentSource, valueBlock);
103 	}
104 	else if (vertexSource)
105 	{
106 		m_caseType		 = CASETYPE_VERTEX_ONLY;
107 		m_vertexSource   = specializeVertexShader(vertexSource, valueBlock);
108 		m_fragmentSource = genFragmentShader(valueBlock);
109 	}
110 	else
111 	{
112 		DE_ASSERT(fragmentSource);
113 		m_caseType		 = CASETYPE_FRAGMENT_ONLY;
114 		m_vertexSource   = genVertexShader(valueBlock);
115 		m_fragmentSource = specializeFragmentShader(fragmentSource, valueBlock);
116 	}
117 }
118 
~ShaderCase(void)119 ShaderCase::~ShaderCase(void)
120 {
121 }
122 
setUniformValue(const glw::Functions & gl,deUint32 programID,const std::string & name,const ShaderCase::Value & val,int arrayNdx)123 static void setUniformValue(const glw::Functions& gl, deUint32 programID, const std::string& name,
124 							const ShaderCase::Value& val, int arrayNdx)
125 {
126 	int scalarSize = getDataTypeScalarSize(val.dataType);
127 	int loc		   = gl.getUniformLocation(programID, name.c_str());
128 
129 	TCU_CHECK_MSG(loc != -1, "uniform location not found");
130 
131 	DE_STATIC_ASSERT(sizeof(ShaderCase::Value::Element) == sizeof(glw::GLfloat));
132 	DE_STATIC_ASSERT(sizeof(ShaderCase::Value::Element) == sizeof(glw::GLint));
133 
134 	int elemNdx = (val.arrayLength == 1) ? 0 : (arrayNdx * scalarSize);
135 
136 	switch (val.dataType)
137 	{
138 	case TYPE_FLOAT:
139 		gl.uniform1fv(loc, 1, &val.elements[elemNdx].float32);
140 		break;
141 	case TYPE_FLOAT_VEC2:
142 		gl.uniform2fv(loc, 1, &val.elements[elemNdx].float32);
143 		break;
144 	case TYPE_FLOAT_VEC3:
145 		gl.uniform3fv(loc, 1, &val.elements[elemNdx].float32);
146 		break;
147 	case TYPE_FLOAT_VEC4:
148 		gl.uniform4fv(loc, 1, &val.elements[elemNdx].float32);
149 		break;
150 	case TYPE_FLOAT_MAT2:
151 		gl.uniformMatrix2fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32);
152 		break;
153 	case TYPE_FLOAT_MAT3:
154 		gl.uniformMatrix3fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32);
155 		break;
156 	case TYPE_FLOAT_MAT4:
157 		gl.uniformMatrix4fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32);
158 		break;
159 	case TYPE_INT:
160 		gl.uniform1iv(loc, 1, &val.elements[elemNdx].int32);
161 		break;
162 	case TYPE_INT_VEC2:
163 		gl.uniform2iv(loc, 1, &val.elements[elemNdx].int32);
164 		break;
165 	case TYPE_INT_VEC3:
166 		gl.uniform3iv(loc, 1, &val.elements[elemNdx].int32);
167 		break;
168 	case TYPE_INT_VEC4:
169 		gl.uniform4iv(loc, 1, &val.elements[elemNdx].int32);
170 		break;
171 	case TYPE_BOOL:
172 		gl.uniform1iv(loc, 1, &val.elements[elemNdx].int32);
173 		break;
174 	case TYPE_BOOL_VEC2:
175 		gl.uniform2iv(loc, 1, &val.elements[elemNdx].int32);
176 		break;
177 	case TYPE_BOOL_VEC3:
178 		gl.uniform3iv(loc, 1, &val.elements[elemNdx].int32);
179 		break;
180 	case TYPE_BOOL_VEC4:
181 		gl.uniform4iv(loc, 1, &val.elements[elemNdx].int32);
182 		break;
183 	case TYPE_UINT:
184 		gl.uniform1uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32);
185 		break;
186 	case TYPE_UINT_VEC2:
187 		gl.uniform2uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32);
188 		break;
189 	case TYPE_UINT_VEC3:
190 		gl.uniform3uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32);
191 		break;
192 	case TYPE_UINT_VEC4:
193 		gl.uniform4uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32);
194 		break;
195 	case TYPE_FLOAT_MAT2X3:
196 		gl.uniformMatrix2x3fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32);
197 		break;
198 	case TYPE_FLOAT_MAT2X4:
199 		gl.uniformMatrix2x4fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32);
200 		break;
201 	case TYPE_FLOAT_MAT3X2:
202 		gl.uniformMatrix3x2fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32);
203 		break;
204 	case TYPE_FLOAT_MAT3X4:
205 		gl.uniformMatrix3x4fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32);
206 		break;
207 	case TYPE_FLOAT_MAT4X2:
208 		gl.uniformMatrix4x2fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32);
209 		break;
210 	case TYPE_FLOAT_MAT4X3:
211 		gl.uniformMatrix4x3fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32);
212 		break;
213 
214 	case TYPE_SAMPLER_2D:
215 	case TYPE_SAMPLER_CUBE:
216 		DE_ASSERT(DE_FALSE && "implement!");
217 		break;
218 
219 	default:
220 		DE_ASSERT(false);
221 	}
222 }
223 
checkPixels(Surface & surface,int minX,int maxX,int minY,int maxY)224 bool ShaderCase::checkPixels(Surface& surface, int minX, int maxX, int minY, int maxY)
225 {
226 	TestLog& log		   = m_testCtx.getLog();
227 	bool	 allWhite	  = true;
228 	bool	 allBlack	  = true;
229 	bool	 anyUnexpected = false;
230 
231 	DE_ASSERT((maxX > minX) && (maxY > minY));
232 
233 	for (int y = minY; y <= maxY; y++)
234 	{
235 		for (int x = minX; x <= maxX; x++)
236 		{
237 			RGBA pixel = surface.getPixel(x, y);
238 			// Note: we really do not want to involve alpha in the check comparison
239 			// \todo [2010-09-22 kalle] Do we know that alpha would be one? If yes, could use color constants white and black.
240 			bool isWhite = (pixel.getRed() == 255) && (pixel.getGreen() == 255) && (pixel.getBlue() == 255);
241 			bool isBlack = (pixel.getRed() == 0) && (pixel.getGreen() == 0) && (pixel.getBlue() == 0);
242 
243 			allWhite	  = allWhite && isWhite;
244 			allBlack	  = allBlack && isBlack;
245 			anyUnexpected = anyUnexpected || (!isWhite && !isBlack);
246 		}
247 	}
248 
249 	if (!allWhite)
250 	{
251 		if (anyUnexpected)
252 			log << TestLog::Message
253 				<< "WARNING: expecting all rendered pixels to be white or black, but got other colors as well!"
254 				<< TestLog::EndMessage;
255 		else if (!allBlack)
256 			log << TestLog::Message
257 				<< "WARNING: got inconsistent results over the image, when all pixels should be the same color!"
258 				<< TestLog::EndMessage;
259 
260 		return false;
261 	}
262 	return true;
263 }
264 
execute(void)265 bool ShaderCase::execute(void)
266 {
267 	TestLog&			  log = m_testCtx.getLog();
268 	const glw::Functions& gl  = m_renderCtx.getFunctions();
269 
270 	// Compute viewport.
271 	const tcu::RenderTarget& renderTarget = m_renderCtx.getRenderTarget();
272 	de::Random				 rnd(deStringHash(getName()));
273 	int						 width				= deMin32(renderTarget.getWidth(), VIEWPORT_WIDTH);
274 	int						 height				= deMin32(renderTarget.getHeight(), VIEWPORT_HEIGHT);
275 	int						 viewportX			= rnd.getInt(0, renderTarget.getWidth() - width);
276 	int						 viewportY			= rnd.getInt(0, renderTarget.getHeight() - height);
277 	const int				 numVerticesPerDraw = 4;
278 
279 	GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderCase::execute(): start");
280 
281 	// Setup viewport.
282 	gl.viewport(viewportX, viewportY, width, height);
283 
284 	const float		   quadSize			  = 1.0f;
285 	static const float s_positions[4 * 4] = { -quadSize, -quadSize, 0.0f, 1.0f, -quadSize, +quadSize, 0.0f, 1.0f,
286 											  +quadSize, -quadSize, 0.0f, 1.0f, +quadSize, +quadSize, 0.0f, 1.0f };
287 
288 	static const deUint16 s_indices[2 * 3] = { 0, 1, 2, 1, 3, 2 };
289 
290 	// Setup program.
291 	glu::ShaderProgram program(m_renderCtx, glu::makeVtxFragSources(m_vertexSource.c_str(), m_fragmentSource.c_str()));
292 
293 	// Check that compile/link results are what we expect.
294 	bool		vertexOk   = program.getShaderInfo(SHADERTYPE_VERTEX).compileOk;
295 	bool		fragmentOk = program.getShaderInfo(SHADERTYPE_FRAGMENT).compileOk;
296 	bool		linkOk	 = program.getProgramInfo().linkOk;
297 	const char* failReason = DE_NULL;
298 
299 	log << program;
300 
301 	switch (m_expectResult)
302 	{
303 	case EXPECT_PASS:
304 		if (!vertexOk || !fragmentOk)
305 			failReason = "expected shaders to compile and link properly, but failed to compile.";
306 		else if (!linkOk)
307 			failReason = "expected shaders to compile and link properly, but failed to link.";
308 		break;
309 
310 	case EXPECT_COMPILE_FAIL:
311 		if (vertexOk && fragmentOk && !linkOk)
312 			failReason = "expected compilation to fail, but both shaders compiled and link failed.";
313 		else if (vertexOk && fragmentOk)
314 			failReason = "expected compilation to fail, but both shaders compiled correctly.";
315 		break;
316 
317 	case EXPECT_LINK_FAIL:
318 		if (!vertexOk || !fragmentOk)
319 			failReason = "expected linking to fail, but unable to compile.";
320 		else if (linkOk)
321 			failReason = "expected linking to fail, but passed.";
322 		break;
323 
324 	default:
325 		DE_ASSERT(false);
326 		return false;
327 	}
328 
329 	if (failReason != DE_NULL)
330 	{
331 		// \todo [2010-06-07 petri] These should be handled in the test case?
332 		log << TestLog::Message << "ERROR: " << failReason << TestLog::EndMessage;
333 
334 		// If implementation parses shader at link time, report it as quality warning.
335 		if (m_expectResult == EXPECT_COMPILE_FAIL && vertexOk && fragmentOk && !linkOk)
336 			m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, failReason);
337 		else
338 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, failReason);
339 		return false;
340 	}
341 
342 	// Return if compile/link expected to fail.
343 	if (m_expectResult != EXPECT_PASS)
344 		return (failReason == DE_NULL);
345 
346 	// Start using program.
347 	deUint32 programID = program.getProgram();
348 	gl.useProgram(programID);
349 	GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram()");
350 
351 	// Fetch location for positions positions.
352 	int positionLoc = gl.getAttribLocation(programID, "dEQP_Position");
353 	if (positionLoc == -1)
354 	{
355 		string errStr = string("no location found for attribute 'dEQP_Position'");
356 		TCU_FAIL(errStr.c_str());
357 	}
358 
359 	// Iterate all value blocks.
360 	for (int blockNdx = 0; blockNdx < (int)m_valueBlocks.size(); blockNdx++)
361 	{
362 		const ValueBlock& valueBlock = m_valueBlocks[blockNdx];
363 
364 		// Iterate all array sub-cases.
365 		for (int arrayNdx = 0; arrayNdx < valueBlock.arrayLength; arrayNdx++)
366 		{
367 			int						   numValues = (int)valueBlock.values.size();
368 			vector<VertexArrayBinding> vertexArrays;
369 
370 			int					   attribValueNdx = 0;
371 			vector<vector<float> > attribValues(numValues);
372 
373 			vertexArrays.push_back(va::Float(positionLoc, 4, numVerticesPerDraw, 0, &s_positions[0]));
374 
375 			// Collect VA pointer for inputs and set uniform values for outputs (refs).
376 			for (int valNdx = 0; valNdx < numValues; valNdx++)
377 			{
378 				const ShaderCase::Value& val		= valueBlock.values[valNdx];
379 				const char*				 valueName  = val.valueName.c_str();
380 				DataType				 dataType   = val.dataType;
381 				int						 scalarSize = getDataTypeScalarSize(val.dataType);
382 
383 				GLU_EXPECT_NO_ERROR(gl.getError(), "before set uniforms");
384 
385 				if (val.storageType == ShaderCase::Value::STORAGE_INPUT)
386 				{
387 					// Replicate values four times.
388 					std::vector<float>& scalars = attribValues[attribValueNdx++];
389 					scalars.resize(numVerticesPerDraw * scalarSize);
390 					if (isDataTypeFloatOrVec(dataType) || isDataTypeMatrix(dataType))
391 					{
392 						for (int repNdx = 0; repNdx < numVerticesPerDraw; repNdx++)
393 							for (int ndx						   = 0; ndx < scalarSize; ndx++)
394 								scalars[repNdx * scalarSize + ndx] = val.elements[arrayNdx * scalarSize + ndx].float32;
395 					}
396 					else
397 					{
398 						// convert to floats.
399 						for (int repNdx = 0; repNdx < numVerticesPerDraw; repNdx++)
400 						{
401 							for (int ndx = 0; ndx < scalarSize; ndx++)
402 							{
403 								float v = (float)val.elements[arrayNdx * scalarSize + ndx].int32;
404 								DE_ASSERT(val.elements[arrayNdx * scalarSize + ndx].int32 == (int)v);
405 								scalars[repNdx * scalarSize + ndx] = v;
406 							}
407 						}
408 					}
409 
410 					// Attribute name prefix.
411 					string attribPrefix = "";
412 					// \todo [2010-05-27 petri] Should latter condition only apply for vertex cases (or actually non-fragment cases)?
413 					if ((m_caseType == CASETYPE_FRAGMENT_ONLY) || (getDataTypeScalarType(dataType) != TYPE_FLOAT))
414 						attribPrefix = "a_";
415 
416 					// Input always given as attribute.
417 					string attribName = attribPrefix + valueName;
418 					int	attribLoc  = gl.getAttribLocation(programID, attribName.c_str());
419 					if (attribLoc == -1)
420 					{
421 						log << TestLog::Message << "Warning: no location found for attribute '" << attribName << "'"
422 							<< TestLog::EndMessage;
423 						continue;
424 					}
425 
426 					if (isDataTypeMatrix(dataType))
427 					{
428 						int numCols = getDataTypeMatrixNumColumns(dataType);
429 						int numRows = getDataTypeMatrixNumRows(dataType);
430 						DE_ASSERT(scalarSize == numCols * numRows);
431 
432 						for (int i = 0; i < numCols; i++)
433 							vertexArrays.push_back(va::Float(attribLoc + i, numRows, numVerticesPerDraw,
434 															 static_cast<int>(scalarSize * sizeof(float)),
435 															 &scalars[i * numRows]));
436 					}
437 					else
438 					{
439 						DE_ASSERT(isDataTypeFloatOrVec(dataType) || isDataTypeIntOrIVec(dataType) ||
440 								  isDataTypeUintOrUVec(dataType) || isDataTypeBoolOrBVec(dataType));
441 						vertexArrays.push_back(va::Float(attribLoc, scalarSize, numVerticesPerDraw, 0, &scalars[0]));
442 					}
443 
444 					GLU_EXPECT_NO_ERROR(gl.getError(), "set vertex attrib array");
445 				}
446 				else if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT)
447 				{
448 					// Set reference value.
449 					string refName = string("ref_") + valueName;
450 					setUniformValue(gl, programID, refName, val, arrayNdx);
451 					GLU_EXPECT_NO_ERROR(gl.getError(), "set reference uniforms");
452 				}
453 				else
454 				{
455 					DE_ASSERT(val.storageType == ShaderCase::Value::STORAGE_UNIFORM);
456 					setUniformValue(gl, programID, valueName, val, arrayNdx);
457 					GLU_EXPECT_NO_ERROR(gl.getError(), "set uniforms");
458 				}
459 			}
460 
461 			// Clear.
462 			gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
463 			gl.clear(GL_COLOR_BUFFER_BIT);
464 			GLU_EXPECT_NO_ERROR(gl.getError(), "clear buffer");
465 
466 			// Draw.
467 			draw(m_renderCtx, program.getProgram(), (int)vertexArrays.size(), &vertexArrays[0],
468 				 pr::Triangles(DE_LENGTH_OF_ARRAY(s_indices), &s_indices[0]));
469 			GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
470 
471 			// Read back results.
472 			Surface surface(width, height);
473 			glu::readPixels(m_renderCtx, viewportX, viewportY, surface.getAccess());
474 			GLU_EXPECT_NO_ERROR(gl.getError(), "read pixels");
475 
476 			float w	= s_positions[3];
477 			int   minY = deCeilFloatToInt32(((-quadSize / w) * 0.5f + 0.5f) * (float)height + 1.0f);
478 			int   maxY = deFloorFloatToInt32(((+quadSize / w) * 0.5f + 0.5f) * (float)height - 0.5f);
479 			int   minX = deCeilFloatToInt32(((-quadSize / w) * 0.5f + 0.5f) * (float)width + 1.0f);
480 			int   maxX = deFloorFloatToInt32(((+quadSize / w) * 0.5f + 0.5f) * (float)width - 0.5f);
481 
482 			if (!checkPixels(surface, minX, maxX, minY, maxY))
483 			{
484 				log << TestLog::Message << "INCORRECT RESULT for (value block " << (blockNdx + 1) << " of "
485 					<< (int)m_valueBlocks.size() << ", sub-case " << arrayNdx + 1 << " of " << valueBlock.arrayLength
486 					<< "):" << TestLog::EndMessage;
487 
488 				log << TestLog::Message << "Failing shader input/output values:" << TestLog::EndMessage;
489 				dumpValues(valueBlock, arrayNdx);
490 
491 				// Dump image on failure.
492 				log << TestLog::Image("Result", "Rendered result image", surface);
493 
494 				gl.useProgram(0);
495 				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
496 				return false;
497 			}
498 		}
499 	}
500 
501 	gl.useProgram(0);
502 	GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderCase::execute(): end");
503 	return true;
504 }
505 
iterate(void)506 TestCase::IterateResult ShaderCase::iterate(void)
507 {
508 	// Initialize state to pass.
509 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
510 
511 	bool executeOk = execute();
512 
513 	DE_ASSERT(executeOk ? m_testCtx.getTestResult() == QP_TEST_RESULT_PASS :
514 						  m_testCtx.getTestResult() != QP_TEST_RESULT_PASS);
515 	(void)executeOk;
516 	return TestCase::STOP;
517 }
518 
519 // This functions builds a matching vertex shader for a 'both' case, when
520 // the fragment shader is being tested.
521 // We need to build attributes and varyings for each 'input'.
genVertexShader(const ValueBlock & valueBlock)522 string ShaderCase::genVertexShader(const ValueBlock& valueBlock)
523 {
524 	ostringstream res;
525 	const bool	usesInout = usesShaderInoutQualifiers(m_targetVersion);
526 	const char*   vtxIn		= usesInout ? "in" : "attribute";
527 	const char*   vtxOut	= usesInout ? "out" : "varying";
528 
529 	res << glu::getGLSLVersionDeclaration(m_targetVersion) << "\n";
530 
531 	// Declarations (position + attribute/varying for each input).
532 	res << "precision highp float;\n";
533 	res << "precision highp int;\n";
534 	res << "\n";
535 	res << vtxIn << " highp vec4 dEQP_Position;\n";
536 	for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
537 	{
538 		const ShaderCase::Value& val = valueBlock.values[ndx];
539 		if (val.storageType == ShaderCase::Value::STORAGE_INPUT)
540 		{
541 			DataType	floatType = getDataTypeFloatScalars(val.dataType);
542 			const char* typeStr   = getDataTypeName(floatType);
543 			res << vtxIn << " " << typeStr << " a_" << val.valueName << ";\n";
544 
545 			if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT)
546 				res << vtxOut << " " << typeStr << " " << val.valueName << ";\n";
547 			else
548 				res << vtxOut << " " << typeStr << " v_" << val.valueName << ";\n";
549 		}
550 	}
551 	res << "\n";
552 
553 	// Main function.
554 	// - gl_Position = dEQP_Position;
555 	// - for each input: write attribute directly to varying
556 	res << "void main()\n";
557 	res << "{\n";
558 	res << "    gl_Position = dEQP_Position;\n";
559 	for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
560 	{
561 		const ShaderCase::Value& val = valueBlock.values[ndx];
562 		if (val.storageType == ShaderCase::Value::STORAGE_INPUT)
563 		{
564 			const string& name = val.valueName;
565 			if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT)
566 				res << "    " << name << " = a_" << name << ";\n";
567 			else
568 				res << "    v_" << name << " = a_" << name << ";\n";
569 		}
570 	}
571 
572 	res << "}\n";
573 	return res.str();
574 }
575 
genCompareFunctions(ostringstream & stream,const ShaderCase::ValueBlock & valueBlock,bool useFloatTypes)576 static void genCompareFunctions(ostringstream& stream, const ShaderCase::ValueBlock& valueBlock, bool useFloatTypes)
577 {
578 	bool cmpTypeFound[TYPE_LAST];
579 	for (int i			= 0; i < TYPE_LAST; i++)
580 		cmpTypeFound[i] = false;
581 
582 	for (int valueNdx = 0; valueNdx < (int)valueBlock.values.size(); valueNdx++)
583 	{
584 		const ShaderCase::Value& val = valueBlock.values[valueNdx];
585 		if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT)
586 			cmpTypeFound[(int)val.dataType] = true;
587 	}
588 
589 	if (useFloatTypes)
590 	{
591 		if (cmpTypeFound[TYPE_BOOL])
592 			stream << "bool isOk (float a, bool b) { return ((a > 0.5) == b); }\n";
593 		if (cmpTypeFound[TYPE_BOOL_VEC2])
594 			stream << "bool isOk (vec2 a, bvec2 b) { return (greaterThan(a, vec2(0.5)) == b); }\n";
595 		if (cmpTypeFound[TYPE_BOOL_VEC3])
596 			stream << "bool isOk (vec3 a, bvec3 b) { return (greaterThan(a, vec3(0.5)) == b); }\n";
597 		if (cmpTypeFound[TYPE_BOOL_VEC4])
598 			stream << "bool isOk (vec4 a, bvec4 b) { return (greaterThan(a, vec4(0.5)) == b); }\n";
599 		if (cmpTypeFound[TYPE_INT])
600 			stream << "bool isOk (float a, int b)  { float atemp = a+0.5; return (float(b) <= atemp && atemp <= "
601 					  "float(b+1)); }\n";
602 		if (cmpTypeFound[TYPE_INT_VEC2])
603 			stream << "bool isOk (vec2 a, ivec2 b) { return (ivec2(floor(a + 0.5)) == b); }\n";
604 		if (cmpTypeFound[TYPE_INT_VEC3])
605 			stream << "bool isOk (vec3 a, ivec3 b) { return (ivec3(floor(a + 0.5)) == b); }\n";
606 		if (cmpTypeFound[TYPE_INT_VEC4])
607 			stream << "bool isOk (vec4 a, ivec4 b) { return (ivec4(floor(a + 0.5)) == b); }\n";
608 		if (cmpTypeFound[TYPE_UINT])
609 			stream << "bool isOk (float a, uint b) { float atemp = a+0.5; return (float(b) <= atemp && atemp <= "
610 					  "float(b+1)); }\n";
611 		if (cmpTypeFound[TYPE_UINT_VEC2])
612 			stream << "bool isOk (vec2 a, uvec2 b) { return (uvec2(floor(a + 0.5)) == b); }\n";
613 		if (cmpTypeFound[TYPE_UINT_VEC3])
614 			stream << "bool isOk (vec3 a, uvec3 b) { return (uvec3(floor(a + 0.5)) == b); }\n";
615 		if (cmpTypeFound[TYPE_UINT_VEC4])
616 			stream << "bool isOk (vec4 a, uvec4 b) { return (uvec4(floor(a + 0.5)) == b); }\n";
617 	}
618 	else
619 	{
620 		if (cmpTypeFound[TYPE_BOOL])
621 			stream << "bool isOk (bool a, bool b)   { return (a == b); }\n";
622 		if (cmpTypeFound[TYPE_BOOL_VEC2])
623 			stream << "bool isOk (bvec2 a, bvec2 b) { return (a == b); }\n";
624 		if (cmpTypeFound[TYPE_BOOL_VEC3])
625 			stream << "bool isOk (bvec3 a, bvec3 b) { return (a == b); }\n";
626 		if (cmpTypeFound[TYPE_BOOL_VEC4])
627 			stream << "bool isOk (bvec4 a, bvec4 b) { return (a == b); }\n";
628 		if (cmpTypeFound[TYPE_INT])
629 			stream << "bool isOk (int a, int b)     { return (a == b); }\n";
630 		if (cmpTypeFound[TYPE_INT_VEC2])
631 			stream << "bool isOk (ivec2 a, ivec2 b) { return (a == b); }\n";
632 		if (cmpTypeFound[TYPE_INT_VEC3])
633 			stream << "bool isOk (ivec3 a, ivec3 b) { return (a == b); }\n";
634 		if (cmpTypeFound[TYPE_INT_VEC4])
635 			stream << "bool isOk (ivec4 a, ivec4 b) { return (a == b); }\n";
636 		if (cmpTypeFound[TYPE_UINT])
637 			stream << "bool isOk (uint a, uint b)   { return (a == b); }\n";
638 		if (cmpTypeFound[TYPE_UINT_VEC2])
639 			stream << "bool isOk (uvec2 a, uvec2 b) { return (a == b); }\n";
640 		if (cmpTypeFound[TYPE_UINT_VEC3])
641 			stream << "bool isOk (uvec3 a, uvec3 b) { return (a == b); }\n";
642 		if (cmpTypeFound[TYPE_UINT_VEC4])
643 			stream << "bool isOk (uvec4 a, uvec4 b) { return (a == b); }\n";
644 	}
645 
646 	if (cmpTypeFound[TYPE_FLOAT])
647 		stream << "bool isOk (float a, float b, float eps) { return (abs(a-b) <= (eps*abs(b) + eps)); }\n";
648 	if (cmpTypeFound[TYPE_FLOAT_VEC2])
649 		stream
650 			<< "bool isOk (vec2 a, vec2 b, float eps) { return all(lessThanEqual(abs(a-b), (eps*abs(b) + eps))); }\n";
651 	if (cmpTypeFound[TYPE_FLOAT_VEC3])
652 		stream
653 			<< "bool isOk (vec3 a, vec3 b, float eps) { return all(lessThanEqual(abs(a-b), (eps*abs(b) + eps))); }\n";
654 	if (cmpTypeFound[TYPE_FLOAT_VEC4])
655 		stream
656 			<< "bool isOk (vec4 a, vec4 b, float eps) { return all(lessThanEqual(abs(a-b), (eps*abs(b) + eps))); }\n";
657 
658 	if (cmpTypeFound[TYPE_FLOAT_MAT2])
659 		stream << "bool isOk (mat2 a, mat2 b, float eps) { vec2 diff = max(abs(a[0]-b[0]), abs(a[1]-b[1])); return "
660 				  "all(lessThanEqual(diff, vec2(eps))); }\n";
661 	if (cmpTypeFound[TYPE_FLOAT_MAT2X3])
662 		stream << "bool isOk (mat2x3 a, mat2x3 b, float eps) { vec3 diff = max(abs(a[0]-b[0]), abs(a[1]-b[1])); return "
663 				  "all(lessThanEqual(diff, vec3(eps))); }\n";
664 	if (cmpTypeFound[TYPE_FLOAT_MAT2X4])
665 		stream << "bool isOk (mat2x4 a, mat2x4 b, float eps) { vec4 diff = max(abs(a[0]-b[0]), abs(a[1]-b[1])); return "
666 				  "all(lessThanEqual(diff, vec4(eps))); }\n";
667 	if (cmpTypeFound[TYPE_FLOAT_MAT3X2])
668 		stream << "bool isOk (mat3x2 a, mat3x2 b, float eps) { vec2 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), "
669 				  "abs(a[2]-b[2])); return all(lessThanEqual(diff, vec2(eps))); }\n";
670 	if (cmpTypeFound[TYPE_FLOAT_MAT3])
671 		stream << "bool isOk (mat3 a, mat3 b, float eps) { vec3 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), "
672 				  "abs(a[2]-b[2])); return all(lessThanEqual(diff, vec3(eps))); }\n";
673 	if (cmpTypeFound[TYPE_FLOAT_MAT3X4])
674 		stream << "bool isOk (mat3x4 a, mat3x4 b, float eps) { vec4 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), "
675 				  "abs(a[2]-b[2])); return all(lessThanEqual(diff, vec4(eps))); }\n";
676 	if (cmpTypeFound[TYPE_FLOAT_MAT4X2])
677 		stream << "bool isOk (mat4x2 a, mat4x2 b, float eps) { vec2 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), "
678 				  "max(abs(a[2]-b[2]), abs(a[3]-b[3]))); return all(lessThanEqual(diff, vec2(eps))); }\n";
679 	if (cmpTypeFound[TYPE_FLOAT_MAT4X3])
680 		stream << "bool isOk (mat4x3 a, mat4x3 b, float eps) { vec3 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), "
681 				  "max(abs(a[2]-b[2]), abs(a[3]-b[3]))); return all(lessThanEqual(diff, vec3(eps))); }\n";
682 	if (cmpTypeFound[TYPE_FLOAT_MAT4])
683 		stream << "bool isOk (mat4 a, mat4 b, float eps) { vec4 diff = max(max(abs(a[0]-b[0]), abs(a[1]-b[1])), "
684 				  "max(abs(a[2]-b[2]), abs(a[3]-b[3]))); return all(lessThanEqual(diff, vec4(eps))); }\n";
685 }
686 
genCompareOp(ostringstream & output,const char * dstVec4Var,const ShaderCase::ValueBlock & valueBlock,const char * nonFloatNamePrefix,const char * checkVarName)687 static void genCompareOp(ostringstream& output, const char* dstVec4Var, const ShaderCase::ValueBlock& valueBlock,
688 						 const char* nonFloatNamePrefix, const char* checkVarName)
689 {
690 	bool isFirstOutput = true;
691 
692 	for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
693 	{
694 		const ShaderCase::Value& val	   = valueBlock.values[ndx];
695 		const char*				 valueName = val.valueName.c_str();
696 
697 		if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT)
698 		{
699 			// Check if we're only interested in one variable (then skip if not the right one).
700 			if (checkVarName && !deStringEqual(valueName, checkVarName))
701 				continue;
702 
703 			// Prefix.
704 			if (isFirstOutput)
705 			{
706 				output << "bool RES = ";
707 				isFirstOutput = false;
708 			}
709 			else
710 				output << "RES = RES && ";
711 
712 			// Generate actual comparison.
713 			if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT)
714 				output << "isOk(" << valueName << ", ref_" << valueName << ", 0.05);\n";
715 			else
716 				output << "isOk(" << nonFloatNamePrefix << valueName << ", ref_" << valueName << ");\n";
717 		}
718 		// \note Uniforms are already declared in shader.
719 	}
720 
721 	if (isFirstOutput)
722 		output << dstVec4Var << " = vec4(1.0);\n"; // \todo [petri] Should we give warning if not expect-failure case?
723 	else
724 		output << dstVec4Var << " = vec4(RES, RES, RES, 1.0);\n";
725 }
726 
genFragmentShader(const ValueBlock & valueBlock)727 string ShaderCase::genFragmentShader(const ValueBlock& valueBlock)
728 {
729 	ostringstream shader;
730 	const bool	usesInout		 = usesShaderInoutQualifiers(m_targetVersion);
731 	const bool	customColorOut = usesInout;
732 	const char*   fragIn		 = usesInout ? "in" : "varying";
733 
734 	shader << glu::getGLSLVersionDeclaration(m_targetVersion) << "\n";
735 
736 	shader << "precision mediump float;\n";
737 	shader << "precision mediump int;\n";
738 	shader << "\n";
739 
740 	if (customColorOut)
741 	{
742 		shader << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
743 		shader << "\n";
744 	}
745 
746 	genCompareFunctions(shader, valueBlock, true);
747 	shader << "\n";
748 
749 	// Declarations (varying, reference for each output).
750 	for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
751 	{
752 		const ShaderCase::Value& val		  = valueBlock.values[ndx];
753 		DataType				 floatType	= getDataTypeFloatScalars(val.dataType);
754 		const char*				 floatTypeStr = getDataTypeName(floatType);
755 		const char*				 refTypeStr   = getDataTypeName(val.dataType);
756 
757 		if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT)
758 		{
759 			if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT)
760 				shader << fragIn << " " << floatTypeStr << " " << val.valueName << ";\n";
761 			else
762 				shader << fragIn << " " << floatTypeStr << " v_" << val.valueName << ";\n";
763 
764 			shader << "uniform " << refTypeStr << " ref_" << val.valueName << ";\n";
765 		}
766 	}
767 
768 	shader << "\n";
769 	shader << "void main()\n";
770 	shader << "{\n";
771 
772 	shader << " ";
773 	genCompareOp(shader, customColorOut ? "dEQP_FragColor" : "gl_FragColor", valueBlock, "v_", DE_NULL);
774 
775 	shader << "}\n";
776 	return shader.str();
777 }
778 
779 // Specialize a shader for the vertex shader test case.
specializeVertexShader(const char * src,const ValueBlock & valueBlock)780 string ShaderCase::specializeVertexShader(const char* src, const ValueBlock& valueBlock)
781 {
782 	ostringstream decl;
783 	ostringstream setup;
784 	ostringstream output;
785 	const bool	usesInout = usesShaderInoutQualifiers(m_targetVersion);
786 	const char*   vtxIn		= usesInout ? "in" : "attribute";
787 	const char*   vtxOut	= usesInout ? "out" : "varying";
788 
789 	// Output (write out position).
790 	output << "gl_Position = dEQP_Position;\n";
791 
792 	// Declarations (position + attribute for each input, varying for each output).
793 	decl << vtxIn << " highp vec4 dEQP_Position;\n";
794 	for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
795 	{
796 		const ShaderCase::Value& val		  = valueBlock.values[ndx];
797 		const char*				 valueName	= val.valueName.c_str();
798 		DataType				 floatType	= getDataTypeFloatScalars(val.dataType);
799 		const char*				 floatTypeStr = getDataTypeName(floatType);
800 		const char*				 refTypeStr   = getDataTypeName(val.dataType);
801 
802 		if (val.storageType == ShaderCase::Value::STORAGE_INPUT)
803 		{
804 			if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT)
805 			{
806 				decl << vtxIn << " " << floatTypeStr << " " << valueName << ";\n";
807 			}
808 			else
809 			{
810 				decl << vtxIn << " " << floatTypeStr << " a_" << valueName << ";\n";
811 				setup << refTypeStr << " " << valueName << " = " << refTypeStr << "(a_" << valueName << ");\n";
812 			}
813 		}
814 		else if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT)
815 		{
816 			if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT)
817 				decl << vtxOut << " " << floatTypeStr << " " << valueName << ";\n";
818 			else
819 			{
820 				decl << vtxOut << " " << floatTypeStr << " v_" << valueName << ";\n";
821 				decl << refTypeStr << " " << valueName << ";\n";
822 
823 				output << "v_" << valueName << " = " << floatTypeStr << "(" << valueName << ");\n";
824 			}
825 		}
826 	}
827 
828 	// Shader specialization.
829 	map<string, string> params;
830 	params.insert(pair<string, string>("DECLARATIONS", decl.str()));
831 	params.insert(pair<string, string>("SETUP", setup.str()));
832 	params.insert(pair<string, string>("OUTPUT", output.str()));
833 	params.insert(pair<string, string>("POSITION_FRAG_COLOR", "gl_Position"));
834 
835 	StringTemplate tmpl(src);
836 	return tmpl.specialize(params);
837 }
838 
839 // Specialize a shader for the fragment shader test case.
specializeFragmentShader(const char * src,const ValueBlock & valueBlock)840 string ShaderCase::specializeFragmentShader(const char* src, const ValueBlock& valueBlock)
841 {
842 	ostringstream decl;
843 	ostringstream setup;
844 	ostringstream output;
845 
846 	const bool  usesInout	  = usesShaderInoutQualifiers(m_targetVersion);
847 	const bool  customColorOut = usesInout;
848 	const char* fragIn		   = usesInout ? "in" : "varying";
849 	const char* fragColor	  = customColorOut ? "dEQP_FragColor" : "gl_FragColor";
850 
851 	genCompareFunctions(decl, valueBlock, false);
852 	genCompareOp(output, fragColor, valueBlock, "", DE_NULL);
853 
854 	if (customColorOut)
855 		decl << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
856 
857 	for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
858 	{
859 		const ShaderCase::Value& val		  = valueBlock.values[ndx];
860 		const char*				 valueName	= val.valueName.c_str();
861 		DataType				 floatType	= getDataTypeFloatScalars(val.dataType);
862 		const char*				 floatTypeStr = getDataTypeName(floatType);
863 		const char*				 refTypeStr   = getDataTypeName(val.dataType);
864 
865 		if (val.storageType == ShaderCase::Value::STORAGE_INPUT)
866 		{
867 			if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT)
868 				decl << fragIn << " " << floatTypeStr << " " << valueName << ";\n";
869 			else
870 			{
871 				decl << fragIn << " " << floatTypeStr << " v_" << valueName << ";\n";
872 				std::string offset =
873 					isDataTypeIntOrIVec(val.dataType) ?
874 						" * 1.0025" :
875 						""; // \todo [petri] bit of a hack to avoid errors in chop() due to varying interpolation
876 				setup << refTypeStr << " " << valueName << " = " << refTypeStr << "(v_" << valueName << offset
877 					  << ");\n";
878 			}
879 		}
880 		else if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT)
881 		{
882 			decl << "uniform " << refTypeStr << " ref_" << valueName << ";\n";
883 			decl << refTypeStr << " " << valueName << ";\n";
884 		}
885 	}
886 
887 	/* \todo [2010-04-01 petri] Check all outputs. */
888 
889 	// Shader specialization.
890 	map<string, string> params;
891 	params.insert(pair<string, string>("DECLARATIONS", decl.str()));
892 	params.insert(pair<string, string>("SETUP", setup.str()));
893 	params.insert(pair<string, string>("OUTPUT", output.str()));
894 	params.insert(pair<string, string>("POSITION_FRAG_COLOR", fragColor));
895 
896 	StringTemplate tmpl(src);
897 	return tmpl.specialize(params);
898 }
899 
specializeShaders(const char * vertexSource,const char * fragmentSource,string & outVertexSource,string & outFragmentSource,const ValueBlock & valueBlock)900 void ShaderCase::specializeShaders(const char* vertexSource, const char* fragmentSource, string& outVertexSource,
901 								   string& outFragmentSource, const ValueBlock& valueBlock)
902 {
903 	const bool usesInout	  = usesShaderInoutQualifiers(m_targetVersion);
904 	const bool customColorOut = usesInout;
905 
906 	// Vertex shader specialization.
907 	{
908 		ostringstream decl;
909 		ostringstream setup;
910 		const char*   vtxIn = usesInout ? "in" : "attribute";
911 
912 		decl << vtxIn << " highp vec4 dEQP_Position;\n";
913 
914 		for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
915 		{
916 			const ShaderCase::Value& val	 = valueBlock.values[ndx];
917 			const char*				 typeStr = getDataTypeName(val.dataType);
918 
919 			if (val.storageType == ShaderCase::Value::STORAGE_INPUT)
920 			{
921 				if (getDataTypeScalarType(val.dataType) == TYPE_FLOAT)
922 				{
923 					decl << vtxIn << " " << typeStr << " " << val.valueName << ";\n";
924 				}
925 				else
926 				{
927 					DataType	floatType	= getDataTypeFloatScalars(val.dataType);
928 					const char* floatTypeStr = getDataTypeName(floatType);
929 
930 					decl << vtxIn << " " << floatTypeStr << " a_" << val.valueName << ";\n";
931 					setup << typeStr << " " << val.valueName << " = " << typeStr << "(a_" << val.valueName << ");\n";
932 				}
933 			}
934 			else if (val.storageType == ShaderCase::Value::STORAGE_UNIFORM && val.valueName.find('.') == string::npos)
935 			{
936 				decl << "uniform " << typeStr << " " << val.valueName << ";\n";
937 			}
938 		}
939 
940 		map<string, string> params;
941 		params.insert(pair<string, string>("VERTEX_DECLARATIONS", decl.str()));
942 		params.insert(pair<string, string>("VERTEX_SETUP", setup.str()));
943 		params.insert(pair<string, string>("VERTEX_OUTPUT", string("gl_Position = dEQP_Position;\n")));
944 		StringTemplate tmpl(vertexSource);
945 		outVertexSource = tmpl.specialize(params);
946 	}
947 
948 	// Fragment shader specialization.
949 	{
950 		ostringstream decl;
951 		ostringstream output;
952 		const char*   fragColor = customColorOut ? "dEQP_FragColor" : "gl_FragColor";
953 
954 		genCompareFunctions(decl, valueBlock, false);
955 		genCompareOp(output, fragColor, valueBlock, "", DE_NULL);
956 
957 		if (customColorOut)
958 			decl << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
959 
960 		for (int ndx = 0; ndx < (int)valueBlock.values.size(); ndx++)
961 		{
962 			const ShaderCase::Value& val		= valueBlock.values[ndx];
963 			const char*				 valueName  = val.valueName.c_str();
964 			const char*				 refTypeStr = getDataTypeName(val.dataType);
965 
966 			if (val.storageType == ShaderCase::Value::STORAGE_OUTPUT)
967 			{
968 				decl << "uniform " << refTypeStr << " ref_" << valueName << ";\n";
969 				decl << refTypeStr << " " << valueName << ";\n";
970 			}
971 			else if (val.storageType == ShaderCase::Value::STORAGE_UNIFORM && val.valueName.find('.') == string::npos)
972 			{
973 				decl << "uniform " << refTypeStr << " " << valueName << ";\n";
974 			}
975 		}
976 
977 		map<string, string> params;
978 		params.insert(pair<string, string>("FRAGMENT_DECLARATIONS", decl.str()));
979 		params.insert(pair<string, string>("FRAGMENT_OUTPUT", output.str()));
980 		params.insert(pair<string, string>("FRAG_COLOR", fragColor));
981 		StringTemplate tmpl(fragmentSource);
982 		outFragmentSource = tmpl.specialize(params);
983 	}
984 }
985 
dumpValues(const ValueBlock & valueBlock,int arrayNdx)986 void ShaderCase::dumpValues(const ValueBlock& valueBlock, int arrayNdx)
987 {
988 	vector<vector<float> > attribValues;
989 
990 	int numValues = (int)valueBlock.values.size();
991 	for (int valNdx = 0; valNdx < numValues; valNdx++)
992 	{
993 		const ShaderCase::Value& val		= valueBlock.values[valNdx];
994 		const char*				 valueName  = val.valueName.c_str();
995 		DataType				 dataType   = val.dataType;
996 		int						 scalarSize = getDataTypeScalarSize(val.dataType);
997 		ostringstream			 result;
998 
999 		result << "    ";
1000 		if (val.storageType == Value::STORAGE_INPUT)
1001 			result << "input ";
1002 		else if (val.storageType == Value::STORAGE_UNIFORM)
1003 			result << "uniform ";
1004 		else if (val.storageType == Value::STORAGE_OUTPUT)
1005 			result << "expected ";
1006 
1007 		result << getDataTypeName(dataType) << " " << valueName << ":";
1008 
1009 		if (isDataTypeScalar(dataType))
1010 			result << " ";
1011 		if (isDataTypeVector(dataType))
1012 			result << " [ ";
1013 		else if (isDataTypeMatrix(dataType))
1014 			result << "\n";
1015 
1016 		if (isDataTypeScalarOrVector(dataType))
1017 		{
1018 			for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
1019 			{
1020 				int					  elemNdx = (val.arrayLength == 1) ? 0 : arrayNdx;
1021 				const Value::Element& e		  = val.elements[elemNdx * scalarSize + scalarNdx];
1022 				result << ((scalarNdx != 0) ? ", " : "");
1023 
1024 				if (isDataTypeFloatOrVec(dataType))
1025 					result << e.float32;
1026 				else if (isDataTypeIntOrIVec(dataType))
1027 					result << e.int32;
1028 				else if (isDataTypeBoolOrBVec(dataType))
1029 					result << (e.bool32 ? "true" : "false");
1030 			}
1031 		}
1032 		else if (isDataTypeMatrix(dataType))
1033 		{
1034 			int numRows = getDataTypeMatrixNumRows(dataType);
1035 			int numCols = getDataTypeMatrixNumColumns(dataType);
1036 			for (int rowNdx = 0; rowNdx < numRows; rowNdx++)
1037 			{
1038 				result << "       [ ";
1039 				for (int colNdx = 0; colNdx < numCols; colNdx++)
1040 				{
1041 					int   elemNdx = (val.arrayLength == 1) ? 0 : arrayNdx;
1042 					float v		  = val.elements[elemNdx * scalarSize + rowNdx * numCols + colNdx].float32;
1043 					result << ((colNdx == 0) ? "" : ", ") << v;
1044 				}
1045 				result << " ]\n";
1046 			}
1047 		}
1048 
1049 		if (isDataTypeScalar(dataType))
1050 			result << "\n";
1051 		else if (isDataTypeVector(dataType))
1052 			result << " ]\n";
1053 
1054 		m_testCtx.getLog() << TestLog::Message << result.str() << TestLog::EndMessage;
1055 	}
1056 }
1057 
1058 } // sl
1059 } // deqp
1060