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