Home | History | Annotate | Download | only in common
      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 
     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 
     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 
    119 ShaderCase::~ShaderCase(void)
    120 {
    121 }
    122 
    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 
    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 
    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 
    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'.
    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 
    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 
    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 
    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.
    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.
    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 
    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 
    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