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