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