xref: /aosp_15_r20/external/deqp/modules/glshared/glsRandomShaderCase.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL (ES) Module
3  * -----------------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Random shader test case.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "glsRandomShaderCase.hpp"
25 
26 #include "gluShaderProgram.hpp"
27 #include "gluPixelTransfer.hpp"
28 #include "gluTextureUtil.hpp"
29 #include "gluStrUtil.hpp"
30 
31 #include "tcuImageCompare.hpp"
32 #include "tcuTestLog.hpp"
33 
34 #include "deRandom.hpp"
35 #include "deStringUtil.hpp"
36 
37 #include "rsgProgramGenerator.hpp"
38 #include "rsgProgramExecutor.hpp"
39 #include "rsgUtils.hpp"
40 
41 #include "tcuTextureUtil.hpp"
42 #include "tcuRenderTarget.hpp"
43 
44 #include "glw.h"
45 #include "glwFunctions.hpp"
46 
47 using std::map;
48 using std::pair;
49 using std::string;
50 using std::vector;
51 
52 namespace deqp
53 {
54 namespace gls
55 {
56 
57 enum
58 {
59     VIEWPORT_WIDTH  = 64,
60     VIEWPORT_HEIGHT = 64,
61 
62     TEXTURE_2D_WIDTH     = 64,
63     TEXTURE_2D_HEIGHT    = 64,
64     TEXTURE_2D_FORMAT    = GL_RGBA,
65     TEXTURE_2D_DATA_TYPE = GL_UNSIGNED_BYTE,
66 
67     TEXTURE_CUBE_SIZE      = 16,
68     TEXTURE_CUBE_FORMAT    = GL_RGBA,
69     TEXTURE_CUBE_DATA_TYPE = GL_UNSIGNED_BYTE,
70 
71     TEXTURE_WRAP_S = GL_CLAMP_TO_EDGE,
72     TEXTURE_WRAP_T = GL_CLAMP_TO_EDGE,
73 
74     TEXTURE_MIN_FILTER = GL_LINEAR,
75     TEXTURE_MAG_FILTER = GL_LINEAR
76 };
77 
VertexArray(const rsg::ShaderInput * input,int numVertices)78 VertexArray::VertexArray(const rsg::ShaderInput *input, int numVertices)
79     : m_input(input)
80     , m_vertices(input->getVariable()->getType().getNumElements() * numVertices)
81 {
82 }
83 
TextureManager(void)84 TextureManager::TextureManager(void)
85 {
86 }
87 
~TextureManager(void)88 TextureManager::~TextureManager(void)
89 {
90 }
91 
bindTexture(int unit,const glu::Texture2D * tex2D)92 void TextureManager::bindTexture(int unit, const glu::Texture2D *tex2D)
93 {
94     m_tex2D[unit] = tex2D;
95 }
96 
bindTexture(int unit,const glu::TextureCube * texCube)97 void TextureManager::bindTexture(int unit, const glu::TextureCube *texCube)
98 {
99     m_texCube[unit] = texCube;
100 }
101 
getBindings2D(void) const102 inline vector<pair<int, const glu::Texture2D *>> TextureManager::getBindings2D(void) const
103 {
104     vector<pair<int, const glu::Texture2D *>> bindings;
105     for (map<int, const glu::Texture2D *>::const_iterator i = m_tex2D.begin(); i != m_tex2D.end(); i++)
106         bindings.push_back(*i);
107     return bindings;
108 }
109 
getBindingsCube(void) const110 inline vector<pair<int, const glu::TextureCube *>> TextureManager::getBindingsCube(void) const
111 {
112     vector<pair<int, const glu::TextureCube *>> bindings;
113     for (map<int, const glu::TextureCube *>::const_iterator i = m_texCube.begin(); i != m_texCube.end(); i++)
114         bindings.push_back(*i);
115     return bindings;
116 }
117 
RandomShaderCase(tcu::TestContext & testCtx,glu::RenderContext & renderCtx,const char * name,const char * description,const rsg::ProgramParameters & params)118 RandomShaderCase::RandomShaderCase(tcu::TestContext &testCtx, glu::RenderContext &renderCtx, const char *name,
119                                    const char *description, const rsg::ProgramParameters &params)
120     : tcu::TestCase(testCtx, name, description)
121     , m_renderCtx(renderCtx)
122     , m_parameters(params)
123     , m_gridWidth(1)
124     , m_gridHeight(1)
125     , m_vertexShader(rsg::Shader::TYPE_VERTEX)
126     , m_fragmentShader(rsg::Shader::TYPE_FRAGMENT)
127     , m_tex2D(DE_NULL)
128     , m_texCube(DE_NULL)
129 {
130 }
131 
~RandomShaderCase(void)132 RandomShaderCase::~RandomShaderCase(void)
133 {
134     delete m_tex2D;
135     delete m_texCube;
136 }
137 
init(void)138 void RandomShaderCase::init(void)
139 {
140     // Generate shaders
141     rsg::ProgramGenerator programGenerator;
142     programGenerator.generate(m_parameters, m_vertexShader, m_fragmentShader);
143 
144     checkShaderLimits(m_vertexShader);
145     checkShaderLimits(m_fragmentShader);
146     checkProgramLimits(m_vertexShader, m_fragmentShader);
147 
148     // Compute uniform values
149     std::vector<const rsg::ShaderInput *> unifiedUniforms;
150     de::Random rnd(m_parameters.seed);
151     rsg::computeUnifiedUniforms(m_vertexShader, m_fragmentShader, unifiedUniforms);
152     rsg::computeUniformValues(rnd, m_uniforms, unifiedUniforms);
153 
154     // Generate vertices
155     const vector<rsg::ShaderInput *> &inputs = m_vertexShader.getInputs();
156     int numVertices                          = (m_gridWidth + 1) * (m_gridHeight + 1);
157 
158     for (vector<rsg::ShaderInput *>::const_iterator i = inputs.begin(); i != inputs.end(); i++)
159     {
160         const rsg::ShaderInput *input         = *i;
161         rsg::ConstValueRangeAccess valueRange = input->getValueRange();
162         int numComponents                     = input->getVariable()->getType().getNumElements();
163         VertexArray vtxArray(input, numVertices);
164         bool isPosition = string(input->getVariable()->getName()) == "dEQP_Position";
165 
166         TCU_CHECK(input->getVariable()->getType().getBaseType() == rsg::VariableType::TYPE_FLOAT);
167 
168         for (int vtxNdx = 0; vtxNdx < numVertices; vtxNdx++)
169         {
170             int y      = vtxNdx / (m_gridWidth + 1);
171             int x      = vtxNdx - y * (m_gridWidth + 1);
172             float xf   = (float)x / (float)m_gridWidth;
173             float yf   = (float)y / (float)m_gridHeight;
174             float *dst = &vtxArray.getVertices()[vtxNdx * numComponents];
175 
176             if (isPosition)
177             {
178                 // Position attribute gets special interpolation handling.
179                 DE_ASSERT(numComponents == 4);
180                 dst[0] = -1.0f + xf * 2.0f;
181                 dst[1] = 1.0f + yf * -2.0f;
182                 dst[2] = 0.0f;
183                 dst[3] = 1.0f;
184             }
185             else
186             {
187                 for (int compNdx = 0; compNdx < numComponents; compNdx++)
188                 {
189                     float minVal = valueRange.getMin().component(compNdx).asFloat();
190                     float maxVal = valueRange.getMax().component(compNdx).asFloat();
191                     float xd, yd;
192 
193                     rsg::getVertexInterpolationCoords(xd, yd, xf, yf, compNdx);
194 
195                     float f = (xd + yd) / 2.0f;
196 
197                     dst[compNdx] = minVal + f * (maxVal - minVal);
198                 }
199             }
200         }
201 
202         m_vertexArrays.push_back(vtxArray);
203     }
204 
205     // Generate indices
206     int numQuads   = m_gridWidth * m_gridHeight;
207     int numIndices = numQuads * 6;
208     m_indices.resize(numIndices);
209     for (int quadNdx = 0; quadNdx < numQuads; quadNdx++)
210     {
211         int quadY = quadNdx / (m_gridWidth);
212         int quadX = quadNdx - quadY * m_gridWidth;
213 
214         m_indices[quadNdx * 6 + 0] = (uint16_t)(quadX + quadY * (m_gridWidth + 1));
215         m_indices[quadNdx * 6 + 1] = (uint16_t)(quadX + (quadY + 1) * (m_gridWidth + 1));
216         m_indices[quadNdx * 6 + 2] = (uint16_t)(quadX + quadY * (m_gridWidth + 1) + 1);
217         m_indices[quadNdx * 6 + 3] = (uint16_t)(m_indices[quadNdx * 6 + 2]);
218         m_indices[quadNdx * 6 + 4] = (uint16_t)(m_indices[quadNdx * 6 + 1]);
219         m_indices[quadNdx * 6 + 5] = (uint16_t)(quadX + (quadY + 1) * (m_gridWidth + 1) + 1);
220     }
221 
222     // Create textures.
223     for (vector<rsg::VariableValue>::const_iterator uniformIter = m_uniforms.begin(); uniformIter != m_uniforms.end();
224          uniformIter++)
225     {
226         const rsg::VariableType &type = uniformIter->getVariable()->getType();
227 
228         if (!type.isSampler())
229             continue;
230 
231         int unitNdx = uniformIter->getValue().asInt(0);
232 
233         if (type == rsg::VariableType(rsg::VariableType::TYPE_SAMPLER_2D, 1))
234             m_texManager.bindTexture(unitNdx, getTex2D());
235         else if (type == rsg::VariableType(rsg::VariableType::TYPE_SAMPLER_CUBE, 1))
236             m_texManager.bindTexture(unitNdx, getTexCube());
237         else
238             DE_ASSERT(false);
239     }
240 }
241 
getNumSamplerUniforms(const std::vector<rsg::ShaderInput * > & uniforms)242 static int getNumSamplerUniforms(const std::vector<rsg::ShaderInput *> &uniforms)
243 {
244     int numSamplers = 0;
245 
246     for (std::vector<rsg::ShaderInput *>::const_iterator it = uniforms.begin(); it != uniforms.end(); ++it)
247     {
248         if ((*it)->getVariable()->getType().isSampler())
249             ++numSamplers;
250     }
251 
252     return numSamplers;
253 }
254 
checkShaderLimits(const rsg::Shader & shader) const255 void RandomShaderCase::checkShaderLimits(const rsg::Shader &shader) const
256 {
257     const int numRequiredSamplers = getNumSamplerUniforms(shader.getUniforms());
258 
259     if (numRequiredSamplers > 0)
260     {
261         const GLenum pname = (shader.getType() == rsg::Shader::TYPE_VERTEX) ? (GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS) :
262                                                                               (GL_MAX_TEXTURE_IMAGE_UNITS);
263         int numSupported   = -1;
264         GLenum error;
265 
266         m_renderCtx.getFunctions().getIntegerv(pname, &numSupported);
267         error = m_renderCtx.getFunctions().getError();
268 
269         if (error != GL_NO_ERROR)
270             throw tcu::TestError("Limit query failed: " + de::toString(glu::getErrorStr(error)));
271 
272         if (numSupported < numRequiredSamplers)
273             throw tcu::NotSupportedError("Shader requires " + de::toString(numRequiredSamplers) +
274                                          " sampler(s). Implementation supports " + de::toString(numSupported));
275     }
276 }
277 
checkProgramLimits(const rsg::Shader & vtxShader,const rsg::Shader & frgShader) const278 void RandomShaderCase::checkProgramLimits(const rsg::Shader &vtxShader, const rsg::Shader &frgShader) const
279 {
280     const int numRequiredCombinedSamplers =
281         getNumSamplerUniforms(vtxShader.getUniforms()) + getNumSamplerUniforms(frgShader.getUniforms());
282 
283     if (numRequiredCombinedSamplers > 0)
284     {
285         int numSupported = -1;
286         GLenum error;
287 
288         m_renderCtx.getFunctions().getIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &numSupported);
289         error = m_renderCtx.getFunctions().getError();
290 
291         if (error != GL_NO_ERROR)
292             throw tcu::TestError("Limit query failed: " + de::toString(glu::getErrorStr(error)));
293 
294         if (numSupported < numRequiredCombinedSamplers)
295             throw tcu::NotSupportedError("Program requires " + de::toString(numRequiredCombinedSamplers) +
296                                          " sampler(s). Implementation supports " + de::toString(numSupported));
297     }
298 }
299 
getTex2D(void)300 const glu::Texture2D *RandomShaderCase::getTex2D(void)
301 {
302     if (!m_tex2D)
303     {
304         m_tex2D = new glu::Texture2D(m_renderCtx, TEXTURE_2D_FORMAT, TEXTURE_2D_DATA_TYPE, TEXTURE_2D_WIDTH,
305                                      TEXTURE_2D_HEIGHT);
306 
307         m_tex2D->getRefTexture().allocLevel(0);
308         tcu::fillWithComponentGradients(m_tex2D->getRefTexture().getLevel(0), tcu::Vec4(-1.0f, -1.0f, -1.0f, 2.0f),
309                                         tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f));
310         m_tex2D->upload();
311 
312         // Setup parameters.
313         glBindTexture(GL_TEXTURE_2D, m_tex2D->getGLTexture());
314         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, TEXTURE_WRAP_S);
315         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, TEXTURE_WRAP_T);
316         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, TEXTURE_MIN_FILTER);
317         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, TEXTURE_MAG_FILTER);
318 
319         GLU_CHECK();
320     }
321 
322     return m_tex2D;
323 }
324 
getTexCube(void)325 const glu::TextureCube *RandomShaderCase::getTexCube(void)
326 {
327     if (!m_texCube)
328     {
329         m_texCube = new glu::TextureCube(m_renderCtx, TEXTURE_CUBE_FORMAT, TEXTURE_CUBE_DATA_TYPE, TEXTURE_CUBE_SIZE);
330 
331         static const tcu::Vec4 gradients[tcu::CUBEFACE_LAST][2] = {
332             {tcu::Vec4(-1.0f, -1.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)}, // negative x
333             {tcu::Vec4(0.0f, -1.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)},  // positive x
334             {tcu::Vec4(-1.0f, 0.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)},  // negative y
335             {tcu::Vec4(-1.0f, -1.0f, 0.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)},  // positive y
336             {tcu::Vec4(-1.0f, -1.0f, -1.0f, 0.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f)}, // negative z
337             {tcu::Vec4(0.0f, 0.0f, 0.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)}     // positive z
338         };
339 
340         // Fill level 0.
341         for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
342         {
343             m_texCube->getRefTexture().allocLevel((tcu::CubeFace)face, 0);
344             tcu::fillWithComponentGradients(m_texCube->getRefTexture().getLevelFace(0, (tcu::CubeFace)face),
345                                             gradients[face][0], gradients[face][1]);
346         }
347 
348         m_texCube->upload();
349 
350         // Setup parameters.
351         glBindTexture(GL_TEXTURE_CUBE_MAP, m_texCube->getGLTexture());
352         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, TEXTURE_WRAP_S);
353         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, TEXTURE_WRAP_T);
354         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, TEXTURE_MIN_FILTER);
355         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, TEXTURE_MAG_FILTER);
356 
357         GLU_CHECK();
358     }
359 
360     return m_texCube;
361 }
362 
deinit(void)363 void RandomShaderCase::deinit(void)
364 {
365     delete m_tex2D;
366     delete m_texCube;
367 
368     m_tex2D   = DE_NULL;
369     m_texCube = DE_NULL;
370 
371     // Free up memory
372     m_vertexArrays.clear();
373     m_indices.clear();
374 }
375 
376 namespace
377 {
378 
setUniformValue(int location,rsg::ConstValueAccess value)379 void setUniformValue(int location, rsg::ConstValueAccess value)
380 {
381     DE_STATIC_ASSERT(sizeof(rsg::Scalar) == sizeof(float));
382     DE_STATIC_ASSERT(sizeof(rsg::Scalar) == sizeof(int));
383 
384     switch (value.getType().getBaseType())
385     {
386     case rsg::VariableType::TYPE_FLOAT:
387         switch (value.getType().getNumElements())
388         {
389         case 1:
390             glUniform1fv(location, 1, (float *)value.value().getValuePtr());
391             break;
392         case 2:
393             glUniform2fv(location, 1, (float *)value.value().getValuePtr());
394             break;
395         case 3:
396             glUniform3fv(location, 1, (float *)value.value().getValuePtr());
397             break;
398         case 4:
399             glUniform4fv(location, 1, (float *)value.value().getValuePtr());
400             break;
401         default:
402             TCU_FAIL("Unsupported type");
403         }
404         break;
405 
406     case rsg::VariableType::TYPE_INT:
407     case rsg::VariableType::TYPE_BOOL:
408     case rsg::VariableType::TYPE_SAMPLER_2D:
409     case rsg::VariableType::TYPE_SAMPLER_CUBE:
410         switch (value.getType().getNumElements())
411         {
412         case 1:
413             glUniform1iv(location, 1, (int *)value.value().getValuePtr());
414             break;
415         case 2:
416             glUniform2iv(location, 1, (int *)value.value().getValuePtr());
417             break;
418         case 3:
419             glUniform3iv(location, 1, (int *)value.value().getValuePtr());
420             break;
421         case 4:
422             glUniform4iv(location, 1, (int *)value.value().getValuePtr());
423             break;
424         default:
425             TCU_FAIL("Unsupported type");
426         }
427         break;
428 
429     default:
430         TCU_FAIL("Unsupported type");
431     }
432 }
433 
operator <<(tcu::MessageBuilder & message,rsg::ConstValueAccess value)434 tcu::MessageBuilder &operator<<(tcu::MessageBuilder &message, rsg::ConstValueAccess value)
435 {
436     const char *scalarType = DE_NULL;
437     const char *vecType    = DE_NULL;
438 
439     switch (value.getType().getBaseType())
440     {
441     case rsg::VariableType::TYPE_FLOAT:
442         scalarType = "float";
443         vecType    = "vec";
444         break;
445     case rsg::VariableType::TYPE_INT:
446         scalarType = "int";
447         vecType    = "ivec";
448         break;
449     case rsg::VariableType::TYPE_BOOL:
450         scalarType = "bool";
451         vecType    = "bvec";
452         break;
453     case rsg::VariableType::TYPE_SAMPLER_2D:
454         scalarType = "sampler2D";
455         break;
456     case rsg::VariableType::TYPE_SAMPLER_CUBE:
457         scalarType = "samplerCube";
458         break;
459     default:
460         TCU_FAIL("Unsupported type.");
461     }
462 
463     int numElements = value.getType().getNumElements();
464     if (numElements == 1)
465         message << scalarType << "(";
466     else
467         message << vecType << numElements << "(";
468 
469     for (int elementNdx = 0; elementNdx < numElements; elementNdx++)
470     {
471         if (elementNdx > 0)
472             message << ", ";
473 
474         switch (value.getType().getBaseType())
475         {
476         case rsg::VariableType::TYPE_FLOAT:
477             message << value.component(elementNdx).asFloat();
478             break;
479         case rsg::VariableType::TYPE_INT:
480             message << value.component(elementNdx).asInt();
481             break;
482         case rsg::VariableType::TYPE_BOOL:
483             message << (value.component(elementNdx).asBool() ? "true" : "false");
484             break;
485         case rsg::VariableType::TYPE_SAMPLER_2D:
486             message << value.component(elementNdx).asInt();
487             break;
488         case rsg::VariableType::TYPE_SAMPLER_CUBE:
489             message << value.component(elementNdx).asInt();
490             break;
491         default:
492             DE_ASSERT(false);
493         }
494     }
495 
496     message << ")";
497 
498     return message;
499 }
500 
operator <<(tcu::MessageBuilder & message,rsg::ConstValueRangeAccess valueRange)501 tcu::MessageBuilder &operator<<(tcu::MessageBuilder &message, rsg::ConstValueRangeAccess valueRange)
502 {
503     return message << valueRange.getMin() << " -> " << valueRange.getMax();
504 }
505 
506 } // namespace
507 
iterate(void)508 RandomShaderCase::IterateResult RandomShaderCase::iterate(void)
509 {
510     tcu::TestLog &log = m_testCtx.getLog();
511 
512     // Compile program
513     glu::ShaderProgram program(m_renderCtx,
514                                glu::makeVtxFragSources(m_vertexShader.getSource(), m_fragmentShader.getSource()));
515     log << program;
516 
517     if (!program.isOk())
518     {
519         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Failed to compile shader");
520         return STOP;
521     }
522 
523     // Compute random viewport
524     de::Random rnd(m_parameters.seed);
525     int viewportWidth  = de::min<int>(VIEWPORT_WIDTH, m_renderCtx.getRenderTarget().getWidth());
526     int viewportHeight = de::min<int>(VIEWPORT_HEIGHT, m_renderCtx.getRenderTarget().getHeight());
527     int viewportX      = rnd.getInt(0, m_renderCtx.getRenderTarget().getWidth() - viewportWidth);
528     int viewportY      = rnd.getInt(0, m_renderCtx.getRenderTarget().getHeight() - viewportHeight);
529     bool hasAlpha      = m_renderCtx.getRenderTarget().getPixelFormat().alphaBits > 0;
530     tcu::TextureLevel rendered(tcu::TextureFormat(hasAlpha ? tcu::TextureFormat::RGBA : tcu::TextureFormat::RGB,
531                                                   tcu::TextureFormat::UNORM_INT8),
532                                viewportWidth, viewportHeight);
533     tcu::TextureLevel reference(tcu::TextureFormat(hasAlpha ? tcu::TextureFormat::RGBA : tcu::TextureFormat::RGB,
534                                                    tcu::TextureFormat::UNORM_INT8),
535                                 viewportWidth, viewportHeight);
536 
537     // Reference program executor.
538     rsg::ProgramExecutor executor(reference.getAccess(), m_gridWidth, m_gridHeight);
539 
540     GLU_CHECK_CALL(glUseProgram(program.getProgram()));
541 
542     // Set up attributes
543     for (vector<VertexArray>::const_iterator attribIter = m_vertexArrays.begin(); attribIter != m_vertexArrays.end();
544          attribIter++)
545     {
546         GLint location = glGetAttribLocation(program.getProgram(), attribIter->getName());
547 
548         // Print to log.
549         log << tcu::TestLog::Message << "attribute[" << location << "]: " << attribIter->getName() << " = "
550             << attribIter->getValueRange() << tcu::TestLog::EndMessage;
551 
552         if (location >= 0)
553         {
554             glVertexAttribPointer(location, attribIter->getNumComponents(), GL_FLOAT, GL_FALSE, 0,
555                                   &attribIter->getVertices()[0]);
556             glEnableVertexAttribArray(location);
557         }
558     }
559     GLU_CHECK_MSG("After attribute setup");
560 
561     // Uniforms
562     for (vector<rsg::VariableValue>::const_iterator uniformIter = m_uniforms.begin(); uniformIter != m_uniforms.end();
563          uniformIter++)
564     {
565         GLint location = glGetUniformLocation(program.getProgram(), uniformIter->getVariable()->getName());
566 
567         log << tcu::TestLog::Message << "uniform[" << location << "]: " << uniformIter->getVariable()->getName()
568             << " = " << uniformIter->getValue() << tcu::TestLog::EndMessage;
569 
570         if (location >= 0)
571             setUniformValue(location, uniformIter->getValue());
572     }
573     GLU_CHECK_MSG("After uniform setup");
574 
575     // Textures
576     vector<pair<int, const glu::Texture2D *>> tex2DBindings     = m_texManager.getBindings2D();
577     vector<pair<int, const glu::TextureCube *>> texCubeBindings = m_texManager.getBindingsCube();
578 
579     for (vector<pair<int, const glu::Texture2D *>>::const_iterator i = tex2DBindings.begin(); i != tex2DBindings.end();
580          i++)
581     {
582         int unitNdx                   = i->first;
583         const glu::Texture2D *texture = i->second;
584 
585         glActiveTexture(GL_TEXTURE0 + unitNdx);
586         glBindTexture(GL_TEXTURE_2D, texture->getGLTexture());
587 
588         executor.setTexture(unitNdx, &texture->getRefTexture(),
589                             glu::mapGLSampler(TEXTURE_WRAP_S, TEXTURE_WRAP_T, TEXTURE_MIN_FILTER, TEXTURE_MAG_FILTER));
590     }
591     GLU_CHECK_MSG("After 2D texture setup");
592 
593     for (vector<pair<int, const glu::TextureCube *>>::const_iterator i = texCubeBindings.begin();
594          i != texCubeBindings.end(); i++)
595     {
596         int unitNdx                     = i->first;
597         const glu::TextureCube *texture = i->second;
598 
599         glActiveTexture(GL_TEXTURE0 + unitNdx);
600         glBindTexture(GL_TEXTURE_CUBE_MAP, texture->getGLTexture());
601 
602         executor.setTexture(unitNdx, &texture->getRefTexture(),
603                             glu::mapGLSampler(TEXTURE_WRAP_S, TEXTURE_WRAP_T, TEXTURE_MIN_FILTER, TEXTURE_MAG_FILTER));
604     }
605     GLU_CHECK_MSG("After cubemap setup");
606 
607     // Draw and read
608     glViewport(viewportX, viewportY, viewportWidth, viewportHeight);
609     glDrawElements(GL_TRIANGLES, (GLsizei)m_indices.size(), GL_UNSIGNED_SHORT, &m_indices[0]);
610     glFlush();
611     GLU_CHECK_MSG("Draw");
612 
613     // Render reference while GPU is doing work
614     executor.execute(m_vertexShader, m_fragmentShader, m_uniforms);
615 
616     if (rendered.getFormat().order != tcu::TextureFormat::RGBA ||
617         rendered.getFormat().type != tcu::TextureFormat::UNORM_INT8)
618     {
619         // Read as GL_RGBA8
620         tcu::TextureLevel readBuf(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8),
621                                   rendered.getWidth(), rendered.getHeight());
622         glu::readPixels(m_renderCtx, viewportX, viewportY, readBuf.getAccess());
623         GLU_CHECK_MSG("Read pixels");
624         tcu::copy(rendered, readBuf);
625     }
626     else
627         glu::readPixels(m_renderCtx, viewportX, viewportY, rendered.getAccess());
628 
629     // Compare
630     {
631         float threshold = 0.02f;
632         bool imagesOk   = tcu::fuzzyCompare(log, "Result", "Result images", reference.getAccess(), rendered.getAccess(),
633                                             threshold, tcu::COMPARE_LOG_RESULT);
634 
635         if (imagesOk)
636             m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
637         else
638             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
639     }
640 
641     return STOP;
642 }
643 
644 } // namespace gls
645 } // namespace deqp
646