Always apply flat qualifier to double inputs, same as int/uint
[platform/upstream/VK-GL-CTS.git] / external / openglcts / modules / common / glcShaderLibraryCase.cpp
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