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