1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.1 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 gl_HelperInvocation tests.
22 *//*--------------------------------------------------------------------*/
23
24 #include "es31fShaderHelperInvocationTests.hpp"
25
26 #include "gluObjectWrapper.hpp"
27 #include "gluShaderProgram.hpp"
28 #include "gluDrawUtil.hpp"
29 #include "gluPixelTransfer.hpp"
30
31 #include "glwFunctions.hpp"
32 #include "glwEnums.hpp"
33
34 #include "tcuTestLog.hpp"
35 #include "tcuVector.hpp"
36 #include "tcuSurface.hpp"
37
38 #include "deUniquePtr.hpp"
39 #include "deStringUtil.hpp"
40 #include "deRandom.hpp"
41 #include "deString.h"
42
43 namespace deqp
44 {
45 namespace gles31
46 {
47 namespace Functional
48 {
49 namespace
50 {
51
52 using glu::ShaderProgram;
53 using tcu::TestLog;
54 using tcu::Vec2;
55 using tcu::IVec2;
56 using de::MovePtr;
57 using std::string;
58 using std::vector;
59
60 enum PrimitiveType
61 {
62 PRIMITIVETYPE_TRIANGLE = 0,
63 PRIMITIVETYPE_LINE,
64 PRIMITIVETYPE_WIDE_LINE,
65 PRIMITIVETYPE_POINT,
66 PRIMITIVETYPE_WIDE_POINT,
67
68 PRIMITIVETYPE_LAST
69 };
70
getNumVerticesPerPrimitive(PrimitiveType primType)71 static int getNumVerticesPerPrimitive (PrimitiveType primType)
72 {
73 switch (primType)
74 {
75 case PRIMITIVETYPE_TRIANGLE: return 3;
76 case PRIMITIVETYPE_LINE: return 2;
77 case PRIMITIVETYPE_WIDE_LINE: return 2;
78 case PRIMITIVETYPE_POINT: return 1;
79 case PRIMITIVETYPE_WIDE_POINT: return 1;
80 default:
81 DE_ASSERT(false);
82 return 0;
83 }
84 }
85
getGluPrimitiveType(PrimitiveType primType)86 static glu::PrimitiveType getGluPrimitiveType (PrimitiveType primType)
87 {
88 switch (primType)
89 {
90 case PRIMITIVETYPE_TRIANGLE: return glu::PRIMITIVETYPE_TRIANGLES;
91 case PRIMITIVETYPE_LINE: return glu::PRIMITIVETYPE_LINES;
92 case PRIMITIVETYPE_WIDE_LINE: return glu::PRIMITIVETYPE_LINES;
93 case PRIMITIVETYPE_POINT: return glu::PRIMITIVETYPE_POINTS;
94 case PRIMITIVETYPE_WIDE_POINT: return glu::PRIMITIVETYPE_POINTS;
95 default:
96 DE_ASSERT(false);
97 return glu::PRIMITIVETYPE_LAST;
98 }
99 }
100
genVertices(PrimitiveType primType,int numPrimitives,de::Random * rnd,vector<Vec2> * dst)101 static void genVertices (PrimitiveType primType, int numPrimitives, de::Random* rnd, vector<Vec2>* dst)
102 {
103 const bool isTri = primType == PRIMITIVETYPE_TRIANGLE;
104 const float minCoord = isTri ? -1.5f : -1.0f;
105 const float maxCoord = isTri ? +1.5f : +1.0f;
106 const int numVert = getNumVerticesPerPrimitive(primType)*numPrimitives;
107
108 dst->resize(numVert);
109
110 for (size_t ndx = 0; ndx < dst->size(); ndx++)
111 {
112 (*dst)[ndx][0] = rnd->getFloat(minCoord, maxCoord);
113 (*dst)[ndx][1] = rnd->getFloat(minCoord, maxCoord);
114 }
115 }
116
getInteger(const glw::Functions & gl,deUint32 pname)117 static int getInteger (const glw::Functions& gl, deUint32 pname)
118 {
119 int v = 0;
120 gl.getIntegerv(pname, &v);
121 GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv()");
122 return v;
123 }
124
getRange(const glw::Functions & gl,deUint32 pname)125 static Vec2 getRange (const glw::Functions& gl, deUint32 pname)
126 {
127 Vec2 v(0.0f);
128 gl.getFloatv(pname, v.getPtr());
129 GLU_EXPECT_NO_ERROR(gl.getError(), "glGetFloatv()");
130 return v;
131 }
132
drawRandomPrimitives(const glu::RenderContext & renderCtx,deUint32 program,PrimitiveType primType,int numPrimitives,de::Random * rnd)133 static void drawRandomPrimitives (const glu::RenderContext& renderCtx, deUint32 program, PrimitiveType primType, int numPrimitives, de::Random* rnd)
134 {
135 const glw::Functions& gl = renderCtx.getFunctions();
136 const float minPointSize = 16.0f;
137 const float maxPointSize = 32.0f;
138 const float minLineWidth = 16.0f;
139 const float maxLineWidth = 32.0f;
140 vector<Vec2> vertices;
141 vector<glu::VertexArrayBinding> vertexArrays;
142
143 genVertices(primType, numPrimitives, rnd, &vertices);
144
145 vertexArrays.push_back(glu::va::Float("a_position", 2, (int)vertices.size(), 0, (const float*)&vertices[0]));
146
147 gl.useProgram(program);
148
149 // Special state for certain primitives
150 if (primType == PRIMITIVETYPE_POINT || primType == PRIMITIVETYPE_WIDE_POINT)
151 {
152 const Vec2 range = getRange(gl, GL_ALIASED_POINT_SIZE_RANGE);
153 const bool isWidePoint = primType == PRIMITIVETYPE_WIDE_POINT;
154 const float pointSize = isWidePoint ? de::min(rnd->getFloat(minPointSize, maxPointSize), range.y()) : 1.0f;
155 const int pointSizeLoc = gl.getUniformLocation(program, "u_pointSize");
156
157 gl.uniform1f(pointSizeLoc, pointSize);
158 }
159 else if (primType == PRIMITIVETYPE_WIDE_LINE)
160 {
161 const Vec2 range = getRange(gl, GL_ALIASED_LINE_WIDTH_RANGE);
162 const float lineWidth = de::min(rnd->getFloat(minLineWidth, maxLineWidth), range.y());
163
164 gl.lineWidth(lineWidth);
165 }
166
167 glu::draw(renderCtx, program, (int)vertexArrays.size(), &vertexArrays[0],
168 glu::PrimitiveList(getGluPrimitiveType(primType), (int)vertices.size()));
169 }
170
171 class FboHelper
172 {
173 public:
174 FboHelper (const glu::RenderContext& renderCtx, int width, int height, deUint32 format, int numSamples);
175 ~FboHelper (void);
176
177 void bindForRendering (void);
178 void readPixels (int x, int y, const tcu::PixelBufferAccess& dst);
179
180 private:
181 const glu::RenderContext& m_renderCtx;
182 const int m_numSamples;
183
184 glu::Renderbuffer m_colorbuffer;
185 glu::Framebuffer m_framebuffer;
186 glu::Renderbuffer m_resolveColorbuffer;
187 glu::Framebuffer m_resolveFramebuffer;
188 };
189
FboHelper(const glu::RenderContext & renderCtx,int width,int height,deUint32 format,int numSamples)190 FboHelper::FboHelper (const glu::RenderContext& renderCtx, int width, int height, deUint32 format, int numSamples)
191 : m_renderCtx (renderCtx)
192 , m_numSamples (numSamples)
193 , m_colorbuffer (renderCtx)
194 , m_framebuffer (renderCtx)
195 , m_resolveColorbuffer (renderCtx)
196 , m_resolveFramebuffer (renderCtx)
197 {
198 const glw::Functions& gl = m_renderCtx.getFunctions();
199 const int maxSamples = getInteger(gl, GL_MAX_SAMPLES);
200
201 gl.bindRenderbuffer(GL_RENDERBUFFER, *m_colorbuffer);
202 gl.renderbufferStorageMultisample(GL_RENDERBUFFER, m_numSamples, format, width, height);
203 gl.bindFramebuffer(GL_FRAMEBUFFER, *m_framebuffer);
204 gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *m_colorbuffer);
205
206 if (m_numSamples > maxSamples && gl.checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
207 throw tcu::NotSupportedError("Sample count exceeds GL_MAX_SAMPLES");
208
209 TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
210
211 if (m_numSamples != 0)
212 {
213 gl.bindRenderbuffer(GL_RENDERBUFFER, *m_resolveColorbuffer);
214 gl.renderbufferStorage(GL_RENDERBUFFER, format, width, height);
215 gl.bindFramebuffer(GL_FRAMEBUFFER, *m_resolveFramebuffer);
216 gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *m_resolveColorbuffer);
217 TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
218 }
219
220 GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to create framebuffer");
221 }
222
~FboHelper(void)223 FboHelper::~FboHelper (void)
224 {
225 }
226
bindForRendering(void)227 void FboHelper::bindForRendering (void)
228 {
229 const glw::Functions& gl = m_renderCtx.getFunctions();
230 gl.bindFramebuffer(GL_FRAMEBUFFER, *m_framebuffer);
231 GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer()");
232 }
233
readPixels(int x,int y,const tcu::PixelBufferAccess & dst)234 void FboHelper::readPixels (int x, int y, const tcu::PixelBufferAccess& dst)
235 {
236 const glw::Functions& gl = m_renderCtx.getFunctions();
237 const int width = dst.getWidth();
238 const int height = dst.getHeight();
239
240 if (m_numSamples != 0)
241 {
242 gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, *m_resolveFramebuffer);
243 gl.blitFramebuffer(x, y, width, height, x, y, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
244 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, *m_resolveFramebuffer);
245 }
246
247 glu::readPixels(m_renderCtx, x, y, dst);
248 }
249
250 enum
251 {
252 FRAMEBUFFER_WIDTH = 256,
253 FRAMEBUFFER_HEIGHT = 256,
254 FRAMEBUFFER_FORMAT = GL_RGBA8,
255 NUM_SAMPLES_MAX = -1
256 };
257
258 //! Verifies that gl_HelperInvocation is false in all rendered pixels.
259 class HelperInvocationValueCase : public TestCase
260 {
261 public:
262 HelperInvocationValueCase (Context& context, const char* name, const char* description, PrimitiveType primType, int numSamples);
263 ~HelperInvocationValueCase (void);
264
265 void init (void);
266 void deinit (void);
267 IterateResult iterate (void);
268
269 private:
270 const PrimitiveType m_primitiveType;
271 const int m_numSamples;
272
273 const int m_numIters;
274 const int m_numPrimitivesPerIter;
275
276 MovePtr<ShaderProgram> m_program;
277 MovePtr<FboHelper> m_fbo;
278 int m_iterNdx;
279 };
280
HelperInvocationValueCase(Context & context,const char * name,const char * description,PrimitiveType primType,int numSamples)281 HelperInvocationValueCase::HelperInvocationValueCase (Context& context, const char* name, const char* description, PrimitiveType primType, int numSamples)
282 : TestCase (context, name, description)
283 , m_primitiveType (primType)
284 , m_numSamples (numSamples)
285 , m_numIters (5)
286 , m_numPrimitivesPerIter (10)
287 , m_iterNdx (0)
288 {
289 }
290
~HelperInvocationValueCase(void)291 HelperInvocationValueCase::~HelperInvocationValueCase (void)
292 {
293 deinit();
294 }
295
init(void)296 void HelperInvocationValueCase::init (void)
297 {
298 const glu::RenderContext& renderCtx = m_context.getRenderContext();
299 const glw::Functions& gl = renderCtx.getFunctions();
300 const int maxSamples = getInteger(gl, GL_MAX_SAMPLES);
301 const int actualSamples = m_numSamples == NUM_SAMPLES_MAX ? maxSamples : m_numSamples;
302
303 m_program = MovePtr<ShaderProgram>(new ShaderProgram(m_context.getRenderContext(),
304 glu::ProgramSources()
305 << glu::VertexSource(
306 "#version 310 es\n"
307 "in highp vec2 a_position;\n"
308 "uniform highp float u_pointSize;\n"
309 "void main (void)\n"
310 "{\n"
311 " gl_Position = vec4(a_position, 0.0, 1.0);\n"
312 " gl_PointSize = u_pointSize;\n"
313 "}\n")
314 << glu::FragmentSource(
315 "#version 310 es\n"
316 "out mediump vec4 o_color;\n"
317 "void main (void)\n"
318 "{\n"
319 " if (gl_HelperInvocation)\n"
320 " o_color = vec4(1.0, 0.0, 0.0, 1.0);\n"
321 " else\n"
322 " o_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
323 "}\n")));
324
325 m_testCtx.getLog() << *m_program;
326
327 if (!m_program->isOk())
328 {
329 m_program.clear();
330 TCU_FAIL("Compile failed");
331 }
332
333 m_testCtx.getLog() << TestLog::Message << "Using GL_RGBA8 framebuffer with "
334 << actualSamples << " samples" << TestLog::EndMessage;
335
336 m_fbo = MovePtr<FboHelper>(new FboHelper(renderCtx, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT,
337 FRAMEBUFFER_FORMAT, actualSamples));
338
339 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
340 }
341
deinit(void)342 void HelperInvocationValueCase::deinit (void)
343 {
344 m_program.clear();
345 m_fbo.clear();
346 }
347
verifyHelperInvocationValue(TestLog & log,const tcu::Surface & result,bool isMultiSample)348 static bool verifyHelperInvocationValue (TestLog& log, const tcu::Surface& result, bool isMultiSample)
349 {
350 const tcu::RGBA bgRef (0, 0, 0, 255);
351 const tcu::RGBA fgRef (0, 255, 0, 255);
352 const tcu::RGBA threshold (1, isMultiSample ? 254 : 1, 1, 1);
353 int numInvalidPixels = 0;
354
355 for (int y = 0; y < result.getHeight(); ++y)
356 {
357 for (int x = 0; x < result.getWidth(); ++x)
358 {
359 const tcu::RGBA resPix = result.getPixel(x, y);
360
361 if (!tcu::compareThreshold(resPix, bgRef, threshold) &&
362 !tcu::compareThreshold(resPix, fgRef, threshold))
363 numInvalidPixels += 1;
364 }
365 }
366
367 if (numInvalidPixels > 0)
368 {
369 log << TestLog::Image("Result", "Result image", result);
370 log << TestLog::Message << "ERROR: Found " << numInvalidPixels << " invalid result pixels!" << TestLog::EndMessage;
371 }
372 else
373 log << TestLog::Message << "All result pixels are valid" << TestLog::EndMessage;
374
375 return numInvalidPixels == 0;
376 }
377
iterate(void)378 HelperInvocationValueCase::IterateResult HelperInvocationValueCase::iterate (void)
379 {
380 const glu::RenderContext& renderCtx = m_context.getRenderContext();
381 const glw::Functions& gl = renderCtx.getFunctions();
382 const string sectionName = string("Iteration ") + de::toString(m_iterNdx+1) + " / " + de::toString(m_numIters);
383 const tcu::ScopedLogSection section (m_testCtx.getLog(), (string("Iter") + de::toString(m_iterNdx)), sectionName);
384 de::Random rnd (deStringHash(getName()) ^ deInt32Hash(m_iterNdx));
385 tcu::Surface result (FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT);
386
387 m_fbo->bindForRendering();
388 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
389 gl.clear(GL_COLOR_BUFFER_BIT);
390
391 drawRandomPrimitives(renderCtx, m_program->getProgram(), m_primitiveType, m_numPrimitivesPerIter, &rnd);
392
393 m_fbo->readPixels(0, 0, result.getAccess());
394
395 if (!verifyHelperInvocationValue(m_testCtx.getLog(), result, m_numSamples != 0))
396 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid pixels found");
397
398 m_iterNdx += 1;
399 return (m_iterNdx < m_numIters) ? CONTINUE : STOP;
400 }
401
402 //! Checks derivates when value depends on gl_HelperInvocation.
403 class HelperInvocationDerivateCase : public TestCase
404 {
405 public:
406 HelperInvocationDerivateCase (Context& context, const char* name, const char* description, PrimitiveType primType, int numSamples, const char* derivateFunc);
407 ~HelperInvocationDerivateCase (void);
408
409 void init (void);
410 void deinit (void);
411 IterateResult iterate (void);
412
413 private:
414 const PrimitiveType m_primitiveType;
415 const int m_numSamples;
416 const std::string m_derivateFunc;
417
418 const int m_numIters;
419
420 MovePtr<ShaderProgram> m_program;
421 MovePtr<FboHelper> m_fbo;
422 int m_iterNdx;
423 };
424
HelperInvocationDerivateCase(Context & context,const char * name,const char * description,PrimitiveType primType,int numSamples,const char * derivateFunc)425 HelperInvocationDerivateCase::HelperInvocationDerivateCase (Context& context, const char* name, const char* description, PrimitiveType primType, int numSamples, const char* derivateFunc)
426 : TestCase (context, name, description)
427 , m_primitiveType (primType)
428 , m_numSamples (numSamples)
429 , m_derivateFunc (derivateFunc)
430 , m_numIters (16)
431 , m_iterNdx (0)
432 {
433 }
434
~HelperInvocationDerivateCase(void)435 HelperInvocationDerivateCase::~HelperInvocationDerivateCase (void)
436 {
437 deinit();
438 }
439
init(void)440 void HelperInvocationDerivateCase::init (void)
441 {
442 const glu::RenderContext& renderCtx = m_context.getRenderContext();
443 const glw::Functions& gl = renderCtx.getFunctions();
444 const int maxSamples = getInteger(gl, GL_MAX_SAMPLES);
445 const int actualSamples = m_numSamples == NUM_SAMPLES_MAX ? maxSamples : m_numSamples;
446
447 m_program = MovePtr<ShaderProgram>(new ShaderProgram(m_context.getRenderContext(),
448 glu::ProgramSources()
449 << glu::VertexSource(
450 "#version 310 es\n"
451 "in highp vec2 a_position;\n"
452 "uniform highp float u_pointSize;\n"
453 "void main (void)\n"
454 "{\n"
455 " gl_Position = vec4(a_position, 0.0, 1.0);\n"
456 " gl_PointSize = u_pointSize;\n"
457 "}\n")
458 << glu::FragmentSource(string(
459 "#version 310 es\n"
460 "out mediump vec4 o_color;\n"
461 "void main (void)\n"
462 "{\n"
463 " highp float value = gl_HelperInvocation ? 1.0 : 0.0;\n"
464 " highp float derivate = ") + m_derivateFunc + "(value);\n"
465 " if (gl_HelperInvocation)\n"
466 " o_color = vec4(1.0, 0.0, derivate, 1.0);\n"
467 " else\n"
468 " o_color = vec4(0.0, 1.0, derivate, 1.0);\n"
469 "}\n")));
470
471 m_testCtx.getLog() << *m_program;
472
473 if (!m_program->isOk())
474 {
475 m_program.clear();
476 TCU_FAIL("Compile failed");
477 }
478
479 m_testCtx.getLog() << TestLog::Message << "Using GL_RGBA8 framebuffer with "
480 << actualSamples << " samples" << TestLog::EndMessage;
481
482 m_fbo = MovePtr<FboHelper>(new FboHelper(renderCtx, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT,
483 FRAMEBUFFER_FORMAT, actualSamples));
484
485 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
486 }
487
deinit(void)488 void HelperInvocationDerivateCase::deinit (void)
489 {
490 m_program.clear();
491 m_fbo.clear();
492 }
493
hasNeighborWithColor(const tcu::Surface & surface,int x,int y,tcu::RGBA color,tcu::RGBA threshold)494 static bool hasNeighborWithColor (const tcu::Surface& surface, int x, int y, tcu::RGBA color, tcu::RGBA threshold)
495 {
496 static const IVec2 s_neighbors[] =
497 {
498 IVec2(-1, -1),
499 IVec2( 0, -1),
500 IVec2(+1, -1),
501 IVec2(-1, 0),
502 IVec2(+1, 0),
503 IVec2(-1, +1),
504 IVec2( 0, +1),
505 IVec2(+1, +1)
506 };
507
508 const int w = surface.getWidth();
509 const int h = surface.getHeight();
510
511 for (int sample = 0; sample < DE_LENGTH_OF_ARRAY(s_neighbors); sample++)
512 {
513 const IVec2 pos = IVec2(x, y) + s_neighbors[sample];
514
515 if (de::inBounds(pos.x(), 0, w) && de::inBounds(pos.y(), 0, h))
516 {
517 const tcu::RGBA neighborColor = surface.getPixel(pos.x(), pos.y());
518
519 if (tcu::compareThreshold(color, neighborColor, threshold))
520 return true;
521 }
522 else
523 return true; // Can't know for certain
524 }
525
526 return false;
527 }
528
verifyHelperInvocationDerivate(TestLog & log,const tcu::Surface & result,bool isMultiSample)529 static bool verifyHelperInvocationDerivate (TestLog& log, const tcu::Surface& result, bool isMultiSample)
530 {
531 const tcu::RGBA bgRef (0, 0, 0, 255);
532 const tcu::RGBA fgRef (0, 255, 0, 255);
533 const tcu::RGBA isBgThreshold (1, isMultiSample ? 254 : 1, 0, 1);
534 const tcu::RGBA isFgThreshold (1, isMultiSample ? 254 : 1, 255, 1);
535 int numInvalidPixels = 0;
536 int numNonZeroDeriv = 0;
537
538 for (int y = 0; y < result.getHeight(); ++y)
539 {
540 for (int x = 0; x < result.getWidth(); ++x)
541 {
542 const tcu::RGBA resPix = result.getPixel(x, y);
543 const bool isBg = tcu::compareThreshold(resPix, bgRef, isBgThreshold);
544 const bool isFg = tcu::compareThreshold(resPix, fgRef, isFgThreshold);
545 const bool nonZeroDeriv = resPix.getBlue() > 0;
546 const bool neighborBg = nonZeroDeriv ? hasNeighborWithColor(result, x, y, bgRef, isBgThreshold) : false;
547
548 if (nonZeroDeriv)
549 numNonZeroDeriv += 1;
550
551 if ((!isBg && !isFg) || // Neither of valid colors (ignoring blue channel that has derivate)
552 (nonZeroDeriv && !neighborBg)) // Has non-zero derivate, but sample not at primitive edge
553 numInvalidPixels += 1;
554 }
555 }
556
557 if (numInvalidPixels > 0)
558 {
559 log << TestLog::Image("Result", "Result image", result);
560 log << TestLog::Message << "ERROR: Found " << numInvalidPixels << " invalid result pixels!" << TestLog::EndMessage;
561 }
562 else
563 log << TestLog::Message << "All result pixels are valid" << TestLog::EndMessage;
564
565 log << TestLog::Message << "Found " << numNonZeroDeriv << " pixels with non-zero derivate (neighbor sample has gl_HelperInvocation = true)" << TestLog::EndMessage;
566
567 return numInvalidPixels == 0;
568 }
569
iterate(void)570 HelperInvocationDerivateCase::IterateResult HelperInvocationDerivateCase::iterate (void)
571 {
572 const glu::RenderContext& renderCtx = m_context.getRenderContext();
573 const glw::Functions& gl = renderCtx.getFunctions();
574 const string sectionName = string("Iteration ") + de::toString(m_iterNdx+1) + " / " + de::toString(m_numIters);
575 const tcu::ScopedLogSection section (m_testCtx.getLog(), (string("Iter") + de::toString(m_iterNdx)), sectionName);
576 de::Random rnd (deStringHash(getName()) ^ deInt32Hash(m_iterNdx));
577 tcu::Surface result (FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT);
578
579 m_fbo->bindForRendering();
580 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
581 gl.clear(GL_COLOR_BUFFER_BIT);
582
583 drawRandomPrimitives(renderCtx, m_program->getProgram(), m_primitiveType, 1, &rnd);
584
585 m_fbo->readPixels(0, 0, result.getAccess());
586
587 if (!verifyHelperInvocationDerivate(m_testCtx.getLog(), result, m_numSamples != 0))
588 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid pixels found");
589
590 m_iterNdx += 1;
591 return (m_iterNdx < m_numIters) ? CONTINUE : STOP;
592 }
593
594 } // anonymous
595
ShaderHelperInvocationTests(Context & context)596 ShaderHelperInvocationTests::ShaderHelperInvocationTests (Context& context)
597 : TestCaseGroup(context, "helper_invocation", "gl_HelperInvocation tests")
598 {
599 }
600
~ShaderHelperInvocationTests(void)601 ShaderHelperInvocationTests::~ShaderHelperInvocationTests (void)
602 {
603 }
604
init(void)605 void ShaderHelperInvocationTests::init (void)
606 {
607 static const struct
608 {
609 const char* caseName;
610 PrimitiveType primType;
611 } s_primTypes[] =
612 {
613 { "triangles", PRIMITIVETYPE_TRIANGLE },
614 { "lines", PRIMITIVETYPE_LINE },
615 { "wide_lines", PRIMITIVETYPE_WIDE_LINE },
616 { "points", PRIMITIVETYPE_POINT },
617 { "wide_points", PRIMITIVETYPE_WIDE_POINT }
618 };
619
620 static const struct
621 {
622 const char* suffix;
623 int numSamples;
624 } s_sampleCounts[] =
625 {
626 { "", 0 },
627 { "_4_samples", 4 },
628 { "_8_samples", 8 },
629 { "_max_samples", NUM_SAMPLES_MAX }
630 };
631
632 // value
633 {
634 tcu::TestCaseGroup* const valueGroup = new tcu::TestCaseGroup(m_testCtx, "value", "gl_HelperInvocation value in rendered pixels");
635 addChild(valueGroup);
636
637 for (int sampleCountNdx = 0; sampleCountNdx < DE_LENGTH_OF_ARRAY(s_sampleCounts); sampleCountNdx++)
638 {
639 for (int primTypeNdx = 0; primTypeNdx < DE_LENGTH_OF_ARRAY(s_primTypes); primTypeNdx++)
640 {
641 const string name = string(s_primTypes[primTypeNdx].caseName) + s_sampleCounts[sampleCountNdx].suffix;
642 const PrimitiveType primType = s_primTypes[primTypeNdx].primType;
643 const int numSamples = s_sampleCounts[sampleCountNdx].numSamples;
644
645 valueGroup->addChild(new HelperInvocationValueCase(m_context, name.c_str(), "", primType, numSamples));
646 }
647 }
648 }
649
650 // derivate
651 {
652 tcu::TestCaseGroup* const derivateGroup = new tcu::TestCaseGroup(m_testCtx, "derivate", "Derivate of gl_HelperInvocation-dependent value");
653 addChild(derivateGroup);
654
655 for (int sampleCountNdx = 0; sampleCountNdx < DE_LENGTH_OF_ARRAY(s_sampleCounts); sampleCountNdx++)
656 {
657 for (int primTypeNdx = 0; primTypeNdx < DE_LENGTH_OF_ARRAY(s_primTypes); primTypeNdx++)
658 {
659 const string name = string(s_primTypes[primTypeNdx].caseName) + s_sampleCounts[sampleCountNdx].suffix;
660 const PrimitiveType primType = s_primTypes[primTypeNdx].primType;
661 const int numSamples = s_sampleCounts[sampleCountNdx].numSamples;
662
663 derivateGroup->addChild(new HelperInvocationDerivateCase(m_context, (name + "_dfdx").c_str(), "", primType, numSamples, "dFdx"));
664 derivateGroup->addChild(new HelperInvocationDerivateCase(m_context, (name + "_dfdy").c_str(), "", primType, numSamples, "dFdy"));
665 derivateGroup->addChild(new HelperInvocationDerivateCase(m_context, (name + "_fwidth").c_str(), "", primType, numSamples, "fwidth"));
666 }
667 }
668 }
669 }
670
671 } // Functional
672 } // gles31
673 } // deqp
674