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
30 #include "tcuImageCompare.hpp"
31 #include "tcuTestLog.hpp"
32 #include "deRandom.hpp"
33
34 #include "rsgProgramGenerator.hpp"
35 #include "rsgProgramExecutor.hpp"
36 #include "rsgUtils.hpp"
37
38 #include "tcuTextureUtil.hpp"
39 #include "tcuRenderTarget.hpp"
40
41 #include "glw.h"
42
43 using std::vector;
44 using std::string;
45 using std::pair;
46 using std::map;
47
48 namespace deqp
49 {
50 namespace gls
51 {
52
53 enum
54 {
55 VIEWPORT_WIDTH = 64,
56 VIEWPORT_HEIGHT = 64,
57
58 TEXTURE_2D_WIDTH = 64,
59 TEXTURE_2D_HEIGHT = 64,
60 TEXTURE_2D_FORMAT = GL_RGBA,
61 TEXTURE_2D_DATA_TYPE = GL_UNSIGNED_BYTE,
62
63 TEXTURE_CUBE_SIZE = 16,
64 TEXTURE_CUBE_FORMAT = GL_RGBA,
65 TEXTURE_CUBE_DATA_TYPE = GL_UNSIGNED_BYTE,
66
67 TEXTURE_WRAP_S = GL_CLAMP_TO_EDGE,
68 TEXTURE_WRAP_T = GL_CLAMP_TO_EDGE,
69
70 TEXTURE_MIN_FILTER = GL_LINEAR,
71 TEXTURE_MAG_FILTER = GL_LINEAR
72 };
73
VertexArray(const rsg::ShaderInput * input,int numVertices)74 VertexArray::VertexArray (const rsg::ShaderInput* input, int numVertices)
75 : m_input (input)
76 , m_vertices (input->getVariable()->getType().getNumElements() * numVertices)
77 {
78 }
79
TextureManager(void)80 TextureManager::TextureManager (void)
81 {
82 }
83
~TextureManager(void)84 TextureManager::~TextureManager (void)
85 {
86 }
87
bindTexture(int unit,const glu::Texture2D * tex2D)88 void TextureManager::bindTexture (int unit, const glu::Texture2D* tex2D)
89 {
90 m_tex2D[unit] = tex2D;
91 }
92
bindTexture(int unit,const glu::TextureCube * texCube)93 void TextureManager::bindTexture (int unit, const glu::TextureCube* texCube)
94 {
95 m_texCube[unit] = texCube;
96 }
97
getBindings2D(void) const98 inline vector<pair<int, const glu::Texture2D*> > TextureManager::getBindings2D (void) const
99 {
100 vector<pair<int, const glu::Texture2D*> > bindings;
101 for (map<int, const glu::Texture2D*>::const_iterator i = m_tex2D.begin(); i != m_tex2D.end(); i++)
102 bindings.push_back(*i);
103 return bindings;
104 }
105
getBindingsCube(void) const106 inline vector<pair<int, const glu::TextureCube*> > TextureManager::getBindingsCube (void) const
107 {
108 vector<pair<int, const glu::TextureCube*> > bindings;
109 for (map<int, const glu::TextureCube*>::const_iterator i = m_texCube.begin(); i != m_texCube.end(); i++)
110 bindings.push_back(*i);
111 return bindings;
112 }
113
RandomShaderCase(tcu::TestContext & testCtx,glu::RenderContext & renderCtx,const char * name,const char * description,const rsg::ProgramParameters & params)114 RandomShaderCase::RandomShaderCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* description, const rsg::ProgramParameters& params)
115 : tcu::TestCase (testCtx, name, description)
116 , m_renderCtx (renderCtx)
117 , m_parameters (params)
118 , m_gridWidth (1)
119 , m_gridHeight (1)
120 , m_vertexShader (rsg::Shader::TYPE_VERTEX)
121 , m_fragmentShader (rsg::Shader::TYPE_FRAGMENT)
122 , m_tex2D (DE_NULL)
123 , m_texCube (DE_NULL)
124 {
125 }
126
~RandomShaderCase(void)127 RandomShaderCase::~RandomShaderCase (void)
128 {
129 delete m_tex2D;
130 delete m_texCube;
131 }
132
init(void)133 void RandomShaderCase::init (void)
134 {
135 // Generate shaders
136 rsg::ProgramGenerator programGenerator;
137 programGenerator.generate(m_parameters, m_vertexShader, m_fragmentShader);
138
139 // Compute uniform values
140 std::vector<const rsg::ShaderInput*> unifiedUniforms;
141 de::Random rnd(m_parameters.seed);
142 rsg::computeUnifiedUniforms(m_vertexShader, m_fragmentShader, unifiedUniforms);
143 rsg::computeUniformValues(rnd, m_uniforms, unifiedUniforms);
144
145 // Generate vertices
146 const vector<rsg::ShaderInput*>& inputs = m_vertexShader.getInputs();
147 int numVertices = (m_gridWidth+1)*(m_gridHeight+1);
148
149 for (vector<rsg::ShaderInput*>::const_iterator i = inputs.begin(); i != inputs.end(); i++)
150 {
151 const rsg::ShaderInput* input = *i;
152 rsg::ConstValueRangeAccess valueRange = input->getValueRange();
153 int numComponents = input->getVariable()->getType().getNumElements();
154 VertexArray vtxArray(input, numVertices);
155 bool isPosition = string(input->getVariable()->getName()) == "dEQP_Position";
156
157 TCU_CHECK(input->getVariable()->getType().getBaseType() == rsg::VariableType::TYPE_FLOAT);
158
159 for (int vtxNdx = 0; vtxNdx < numVertices; vtxNdx++)
160 {
161 int y = vtxNdx / (m_gridWidth+1);
162 int x = vtxNdx - y*(m_gridWidth+1);
163 float xf = (float)x / (float)m_gridWidth;
164 float yf = (float)y / (float)m_gridHeight;
165 float* dst = &vtxArray.getVertices()[vtxNdx*numComponents];
166
167 if (isPosition)
168 {
169 // Position attribute gets special interpolation handling.
170 DE_ASSERT(numComponents == 4);
171 dst[0] = -1.0f + xf * 2.0f;
172 dst[1] = 1.0f + yf * -2.0f;
173 dst[2] = 0.0f;
174 dst[3] = 1.0f;
175 }
176 else
177 {
178 for (int compNdx = 0; compNdx < numComponents; compNdx++)
179 {
180 float minVal = valueRange.getMin().component(compNdx).asFloat();
181 float maxVal = valueRange.getMax().component(compNdx).asFloat();
182 float xd, yd;
183
184 rsg::getVertexInterpolationCoords(xd, yd, xf, yf, compNdx);
185
186 float f = (xd+yd) / 2.0f;
187
188 dst[compNdx] = minVal + f * (maxVal-minVal);
189 }
190 }
191 }
192
193 m_vertexArrays.push_back(vtxArray);
194 }
195
196 // Generate indices
197 int numQuads = m_gridWidth*m_gridHeight;
198 int numIndices = numQuads*6;
199 m_indices.resize(numIndices);
200 for (int quadNdx = 0; quadNdx < numQuads; quadNdx++)
201 {
202 int quadY = quadNdx / (m_gridWidth);
203 int quadX = quadNdx - quadY*m_gridWidth;
204
205 m_indices[quadNdx*6+0] = quadX + quadY*(m_gridWidth+1);
206 m_indices[quadNdx*6+1] = quadX + (quadY+1)*(m_gridWidth+1);
207 m_indices[quadNdx*6+2] = quadX + quadY*(m_gridWidth+1) + 1;
208 m_indices[quadNdx*6+3] = m_indices[quadNdx*6+2];
209 m_indices[quadNdx*6+4] = m_indices[quadNdx*6+1];
210 m_indices[quadNdx*6+5] = quadX + (quadY+1)*(m_gridWidth+1) + 1;
211 }
212
213 // Create textures.
214 for (vector<rsg::VariableValue>::const_iterator uniformIter = m_uniforms.begin(); uniformIter != m_uniforms.end(); uniformIter++)
215 {
216 const rsg::VariableType& type = uniformIter->getVariable()->getType();
217
218 if (!type.isSampler())
219 continue;
220
221 int unitNdx = uniformIter->getValue().asInt(0);
222
223 if (type == rsg::VariableType(rsg::VariableType::TYPE_SAMPLER_2D, 1))
224 m_texManager.bindTexture(unitNdx, getTex2D());
225 else if (type == rsg::VariableType(rsg::VariableType::TYPE_SAMPLER_CUBE, 1))
226 m_texManager.bindTexture(unitNdx, getTexCube());
227 else
228 DE_ASSERT(DE_FALSE);
229 }
230 }
231
getTex2D(void)232 const glu::Texture2D* RandomShaderCase::getTex2D (void)
233 {
234 if (!m_tex2D)
235 {
236 m_tex2D = new glu::Texture2D(m_renderCtx, TEXTURE_2D_FORMAT, TEXTURE_2D_DATA_TYPE, TEXTURE_2D_WIDTH, TEXTURE_2D_HEIGHT);
237
238 m_tex2D->getRefTexture().allocLevel(0);
239 tcu::fillWithComponentGradients(m_tex2D->getRefTexture().getLevel(0), tcu::Vec4(-1.0f, -1.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f));
240 m_tex2D->upload();
241
242 // Setup parameters.
243 glBindTexture(GL_TEXTURE_2D, m_tex2D->getGLTexture());
244 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, TEXTURE_WRAP_S);
245 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, TEXTURE_WRAP_T);
246 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, TEXTURE_MIN_FILTER);
247 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, TEXTURE_MAG_FILTER);
248
249 GLU_CHECK();
250 }
251
252 return m_tex2D;
253 }
254
getTexCube(void)255 const glu::TextureCube* RandomShaderCase::getTexCube (void)
256 {
257 if (!m_texCube)
258 {
259 m_texCube = new glu::TextureCube(m_renderCtx, TEXTURE_CUBE_FORMAT, TEXTURE_CUBE_DATA_TYPE, TEXTURE_CUBE_SIZE);
260
261 static const tcu::Vec4 gradients[tcu::CUBEFACE_LAST][2] =
262 {
263 { tcu::Vec4(-1.0f, -1.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative x
264 { tcu::Vec4( 0.0f, -1.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive x
265 { tcu::Vec4(-1.0f, 0.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative y
266 { tcu::Vec4(-1.0f, -1.0f, 0.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive y
267 { tcu::Vec4(-1.0f, -1.0f, -1.0f, 0.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f) }, // negative z
268 { tcu::Vec4( 0.0f, 0.0f, 0.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) } // positive z
269 };
270
271 // Fill level 0.
272 for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
273 {
274 m_texCube->getRefTexture().allocLevel((tcu::CubeFace)face, 0);
275 tcu::fillWithComponentGradients(m_texCube->getRefTexture().getLevelFace(0, (tcu::CubeFace)face), gradients[face][0], gradients[face][1]);
276 }
277
278 m_texCube->upload();
279
280 // Setup parameters.
281 glBindTexture(GL_TEXTURE_CUBE_MAP, m_texCube->getGLTexture());
282 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, TEXTURE_WRAP_S);
283 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, TEXTURE_WRAP_T);
284 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, TEXTURE_MIN_FILTER);
285 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, TEXTURE_MAG_FILTER);
286
287 GLU_CHECK();
288 }
289
290 return m_texCube;
291 }
292
deinit(void)293 void RandomShaderCase::deinit (void)
294 {
295 delete m_tex2D;
296 delete m_texCube;
297
298 m_tex2D = DE_NULL;
299 m_texCube = DE_NULL;
300
301 // Free up memory
302 m_vertexArrays.clear();
303 m_indices.clear();
304 }
305
306 namespace
307 {
308
setUniformValue(int location,rsg::ConstValueAccess value)309 void setUniformValue (int location, rsg::ConstValueAccess value)
310 {
311 DE_STATIC_ASSERT(sizeof(rsg::Scalar) == sizeof(float));
312 DE_STATIC_ASSERT(sizeof(rsg::Scalar) == sizeof(int));
313
314 switch (value.getType().getBaseType())
315 {
316 case rsg::VariableType::TYPE_FLOAT:
317 switch (value.getType().getNumElements())
318 {
319 case 1: glUniform1fv(location, 1, (float*)value.value().getValuePtr()); break;
320 case 2: glUniform2fv(location, 1, (float*)value.value().getValuePtr()); break;
321 case 3: glUniform3fv(location, 1, (float*)value.value().getValuePtr()); break;
322 case 4: glUniform4fv(location, 1, (float*)value.value().getValuePtr()); break;
323 default: TCU_FAIL("Unsupported type"); break;
324 }
325 break;
326
327 case rsg::VariableType::TYPE_INT:
328 case rsg::VariableType::TYPE_BOOL:
329 case rsg::VariableType::TYPE_SAMPLER_2D:
330 case rsg::VariableType::TYPE_SAMPLER_CUBE:
331 switch (value.getType().getNumElements())
332 {
333 case 1: glUniform1iv(location, 1, (int*)value.value().getValuePtr()); break;
334 case 2: glUniform2iv(location, 1, (int*)value.value().getValuePtr()); break;
335 case 3: glUniform3iv(location, 1, (int*)value.value().getValuePtr()); break;
336 case 4: glUniform4iv(location, 1, (int*)value.value().getValuePtr()); break;
337 default: TCU_FAIL("Unsupported type"); break;
338 }
339 break;
340
341 default:
342 TCU_FAIL("Unsupported type");
343 }
344 }
345
operator <<(tcu::MessageBuilder & message,rsg::ConstValueAccess value)346 tcu::MessageBuilder& operator<< (tcu::MessageBuilder& message, rsg::ConstValueAccess value)
347 {
348 const char* scalarType = DE_NULL;
349 const char* vecType = DE_NULL;
350
351 switch (value.getType().getBaseType())
352 {
353 case rsg::VariableType::TYPE_FLOAT: scalarType = "float"; vecType = "vec"; break;
354 case rsg::VariableType::TYPE_INT: scalarType = "int"; vecType = "ivec"; break;
355 case rsg::VariableType::TYPE_BOOL: scalarType = "bool"; vecType = "bvec"; break;
356 case rsg::VariableType::TYPE_SAMPLER_2D: scalarType = "sampler2D"; break;
357 case rsg::VariableType::TYPE_SAMPLER_CUBE: scalarType = "samplerCube"; break;
358 default:
359 TCU_FAIL("Unsupported type.");
360 }
361
362 int numElements = value.getType().getNumElements();
363 if (numElements == 1)
364 message << scalarType << "(";
365 else
366 message << vecType << numElements << "(";
367
368 for (int elementNdx = 0; elementNdx < numElements; elementNdx++)
369 {
370 if (elementNdx > 0)
371 message << ", ";
372
373 switch (value.getType().getBaseType())
374 {
375 case rsg::VariableType::TYPE_FLOAT: message << value.component(elementNdx).asFloat(); break;
376 case rsg::VariableType::TYPE_INT: message << value.component(elementNdx).asInt(); break;
377 case rsg::VariableType::TYPE_BOOL: message << (value.component(elementNdx).asBool() ? "true" : "false"); break;
378 case rsg::VariableType::TYPE_SAMPLER_2D: message << value.component(elementNdx).asInt(); break;
379 case rsg::VariableType::TYPE_SAMPLER_CUBE: message << value.component(elementNdx).asInt(); break;
380 default:
381 DE_ASSERT(DE_FALSE);
382 }
383 }
384
385 message << ")";
386
387 return message;
388 }
389
operator <<(tcu::MessageBuilder & message,rsg::ConstValueRangeAccess valueRange)390 tcu::MessageBuilder& operator<< (tcu::MessageBuilder& message, rsg::ConstValueRangeAccess valueRange)
391 {
392 return message << valueRange.getMin() << " -> " << valueRange.getMax();
393 }
394
395 } // anonymous
396
iterate(void)397 RandomShaderCase::IterateResult RandomShaderCase::iterate (void)
398 {
399 tcu::TestLog& log = m_testCtx.getLog();
400
401 // Compile program
402 glu::ShaderProgram program(m_renderCtx, glu::makeVtxFragSources(m_vertexShader.getSource(), m_fragmentShader.getSource()));
403 log << program;
404
405 if (!program.isOk())
406 {
407 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Failed to compile shader");
408 return STOP;
409 }
410
411 // Compute random viewport
412 de::Random rnd (m_parameters.seed);
413 int viewportWidth = de::min<int>(VIEWPORT_WIDTH, m_renderCtx.getRenderTarget().getWidth());
414 int viewportHeight = de::min<int>(VIEWPORT_HEIGHT, m_renderCtx.getRenderTarget().getHeight());
415 int viewportX = rnd.getInt(0, m_renderCtx.getRenderTarget().getWidth() - viewportWidth);
416 int viewportY = rnd.getInt(0, m_renderCtx.getRenderTarget().getHeight() - viewportHeight);
417 bool hasAlpha = m_renderCtx.getRenderTarget().getPixelFormat().alphaBits > 0;
418 tcu::TextureLevel rendered (tcu::TextureFormat(hasAlpha ? tcu::TextureFormat::RGBA : tcu::TextureFormat::RGB, tcu::TextureFormat::UNORM_INT8), viewportWidth, viewportHeight);
419 tcu::TextureLevel reference (tcu::TextureFormat(hasAlpha ? tcu::TextureFormat::RGBA : tcu::TextureFormat::RGB, tcu::TextureFormat::UNORM_INT8), viewportWidth, viewportHeight);
420
421 // Reference program executor.
422 rsg::ProgramExecutor executor (reference.getAccess(), m_gridWidth, m_gridHeight);
423
424 GLU_CHECK_CALL(glUseProgram(program.getProgram()));
425
426 // Set up attributes
427 for (vector<VertexArray>::const_iterator attribIter = m_vertexArrays.begin(); attribIter != m_vertexArrays.end(); attribIter++)
428 {
429 GLint location = glGetAttribLocation(program.getProgram(), attribIter->getName());
430
431 // Print to log.
432 log << tcu::TestLog::Message << "attribute[" << location << "]: " << attribIter->getName() << " = " << attribIter->getValueRange() << tcu::TestLog::EndMessage;
433
434 if (location >= 0)
435 {
436 glVertexAttribPointer(location, attribIter->getNumComponents(), GL_FLOAT, GL_FALSE, 0, &attribIter->getVertices()[0]);
437 glEnableVertexAttribArray(location);
438 }
439 }
440 GLU_CHECK_MSG("After attribute setup");
441
442 // Uniforms
443 for (vector<rsg::VariableValue>::const_iterator uniformIter = m_uniforms.begin(); uniformIter != m_uniforms.end(); uniformIter++)
444 {
445 GLint location = glGetUniformLocation(program.getProgram(), uniformIter->getVariable()->getName());
446
447 log << tcu::TestLog::Message << "uniform[" << location << "]: " << uniformIter->getVariable()->getName() << " = " << uniformIter->getValue() << tcu::TestLog::EndMessage;
448
449 if (location >= 0)
450 setUniformValue(location, uniformIter->getValue());
451 }
452 GLU_CHECK_MSG("After uniform setup");
453
454 // Textures
455 vector<pair<int, const glu::Texture2D*> > tex2DBindings = m_texManager.getBindings2D();
456 vector<pair<int, const glu::TextureCube*> > texCubeBindings = m_texManager.getBindingsCube();
457
458 for (vector<pair<int, const glu::Texture2D*> >::const_iterator i = tex2DBindings.begin(); i != tex2DBindings.end(); i++)
459 {
460 int unitNdx = i->first;
461 const glu::Texture2D* texture = i->second;
462
463 glActiveTexture(GL_TEXTURE0 + unitNdx);
464 glBindTexture(GL_TEXTURE_2D, texture->getGLTexture());
465
466 executor.setTexture(unitNdx, &texture->getRefTexture(), glu::mapGLSampler(TEXTURE_WRAP_S, TEXTURE_WRAP_T, TEXTURE_MIN_FILTER, TEXTURE_MAG_FILTER));
467 }
468 GLU_CHECK_MSG("After 2D texture setup");
469
470 for (vector<pair<int, const glu::TextureCube*> >::const_iterator i = texCubeBindings.begin(); i != texCubeBindings.end(); i++)
471 {
472 int unitNdx = i->first;
473 const glu::TextureCube* texture = i->second;
474
475 glActiveTexture(GL_TEXTURE0 + unitNdx);
476 glBindTexture(GL_TEXTURE_CUBE_MAP, texture->getGLTexture());
477
478 executor.setTexture(unitNdx, &texture->getRefTexture(), glu::mapGLSampler(TEXTURE_WRAP_S, TEXTURE_WRAP_T, TEXTURE_MIN_FILTER, TEXTURE_MAG_FILTER));
479 }
480 GLU_CHECK_MSG("After cubemap setup");
481
482 // Draw and read
483 glViewport(viewportX, viewportY, viewportWidth, viewportHeight);
484 glDrawElements(GL_TRIANGLES, (GLsizei)m_indices.size(), GL_UNSIGNED_SHORT, &m_indices[0]);
485 glFlush();
486 GLU_CHECK_MSG("Draw");
487
488 // Render reference while GPU is doing work
489 executor.execute(m_vertexShader, m_fragmentShader, m_uniforms);
490
491 if (rendered.getFormat().order != tcu::TextureFormat::RGBA || rendered.getFormat().type != tcu::TextureFormat::UNORM_INT8)
492 {
493 // Read as GL_RGBA8
494 tcu::TextureLevel readBuf(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), rendered.getWidth(), rendered.getHeight());
495 glu::readPixels(m_renderCtx, viewportX, viewportY, readBuf.getAccess());
496 GLU_CHECK_MSG("Read pixels");
497 tcu::copy(rendered, readBuf);
498 }
499 else
500 glu::readPixels(m_renderCtx, viewportX, viewportY, rendered.getAccess());
501
502 // Compare
503 {
504 float threshold = 0.02f;
505 bool imagesOk = tcu::fuzzyCompare(log, "Result", "Result images", reference.getAccess(), rendered.getAccess(), threshold, tcu::COMPARE_LOG_RESULT);
506
507 if (imagesOk)
508 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
509 else
510 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
511 }
512
513 return STOP;
514 }
515
516 } // gls
517 } // deqp
518