xref: /aosp_15_r20/external/deqp/modules/gles31/functional/es31fPrimitiveBoundingBoxTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 3.1 Module
3  * -------------------------------------------------
4  *
5  * Copyright 2015 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 Primitive bounding box tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es31fPrimitiveBoundingBoxTests.hpp"
25 
26 #include "tcuTestLog.hpp"
27 #include "tcuRenderTarget.hpp"
28 #include "tcuStringTemplate.hpp"
29 #include "tcuSurface.hpp"
30 #include "tcuTextureUtil.hpp"
31 #include "tcuVectorUtil.hpp"
32 #include "gluCallLogWrapper.hpp"
33 #include "gluContextInfo.hpp"
34 #include "gluRenderContext.hpp"
35 #include "gluStrUtil.hpp"
36 #include "gluShaderProgram.hpp"
37 #include "gluObjectWrapper.hpp"
38 #include "gluPixelTransfer.hpp"
39 #include "glsStateQueryUtil.hpp"
40 #include "glwFunctions.hpp"
41 #include "glwEnums.hpp"
42 #include "deRandom.hpp"
43 #include "deUniquePtr.hpp"
44 #include "deStringUtil.hpp"
45 
46 #include <vector>
47 #include <sstream>
48 #include <algorithm>
49 
50 namespace deqp
51 {
52 namespace gles31
53 {
54 namespace Functional
55 {
56 namespace
57 {
58 
59 namespace StateQueryUtil = ::deqp::gls::StateQueryUtil;
60 
61 struct BoundingBox
62 {
63     tcu::Vec4 min;
64     tcu::Vec4 max;
65 
66     /*--------------------------------------------------------------------*//*!
67      * Get component by index of a 8-component vector constructed by
68      * concatenating 4-component min and max vectors.
69      *//*--------------------------------------------------------------------*/
70     float &getComponentAccess(int ndx);
71     const float &getComponentAccess(int ndx) const;
72 };
73 
getComponentAccess(int ndx)74 float &BoundingBox::getComponentAccess(int ndx)
75 {
76     DE_ASSERT(ndx >= 0 && ndx < 8);
77     if (ndx < 4)
78         return min[ndx];
79     else
80         return max[ndx - 4];
81 }
82 
getComponentAccess(int ndx) const83 const float &BoundingBox::getComponentAccess(int ndx) const
84 {
85     return const_cast<BoundingBox *>(this)->getComponentAccess(ndx);
86 }
87 
88 struct ProjectedBBox
89 {
90     tcu::Vec3 min;
91     tcu::Vec3 max;
92 };
93 
projectBoundingBox(const BoundingBox & bbox)94 static ProjectedBBox projectBoundingBox(const BoundingBox &bbox)
95 {
96     const float wMin = de::max(0.0f, bbox.min.w()); // clamp to w=0 as extension requires
97     const float wMax = de::max(0.0f, bbox.max.w());
98     ProjectedBBox retVal;
99 
100     retVal.min = tcu::min(bbox.min.swizzle(0, 1, 2) / wMin, bbox.min.swizzle(0, 1, 2) / wMax);
101     retVal.max = tcu::max(bbox.max.swizzle(0, 1, 2) / wMin, bbox.max.swizzle(0, 1, 2) / wMax);
102     return retVal;
103 }
104 
getViewportBoundingBoxArea(const ProjectedBBox & bbox,const tcu::IVec2 & viewportSize,float size=0.0f)105 static tcu::IVec4 getViewportBoundingBoxArea(const ProjectedBBox &bbox, const tcu::IVec2 &viewportSize,
106                                              float size = 0.0f)
107 {
108     tcu::Vec4 vertexBox;
109     tcu::IVec4 pixelBox;
110 
111     vertexBox.x() = (bbox.min.x() * 0.5f + 0.5f) * (float)viewportSize.x();
112     vertexBox.y() = (bbox.min.y() * 0.5f + 0.5f) * (float)viewportSize.y();
113     vertexBox.z() = (bbox.max.x() * 0.5f + 0.5f) * (float)viewportSize.x();
114     vertexBox.w() = (bbox.max.y() * 0.5f + 0.5f) * (float)viewportSize.y();
115 
116     pixelBox.x() = deFloorFloatToInt32(vertexBox.x() - size / 2.0f);
117     pixelBox.y() = deFloorFloatToInt32(vertexBox.y() - size / 2.0f);
118     pixelBox.z() = deCeilFloatToInt32(vertexBox.z() + size / 2.0f);
119     pixelBox.w() = deCeilFloatToInt32(vertexBox.w() + size / 2.0f);
120     return pixelBox;
121 }
122 
specializeShader(Context & context,const char * code)123 static std::string specializeShader(Context &context, const char *code)
124 {
125     const glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(context.getRenderContext().getType());
126     std::map<std::string, std::string> specializationMap;
127 
128     specializationMap["GLSL_VERSION_DECL"] = glu::getGLSLVersionDeclaration(glslVersion);
129 
130     if (glu::contextSupports(context.getRenderContext().getType(), glu::ApiType::es(3, 2)))
131     {
132         specializationMap["ARB_ES32_COMPATIBILITY_REQUIRE"]  = "";
133         specializationMap["GEOMETRY_SHADER_REQUIRE"]         = "";
134         specializationMap["GEOMETRY_POINT_SIZE"]             = "#extension GL_EXT_geometry_point_size : require";
135         specializationMap["GPU_SHADER5_REQUIRE"]             = "";
136         specializationMap["TESSELLATION_SHADER_REQUIRE"]     = "";
137         specializationMap["TESSELLATION_POINT_SIZE_REQUIRE"] = "#extension GL_EXT_tessellation_point_size : require";
138         specializationMap["PRIMITIVE_BOUNDING_BOX_REQUIRE"]  = "";
139         specializationMap["PRIM_GL_BOUNDING_BOX"]            = "gl_BoundingBox";
140     }
141     else if (glu::contextSupports(context.getRenderContext().getType(), glu::ApiType::core(4, 5)))
142     {
143         specializationMap["ARB_ES32_COMPATIBILITY_REQUIRE"]  = "#extension GL_ARB_ES3_2_compatibility : require";
144         specializationMap["GEOMETRY_SHADER_REQUIRE"]         = "";
145         specializationMap["GEOMETRY_POINT_SIZE"]             = "";
146         specializationMap["GPU_SHADER5_REQUIRE"]             = "";
147         specializationMap["TESSELLATION_SHADER_REQUIRE"]     = "";
148         specializationMap["TESSELLATION_POINT_SIZE_REQUIRE"] = "";
149         specializationMap["PRIMITIVE_BOUNDING_BOX_REQUIRE"]  = "";
150         specializationMap["PRIM_GL_BOUNDING_BOX"]            = "gl_BoundingBox";
151     }
152     else
153     {
154         specializationMap["ARB_ES32_COMPATIBILITY_REQUIRE"]  = "";
155         specializationMap["GEOMETRY_SHADER_REQUIRE"]         = "#extension GL_EXT_geometry_shader : require";
156         specializationMap["GEOMETRY_POINT_SIZE"]             = "#extension GL_EXT_geometry_point_size : require";
157         specializationMap["GPU_SHADER5_REQUIRE"]             = "#extension GL_EXT_gpu_shader5 : require";
158         specializationMap["TESSELLATION_SHADER_REQUIRE"]     = "#extension GL_EXT_tessellation_shader : require";
159         specializationMap["TESSELLATION_POINT_SIZE_REQUIRE"] = "#extension GL_EXT_tessellation_point_size : require";
160         specializationMap["PRIMITIVE_BOUNDING_BOX_REQUIRE"]  = "#extension GL_EXT_primitive_bounding_box : require";
161         specializationMap["PRIM_GL_BOUNDING_BOX"]            = "gl_BoundingBoxEXT";
162     }
163 
164     return tcu::StringTemplate(code).specialize(specializationMap);
165 }
166 
getBoundingBoxFunction(Context & context)167 static decltype(glw::Functions::primitiveBoundingBox) getBoundingBoxFunction(Context &context)
168 {
169     decltype(glw::Functions::primitiveBoundingBox) boundingBoxFunc;
170     const glw::Functions &funcs = context.getRenderContext().getFunctions();
171 
172     /* OpenGL ES is assumed to have it (extensions checks passed). */
173     if (glu::isContextTypeES(context.getRenderContext().getType()))
174         return funcs.primitiveBoundingBox;
175 
176     boundingBoxFunc = (decltype(boundingBoxFunc))context.getRenderContext().getProcAddress("glPrimitiveBoundingBoxARB");
177 
178     DE_ASSERT(boundingBoxFunc);
179 
180     return boundingBoxFunc;
181 }
182 
supportsES32OrGL45(Context & context)183 static bool supportsES32OrGL45(Context &context)
184 {
185     return glu::contextSupports(context.getRenderContext().getType(), glu::ApiType::es(3, 2)) ||
186            glu::contextSupports(context.getRenderContext().getType(), glu::ApiType::core(4, 5));
187 }
188 
boundingBoxSupported(Context & context)189 static bool boundingBoxSupported(Context &context)
190 {
191     /* Require one of:
192      *    - OpenGL ES 3.2
193      *    - OpenGL 4.5 + GL_ARB_ES3_2_compatibility
194      *    - OpenGL ES 3.1 + GL_EXT_primitive_bounding_box
195      */
196     return glu::contextSupports(context.getRenderContext().getType(), glu::ApiType::es(3, 2)) ||
197            ((glu::contextSupports(context.getRenderContext().getType(), glu::ApiType::core(4, 5)) &&
198              context.getContextInfo().isExtensionSupported("GL_ARB_ES3_2_compatibility")) ||
199             context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box"));
200 }
201 
202 class InitialValueCase : public TestCase
203 {
204 public:
205     InitialValueCase(Context &context, const char *name, const char *desc);
206 
207     void init(void);
208     IterateResult iterate(void);
209 };
210 
InitialValueCase(Context & context,const char * name,const char * desc)211 InitialValueCase::InitialValueCase(Context &context, const char *name, const char *desc) : TestCase(context, name, desc)
212 {
213 }
214 
init(void)215 void InitialValueCase::init(void)
216 {
217     if (!boundingBoxSupported(m_context))
218         throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
219 }
220 
iterate(void)221 InitialValueCase::IterateResult InitialValueCase::iterate(void)
222 {
223     StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLfloat[8]> state;
224     glu::CallLogWrapper gl(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
225 
226     gl.enableLogging(true);
227 
228     m_testCtx.getLog() << tcu::TestLog::Message
229                        << "Querying GL_PRIMITIVE_BOUNDING_BOX_EXT, expecting (-1, -1, -1, 1) (1, 1, 1, 1)"
230                        << tcu::TestLog::EndMessage;
231 
232     gl.glGetFloatv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
233     GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
234 
235     if (!state.verifyValidity(m_testCtx))
236         return STOP;
237 
238     m_testCtx.getLog() << tcu::TestLog::Message << "Got " << tcu::formatArray(&state[0], &state[8])
239                        << tcu::TestLog::EndMessage;
240 
241     if ((state[0] != -1.0f) || (state[1] != -1.0f) || (state[2] != -1.0f) || (state[3] != 1.0f) || (state[4] != 1.0f) ||
242         (state[5] != 1.0f) || (state[6] != 1.0f) || (state[7] != 1.0f))
243     {
244         m_testCtx.getLog() << tcu::TestLog::Message << "Error, unexpected value" << tcu::TestLog::EndMessage;
245 
246         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid initial value");
247     }
248     else
249         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
250 
251     return STOP;
252 }
253 
254 class QueryCase : public TestCase
255 {
256 public:
257     enum QueryMethod
258     {
259         QUERY_FLOAT = 0,
260         QUERY_BOOLEAN,
261         QUERY_INT,
262         QUERY_INT64,
263 
264         QUERY_LAST
265     };
266 
267     QueryCase(Context &context, const char *name, const char *desc, QueryMethod method);
268 
269 private:
270     void init(void);
271     IterateResult iterate(void);
272 
273     bool verifyState(glu::CallLogWrapper &gl, const BoundingBox &bbox) const;
274 
275     const QueryMethod m_method;
276 };
277 
QueryCase(Context & context,const char * name,const char * desc,QueryMethod method)278 QueryCase::QueryCase(Context &context, const char *name, const char *desc, QueryMethod method)
279     : TestCase(context, name, desc)
280     , m_method(method)
281 {
282     DE_ASSERT(method < QUERY_LAST);
283 }
284 
init(void)285 void QueryCase::init(void)
286 {
287     if (!boundingBoxSupported(m_context))
288         throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
289 }
290 
iterate(void)291 QueryCase::IterateResult QueryCase::iterate(void)
292 {
293     static const BoundingBox fixedCases[] = {
294         {tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f), tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f)},
295         {tcu::Vec4(-0.0f, -0.0f, -0.0f, -0.0f), tcu::Vec4(0.0f, 0.0f, 0.0f, -0.0f)},
296         {tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, -1.0f)},
297         {tcu::Vec4(2.0f, 2.0f, 2.0f, 2.0f), tcu::Vec4(1.5f, 1.5f, 1.5f, 1.0f)},
298         {tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f), tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f)},
299         {tcu::Vec4(1.0f, 1.0f, 1.0f, 0.3f), tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.2f)},
300     };
301 
302     const int numRandomCases = 9;
303     glu::CallLogWrapper gl(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
304     de::Random rnd(0xDE3210);
305     std::vector<BoundingBox> cases;
306 
307     cases.insert(cases.begin(), DE_ARRAY_BEGIN(fixedCases), DE_ARRAY_END(fixedCases));
308     for (int ndx = 0; ndx < numRandomCases; ++ndx)
309     {
310         BoundingBox boundingBox;
311 
312         // parameter evaluation order is not guaranteed, cannot just do "max = (rand(), rand(), ...)
313         for (int coordNdx = 0; coordNdx < 8; ++coordNdx)
314             boundingBox.getComponentAccess(coordNdx) = rnd.getFloat(-4.0f, 4.0f);
315 
316         cases.push_back(boundingBox);
317     }
318 
319     gl.enableLogging(true);
320     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
321 
322     auto boundingBoxFunc = getBoundingBoxFunction(m_context);
323 
324     for (int caseNdx = 0; caseNdx < (int)cases.size(); ++caseNdx)
325     {
326         const tcu::ScopedLogSection section(m_testCtx.getLog(), "Iteration", "Iteration " + de::toString(caseNdx + 1));
327         const BoundingBox &boundingBox = cases[caseNdx];
328 
329         /* On desktop GL, we cannot use call wrapper here but must use resolved extension function. */
330         if (!glu::isContextTypeES(m_context.getRenderContext().getType()))
331         {
332             boundingBoxFunc(boundingBox.min.x(), boundingBox.min.y(), boundingBox.min.z(), boundingBox.min.w(),
333                             boundingBox.max.x(), boundingBox.max.y(), boundingBox.max.z(), boundingBox.max.w());
334         }
335         else
336         {
337             gl.glPrimitiveBoundingBox(boundingBox.min.x(), boundingBox.min.y(), boundingBox.min.z(),
338                                       boundingBox.min.w(), boundingBox.max.x(), boundingBox.max.y(),
339                                       boundingBox.max.z(), boundingBox.max.w());
340         }
341 
342         if (!verifyState(gl, boundingBox))
343             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Unexpected query result");
344     }
345 
346     return STOP;
347 }
348 
verifyState(glu::CallLogWrapper & gl,const BoundingBox & bbox) const349 bool QueryCase::verifyState(glu::CallLogWrapper &gl, const BoundingBox &bbox) const
350 {
351     switch (m_method)
352     {
353     case QUERY_FLOAT:
354     {
355         StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLfloat[8]> state;
356         bool error = false;
357 
358         gl.glGetFloatv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
359         GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
360 
361         if (!state.verifyValidity(m_testCtx))
362             return false;
363 
364         m_testCtx.getLog() << tcu::TestLog::Message << "glGetFloatv returned " << tcu::formatArray(&state[0], &state[8])
365                            << tcu::TestLog::EndMessage;
366 
367         for (int ndx = 0; ndx < 8; ++ndx)
368             if (state[ndx] != bbox.getComponentAccess(ndx))
369                 error = true;
370 
371         if (error)
372         {
373             m_testCtx.getLog() << tcu::TestLog::Message << "Error, unexpected value\n"
374                                << "Expected [" << bbox.min.x() << ", " << bbox.min.y() << ", " << bbox.min.z() << ", "
375                                << bbox.min.w() << ", " << bbox.max.x() << ", " << bbox.max.y() << ", " << bbox.max.z()
376                                << ", " << bbox.max.w() << "]" << tcu::TestLog::EndMessage;
377             return false;
378         }
379         return true;
380     }
381 
382     case QUERY_INT:
383     {
384         StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLint[8]> state;
385         bool error = false;
386 
387         gl.glGetIntegerv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
388         GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
389 
390         if (!state.verifyValidity(m_testCtx))
391             return false;
392 
393         m_testCtx.getLog() << tcu::TestLog::Message << "glGetIntegerv returned "
394                            << tcu::formatArray(&state[0], &state[8]) << tcu::TestLog::EndMessage;
395 
396         for (int ndx = 0; ndx < 8; ++ndx)
397             if (state[ndx] !=
398                     StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint>(bbox.getComponentAccess(ndx)) &&
399                 state[ndx] !=
400                     StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint>(bbox.getComponentAccess(ndx)))
401                 error = true;
402 
403         if (error)
404         {
405             tcu::MessageBuilder builder(&m_testCtx.getLog());
406 
407             builder << "Error, unexpected value\n"
408                     << "Expected [";
409 
410             for (int ndx = 0; ndx < 8; ++ndx)
411             {
412                 const glw::GLint roundDown =
413                     StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint>(bbox.getComponentAccess(ndx));
414                 const glw::GLint roundUp =
415                     StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint>(bbox.getComponentAccess(ndx));
416 
417                 if (ndx != 0)
418                     builder << ", ";
419 
420                 if (roundDown == roundUp)
421                     builder << roundDown;
422                 else
423                     builder << "{" << roundDown << ", " << roundUp << "}";
424             }
425 
426             builder << "]" << tcu::TestLog::EndMessage;
427             return false;
428         }
429         return true;
430     }
431 
432     case QUERY_INT64:
433     {
434         StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLint64[8]> state;
435         bool error = false;
436 
437         gl.glGetInteger64v(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
438         GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
439 
440         if (!state.verifyValidity(m_testCtx))
441             return false;
442 
443         m_testCtx.getLog() << tcu::TestLog::Message << "glGetInteger64v returned "
444                            << tcu::formatArray(&state[0], &state[8]) << tcu::TestLog::EndMessage;
445 
446         for (int ndx = 0; ndx < 8; ++ndx)
447             if (state[ndx] !=
448                     StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint64>(bbox.getComponentAccess(ndx)) &&
449                 state[ndx] !=
450                     StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint64>(bbox.getComponentAccess(ndx)))
451                 error = true;
452 
453         if (error)
454         {
455             tcu::MessageBuilder builder(&m_testCtx.getLog());
456 
457             builder << "Error, unexpected value\n"
458                     << "Expected [";
459 
460             for (int ndx = 0; ndx < 8; ++ndx)
461             {
462                 const glw::GLint64 roundDown =
463                     StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint64>(bbox.getComponentAccess(ndx));
464                 const glw::GLint64 roundUp =
465                     StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint64>(bbox.getComponentAccess(ndx));
466 
467                 if (ndx != 0)
468                     builder << ", ";
469 
470                 if (roundDown == roundUp)
471                     builder << roundDown;
472                 else
473                     builder << "{" << roundDown << ", " << roundUp << "}";
474             }
475 
476             builder << "]" << tcu::TestLog::EndMessage;
477             return false;
478         }
479         return true;
480     }
481 
482     case QUERY_BOOLEAN:
483     {
484         StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLboolean[8]> state;
485         bool error = false;
486 
487         gl.glGetBooleanv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state);
488         GLU_EXPECT_NO_ERROR(gl.glGetError(), "query");
489 
490         if (!state.verifyValidity(m_testCtx))
491             return false;
492 
493         m_testCtx.getLog() << tcu::TestLog::Message << "glGetBooleanv returned [" << glu::getBooleanStr(state[0])
494                            << ", " << glu::getBooleanStr(state[1]) << ", " << glu::getBooleanStr(state[2]) << ", "
495                            << glu::getBooleanStr(state[3]) << ", " << glu::getBooleanStr(state[4]) << ", "
496                            << glu::getBooleanStr(state[5]) << ", " << glu::getBooleanStr(state[6]) << ", "
497                            << glu::getBooleanStr(state[7]) << "]\n"
498                            << tcu::TestLog::EndMessage;
499 
500         for (int ndx = 0; ndx < 8; ++ndx)
501             if (state[ndx] != ((bbox.getComponentAccess(ndx) != 0.0f) ? (GL_TRUE) : (GL_FALSE)))
502                 error = true;
503 
504         if (error)
505         {
506             tcu::MessageBuilder builder(&m_testCtx.getLog());
507 
508             builder << "Error, unexpected value\n"
509                     << "Expected [";
510 
511             for (int ndx = 0; ndx < 8; ++ndx)
512             {
513                 if (ndx != 0)
514                     builder << ", ";
515 
516                 builder << ((bbox.getComponentAccess(ndx) != 0.0f) ? ("GL_TRUE") : ("GL_FALSE"));
517             }
518 
519             builder << "]" << tcu::TestLog::EndMessage;
520             return false;
521         }
522         return true;
523     }
524 
525     default:
526         DE_ASSERT(false);
527         return true;
528     }
529 }
530 
531 class BBoxRenderCase : public TestCase
532 {
533 public:
534     enum
535     {
536         FLAG_RENDERTARGET_DEFAULT = 1u << 0, //!< render to default renderbuffer
537         FLAG_RENDERTARGET_FBO     = 1u << 1, //!< render to framebuffer object
538 
539         FLAG_BBOXSIZE_EQUAL   = 1u << 2, //!< set tight primitive bounding box
540         FLAG_BBOXSIZE_LARGER  = 1u << 3, //!< set padded primitive bounding box
541         FLAG_BBOXSIZE_SMALLER = 1u << 4, //!< set too small primitive bounding box
542 
543         FLAG_TESSELLATION = 1u << 5, //!< use tessellation shader
544         FLAG_GEOMETRY     = 1u << 6, //!< use geometry shader
545 
546         FLAG_SET_BBOX_STATE     = 1u << 7, //!< set primitive bounding box using global state
547         FLAG_SET_BBOX_OUTPUT    = 1u << 8, //!< set primitive bounding box using tessellation output
548         FLAG_PER_PRIMITIVE_BBOX = 1u << 9, //!< set primitive bounding per primitive
549 
550         FLAGBIT_USER_BIT = 10u //!< bits N and and up are reserved for subclasses
551     };
552 
553     BBoxRenderCase(Context &context, const char *name, const char *description, int numIterations, uint32_t flags);
554     ~BBoxRenderCase(void);
555 
556 protected:
557     enum RenderTarget
558     {
559         RENDERTARGET_DEFAULT,
560         RENDERTARGET_FBO,
561     };
562     enum BBoxSize
563     {
564         BBOXSIZE_EQUAL,
565         BBOXSIZE_LARGER,
566         BBOXSIZE_SMALLER,
567     };
568 
569     enum
570     {
571         RENDER_TARGET_MIN_SIZE = 256,
572         FBO_SIZE               = 512,
573         MIN_VIEWPORT_SIZE      = 256,
574         MAX_VIEWPORT_SIZE      = 512,
575     };
576     DE_STATIC_ASSERT(MIN_VIEWPORT_SIZE <= RENDER_TARGET_MIN_SIZE);
577 
578     enum
579     {
580         VA_POS_VEC_NDX     = 0,
581         VA_COL_VEC_NDX     = 1,
582         VA_NUM_ATTRIB_VECS = 2,
583     };
584 
585     enum AABBRoundDirection
586     {
587         ROUND_INWARDS = 0,
588         ROUND_OUTWARDS
589     };
590 
591     struct IterationConfig
592     {
593         tcu::IVec2 viewportPos;
594         tcu::IVec2 viewportSize;
595         tcu::Vec2 patternPos;  //!< in NDC
596         tcu::Vec2 patternSize; //!< in NDC
597         BoundingBox bbox;
598     };
599 
600     virtual void init(void);
601     virtual void deinit(void);
602     IterateResult iterate(void);
603 
604     virtual std::string genVertexSource(void) const                 = 0;
605     virtual std::string genFragmentSource(void) const               = 0;
606     virtual std::string genTessellationControlSource(void) const    = 0;
607     virtual std::string genTessellationEvaluationSource(void) const = 0;
608     virtual std::string genGeometrySource(void) const               = 0;
609 
610     virtual IterationConfig generateConfig(int iteration, const tcu::IVec2 &renderTargetSize) const = 0;
611     virtual void getAttributeData(std::vector<tcu::Vec4> &data) const                               = 0;
612     virtual void renderTestPattern(const IterationConfig &config)                                   = 0;
613     virtual void verifyRenderResult(const IterationConfig &config)                                  = 0;
614 
615     IterationConfig generateRandomConfig(int seed, const tcu::IVec2 &renderTargetSize) const;
616     tcu::IVec4 getViewportPatternArea(const tcu::Vec2 &patternPos, const tcu::Vec2 &patternSize,
617                                       const tcu::IVec2 &viewportSize, AABBRoundDirection roundDir) const;
618 
619     void setupRender(const IterationConfig &config);
620 
621     enum ShaderFunction
622     {
623         SHADER_FUNC_MIRROR_X,
624         SHADER_FUNC_MIRROR_Y,
625         SHADER_FUNC_INSIDE_BBOX,
626     };
627 
628     const char *genShaderFunction(ShaderFunction func) const;
629 
630     const RenderTarget m_renderTarget;
631     const BBoxSize m_bboxSize;
632     const bool m_hasTessellationStage;
633     const bool m_hasGeometryStage;
634     const bool m_useGlobalState;
635     const bool m_calcPerPrimitiveBBox;
636     const int m_numIterations;
637 
638     de::MovePtr<glu::ShaderProgram> m_program;
639     de::MovePtr<glu::Buffer> m_vbo;
640     de::MovePtr<glu::Framebuffer> m_fbo;
641     glw::GLuint m_vao;
642 
643     decltype(glw::Functions::primitiveBoundingBox) m_boundingBoxFunc;
644 
645 private:
646     std::vector<IterationConfig> m_iterationConfigs;
647     int m_iteration;
648 };
649 
BBoxRenderCase(Context & context,const char * name,const char * description,int numIterations,uint32_t flags)650 BBoxRenderCase::BBoxRenderCase(Context &context, const char *name, const char *description, int numIterations,
651                                uint32_t flags)
652     : TestCase(context, name, description)
653     , m_renderTarget((flags & FLAG_RENDERTARGET_DEFAULT) ? (RENDERTARGET_DEFAULT) : (RENDERTARGET_FBO))
654     , m_bboxSize((flags & FLAG_BBOXSIZE_EQUAL)   ? (BBOXSIZE_EQUAL) :
655                  (flags & FLAG_BBOXSIZE_SMALLER) ? (BBOXSIZE_SMALLER) :
656                                                    (BBOXSIZE_LARGER))
657     , m_hasTessellationStage((flags & FLAG_TESSELLATION) != 0)
658     , m_hasGeometryStage((flags & FLAG_GEOMETRY) != 0)
659     , m_useGlobalState((flags & FLAG_SET_BBOX_STATE) != 0)
660     , m_calcPerPrimitiveBBox((flags & FLAG_PER_PRIMITIVE_BBOX) != 0)
661     , m_numIterations(numIterations)
662     , m_vao(0)
663     , m_boundingBoxFunc(NULL)
664     , m_iteration(0)
665 {
666     // validate flags
667     DE_ASSERT(
668         (((m_renderTarget == RENDERTARGET_DEFAULT) ? (FLAG_RENDERTARGET_DEFAULT) : (0)) |
669          ((m_renderTarget == RENDERTARGET_FBO) ? (FLAG_RENDERTARGET_FBO) : (0)) |
670          ((m_bboxSize == BBOXSIZE_EQUAL) ? (FLAG_BBOXSIZE_EQUAL) : (0)) |
671          ((m_bboxSize == BBOXSIZE_LARGER) ? (FLAG_BBOXSIZE_LARGER) : (0)) |
672          ((m_bboxSize == BBOXSIZE_SMALLER) ? (FLAG_BBOXSIZE_SMALLER) : (0)) |
673          ((m_hasTessellationStage) ? (FLAG_TESSELLATION) : (0)) | ((m_hasGeometryStage) ? (FLAG_GEOMETRY) : (0)) |
674          ((m_useGlobalState) ? (FLAG_SET_BBOX_STATE) : (0)) | ((!m_useGlobalState) ? (FLAG_SET_BBOX_OUTPUT) : (0)) |
675          ((m_calcPerPrimitiveBBox) ? (FLAG_PER_PRIMITIVE_BBOX) : (0))) == (flags & ((1u << FLAGBIT_USER_BIT) - 1)));
676 
677     DE_ASSERT(m_useGlobalState || m_hasTessellationStage); // using non-global state requires tessellation
678 
679     if (m_calcPerPrimitiveBBox)
680     {
681         DE_ASSERT(!m_useGlobalState);            // per-primitive test requires per-primitive (non-global) state
682         DE_ASSERT(m_bboxSize == BBOXSIZE_EQUAL); // smaller is hard to verify, larger not interesting
683     }
684 }
685 
~BBoxRenderCase(void)686 BBoxRenderCase::~BBoxRenderCase(void)
687 {
688     deinit();
689 }
690 
init(void)691 void BBoxRenderCase::init(void)
692 {
693     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
694     const tcu::IVec2 renderTargetSize =
695         (m_renderTarget == RENDERTARGET_DEFAULT) ?
696             (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) :
697             (tcu::IVec2(FBO_SIZE, FBO_SIZE));
698     const bool hasES32OrGL45 = supportsES32OrGL45(m_context);
699 
700     // requirements
701     if (!boundingBoxSupported(m_context))
702         throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
703     if (!hasES32OrGL45 && m_hasTessellationStage &&
704         !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
705         throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
706     if (!hasES32OrGL45 && m_hasGeometryStage &&
707         !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader"))
708         throw tcu::NotSupportedError("Test requires GL_EXT_geometry_shader extension");
709     if (m_renderTarget == RENDERTARGET_DEFAULT &&
710         (renderTargetSize.x() < RENDER_TARGET_MIN_SIZE || renderTargetSize.y() < RENDER_TARGET_MIN_SIZE))
711         throw tcu::NotSupportedError(std::string() + "Test requires " + de::toString<int>(RENDER_TARGET_MIN_SIZE) +
712                                      "x" + de::toString<int>(RENDER_TARGET_MIN_SIZE) + " default framebuffer");
713 
714     // log case specifics
715     m_testCtx.getLog() << tcu::TestLog::Message << "Setting primitive bounding box "
716                        << ((m_calcPerPrimitiveBBox)         ? ("to exactly cover each generated primitive") :
717                            (m_bboxSize == BBOXSIZE_EQUAL)   ? ("to exactly cover rendered grid") :
718                            (m_bboxSize == BBOXSIZE_LARGER)  ? ("to cover the grid and include some padding") :
719                            (m_bboxSize == BBOXSIZE_SMALLER) ? ("to cover only a subset of the grid") :
720                                                               (DE_NULL))
721                        << ".\n"
722                        << "Rendering with vertex" << ((m_hasTessellationStage) ? ("-tessellation{ctrl,eval}") : (""))
723                        << ((m_hasGeometryStage) ? ("-geometry") : ("")) << "-fragment program.\n"
724                        << "Set bounding box using "
725                        << ((m_useGlobalState) ? ("PRIMITIVE_BOUNDING_BOX_EXT state") : ("gl_BoundingBoxEXT output"))
726                        << "\n"
727                        << "Verifying rendering results are valid within the bounding box." << tcu::TestLog::EndMessage;
728 
729     // resources
730 
731     {
732         glu::ProgramSources sources;
733         sources << glu::VertexSource(specializeShader(m_context, genVertexSource().c_str()));
734         sources << glu::FragmentSource(specializeShader(m_context, genFragmentSource().c_str()));
735 
736         if (m_hasTessellationStage)
737             sources << glu::TessellationControlSource(
738                            specializeShader(m_context, genTessellationControlSource().c_str()))
739                     << glu::TessellationEvaluationSource(
740                            specializeShader(m_context, genTessellationEvaluationSource().c_str()));
741         if (m_hasGeometryStage)
742             sources << glu::GeometrySource(specializeShader(m_context, genGeometrySource().c_str()));
743 
744         m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), sources));
745         GLU_EXPECT_NO_ERROR(gl.getError(), "build program");
746 
747         {
748             const tcu::ScopedLogSection section(m_testCtx.getLog(), "ShaderProgram", "Shader program");
749             m_testCtx.getLog() << *m_program;
750         }
751 
752         if (!m_program->isOk())
753             throw tcu::TestError("failed to build program");
754     }
755 
756     if (m_renderTarget == RENDERTARGET_FBO)
757     {
758         glu::Texture colorAttachment(m_context.getRenderContext());
759 
760         gl.bindTexture(GL_TEXTURE_2D, *colorAttachment);
761         gl.texStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, FBO_SIZE, FBO_SIZE);
762         GLU_EXPECT_NO_ERROR(gl.getError(), "gen tex");
763 
764         m_fbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext()));
765         gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, **m_fbo);
766         gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *colorAttachment, 0);
767         GLU_EXPECT_NO_ERROR(gl.getError(), "attach");
768 
769         // unbind to prevent texture name deletion from removing it from current fbo attachments
770         gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
771     }
772 
773     m_boundingBoxFunc = getBoundingBoxFunction(m_context);
774 
775     {
776         std::vector<tcu::Vec4> data;
777 
778         getAttributeData(data);
779 
780         // Generate VAO for desktop OpenGL
781         if (!glu::isContextTypeES(m_context.getRenderContext().getType()))
782         {
783             gl.genVertexArrays(1, &m_vao);
784             gl.bindVertexArray(m_vao);
785         }
786 
787         m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
788         gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
789         gl.bufferData(GL_ARRAY_BUFFER, (int)(data.size() * sizeof(tcu::Vec4)), &data[0], GL_STATIC_DRAW);
790         GLU_EXPECT_NO_ERROR(gl.getError(), "create vbo");
791     }
792 
793     // Iterations
794     for (int iterationNdx = 0; iterationNdx < m_numIterations; ++iterationNdx)
795         m_iterationConfigs.push_back(generateConfig(iterationNdx, renderTargetSize));
796 }
797 
deinit(void)798 void BBoxRenderCase::deinit(void)
799 {
800     m_program.clear();
801     m_vbo.clear();
802     m_fbo.clear();
803 
804     if (m_vao)
805     {
806         m_context.getRenderContext().getFunctions().deleteVertexArrays(1, &m_vao);
807         m_vao = 0;
808     }
809 }
810 
iterate(void)811 BBoxRenderCase::IterateResult BBoxRenderCase::iterate(void)
812 {
813     const tcu::ScopedLogSection section(m_testCtx.getLog(),
814                                         std::string() + "Iteration" + de::toString((int)m_iteration),
815                                         std::string() + "Iteration " + de::toString((int)m_iteration + 1) + "/" +
816                                             de::toString((int)m_iterationConfigs.size()));
817     const IterationConfig &config = m_iterationConfigs[m_iteration];
818 
819     // default
820     if (m_iteration == 0)
821         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
822 
823     renderTestPattern(config);
824     verifyRenderResult(config);
825 
826     if (++m_iteration < (int)m_iterationConfigs.size())
827         return CONTINUE;
828 
829     return STOP;
830 }
831 
generateRandomConfig(int seed,const tcu::IVec2 & renderTargetSize) const832 BBoxRenderCase::IterationConfig BBoxRenderCase::generateRandomConfig(int seed, const tcu::IVec2 &renderTargetSize) const
833 {
834     de::Random rnd(seed);
835     IterationConfig config;
836 
837     // viewport config
838     config.viewportSize.x() = rnd.getInt(MIN_VIEWPORT_SIZE, de::min<int>(renderTargetSize.x(), MAX_VIEWPORT_SIZE));
839     config.viewportSize.y() = rnd.getInt(MIN_VIEWPORT_SIZE, de::min<int>(renderTargetSize.y(), MAX_VIEWPORT_SIZE));
840     config.viewportPos.x()  = rnd.getInt(0, renderTargetSize.x() - config.viewportSize.x());
841     config.viewportPos.y()  = rnd.getInt(0, renderTargetSize.y() - config.viewportSize.y());
842 
843     // pattern location inside viewport
844     config.patternSize.x() = rnd.getFloat(0.4f, 1.4f);
845     config.patternSize.y() = rnd.getFloat(0.4f, 1.4f);
846     config.patternPos.x()  = rnd.getFloat(-1.0f, 1.0f - config.patternSize.x());
847     config.patternPos.y()  = rnd.getFloat(-1.0f, 1.0f - config.patternSize.y());
848 
849     // accurate bounding box
850     config.bbox.min = tcu::Vec4(config.patternPos.x(), config.patternPos.y(), 0.0f, 1.0f);
851     config.bbox.max = tcu::Vec4(config.patternPos.x() + config.patternSize.x(),
852                                 config.patternPos.y() + config.patternSize.y(), 0.0f, 1.0f);
853 
854     if (m_bboxSize == BBOXSIZE_LARGER)
855     {
856         // increase bbox size
857         config.bbox.min.x() -= rnd.getFloat() * 0.5f;
858         config.bbox.min.y() -= rnd.getFloat() * 0.5f;
859         config.bbox.min.z() -= rnd.getFloat() * 0.5f;
860 
861         config.bbox.max.x() += rnd.getFloat() * 0.5f;
862         config.bbox.max.y() += rnd.getFloat() * 0.5f;
863         config.bbox.max.z() += rnd.getFloat() * 0.5f;
864     }
865     else if (m_bboxSize == BBOXSIZE_SMALLER)
866     {
867         // reduce bbox size
868         config.bbox.min.x() += rnd.getFloat() * 0.4f * config.patternSize.x();
869         config.bbox.min.y() += rnd.getFloat() * 0.4f * config.patternSize.y();
870 
871         config.bbox.max.x() -= rnd.getFloat() * 0.4f * config.patternSize.x();
872         config.bbox.max.y() -= rnd.getFloat() * 0.4f * config.patternSize.y();
873     }
874 
875     return config;
876 }
877 
getViewportPatternArea(const tcu::Vec2 & patternPos,const tcu::Vec2 & patternSize,const tcu::IVec2 & viewportSize,AABBRoundDirection roundDir) const878 tcu::IVec4 BBoxRenderCase::getViewportPatternArea(const tcu::Vec2 &patternPos, const tcu::Vec2 &patternSize,
879                                                   const tcu::IVec2 &viewportSize, AABBRoundDirection roundDir) const
880 {
881     const float halfPixel = 0.5f;
882     tcu::Vec4 vertexBox;
883     tcu::IVec4 pixelBox;
884 
885     vertexBox.x() = (patternPos.x() * 0.5f + 0.5f) * (float)viewportSize.x();
886     vertexBox.y() = (patternPos.y() * 0.5f + 0.5f) * (float)viewportSize.y();
887     vertexBox.z() = ((patternPos.x() + patternSize.x()) * 0.5f + 0.5f) * (float)viewportSize.x();
888     vertexBox.w() = ((patternPos.y() + patternSize.y()) * 0.5f + 0.5f) * (float)viewportSize.y();
889 
890     if (roundDir == ROUND_INWARDS)
891     {
892         pixelBox.x() = (int)deFloatCeil(vertexBox.x() + halfPixel);
893         pixelBox.y() = (int)deFloatCeil(vertexBox.y() + halfPixel);
894         pixelBox.z() = (int)deFloatFloor(vertexBox.z() - halfPixel);
895         pixelBox.w() = (int)deFloatFloor(vertexBox.w() - halfPixel);
896     }
897     else
898     {
899         pixelBox.x() = (int)deFloatFloor(vertexBox.x() - halfPixel);
900         pixelBox.y() = (int)deFloatFloor(vertexBox.y() - halfPixel);
901         pixelBox.z() = (int)deFloatCeil(vertexBox.z() + halfPixel);
902         pixelBox.w() = (int)deFloatCeil(vertexBox.w() + halfPixel);
903     }
904 
905     return pixelBox;
906 }
907 
setupRender(const IterationConfig & config)908 void BBoxRenderCase::setupRender(const IterationConfig &config)
909 {
910     const glw::Functions &gl          = m_context.getRenderContext().getFunctions();
911     const glw::GLint posLocation      = gl.getAttribLocation(m_program->getProgram(), "a_position");
912     const glw::GLint colLocation      = gl.getAttribLocation(m_program->getProgram(), "a_color");
913     const glw::GLint posScaleLocation = gl.getUniformLocation(m_program->getProgram(), "u_posScale");
914 
915     TCU_CHECK(posLocation != -1);
916     TCU_CHECK(colLocation != -1);
917     TCU_CHECK(posScaleLocation != -1);
918 
919     m_testCtx.getLog() << tcu::TestLog::Message << "Setting viewport to ("
920                        << "x: " << config.viewportPos.x() << ", "
921                        << "y: " << config.viewportPos.y() << ", "
922                        << "w: " << config.viewportSize.x() << ", "
923                        << "h: " << config.viewportSize.y() << ")\n"
924                        << "Vertex coordinates are in range:\n"
925                        << "\tx: [" << config.patternPos.x() << ", " << (config.patternPos.x() + config.patternSize.x())
926                        << "]\n"
927                        << "\ty: [" << config.patternPos.y() << ", " << (config.patternPos.y() + config.patternSize.y())
928                        << "]\n"
929                        << tcu::TestLog::EndMessage;
930 
931     if (!m_calcPerPrimitiveBBox)
932         m_testCtx.getLog() << tcu::TestLog::Message << "Setting primitive bounding box to:\n"
933                            << "\t" << config.bbox.min << "\n"
934                            << "\t" << config.bbox.max << "\n"
935                            << tcu::TestLog::EndMessage;
936 
937     if (m_useGlobalState)
938         m_boundingBoxFunc(config.bbox.min.x(), config.bbox.min.y(), config.bbox.min.z(), config.bbox.min.w(),
939                           config.bbox.max.x(), config.bbox.max.y(), config.bbox.max.z(), config.bbox.max.w());
940     else
941         // state is overriden by the tessellation output, set bbox to invisible area to imitiate dirty state left by application
942         m_boundingBoxFunc(-2.0f, -2.0f, 0.0f, 1.0f, -1.7f, -1.7f, 0.0f, 1.0f);
943 
944     if (m_fbo)
945         gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, **m_fbo);
946 
947     gl.viewport(config.viewportPos.x(), config.viewportPos.y(), config.viewportSize.x(), config.viewportSize.y());
948     gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
949     gl.clear(GL_COLOR_BUFFER_BIT);
950 
951     gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
952 
953     gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, (int)(VA_NUM_ATTRIB_VECS * sizeof(float[4])),
954                            glu::BufferOffsetAsPointer(4 * VA_POS_VEC_NDX * sizeof(float)));
955     gl.vertexAttribPointer(colLocation, 4, GL_FLOAT, GL_FALSE, (int)(VA_NUM_ATTRIB_VECS * sizeof(float[4])),
956                            glu::BufferOffsetAsPointer(4 * VA_COL_VEC_NDX * sizeof(float)));
957     gl.enableVertexAttribArray(posLocation);
958     gl.enableVertexAttribArray(colLocation);
959     gl.useProgram(m_program->getProgram());
960     gl.uniform4f(posScaleLocation, config.patternPos.x(), config.patternPos.y(), config.patternSize.x(),
961                  config.patternSize.y());
962 
963     {
964         const glw::GLint bboxMinPos = gl.getUniformLocation(m_program->getProgram(), "u_primitiveBBoxMin");
965         const glw::GLint bboxMaxPos = gl.getUniformLocation(m_program->getProgram(), "u_primitiveBBoxMax");
966 
967         gl.uniform4f(bboxMinPos, config.bbox.min.x(), config.bbox.min.y(), config.bbox.min.z(), config.bbox.min.w());
968         gl.uniform4f(bboxMaxPos, config.bbox.max.x(), config.bbox.max.y(), config.bbox.max.z(), config.bbox.max.w());
969     }
970 
971     gl.uniform2i(gl.getUniformLocation(m_program->getProgram(), "u_viewportPos"), config.viewportPos.x(),
972                  config.viewportPos.y());
973     gl.uniform2i(gl.getUniformLocation(m_program->getProgram(), "u_viewportSize"), config.viewportSize.x(),
974                  config.viewportSize.y());
975 
976     GLU_EXPECT_NO_ERROR(gl.getError(), "setup");
977 }
978 
genShaderFunction(ShaderFunction func) const979 const char *BBoxRenderCase::genShaderFunction(ShaderFunction func) const
980 {
981     switch (func)
982     {
983     case SHADER_FUNC_MIRROR_X:
984         return "vec4 mirrorX(in highp vec4 p)\n"
985                "{\n"
986                "    highp vec2 patternOffset = u_posScale.xy;\n"
987                "    highp vec2 patternScale = u_posScale.zw;\n"
988                "    highp vec2 patternCenter = patternOffset + patternScale * 0.5;\n"
989                "    return vec4(2.0 * patternCenter.x - p.x, p.y, p.z, p.w);\n"
990                "}\n";
991 
992     case SHADER_FUNC_MIRROR_Y:
993         return "vec4 mirrorY(in highp vec4 p)\n"
994                "{\n"
995                "    highp vec2 patternOffset = u_posScale.xy;\n"
996                "    highp vec2 patternScale = u_posScale.zw;\n"
997                "    highp vec2 patternCenter = patternOffset + patternScale * 0.5;\n"
998                "    return vec4(p.x, 2.0 * patternCenter.y - p.y, p.z, p.w);\n"
999                "}\n";
1000 
1001     case SHADER_FUNC_INSIDE_BBOX:
1002         return "uniform highp ivec2 u_viewportPos;\n"
1003                "uniform highp ivec2 u_viewportSize;\n"
1004                "flat in highp float v_bbox_expansionSize;\n"
1005                "flat in highp vec3 v_bbox_clipMin;\n"
1006                "flat in highp vec3 v_bbox_clipMax;\n"
1007                "\n"
1008                "bool fragmentInsideTheBBox(in highp float depth)\n"
1009                "{\n"
1010                "    highp vec4 wc = vec4(floor((v_bbox_clipMin.x * 0.5 + 0.5) * float(u_viewportSize.x) - "
1011                "v_bbox_expansionSize/2.0),\n"
1012                "                         floor((v_bbox_clipMin.y * 0.5 + 0.5) * float(u_viewportSize.y) - "
1013                "v_bbox_expansionSize/2.0),\n"
1014                "                         ceil((v_bbox_clipMax.x * 0.5 + 0.5) * float(u_viewportSize.x) + "
1015                "v_bbox_expansionSize/2.0),\n"
1016                "                         ceil((v_bbox_clipMax.y * 0.5 + 0.5) * float(u_viewportSize.y) + "
1017                "v_bbox_expansionSize/2.0));\n"
1018                "    if (gl_FragCoord.x < float(u_viewportPos.x) + wc.x || gl_FragCoord.x > float(u_viewportPos.x) + "
1019                "wc.z ||\n"
1020                "        gl_FragCoord.y < float(u_viewportPos.y) + wc.y || gl_FragCoord.y > float(u_viewportPos.y) + "
1021                "wc.w)\n"
1022                "        return false;\n"
1023                "    const highp float dEpsilon = 0.001;\n"
1024                "    if (depth*2.0-1.0 < v_bbox_clipMin.z - dEpsilon || depth*2.0-1.0 > v_bbox_clipMax.z + dEpsilon)\n"
1025                "        return false;\n"
1026                "    return true;\n"
1027                "}\n";
1028     default:
1029         DE_ASSERT(false);
1030         return "";
1031     }
1032 }
1033 
1034 class GridRenderCase : public BBoxRenderCase
1035 {
1036 public:
1037     GridRenderCase(Context &context, const char *name, const char *description, uint32_t flags);
1038     ~GridRenderCase(void);
1039 
1040 private:
1041     void init(void);
1042 
1043     std::string genVertexSource(void) const;
1044     std::string genFragmentSource(void) const;
1045     std::string genTessellationControlSource(void) const;
1046     std::string genTessellationEvaluationSource(void) const;
1047     std::string genGeometrySource(void) const;
1048 
1049     IterationConfig generateConfig(int iteration, const tcu::IVec2 &renderTargetSize) const;
1050     void getAttributeData(std::vector<tcu::Vec4> &data) const;
1051     void renderTestPattern(const IterationConfig &config);
1052     void verifyRenderResult(const IterationConfig &config);
1053 
1054     const int m_gridSize;
1055 };
1056 
GridRenderCase(Context & context,const char * name,const char * description,uint32_t flags)1057 GridRenderCase::GridRenderCase(Context &context, const char *name, const char *description, uint32_t flags)
1058     : BBoxRenderCase(context, name, description, 12, flags)
1059     , m_gridSize(24)
1060 {
1061 }
1062 
~GridRenderCase(void)1063 GridRenderCase::~GridRenderCase(void)
1064 {
1065 }
1066 
init(void)1067 void GridRenderCase::init(void)
1068 {
1069     m_testCtx.getLog()
1070         << tcu::TestLog::Message << "Rendering yellow-green grid to "
1071         << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n"
1072         << "Grid cells are in random order, varying grid size and location for each iteration.\n"
1073         << "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated blue channel."
1074         << tcu::TestLog::EndMessage;
1075 
1076     BBoxRenderCase::init();
1077 }
1078 
genVertexSource(void) const1079 std::string GridRenderCase::genVertexSource(void) const
1080 {
1081     std::ostringstream buf;
1082 
1083     buf << "${GLSL_VERSION_DECL}\n"
1084            "in highp vec4 a_position;\n"
1085            "in highp vec4 a_color;\n"
1086            "out highp vec4 vtx_color;\n"
1087            "uniform highp vec4 u_posScale;\n"
1088            "\n";
1089     if (!m_hasTessellationStage)
1090     {
1091         DE_ASSERT(m_useGlobalState);
1092         buf << "uniform highp vec4 u_primitiveBBoxMin;\n"
1093                "uniform highp vec4 u_primitiveBBoxMax;\n"
1094                "\n"
1095                "flat out highp float v_"
1096             << (m_hasGeometryStage ? "geo_" : "")
1097             << "bbox_expansionSize;\n"
1098                "flat out highp vec3 v_"
1099             << (m_hasGeometryStage ? "geo_" : "")
1100             << "bbox_clipMin;\n"
1101                "flat out highp vec3 v_"
1102             << (m_hasGeometryStage ? "geo_" : "")
1103             << "bbox_clipMax;\n"
1104                "\n";
1105     }
1106 
1107     buf << "void main()\n"
1108            "{\n"
1109            "    highp vec2 patternOffset = u_posScale.xy;\n"
1110            "    highp vec2 patternScale = u_posScale.zw;\n"
1111            "    gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n"
1112            "    vtx_color = a_color;\n";
1113 
1114     if (!m_hasTessellationStage)
1115     {
1116         DE_ASSERT(m_useGlobalState);
1117         buf << "\n"
1118                "    v_"
1119             << (m_hasGeometryStage ? "geo_" : "")
1120             << "bbox_expansionSize = 0.0;\n"
1121                "    v_"
1122             << (m_hasGeometryStage ? "geo_" : "")
1123             << "bbox_clipMin =\n"
1124                "        min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / "
1125                "u_primitiveBBoxMin.w,\n"
1126                "            vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / "
1127                "u_primitiveBBoxMax.w);\n"
1128                "    v_"
1129             << (m_hasGeometryStage ? "geo_" : "")
1130             << "bbox_clipMax =\n"
1131                "        min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / "
1132                "u_primitiveBBoxMin.w,\n"
1133                "            vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / "
1134                "u_primitiveBBoxMax.w);\n";
1135     }
1136 
1137     buf << "}\n";
1138 
1139     return buf.str();
1140 }
1141 
genFragmentSource(void) const1142 std::string GridRenderCase::genFragmentSource(void) const
1143 {
1144     const char *const colorInputName = (m_hasGeometryStage)     ? ("geo_color") :
1145                                        (m_hasTessellationStage) ? ("tess_color") :
1146                                                                   ("vtx_color");
1147     std::ostringstream buf;
1148 
1149     buf << "${GLSL_VERSION_DECL}\n"
1150            "in mediump vec4 "
1151         << colorInputName
1152         << ";\n"
1153            "layout(location = 0) out mediump vec4 o_color;\n"
1154         << genShaderFunction(SHADER_FUNC_INSIDE_BBOX)
1155         << "\n"
1156            "void main()\n"
1157            "{\n"
1158            "    mediump vec4 baseColor = "
1159         << colorInputName
1160         << ";\n"
1161            "    mediump float blueChannel;\n"
1162            "    if (fragmentInsideTheBBox(gl_FragCoord.z))\n"
1163            "        blueChannel = 0.0;\n"
1164            "    else\n"
1165            "        blueChannel = 1.0;\n"
1166            "    o_color = vec4(baseColor.r, baseColor.g, blueChannel, baseColor.a);\n"
1167            "}\n";
1168 
1169     return buf.str();
1170 }
1171 
genTessellationControlSource(void) const1172 std::string GridRenderCase::genTessellationControlSource(void) const
1173 {
1174     std::ostringstream buf;
1175 
1176     buf << "${GLSL_VERSION_DECL}\n"
1177            "${ARB_ES32_COMPATIBILITY_REQUIRE}\n"
1178            "${TESSELLATION_SHADER_REQUIRE}\n"
1179            "${PRIMITIVE_BOUNDING_BOX_REQUIRE}\n"
1180            "layout(vertices=3) out;\n"
1181            "\n"
1182            "in highp vec4 vtx_color[];\n"
1183            "out highp vec4 tess_ctrl_color[];\n"
1184            "uniform highp float u_tessellationLevel;\n"
1185            "uniform highp vec4 u_posScale;\n";
1186 
1187     if (!m_calcPerPrimitiveBBox)
1188     {
1189         buf << "uniform highp vec4 u_primitiveBBoxMin;\n"
1190                "uniform highp vec4 u_primitiveBBoxMax;\n";
1191     }
1192 
1193     buf << "patch out highp float vp_bbox_expansionSize;\n"
1194            "patch out highp vec3 vp_bbox_clipMin;\n"
1195            "patch out highp vec3 vp_bbox_clipMax;\n";
1196 
1197     if (m_calcPerPrimitiveBBox)
1198     {
1199         buf << "\n";
1200         if (m_hasGeometryStage)
1201             buf << genShaderFunction(SHADER_FUNC_MIRROR_X);
1202         buf << genShaderFunction(SHADER_FUNC_MIRROR_Y);
1203 
1204         buf << "vec4 transformVec(in highp vec4 p)\n"
1205                "{\n"
1206                "    return "
1207             << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)"))
1208             << ";\n"
1209                "}\n";
1210     }
1211 
1212     buf << "\n"
1213            "void main()\n"
1214            "{\n"
1215            "    // convert to nonsensical coordinates, just in case\n"
1216            "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n"
1217            "    tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n"
1218            "\n"
1219            "    gl_TessLevelOuter[0] = u_tessellationLevel;\n"
1220            "    gl_TessLevelOuter[1] = u_tessellationLevel;\n"
1221            "    gl_TessLevelOuter[2] = u_tessellationLevel;\n"
1222            "    gl_TessLevelInner[0] = u_tessellationLevel;\n";
1223 
1224     if (m_calcPerPrimitiveBBox)
1225     {
1226         buf << "\n"
1227                "    highp vec4 bboxMin = min(min(transformVec(gl_in[0].gl_Position),\n"
1228                "                                 transformVec(gl_in[1].gl_Position)),\n"
1229                "                             transformVec(gl_in[2].gl_Position));\n"
1230                "    highp vec4 bboxMax = max(max(transformVec(gl_in[0].gl_Position),\n"
1231                "                                 transformVec(gl_in[1].gl_Position)),\n"
1232                "                             transformVec(gl_in[2].gl_Position));\n";
1233     }
1234     else
1235     {
1236         buf << "\n"
1237                "    highp vec4 bboxMin = u_primitiveBBoxMin;\n"
1238                "    highp vec4 bboxMax = u_primitiveBBoxMax;\n";
1239     }
1240 
1241     if (!m_useGlobalState)
1242         buf << "\n"
1243                "    ${PRIM_GL_BOUNDING_BOX}[0] = bboxMin;\n"
1244                "    ${PRIM_GL_BOUNDING_BOX}[1] = bboxMax;\n";
1245 
1246     buf << "    vp_bbox_expansionSize = 0.0;\n"
1247            "    vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n"
1248            "                          vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n"
1249            "    vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n"
1250            "                          vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n"
1251            "}\n";
1252 
1253     return buf.str();
1254 }
1255 
genTessellationEvaluationSource(void) const1256 std::string GridRenderCase::genTessellationEvaluationSource(void) const
1257 {
1258     std::ostringstream buf;
1259 
1260     buf << "${GLSL_VERSION_DECL}\n"
1261            "${TESSELLATION_SHADER_REQUIRE}\n"
1262            "${GPU_SHADER5_REQUIRE}\n"
1263            "layout(triangles) in;\n"
1264            "\n"
1265            "in highp vec4 tess_ctrl_color[];\n"
1266            "out highp vec4 tess_color;\n"
1267            "uniform highp vec4 u_posScale;\n"
1268            "patch in highp float vp_bbox_expansionSize;\n"
1269            "patch in highp vec3 vp_bbox_clipMin;\n"
1270            "patch in highp vec3 vp_bbox_clipMax;\n"
1271            "flat out highp float v_"
1272         << (m_hasGeometryStage ? "geo_" : "")
1273         << "bbox_expansionSize;\n"
1274            "flat out highp vec3 v_"
1275         << (m_hasGeometryStage ? "geo_" : "")
1276         << "bbox_clipMin;\n"
1277            "flat out highp vec3 v_"
1278         << (m_hasGeometryStage ? "geo_" : "")
1279         << "bbox_clipMax;\n"
1280            "\n"
1281            "precise gl_Position;\n"
1282            "\n"
1283         << genShaderFunction(SHADER_FUNC_MIRROR_Y)
1284         << "void main()\n"
1285            "{\n"
1286            "    // non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n"
1287            "    gl_Position = mirrorY(gl_TessCoord.x * gl_in[0].gl_Position.zwyx +\n"
1288            "                          gl_TessCoord.y * gl_in[1].gl_Position.zwyx +\n"
1289            "                          gl_TessCoord.z * gl_in[2].gl_Position.zwyx);\n"
1290            "    tess_color = tess_ctrl_color[0];\n"
1291            "    v_"
1292         << (m_hasGeometryStage ? "geo_" : "")
1293         << "bbox_expansionSize = vp_bbox_expansionSize;\n"
1294            "    v_"
1295         << (m_hasGeometryStage ? "geo_" : "")
1296         << "bbox_clipMin = vp_bbox_clipMin;\n"
1297            "    v_"
1298         << (m_hasGeometryStage ? "geo_" : "")
1299         << "bbox_clipMax = vp_bbox_clipMax;\n"
1300            "}\n";
1301 
1302     return buf.str();
1303 }
1304 
genGeometrySource(void) const1305 std::string GridRenderCase::genGeometrySource(void) const
1306 {
1307     const char *const colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
1308     std::ostringstream buf;
1309 
1310     buf << "${GLSL_VERSION_DECL}\n"
1311            "${GEOMETRY_SHADER_REQUIRE}\n"
1312            "layout(triangles) in;\n"
1313            "layout(max_vertices=9, triangle_strip) out;\n"
1314            "\n"
1315            "in highp vec4 "
1316         << colorInputName
1317         << "[3];\n"
1318            "out highp vec4 geo_color;\n"
1319            "uniform highp vec4 u_posScale;\n"
1320            "\n"
1321            "flat in highp float v_geo_bbox_expansionSize[3];\n"
1322            "flat in highp vec3 v_geo_bbox_clipMin[3];\n"
1323            "flat in highp vec3 v_geo_bbox_clipMax[3];\n"
1324            "flat out highp vec3 v_bbox_clipMin;\n"
1325            "flat out highp vec3 v_bbox_clipMax;\n"
1326            "flat out highp float v_bbox_expansionSize;\n"
1327         << genShaderFunction(SHADER_FUNC_MIRROR_X)
1328         << "\n"
1329            "void setVisualizationVaryings()\n"
1330            "{\n"
1331            "    v_bbox_expansionSize = v_geo_bbox_expansionSize[0];\n"
1332            "    v_bbox_clipMin = v_geo_bbox_clipMin[0];\n"
1333            "    v_bbox_clipMax = v_geo_bbox_clipMax[0];\n"
1334            "}\n"
1335            "void main()\n"
1336            "{\n"
1337            "    // Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n"
1338            "    highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n"
1339            "    highp vec4 p1 = mirrorX(gl_in[1].gl_Position);\n"
1340            "    highp vec4 p2 = mirrorX(gl_in[2].gl_Position);\n"
1341            "    highp vec4 pCentroid = vec4((p0.xyz + p1.xyz + p2.xyz) / 3.0, 1.0);\n"
1342            "    highp vec4 triangleColor = "
1343         << colorInputName
1344         << "[0];\n"
1345            "\n"
1346            "    gl_Position = p0; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1347            "    gl_Position = p1; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1348            "    gl_Position = pCentroid; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1349            "    EndPrimitive();\n"
1350            "\n"
1351            "    gl_Position = p1; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1352            "    gl_Position = p2; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1353            "    gl_Position = pCentroid; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1354            "    EndPrimitive();\n"
1355            "\n"
1356            "    gl_Position = p2; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1357            "    gl_Position = p0; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1358            "    gl_Position = pCentroid; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n"
1359            "    EndPrimitive();\n"
1360            "}\n";
1361 
1362     return buf.str();
1363 }
1364 
generateConfig(int iteration,const tcu::IVec2 & renderTargetSize) const1365 GridRenderCase::IterationConfig GridRenderCase::generateConfig(int iteration, const tcu::IVec2 &renderTargetSize) const
1366 {
1367     return generateRandomConfig(0xDEDEDEu * (uint32_t)iteration, renderTargetSize);
1368 }
1369 
getAttributeData(std::vector<tcu::Vec4> & data) const1370 void GridRenderCase::getAttributeData(std::vector<tcu::Vec4> &data) const
1371 {
1372     const tcu::Vec4 green(0.0f, 1.0f, 0.0f, 1.0f);
1373     const tcu::Vec4 yellow(1.0f, 1.0f, 0.0f, 1.0f);
1374     std::vector<int> cellOrder(m_gridSize * m_gridSize);
1375     de::Random rnd(0xDE56789);
1376 
1377     // generate grid with cells in random order
1378     for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
1379         cellOrder[ndx] = ndx;
1380     rnd.shuffle(cellOrder.begin(), cellOrder.end());
1381 
1382     data.resize(m_gridSize * m_gridSize * 6 * 2);
1383     for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
1384     {
1385         const int cellNdx          = cellOrder[ndx];
1386         const int cellX            = cellNdx % m_gridSize;
1387         const int cellY            = cellNdx / m_gridSize;
1388         const tcu::Vec4 &cellColor = ((cellX + cellY) % 2 == 0) ? (green) : (yellow);
1389 
1390         data[(ndx * 6 + 0) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] =
1391             tcu::Vec4(float(cellX + 0) / float(m_gridSize), float(cellY + 0) / float(m_gridSize), 0.0f, 1.0f);
1392         data[(ndx * 6 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1393         data[(ndx * 6 + 1) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] =
1394             tcu::Vec4(float(cellX + 1) / float(m_gridSize), float(cellY + 1) / float(m_gridSize), 0.0f, 1.0f);
1395         data[(ndx * 6 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1396         data[(ndx * 6 + 2) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] =
1397             tcu::Vec4(float(cellX + 0) / float(m_gridSize), float(cellY + 1) / float(m_gridSize), 0.0f, 1.0f);
1398         data[(ndx * 6 + 2) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1399         data[(ndx * 6 + 3) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] =
1400             tcu::Vec4(float(cellX + 0) / float(m_gridSize), float(cellY + 0) / float(m_gridSize), 0.0f, 1.0f);
1401         data[(ndx * 6 + 3) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1402         data[(ndx * 6 + 4) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] =
1403             tcu::Vec4(float(cellX + 1) / float(m_gridSize), float(cellY + 0) / float(m_gridSize), 0.0f, 1.0f);
1404         data[(ndx * 6 + 4) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1405         data[(ndx * 6 + 5) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] =
1406             tcu::Vec4(float(cellX + 1) / float(m_gridSize), float(cellY + 1) / float(m_gridSize), 0.0f, 1.0f);
1407         data[(ndx * 6 + 5) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor;
1408     }
1409 }
1410 
renderTestPattern(const IterationConfig & config)1411 void GridRenderCase::renderTestPattern(const IterationConfig &config)
1412 {
1413     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
1414 
1415     setupRender(config);
1416 
1417     if (m_hasTessellationStage)
1418     {
1419         const glw::GLint tessLevelPos = gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel");
1420         const glw::GLfloat tessLevel  = 2.8f; // will be rounded up
1421 
1422         TCU_CHECK(tessLevelPos != -1);
1423 
1424         m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel
1425                            << tcu::TestLog::EndMessage;
1426 
1427         gl.uniform1f(tessLevelPos, tessLevel);
1428         gl.patchParameteri(GL_PATCH_VERTICES, 3);
1429         GLU_EXPECT_NO_ERROR(gl.getError(), "patch param");
1430     }
1431 
1432     m_testCtx.getLog() << tcu::TestLog::Message << "Rendering grid." << tcu::TestLog::EndMessage;
1433 
1434     gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_TRIANGLES), 0, m_gridSize * m_gridSize * 6);
1435     GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
1436 }
1437 
verifyRenderResult(const IterationConfig & config)1438 void GridRenderCase::verifyRenderResult(const IterationConfig &config)
1439 {
1440     const glw::Functions &gl          = m_context.getRenderContext().getFunctions();
1441     const ProjectedBBox projectedBBox = projectBoundingBox(config.bbox);
1442     const tcu::IVec4 viewportBBoxArea = getViewportBoundingBoxArea(projectedBBox, config.viewportSize);
1443     const tcu::IVec4 viewportGridOuterArea =
1444         getViewportPatternArea(config.patternPos, config.patternSize, config.viewportSize, ROUND_OUTWARDS);
1445     const tcu::IVec4 viewportGridInnerArea =
1446         getViewportPatternArea(config.patternPos, config.patternSize, config.viewportSize, ROUND_INWARDS);
1447     tcu::Surface viewportSurface(config.viewportSize.x(), config.viewportSize.y());
1448     tcu::Surface errorMask(config.viewportSize.x(), config.viewportSize.y());
1449     bool anyError = false;
1450 
1451     if (!m_calcPerPrimitiveBBox)
1452         m_testCtx.getLog() << tcu::TestLog::Message << "Projected bounding box: (clip space)\n"
1453                            << "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n"
1454                            << "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n"
1455                            << "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n"
1456                            << "In viewport coordinates:\n"
1457                            << "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n"
1458                            << "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n"
1459                            << "Verifying render results within the bounding box.\n"
1460                            << tcu::TestLog::EndMessage;
1461     else
1462         m_testCtx.getLog() << tcu::TestLog::Message << "Verifying render result." << tcu::TestLog::EndMessage;
1463 
1464     if (m_fbo)
1465         gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo);
1466     glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(),
1467                     viewportSurface.getAccess());
1468 
1469     tcu::clear(errorMask.getAccess(), tcu::IVec4(0, 0, 0, 255));
1470 
1471     for (int y = de::max(viewportBBoxArea.y(), 0); y < de::min(viewportBBoxArea.w(), config.viewportSize.y()); ++y)
1472         for (int x = de::max(viewportBBoxArea.x(), 0); x < de::min(viewportBBoxArea.z(), config.viewportSize.x()); ++x)
1473         {
1474             const tcu::RGBA pixel  = viewportSurface.getPixel(x, y);
1475             const bool outsideGrid = x < viewportGridOuterArea.x() || y < viewportGridOuterArea.y() ||
1476                                      x > viewportGridOuterArea.z() || y > viewportGridOuterArea.w();
1477             const bool insideGrid = x > viewportGridInnerArea.x() && y > viewportGridInnerArea.y() &&
1478                                     x < viewportGridInnerArea.z() && y < viewportGridInnerArea.w();
1479 
1480             bool error = false;
1481 
1482             if (outsideGrid)
1483             {
1484                 // expect black
1485                 if (pixel.getRed() != 0 || pixel.getGreen() != 0 || pixel.getBlue() != 0)
1486                     error = true;
1487             }
1488 
1489             else if (insideGrid)
1490             {
1491                 // expect green, yellow or a combination of these
1492                 if (pixel.getGreen() != 255 || pixel.getBlue() != 0)
1493                     error = true;
1494             }
1495             else
1496             {
1497                 // boundary, allow anything
1498             }
1499 
1500             if (error)
1501             {
1502                 errorMask.setPixel(x, y, tcu::RGBA::red());
1503                 anyError = true;
1504             }
1505         }
1506 
1507     if (anyError)
1508     {
1509         m_testCtx.getLog() << tcu::TestLog::Message << "Image verification failed." << tcu::TestLog::EndMessage
1510                            << tcu::TestLog::ImageSet("Images", "Image verification")
1511                            << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
1512                            << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
1513                            << tcu::TestLog::EndImageSet;
1514 
1515         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
1516     }
1517     else
1518     {
1519         m_testCtx.getLog() << tcu::TestLog::Message << "Result image ok." << tcu::TestLog::EndMessage
1520                            << tcu::TestLog::ImageSet("Images", "Image verification")
1521                            << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
1522                            << tcu::TestLog::EndImageSet;
1523     }
1524 }
1525 
1526 class LineRenderCase : public BBoxRenderCase
1527 {
1528 public:
1529     enum
1530     {
1531         LINEFLAG_WIDE = 1u << FLAGBIT_USER_BIT, //!< use wide lines
1532     };
1533 
1534     LineRenderCase(Context &context, const char *name, const char *description, uint32_t flags);
1535     ~LineRenderCase(void);
1536 
1537 private:
1538     enum
1539     {
1540         GREEN_COMPONENT_NDX = 1,
1541         BLUE_COMPONENT_NDX  = 2,
1542 
1543         SCAN_ROW_COMPONENT_NDX = GREEN_COMPONENT_NDX, // \note: scans are orthogonal to the line
1544         SCAN_COL_COMPONENT_NDX = BLUE_COMPONENT_NDX,
1545     };
1546 
1547     enum QueryDirection
1548     {
1549         DIRECTION_HORIZONTAL = 0,
1550         DIRECTION_VERTICAL,
1551     };
1552 
1553     enum ScanResult
1554     {
1555         SCANRESULT_NUM_LINES_OK_BIT    = (1 << 0),
1556         SCANRESULT_LINE_WIDTH_OK_BIT   = (1 << 1),
1557         SCANRESULT_LINE_WIDTH_WARN_BIT = (1 << 2),
1558         SCANRESULT_LINE_WIDTH_ERR_BIT  = (1 << 3),
1559         SCANRESULT_LINE_CONT_OK_BIT    = (1 << 4),
1560         SCANRESULT_LINE_CONT_ERR_BIT   = (1 << 5),
1561         SCANRESULT_LINE_CONT_WARN_BIT  = (1 << 6),
1562     };
1563 
1564     void init(void);
1565 
1566     std::string genVertexSource(void) const;
1567     std::string genFragmentSource(void) const;
1568     std::string genTessellationControlSource(void) const;
1569     std::string genTessellationEvaluationSource(void) const;
1570     std::string genGeometrySource(void) const;
1571 
1572     IterationConfig generateConfig(int iteration, const tcu::IVec2 &renderTargetSize) const;
1573     void getAttributeData(std::vector<tcu::Vec4> &data) const;
1574     void renderTestPattern(const IterationConfig &config);
1575     void verifyRenderResult(const IterationConfig &config);
1576 
1577     tcu::IVec2 getNumberOfLinesRange(int queryAreaBegin, int queryAreaEnd, float patternStart, float patternSize,
1578                                      int viewportArea, QueryDirection queryDir) const;
1579     uint8_t scanRow(const tcu::ConstPixelBufferAccess &access, int row, int rowBegin, int rowEnd, int rowViewportBegin,
1580                     int rowViewportEnd, const tcu::IVec2 &numLines, int &floodCounter) const;
1581     uint8_t scanColumn(const tcu::ConstPixelBufferAccess &access, int column, int columnBegin, int columnEnd,
1582                        int columnViewportBegin, int columnViewportEnd, const tcu::IVec2 &numLines,
1583                        int &floodCounter) const;
1584     bool checkAreaNumLines(const tcu::ConstPixelBufferAccess &access, const tcu::IVec4 &area, int &floodCounter,
1585                            int componentNdx, const tcu::IVec2 &numLines) const;
1586     uint8_t checkLineContinuity(const tcu::ConstPixelBufferAccess &access, const tcu::IVec2 &begin,
1587                                 const tcu::IVec2 &end, int componentNdx, int &messageLimitCounter) const;
1588     tcu::IVec2 getNumMinimaMaxima(const tcu::ConstPixelBufferAccess &access, int componentNdx) const;
1589     uint8_t checkLineWidths(const tcu::ConstPixelBufferAccess &access, const tcu::IVec2 &begin, const tcu::IVec2 &end,
1590                             int componentNdx, int &floodCounter) const;
1591     void printLineWidthError(const tcu::IVec2 &pos, int detectedLineWidth, const tcu::IVec2 &lineWidthRange,
1592                              bool isHorizontal, int &floodCounter) const;
1593 
1594     const int m_patternSide;
1595     const bool m_isWideLineCase;
1596     const int m_wideLineLineWidth;
1597 };
1598 
LineRenderCase(Context & context,const char * name,const char * description,uint32_t flags)1599 LineRenderCase::LineRenderCase(Context &context, const char *name, const char *description, uint32_t flags)
1600     : BBoxRenderCase(context, name, description, 12, flags)
1601     , m_patternSide(12)
1602     , m_isWideLineCase((flags & LINEFLAG_WIDE) != 0)
1603     , m_wideLineLineWidth(5)
1604 {
1605 }
1606 
~LineRenderCase(void)1607 LineRenderCase::~LineRenderCase(void)
1608 {
1609 }
1610 
init(void)1611 void LineRenderCase::init(void)
1612 {
1613     m_testCtx.getLog()
1614         << tcu::TestLog::Message << "Rendering line pattern to "
1615         << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n"
1616         << "Vertical lines are green, horizontal lines blue. Using additive blending.\n"
1617         << "Line segments are in random order, varying pattern size and location for each iteration.\n"
1618         << "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated red channel."
1619         << tcu::TestLog::EndMessage;
1620 
1621     if (m_isWideLineCase)
1622     {
1623         glw::GLfloat lineWidthRange[2] = {0.0f, 0.0f};
1624         m_context.getRenderContext().getFunctions().getFloatv(GL_ALIASED_LINE_WIDTH_RANGE, lineWidthRange);
1625 
1626         if (lineWidthRange[1] < (float)m_wideLineLineWidth)
1627             throw tcu::NotSupportedError("Test requires line width " + de::toString(m_wideLineLineWidth));
1628     }
1629 
1630     BBoxRenderCase::init();
1631 }
1632 
genVertexSource(void) const1633 std::string LineRenderCase::genVertexSource(void) const
1634 {
1635     std::ostringstream buf;
1636 
1637     buf << "${GLSL_VERSION_DECL}\n"
1638            "in highp vec4 a_position;\n"
1639            "in highp vec4 a_color;\n"
1640            "out highp vec4 vtx_color;\n"
1641            "uniform highp vec4 u_posScale;\n"
1642            "uniform highp float u_lineWidth;\n"
1643            "\n";
1644     if (!m_hasTessellationStage)
1645     {
1646         DE_ASSERT(m_useGlobalState);
1647         buf << "uniform highp vec4 u_primitiveBBoxMin;\n"
1648                "uniform highp vec4 u_primitiveBBoxMax;\n"
1649                "\n"
1650                "flat out highp float v_"
1651             << (m_hasGeometryStage ? "geo_" : "")
1652             << "bbox_expansionSize;\n"
1653                "flat out highp vec3 v_"
1654             << (m_hasGeometryStage ? "geo_" : "")
1655             << "bbox_clipMin;\n"
1656                "flat out highp vec3 v_"
1657             << (m_hasGeometryStage ? "geo_" : "")
1658             << "bbox_clipMax;\n"
1659                "\n";
1660     }
1661     buf << "void main()\n"
1662            "{\n"
1663            "    highp vec2 patternOffset = u_posScale.xy;\n"
1664            "    highp vec2 patternScale = u_posScale.zw;\n"
1665            "    gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n"
1666            "    vtx_color = a_color;\n";
1667     if (!m_hasTessellationStage)
1668     {
1669         DE_ASSERT(m_useGlobalState);
1670         buf << "\n"
1671                "    v_"
1672             << (m_hasGeometryStage ? "geo_" : "")
1673             << "bbox_expansionSize = u_lineWidth;\n"
1674                "    v_"
1675             << (m_hasGeometryStage ? "geo_" : "")
1676             << "bbox_clipMin =\n"
1677                "        min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / "
1678                "u_primitiveBBoxMin.w,\n"
1679                "            vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / "
1680                "u_primitiveBBoxMax.w);\n"
1681                "    v_"
1682             << (m_hasGeometryStage ? "geo_" : "")
1683             << "bbox_clipMax =\n"
1684                "        min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / "
1685                "u_primitiveBBoxMin.w,\n"
1686                "            vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / "
1687                "u_primitiveBBoxMax.w);\n";
1688     }
1689     buf << "}\n";
1690 
1691     return buf.str();
1692 }
1693 
genFragmentSource(void) const1694 std::string LineRenderCase::genFragmentSource(void) const
1695 {
1696     const char *const colorInputName = (m_hasGeometryStage)     ? ("geo_color") :
1697                                        (m_hasTessellationStage) ? ("tess_color") :
1698                                                                   ("vtx_color");
1699     std::ostringstream buf;
1700 
1701     buf << "${GLSL_VERSION_DECL}\n"
1702            "in mediump vec4 "
1703         << colorInputName
1704         << ";\n"
1705            "layout(location = 0) out mediump vec4 o_color;\n"
1706         << genShaderFunction(SHADER_FUNC_INSIDE_BBOX)
1707         << "\n"
1708            "void main()\n"
1709            "{\n"
1710            "    mediump vec4 baseColor = "
1711         << colorInputName
1712         << ";\n"
1713            "    mediump float redChannel;\n"
1714            "    if (fragmentInsideTheBBox(gl_FragCoord.z))\n"
1715            "        redChannel = 0.0;\n"
1716            "    else\n"
1717            "        redChannel = 1.0;\n"
1718            "    o_color = vec4(redChannel, baseColor.g, baseColor.b, baseColor.a);\n"
1719            "}\n";
1720 
1721     return buf.str();
1722 }
1723 
genTessellationControlSource(void) const1724 std::string LineRenderCase::genTessellationControlSource(void) const
1725 {
1726     std::ostringstream buf;
1727 
1728     buf << "${GLSL_VERSION_DECL}\n"
1729            "${ARB_ES32_COMPATIBILITY_REQUIRE}\n"
1730            "${TESSELLATION_SHADER_REQUIRE}\n"
1731            "${PRIMITIVE_BOUNDING_BOX_REQUIRE}\n"
1732            "layout(vertices=2) out;"
1733            "\n"
1734            "in highp vec4 vtx_color[];\n"
1735            "out highp vec4 tess_ctrl_color[];\n"
1736            "uniform highp float u_tessellationLevel;\n"
1737            "uniform highp vec4 u_posScale;\n"
1738            "uniform highp float u_lineWidth;\n";
1739 
1740     if (!m_calcPerPrimitiveBBox)
1741     {
1742         buf << "uniform highp vec4 u_primitiveBBoxMin;\n"
1743                "uniform highp vec4 u_primitiveBBoxMax;\n";
1744     }
1745 
1746     buf << "patch out highp float vp_bbox_expansionSize;\n"
1747            "patch out highp vec3 vp_bbox_clipMin;\n"
1748            "patch out highp vec3 vp_bbox_clipMax;\n";
1749 
1750     if (m_calcPerPrimitiveBBox)
1751     {
1752         buf << "\n";
1753         if (m_hasGeometryStage)
1754             buf << genShaderFunction(SHADER_FUNC_MIRROR_X);
1755         buf << genShaderFunction(SHADER_FUNC_MIRROR_Y);
1756 
1757         buf << "vec4 transformVec(in highp vec4 p)\n"
1758                "{\n"
1759                "    return "
1760             << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)"))
1761             << ";\n"
1762                "}\n";
1763     }
1764 
1765     buf << "\n"
1766            "void main()\n"
1767            "{\n"
1768            "    // convert to nonsensical coordinates, just in case\n"
1769            "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n"
1770            "    tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n"
1771            "\n"
1772            "    gl_TessLevelOuter[0] = 0.8; // will be rounded up to 1\n"
1773            "    gl_TessLevelOuter[1] = u_tessellationLevel;\n";
1774 
1775     if (m_calcPerPrimitiveBBox)
1776     {
1777         buf << "\n"
1778                "    highp vec4 bboxMin = min(transformVec(gl_in[0].gl_Position),\n"
1779                "                             transformVec(gl_in[1].gl_Position));\n"
1780                "    highp vec4 bboxMax = max(transformVec(gl_in[0].gl_Position),\n"
1781                "                             transformVec(gl_in[1].gl_Position));\n";
1782     }
1783     else
1784     {
1785         buf << "\n"
1786                "    highp vec4 bboxMin = u_primitiveBBoxMin;\n"
1787                "    highp vec4 bboxMax = u_primitiveBBoxMax;\n";
1788     }
1789 
1790     if (!m_useGlobalState)
1791         buf << "\n"
1792                "    ${PRIM_GL_BOUNDING_BOX}[0] = bboxMin;\n"
1793                "    ${PRIM_GL_BOUNDING_BOX}[1] = bboxMax;\n";
1794 
1795     buf << "    vp_bbox_expansionSize = u_lineWidth;\n"
1796            "    vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n"
1797            "                          vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n"
1798            "    vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n"
1799            "                          vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n"
1800            "}\n";
1801 
1802     return buf.str();
1803 }
1804 
genTessellationEvaluationSource(void) const1805 std::string LineRenderCase::genTessellationEvaluationSource(void) const
1806 {
1807     std::ostringstream buf;
1808 
1809     buf << "${GLSL_VERSION_DECL}\n"
1810            "${TESSELLATION_SHADER_REQUIRE}\n"
1811            "layout(isolines) in;"
1812            "\n"
1813            "in highp vec4 tess_ctrl_color[];\n"
1814            "out highp vec4 tess_color;\n"
1815            "uniform highp vec4 u_posScale;\n"
1816            "\n"
1817            "patch in highp float vp_bbox_expansionSize;\n"
1818            "patch in highp vec3 vp_bbox_clipMin;\n"
1819            "patch in highp vec3 vp_bbox_clipMax;\n"
1820            "flat out highp float v_"
1821         << (m_hasGeometryStage ? "geo_" : "")
1822         << "bbox_expansionSize;\n"
1823            "flat out highp vec3 v_"
1824         << (m_hasGeometryStage ? "geo_" : "")
1825         << "bbox_clipMin;\n"
1826            "flat out highp vec3 v_"
1827         << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n"
1828         << genShaderFunction(SHADER_FUNC_MIRROR_Y)
1829         << "void main()\n"
1830            "{\n"
1831            "    // non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n"
1832            "    gl_Position = mirrorY(mix(gl_in[0].gl_Position.zwyx, gl_in[1].gl_Position.zwyx, gl_TessCoord.x));\n"
1833            "    tess_color = tess_ctrl_color[0];\n"
1834            "    v_"
1835         << (m_hasGeometryStage ? "geo_" : "")
1836         << "bbox_expansionSize = vp_bbox_expansionSize;\n"
1837            "    v_"
1838         << (m_hasGeometryStage ? "geo_" : "")
1839         << "bbox_clipMin = vp_bbox_clipMin;\n"
1840            "    v_"
1841         << (m_hasGeometryStage ? "geo_" : "")
1842         << "bbox_clipMax = vp_bbox_clipMax;\n"
1843            "}\n";
1844 
1845     return buf.str();
1846 }
1847 
genGeometrySource(void) const1848 std::string LineRenderCase::genGeometrySource(void) const
1849 {
1850     const char *const colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
1851     std::ostringstream buf;
1852 
1853     buf << "${GLSL_VERSION_DECL}\n"
1854            "${GEOMETRY_SHADER_REQUIRE}\n"
1855            "layout(lines) in;\n"
1856            "layout(max_vertices=5, line_strip) out;\n"
1857            "\n"
1858            "in highp vec4 "
1859         << colorInputName
1860         << "[2];\n"
1861            "out highp vec4 geo_color;\n"
1862            "uniform highp vec4 u_posScale;\n"
1863            "\n"
1864            "\n"
1865            "flat in highp float v_geo_bbox_expansionSize[2];\n"
1866            "flat in highp vec3 v_geo_bbox_clipMin[2];\n"
1867            "flat in highp vec3 v_geo_bbox_clipMax[2];\n"
1868            "flat out highp vec3 v_bbox_clipMin;\n"
1869            "flat out highp vec3 v_bbox_clipMax;\n"
1870            "flat out highp float v_bbox_expansionSize;\n"
1871         << genShaderFunction(SHADER_FUNC_MIRROR_X)
1872         << "\n"
1873            "void setVisualizationVaryings()\n"
1874            "{\n"
1875            "    v_bbox_expansionSize = v_geo_bbox_expansionSize[0];\n"
1876            "    v_bbox_clipMin = v_geo_bbox_clipMin[0];\n"
1877            "    v_bbox_clipMax = v_geo_bbox_clipMax[0];\n"
1878            "}\n"
1879            "void main()\n"
1880            "{\n"
1881            "    // Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n"
1882            "    highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n"
1883            "    highp vec4 p1 = mirrorX(gl_in[1].gl_Position);\n"
1884            "    highp vec4 lineColor = "
1885         << colorInputName
1886         << "[0];\n"
1887            "\n"
1888            "    // output two separate primitives, just in case\n"
1889            "    gl_Position = mix(p0, p1, 0.00); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1890            "    gl_Position = mix(p0, p1, 0.33); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1891            "    EndPrimitive();\n"
1892            "\n"
1893            "    gl_Position = mix(p0, p1, 0.33); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1894            "    gl_Position = mix(p0, p1, 0.67); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1895            "    gl_Position = mix(p0, p1, 1.00); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n"
1896            "    EndPrimitive();\n"
1897            "}\n";
1898 
1899     return buf.str();
1900 }
1901 
generateConfig(int iteration,const tcu::IVec2 & renderTargetSize) const1902 LineRenderCase::IterationConfig LineRenderCase::generateConfig(int iteration, const tcu::IVec2 &renderTargetSize) const
1903 {
1904     const int numMaxAttempts = 128;
1905 
1906     // Avoid too narrow viewports, lines could merge together. Require viewport is at least 2.5x the size of the line bodies.
1907     for (int attemptNdx = 0; attemptNdx < numMaxAttempts; ++attemptNdx)
1908     {
1909         const IterationConfig &config =
1910             generateRandomConfig((0xDEDEDEu * (uint32_t)iteration) ^ (0xABAB13 * attemptNdx), renderTargetSize);
1911 
1912         if ((float)config.viewportSize.x() * (config.patternSize.x() * 0.5f) >
1913                 2.5f * (float)m_patternSide * (float)m_wideLineLineWidth &&
1914             (float)config.viewportSize.y() * (config.patternSize.y() * 0.5f) >
1915                 2.5f * (float)m_patternSide * (float)m_wideLineLineWidth)
1916         {
1917             return config;
1918         }
1919     }
1920 
1921     DE_ASSERT(false);
1922     return IterationConfig();
1923 }
1924 
getAttributeData(std::vector<tcu::Vec4> & data) const1925 void LineRenderCase::getAttributeData(std::vector<tcu::Vec4> &data) const
1926 {
1927     const tcu::Vec4 green(0.0f, 1.0f, 0.0f, 1.0f);
1928     const tcu::Vec4 blue(0.0f, 0.0f, 1.0f, 1.0f);
1929     std::vector<int> cellOrder(m_patternSide * m_patternSide * 2);
1930     de::Random rnd(0xDE12345);
1931 
1932     // generate crosshatch pattern with segments in random order
1933     for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
1934         cellOrder[ndx] = ndx;
1935     rnd.shuffle(cellOrder.begin(), cellOrder.end());
1936 
1937     data.resize(cellOrder.size() * 4);
1938     for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
1939     {
1940         const int segmentID  = cellOrder[ndx];
1941         const int direction  = segmentID & 0x01;
1942         const int majorCoord = (segmentID >> 1) / m_patternSide;
1943         const int minorCoord = (segmentID >> 1) % m_patternSide;
1944 
1945         if (direction)
1946         {
1947             data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(
1948                 float(minorCoord) / float(m_patternSide), float(majorCoord) / float(m_patternSide), 0.0f, 1.0f);
1949             data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green;
1950             data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(
1951                 float(minorCoord) / float(m_patternSide), float(majorCoord + 1) / float(m_patternSide), 0.0f, 1.0f);
1952             data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green;
1953         }
1954         else
1955         {
1956             data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(
1957                 float(majorCoord) / float(m_patternSide), float(minorCoord) / float(m_patternSide), 0.0f, 1.0f);
1958             data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue;
1959             data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(
1960                 float(majorCoord + 1) / float(m_patternSide), float(minorCoord) / float(m_patternSide), 0.0f, 1.0f);
1961             data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue;
1962         }
1963     }
1964 }
1965 
renderTestPattern(const IterationConfig & config)1966 void LineRenderCase::renderTestPattern(const IterationConfig &config)
1967 {
1968     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
1969 
1970     setupRender(config);
1971 
1972     if (m_hasTessellationStage)
1973     {
1974         const glw::GLint tessLevelPos = gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel");
1975         const glw::GLfloat tessLevel  = 2.8f; // will be rounded up
1976 
1977         TCU_CHECK(tessLevelPos != -1);
1978 
1979         m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel
1980                            << tcu::TestLog::EndMessage;
1981 
1982         gl.uniform1f(tessLevelPos, tessLevel);
1983         gl.patchParameteri(GL_PATCH_VERTICES, 2);
1984         GLU_EXPECT_NO_ERROR(gl.getError(), "patch param");
1985     }
1986 
1987     if (m_isWideLineCase)
1988         gl.lineWidth((float)m_wideLineLineWidth);
1989 
1990     gl.uniform1f(gl.getUniformLocation(m_program->getProgram(), "u_lineWidth"),
1991                  (m_isWideLineCase) ? ((float)m_wideLineLineWidth) : (1.0f));
1992 
1993     m_testCtx.getLog() << tcu::TestLog::Message << "Rendering pattern." << tcu::TestLog::EndMessage;
1994 
1995     gl.enable(GL_BLEND);
1996     gl.blendFunc(GL_ONE, GL_ONE);
1997     gl.blendEquation(GL_FUNC_ADD);
1998 
1999     gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_LINES), 0, m_patternSide * m_patternSide * 2 * 2);
2000     GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
2001 }
2002 
verifyRenderResult(const IterationConfig & config)2003 void LineRenderCase::verifyRenderResult(const IterationConfig &config)
2004 {
2005     const glw::Functions &gl          = m_context.getRenderContext().getFunctions();
2006     const bool isMsaa                 = m_context.getRenderTarget().getNumSamples() > 1;
2007     const ProjectedBBox projectedBBox = projectBoundingBox(config.bbox);
2008     const float lineWidth             = (m_isWideLineCase) ? ((float)m_wideLineLineWidth) : (1.0f);
2009     const tcu::IVec4 viewportBBoxArea = getViewportBoundingBoxArea(projectedBBox, config.viewportSize, lineWidth);
2010     const tcu::IVec4 viewportPatternArea =
2011         getViewportPatternArea(config.patternPos, config.patternSize, config.viewportSize, ROUND_INWARDS);
2012     const tcu::IVec2 expectedHorizontalLines =
2013         getNumberOfLinesRange(viewportBBoxArea.y(), viewportBBoxArea.w(), config.patternPos.y(), config.patternSize.y(),
2014                               config.viewportSize.y(), DIRECTION_VERTICAL);
2015     const tcu::IVec2 expectedVerticalLines =
2016         getNumberOfLinesRange(viewportBBoxArea.x(), viewportBBoxArea.z(), config.patternPos.x(), config.patternSize.x(),
2017                               config.viewportSize.x(), DIRECTION_HORIZONTAL);
2018     const tcu::IVec4 verificationArea = tcu::IVec4(de::max(viewportBBoxArea.x(), 0), de::max(viewportBBoxArea.y(), 0),
2019                                                    de::min(viewportBBoxArea.z(), config.viewportSize.x()),
2020                                                    de::min(viewportBBoxArea.w(), config.viewportSize.y()));
2021 
2022     tcu::Surface viewportSurface(config.viewportSize.x(), config.viewportSize.y());
2023     int messageLimitCounter = 8;
2024 
2025     enum ScanResultCodes
2026     {
2027         SCANRESULT_NUM_LINES_ERR   = 0,
2028         SCANRESULT_LINE_WIDTH_MSAA = 1,
2029         SCANRESULT_LINE_WIDTH_WARN = 2,
2030         SCANRESULT_LINE_WIDTH_ERR  = 3,
2031         SCANRESULT_LINE_CONT_ERR   = 4,
2032         SCANRESULT_LINE_CONT_WARN  = 5,
2033         SCANRESULT_LINE_LAST
2034     };
2035 
2036     int rowScanResult[SCANRESULT_LINE_LAST]    = {0, 0, 0, 0, 0, 0};
2037     int columnScanResult[SCANRESULT_LINE_LAST] = {0, 0, 0, 0, 0, 0};
2038     bool anyError                              = false;
2039     bool msaaRelaxationRequired                = false;
2040     bool hwIssueRelaxationRequired             = false;
2041 
2042     if (!m_calcPerPrimitiveBBox)
2043         m_testCtx.getLog() << tcu::TestLog::Message << "Projected bounding box: (clip space)\n"
2044                            << "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n"
2045                            << "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n"
2046                            << "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n"
2047                            << "In viewport coordinates:\n"
2048                            << "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n"
2049                            << "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n"
2050                            << "Verifying render results within the bounding box:\n"
2051                            << tcu::TestLog::EndMessage;
2052     else
2053         m_testCtx.getLog() << tcu::TestLog::Message << "Verifying render result:" << tcu::TestLog::EndMessage;
2054 
2055     m_testCtx.getLog() << tcu::TestLog::Message
2056                        << "\tCalculating number of horizontal and vertical lines within the bounding box, expecting:\n"
2057                        << "\t[" << expectedHorizontalLines.x() << ", " << expectedHorizontalLines.y()
2058                        << "] horizontal lines.\n"
2059                        << "\t[" << expectedVerticalLines.x() << ", " << expectedVerticalLines.y()
2060                        << "] vertical lines.\n"
2061                        << tcu::TestLog::EndMessage;
2062 
2063     if (m_fbo)
2064         gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo);
2065     glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(),
2066                     viewportSurface.getAccess());
2067 
2068     // scan rows
2069     for (int y = de::max(verificationArea.y(), viewportPatternArea.y());
2070          y < de::min(verificationArea.w(), viewportPatternArea.w()); ++y)
2071     {
2072         const uint8_t result =
2073             scanRow(viewportSurface.getAccess(), y, verificationArea.x(), verificationArea.z(),
2074                     de::max(verificationArea.x(), viewportPatternArea.x()),
2075                     de::min(verificationArea.z(), viewportPatternArea.z()), expectedVerticalLines, messageLimitCounter);
2076 
2077         if ((result & SCANRESULT_NUM_LINES_OK_BIT) == 0)
2078             rowScanResult[SCANRESULT_NUM_LINES_ERR]++;
2079         if ((result & SCANRESULT_LINE_CONT_OK_BIT) == 0)
2080         {
2081             if ((result & SCANRESULT_LINE_CONT_WARN_BIT) != 0)
2082                 rowScanResult[SCANRESULT_LINE_CONT_WARN]++;
2083             else
2084                 rowScanResult[SCANRESULT_LINE_CONT_ERR]++;
2085         }
2086         else if ((result & SCANRESULT_LINE_WIDTH_OK_BIT) == 0)
2087         {
2088             if (m_isWideLineCase && isMsaa)
2089             {
2090                 // multisampled wide lines might not be supported
2091                 rowScanResult[SCANRESULT_LINE_WIDTH_MSAA]++;
2092             }
2093             else if ((result & SCANRESULT_LINE_WIDTH_ERR_BIT) == 0 && (result & SCANRESULT_LINE_WIDTH_WARN_BIT) != 0)
2094             {
2095                 rowScanResult[SCANRESULT_LINE_WIDTH_WARN]++;
2096             }
2097             else
2098                 rowScanResult[SCANRESULT_LINE_WIDTH_ERR]++;
2099         }
2100     }
2101 
2102     // scan columns
2103     for (int x = de::max(verificationArea.x(), viewportPatternArea.x());
2104          x < de::min(verificationArea.z(), viewportPatternArea.z()); ++x)
2105     {
2106         const uint8_t result = scanColumn(viewportSurface.getAccess(), x, verificationArea.y(), verificationArea.w(),
2107                                           de::min(verificationArea.y(), viewportPatternArea.y()),
2108                                           de::min(verificationArea.w(), viewportPatternArea.w()),
2109                                           expectedHorizontalLines, messageLimitCounter);
2110 
2111         if ((result & SCANRESULT_NUM_LINES_OK_BIT) == 0)
2112             columnScanResult[SCANRESULT_NUM_LINES_ERR]++;
2113         if ((result & SCANRESULT_LINE_CONT_OK_BIT) == 0)
2114         {
2115             if ((result & SCANRESULT_LINE_CONT_WARN_BIT) != 0)
2116                 columnScanResult[SCANRESULT_LINE_CONT_WARN]++;
2117             else
2118                 columnScanResult[SCANRESULT_LINE_CONT_ERR]++;
2119         }
2120         else if ((result & SCANRESULT_LINE_WIDTH_OK_BIT) == 0)
2121         {
2122             if (m_isWideLineCase && isMsaa)
2123             {
2124                 // multisampled wide lines might not be supported
2125                 columnScanResult[SCANRESULT_LINE_WIDTH_MSAA]++;
2126             }
2127             else if ((result & SCANRESULT_LINE_WIDTH_ERR_BIT) == 0 && (result & SCANRESULT_LINE_WIDTH_WARN_BIT) != 0)
2128             {
2129                 columnScanResult[SCANRESULT_LINE_WIDTH_WARN]++;
2130             }
2131             else
2132                 columnScanResult[SCANRESULT_LINE_WIDTH_ERR]++;
2133         }
2134     }
2135 
2136     if (columnScanResult[SCANRESULT_LINE_WIDTH_ERR] != 0 || rowScanResult[SCANRESULT_LINE_WIDTH_ERR] != 0)
2137         anyError = true;
2138     else if (columnScanResult[SCANRESULT_LINE_CONT_ERR] != 0 || rowScanResult[SCANRESULT_LINE_CONT_ERR] != 0)
2139         anyError = true;
2140     else if (columnScanResult[SCANRESULT_LINE_WIDTH_MSAA] != 0 || rowScanResult[SCANRESULT_LINE_WIDTH_MSAA] != 0)
2141         msaaRelaxationRequired = true;
2142     else if (columnScanResult[SCANRESULT_LINE_WIDTH_WARN] != 0 || rowScanResult[SCANRESULT_LINE_WIDTH_WARN] != 0)
2143         hwIssueRelaxationRequired = true;
2144     else if (columnScanResult[SCANRESULT_NUM_LINES_ERR] != 0)
2145     {
2146         // found missing lines in a columnw and row line continuity check reported a warning (not an error) -> line width precision issue
2147         if (rowScanResult[SCANRESULT_LINE_CONT_ERR] == 0 && rowScanResult[SCANRESULT_LINE_CONT_WARN])
2148             hwIssueRelaxationRequired = true;
2149         else
2150             anyError = true;
2151     }
2152     else if (rowScanResult[SCANRESULT_NUM_LINES_ERR] != 0)
2153     {
2154         // found missing lines in a row and column line continuity check reported a warning (not an error) -> line width precision issue
2155         if (columnScanResult[SCANRESULT_LINE_CONT_ERR] == 0 && columnScanResult[SCANRESULT_LINE_CONT_WARN])
2156             hwIssueRelaxationRequired = true;
2157         else
2158             anyError = true;
2159     }
2160 
2161     if (anyError || msaaRelaxationRequired || hwIssueRelaxationRequired)
2162     {
2163         if (messageLimitCounter < 0)
2164             m_testCtx.getLog() << tcu::TestLog::Message << "Omitted " << (-messageLimitCounter)
2165                                << " row/column error descriptions." << tcu::TestLog::EndMessage;
2166 
2167         m_testCtx.getLog() << tcu::TestLog::Message << "Image verification failed." << tcu::TestLog::EndMessage
2168                            << tcu::TestLog::ImageSet("Images", "Image verification")
2169                            << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
2170                            << tcu::TestLog::EndImageSet;
2171 
2172         if (anyError)
2173             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
2174         else if (hwIssueRelaxationRequired)
2175         {
2176             // Line width hw issue
2177             m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Line width verification failed");
2178         }
2179         else
2180         {
2181             // MSAA wide lines are optional
2182             m_testCtx.setTestResult(QP_TEST_RESULT_COMPATIBILITY_WARNING, "Multisampled wide line verification failed");
2183         }
2184     }
2185     else
2186     {
2187         m_testCtx.getLog() << tcu::TestLog::Message << "Result image ok." << tcu::TestLog::EndMessage
2188                            << tcu::TestLog::ImageSet("Images", "Image verification")
2189                            << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
2190                            << tcu::TestLog::EndImageSet;
2191     }
2192 }
2193 
getNumberOfLinesRange(int queryAreaBegin,int queryAreaEnd,float patternStart,float patternSize,int viewportArea,QueryDirection queryDir) const2194 tcu::IVec2 LineRenderCase::getNumberOfLinesRange(int queryAreaBegin, int queryAreaEnd, float patternStart,
2195                                                  float patternSize, int viewportArea, QueryDirection queryDir) const
2196 {
2197     // pattern is not symmetric due to mirroring
2198     const int patternStartNdx = (queryDir == DIRECTION_HORIZONTAL) ? ((m_hasGeometryStage) ? (1) : (0)) :
2199                                                                      ((m_hasTessellationStage) ? (1) : (0));
2200     const int patternEndNdx   = patternStartNdx + m_patternSide;
2201 
2202     int numLinesMin = 0;
2203     int numLinesMax = 0;
2204 
2205     for (int lineNdx = patternStartNdx; lineNdx < patternEndNdx; ++lineNdx)
2206     {
2207         const float linePos   = (patternStart + (float(lineNdx) / float(m_patternSide)) * patternSize) * 0.5f + 0.5f;
2208         const float lineWidth = (m_isWideLineCase) ? ((float)m_wideLineLineWidth) : (1.0f);
2209 
2210         if (linePos * (float)viewportArea > (float)queryAreaBegin + 1.0f &&
2211             linePos * (float)viewportArea < (float)queryAreaEnd - 1.0f)
2212         {
2213             // line center is within the area
2214             ++numLinesMin;
2215             ++numLinesMax;
2216         }
2217         else if (linePos * (float)viewportArea > (float)queryAreaBegin - lineWidth * 0.5f - 1.0f &&
2218                  linePos * (float)viewportArea < (float)queryAreaEnd + lineWidth * 0.5f + 1.0f)
2219         {
2220             // line could leak into area
2221             ++numLinesMax;
2222         }
2223     }
2224 
2225     return tcu::IVec2(numLinesMin, numLinesMax);
2226 }
2227 
scanRow(const tcu::ConstPixelBufferAccess & access,int row,int rowBegin,int rowEnd,int rowViewportBegin,int rowViewportEnd,const tcu::IVec2 & numLines,int & messageLimitCounter) const2228 uint8_t LineRenderCase::scanRow(const tcu::ConstPixelBufferAccess &access, int row, int rowBegin, int rowEnd,
2229                                 int rowViewportBegin, int rowViewportEnd, const tcu::IVec2 &numLines,
2230                                 int &messageLimitCounter) const
2231 {
2232     const bool numLinesOk      = checkAreaNumLines(access, tcu::IVec4(rowBegin, row, rowEnd - rowBegin, 1),
2233                                                    messageLimitCounter, SCAN_ROW_COMPONENT_NDX, numLines);
2234     const uint8_t lineWidthRes = checkLineWidths(access, tcu::IVec2(rowBegin, row), tcu::IVec2(rowEnd, row),
2235                                                  SCAN_ROW_COMPONENT_NDX, messageLimitCounter);
2236     const uint8_t lineContinuityRes =
2237         checkLineContinuity(access, tcu::IVec2(rowViewportBegin, row), tcu::IVec2(rowViewportEnd, row),
2238                             SCAN_COL_COMPONENT_NDX, messageLimitCounter);
2239     uint8_t result = 0;
2240 
2241     if (numLinesOk)
2242         result |= (uint8_t)SCANRESULT_NUM_LINES_OK_BIT;
2243 
2244     if (lineContinuityRes == 0)
2245         result |= (uint8_t)SCANRESULT_LINE_CONT_OK_BIT;
2246     else
2247         result |= lineContinuityRes;
2248 
2249     if (lineWidthRes == 0)
2250         result |= (uint8_t)SCANRESULT_LINE_WIDTH_OK_BIT;
2251     else
2252         result |= lineWidthRes;
2253 
2254     return result;
2255 }
2256 
scanColumn(const tcu::ConstPixelBufferAccess & access,int column,int columnBegin,int columnEnd,int columnViewportBegin,int columnViewportEnd,const tcu::IVec2 & numLines,int & messageLimitCounter) const2257 uint8_t LineRenderCase::scanColumn(const tcu::ConstPixelBufferAccess &access, int column, int columnBegin,
2258                                    int columnEnd, int columnViewportBegin, int columnViewportEnd,
2259                                    const tcu::IVec2 &numLines, int &messageLimitCounter) const
2260 {
2261     const bool numLinesOk      = checkAreaNumLines(access, tcu::IVec4(column, columnBegin, 1, columnEnd - columnBegin),
2262                                                    messageLimitCounter, SCAN_COL_COMPONENT_NDX, numLines);
2263     const uint8_t lineWidthRes = checkLineWidths(access, tcu::IVec2(column, columnBegin), tcu::IVec2(column, columnEnd),
2264                                                  SCAN_COL_COMPONENT_NDX, messageLimitCounter);
2265     const uint8_t lineContinuityRes =
2266         checkLineContinuity(access, tcu::IVec2(column, columnViewportBegin), tcu::IVec2(column, columnViewportEnd),
2267                             SCAN_ROW_COMPONENT_NDX, messageLimitCounter);
2268     uint8_t result = 0;
2269 
2270     if (numLinesOk)
2271         result |= (uint8_t)SCANRESULT_NUM_LINES_OK_BIT;
2272 
2273     if (lineContinuityRes == 0)
2274         result |= (uint8_t)SCANRESULT_LINE_CONT_OK_BIT;
2275     else
2276         result |= lineContinuityRes;
2277 
2278     if (lineWidthRes == 0)
2279         result |= (uint8_t)SCANRESULT_LINE_WIDTH_OK_BIT;
2280     else
2281         result |= lineWidthRes;
2282 
2283     return result;
2284 }
2285 
checkAreaNumLines(const tcu::ConstPixelBufferAccess & access,const tcu::IVec4 & area,int & messageLimitCounter,int componentNdx,const tcu::IVec2 & numLines) const2286 bool LineRenderCase::checkAreaNumLines(const tcu::ConstPixelBufferAccess &access, const tcu::IVec4 &area,
2287                                        int &messageLimitCounter, int componentNdx, const tcu::IVec2 &numLines) const
2288 {
2289     // Num maxima == num lines
2290     const tcu::ConstPixelBufferAccess subAccess =
2291         tcu::getSubregion(access, area.x(), area.y(), 0, area.z(), area.w(), 1);
2292     const tcu::IVec2 numMinimaMaxima = getNumMinimaMaxima(subAccess, componentNdx);
2293     const int numMaxima              = numMinimaMaxima.y();
2294 
2295     // In valid range
2296     if (numMaxima >= numLines.x() && numMaxima <= numLines.y())
2297         return true;
2298 
2299     if (--messageLimitCounter < 0)
2300         return false;
2301 
2302     if (area.z() == 1)
2303         m_testCtx.getLog() << tcu::TestLog::Message << "On column " << area.x() << ", y: [" << area.y() << ","
2304                            << (area.y() + area.w()) << "):\n"
2305                            << "\tExpected [" << numLines.x() << ", " << numLines.y()
2306                            << "] lines but the number of lines in the area is " << numMaxima
2307                            << tcu::TestLog::EndMessage;
2308     else
2309         m_testCtx.getLog() << tcu::TestLog::Message << "On row " << area.y() << ", x: [" << area.x() << ","
2310                            << (area.x() + area.z()) << "):\n"
2311                            << "\tExpected [" << numLines.x() << ", " << numLines.y()
2312                            << "] lines but the number of lines in the area is " << numMaxima
2313                            << tcu::TestLog::EndMessage;
2314 
2315     return false;
2316 }
2317 
getNumMinimaMaxima(const tcu::ConstPixelBufferAccess & access,int componentNdx) const2318 tcu::IVec2 LineRenderCase::getNumMinimaMaxima(const tcu::ConstPixelBufferAccess &access, int componentNdx) const
2319 {
2320     DE_ASSERT(access.getWidth() == 1 || access.getHeight() == 1);
2321 
2322     int previousValue = -1;
2323     int previousSign  = 0;
2324     int numMinima     = 0;
2325     int numMaxima     = 0;
2326 
2327     for (int y = 0; y < access.getHeight(); ++y)
2328         for (int x = 0; x < access.getWidth(); ++x)
2329         {
2330             const int componentValue = access.getPixelInt(x, y)[componentNdx];
2331 
2332             if (previousValue != -1)
2333             {
2334                 const int sign = (componentValue > previousValue) ? (+1) :
2335                                  (componentValue < previousValue) ? (-1) :
2336                                                                     (0);
2337 
2338                 // local minima/maxima in sign changes (zero signless)
2339                 if (sign != 0 && sign == -previousSign)
2340                 {
2341                     previousSign = sign;
2342 
2343                     if (sign > 0)
2344                         ++numMinima;
2345                     else
2346                         ++numMaxima;
2347                 }
2348                 else if (sign != 0 && previousSign == 0)
2349                 {
2350                     previousSign = sign;
2351 
2352                     // local extreme at the start boundary
2353                     if (sign > 0)
2354                         ++numMinima;
2355                     else
2356                         ++numMaxima;
2357                 }
2358             }
2359 
2360             previousValue = componentValue;
2361         }
2362 
2363     // local extreme at the end boundary
2364     if (previousSign > 0)
2365         ++numMaxima;
2366     else if (previousSign < 0)
2367         ++numMinima;
2368     else
2369     {
2370         ++numMaxima;
2371         ++numMinima;
2372     }
2373 
2374     return tcu::IVec2(numMinima, numMaxima);
2375 }
2376 
checkLineContinuity(const tcu::ConstPixelBufferAccess & access,const tcu::IVec2 & begin,const tcu::IVec2 & end,int componentNdx,int & messageLimitCounter) const2377 uint8_t LineRenderCase::checkLineContinuity(const tcu::ConstPixelBufferAccess &access, const tcu::IVec2 &begin,
2378                                             const tcu::IVec2 &end, int componentNdx, int &messageLimitCounter) const
2379 {
2380     bool line                = false;
2381     const tcu::IVec2 advance = (begin.x() == end.x()) ? (tcu::IVec2(0, 1)) : (tcu::IVec2(1, 0));
2382     int missedPixels         = 0;
2383     int totalPixels          = 0;
2384     uint8_t errorMask        = 0;
2385 
2386     for (tcu::IVec2 cursor = begin; cursor != end; cursor += advance)
2387     {
2388         const bool hit = (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0);
2389 
2390         if (hit)
2391             line = true;
2392         else if (line && !hit)
2393         {
2394             // non-continuous line detected
2395             const tcu::IVec2 advanceNeighbor   = tcu::IVec2(1, 1) - advance;
2396             const tcu::IVec2 cursorNeighborPos = cursor + advanceNeighbor;
2397             const tcu::IVec2 cursorNeighborNeg = cursor - advanceNeighbor;
2398             // hw precision issues may lead to a line being non-straight -> check neighboring pixels
2399             if ((access.getPixelInt(cursorNeighborPos.x(), cursorNeighborPos.y())[componentNdx] == 0) &&
2400                 (access.getPixelInt(cursorNeighborNeg.x(), cursorNeighborNeg.y())[componentNdx] == 0))
2401                 ++missedPixels;
2402         }
2403         ++totalPixels;
2404     }
2405 
2406     if (missedPixels > 0)
2407     {
2408         if (--messageLimitCounter >= 0)
2409         {
2410             m_testCtx.getLog() << tcu::TestLog::Message << "Found non-continuous "
2411                                << ((advance.x() == 1) ? ("horizontal") : ("vertical")) << " line near " << begin << ". "
2412                                << "Missed pixels: " << missedPixels << tcu::TestLog::EndMessage;
2413         }
2414         // allow 10% missing pixels for warning
2415         if (missedPixels <= deRoundFloatToInt32((float)totalPixels * 0.1f))
2416             errorMask = SCANRESULT_LINE_CONT_WARN_BIT;
2417         else
2418             errorMask = SCANRESULT_LINE_CONT_ERR_BIT;
2419     }
2420 
2421     return errorMask;
2422 }
2423 
checkLineWidths(const tcu::ConstPixelBufferAccess & access,const tcu::IVec2 & begin,const tcu::IVec2 & end,int componentNdx,int & messageLimitCounter) const2424 uint8_t LineRenderCase::checkLineWidths(const tcu::ConstPixelBufferAccess &access, const tcu::IVec2 &begin,
2425                                         const tcu::IVec2 &end, int componentNdx, int &messageLimitCounter) const
2426 {
2427     const bool multisample    = m_context.getRenderTarget().getNumSamples() > 1;
2428     const int lineRenderWidth = (m_isWideLineCase) ? (m_wideLineLineWidth) : 1;
2429     const tcu::IVec2 lineWidthRange =
2430         (multisample) ? (tcu::IVec2(lineRenderWidth,
2431                                     lineRenderWidth + 1)) // multisampled "smooth" lines may spread to neighboring pixel
2432                         :
2433                         (tcu::IVec2(lineRenderWidth, lineRenderWidth));
2434     const tcu::IVec2 relaxedLineWidthRange = (tcu::IVec2(lineRenderWidth - 1, lineRenderWidth + 1));
2435 
2436     int lineWidth        = 0;
2437     bool bboxLimitedLine = false;
2438     uint8_t errorMask    = 0;
2439 
2440     const tcu::IVec2 advance = (begin.x() == end.x()) ? (tcu::IVec2(0, 1)) : (tcu::IVec2(1, 0));
2441 
2442     // fragments before begin?
2443     if (access.getPixelInt(begin.x(), begin.y())[componentNdx] != 0)
2444     {
2445         bboxLimitedLine = true;
2446 
2447         for (tcu::IVec2 cursor = begin - advance;; cursor -= advance)
2448         {
2449             if (cursor.x() < 0 || cursor.y() < 0)
2450             {
2451                 break;
2452             }
2453             else if (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0)
2454             {
2455                 ++lineWidth;
2456             }
2457             else
2458                 break;
2459         }
2460     }
2461 
2462     for (tcu::IVec2 cursor = begin; cursor != end; cursor += advance)
2463     {
2464         const bool hit = (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0);
2465 
2466         if (hit)
2467             ++lineWidth;
2468         else if (lineWidth)
2469         {
2470             // Line is allowed to be be thinner if it borders the bbox boundary (since part of the line might have been discarded).
2471             const bool incorrectLineWidth =
2472                 (lineWidth < lineWidthRange.x() && !bboxLimitedLine) || (lineWidth > lineWidthRange.y());
2473 
2474             if (incorrectLineWidth)
2475             {
2476                 const bool incorrectRelaxedLineWidth = (lineWidth < relaxedLineWidthRange.x() && !bboxLimitedLine) ||
2477                                                        (lineWidth > relaxedLineWidthRange.y());
2478 
2479                 if (incorrectRelaxedLineWidth)
2480                     errorMask |= SCANRESULT_LINE_WIDTH_ERR_BIT;
2481                 else
2482                     errorMask |= SCANRESULT_LINE_WIDTH_WARN_BIT;
2483 
2484                 printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter);
2485             }
2486 
2487             lineWidth       = 0;
2488             bboxLimitedLine = false;
2489         }
2490     }
2491 
2492     // fragments after end?
2493     if (lineWidth)
2494     {
2495         for (tcu::IVec2 cursor = end;; cursor += advance)
2496         {
2497             if (cursor.x() >= access.getWidth() || cursor.y() >= access.getHeight())
2498             {
2499                 if (lineWidth > lineWidthRange.y())
2500                 {
2501                     if (lineWidth > relaxedLineWidthRange.y())
2502                         errorMask |= SCANRESULT_LINE_WIDTH_ERR_BIT;
2503                     else
2504                         errorMask |= SCANRESULT_LINE_WIDTH_WARN_BIT;
2505 
2506                     printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter);
2507                 }
2508 
2509                 break;
2510             }
2511             else if (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0)
2512             {
2513                 ++lineWidth;
2514             }
2515             else if (lineWidth)
2516             {
2517                 // only check that line width is not larger than expected. Line width may be smaller
2518                 // since the scanning 'cursor' is now outside the bounding box.
2519                 const bool incorrectLineWidth = (lineWidth > lineWidthRange.y());
2520 
2521                 if (incorrectLineWidth)
2522                 {
2523                     const bool incorrectRelaxedLineWidth = (lineWidth > relaxedLineWidthRange.y());
2524 
2525                     if (incorrectRelaxedLineWidth)
2526                         errorMask |= SCANRESULT_LINE_WIDTH_ERR_BIT;
2527                     else
2528                         errorMask |= SCANRESULT_LINE_WIDTH_WARN_BIT;
2529 
2530                     printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter);
2531                 }
2532 
2533                 lineWidth = 0;
2534             }
2535         }
2536     }
2537 
2538     return errorMask;
2539 }
2540 
printLineWidthError(const tcu::IVec2 & pos,int detectedLineWidth,const tcu::IVec2 & lineWidthRange,bool isHorizontal,int & messageLimitCounter) const2541 void LineRenderCase::printLineWidthError(const tcu::IVec2 &pos, int detectedLineWidth, const tcu::IVec2 &lineWidthRange,
2542                                          bool isHorizontal, int &messageLimitCounter) const
2543 {
2544     if (--messageLimitCounter < 0)
2545         return;
2546 
2547     m_testCtx.getLog() << tcu::TestLog::Message << "Found incorrect line width near " << pos << ": ("
2548                        << ((isHorizontal) ? ("horizontal") : ("vertical")) << " line)\n"
2549                        << "\tExpected line width in range [" << lineWidthRange.x() << ", " << lineWidthRange.y()
2550                        << "] but found " << detectedLineWidth << tcu::TestLog::EndMessage;
2551 }
2552 
2553 class PointRenderCase : public BBoxRenderCase
2554 {
2555 public:
2556     enum
2557     {
2558         POINTFLAG_WIDE = 1u << FLAGBIT_USER_BIT, //!< use wide points
2559     };
2560     struct GeneratedPoint
2561     {
2562         tcu::Vec2 center;
2563         int size;
2564         bool even;
2565     };
2566 
2567     PointRenderCase(Context &context, const char *name, const char *description, uint32_t flags);
2568     ~PointRenderCase(void);
2569 
2570 private:
2571     enum ResultPointType
2572     {
2573         POINT_FULL = 0,
2574         POINT_PARTIAL
2575     };
2576 
2577     void init(void);
2578     void deinit(void);
2579 
2580     std::string genVertexSource(void) const;
2581     std::string genFragmentSource(void) const;
2582     std::string genTessellationControlSource(void) const;
2583     std::string genTessellationEvaluationSource(void) const;
2584     std::string genGeometrySource(void) const;
2585 
2586     IterationConfig generateConfig(int iteration, const tcu::IVec2 &renderTargetSize) const;
2587     void generateAttributeData(void);
2588     void getAttributeData(std::vector<tcu::Vec4> &data) const;
2589     void renderTestPattern(const IterationConfig &config);
2590     void verifyRenderResult(const IterationConfig &config);
2591 
2592     void genReferencePointData(const IterationConfig &config, std::vector<GeneratedPoint> &data) const;
2593     bool verifyNarrowPointPattern(const tcu::Surface &viewport, const std::vector<GeneratedPoint> &refPoints,
2594                                   const ProjectedBBox &bbox, int &logFloodCounter);
2595     bool verifyWidePointPattern(const tcu::Surface &viewport, const std::vector<GeneratedPoint> &refPoints,
2596                                 const ProjectedBBox &bbox, int &logFloodCounter);
2597     bool verifyWidePoint(const tcu::Surface &viewport, const GeneratedPoint &refPoint, const ProjectedBBox &bbox,
2598                          ResultPointType pointType, int &logFloodCounter);
2599     bool verifyWidePointAt(const tcu::IVec2 &pointPos, const tcu::Surface &viewport, const GeneratedPoint &refPoint,
2600                            const tcu::IVec4 &bbox, ResultPointType pointType, int componentNdx, int &logFloodCounter);
2601     tcu::IVec2 scanPointWidthAt(const tcu::IVec2 &pointPos, const tcu::Surface &viewport, int expectedPointSize,
2602                                 int componentNdx) const;
2603 
2604     const int m_numStripes;
2605     const bool m_isWidePointCase;
2606     std::vector<tcu::Vec4> m_attribData;
2607 };
2608 
PointRenderCase(Context & context,const char * name,const char * description,uint32_t flags)2609 PointRenderCase::PointRenderCase(Context &context, const char *name, const char *description, uint32_t flags)
2610     : BBoxRenderCase(context, name, description, 12, flags)
2611     , m_numStripes(4)
2612     , m_isWidePointCase((flags & POINTFLAG_WIDE) != 0)
2613 {
2614 }
2615 
~PointRenderCase(void)2616 PointRenderCase::~PointRenderCase(void)
2617 {
2618 }
2619 
init(void)2620 void PointRenderCase::init(void)
2621 {
2622     if (m_isWidePointCase)
2623     {
2624         const bool supportsGL45 =
2625             glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5));
2626 
2627         if (!supportsGL45)
2628         {
2629             // extensions
2630             if (m_hasGeometryStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_point_size"))
2631                 throw tcu::NotSupportedError("Test requires GL_EXT_geometry_point_size extension");
2632             if (m_hasTessellationStage && !m_hasGeometryStage &&
2633                 !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_point_size"))
2634                 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_point_size extension");
2635         }
2636 
2637         // Enable program point size for desktop GL
2638         if (supportsGL45)
2639         {
2640             const glw::Functions &gl = m_context.getRenderContext().getFunctions();
2641             gl.enable(GL_PROGRAM_POINT_SIZE);
2642         }
2643 
2644         // point size range
2645         {
2646             glw::GLfloat pointSizeRange[2] = {0.0f, 0.0f};
2647             m_context.getRenderContext().getFunctions().getFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
2648 
2649             if (pointSizeRange[1] < 5.0f)
2650                 throw tcu::NotSupportedError("Test requires point size 5.0");
2651         }
2652     }
2653 
2654     m_testCtx.getLog()
2655         << tcu::TestLog::Message << "Rendering point pattern to "
2656         << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n"
2657         << "Half of the points are green, half blue. Using additive blending.\n"
2658         << "Points are in random order, varying pattern size and location for each iteration.\n"
2659         << "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated red channel."
2660         << tcu::TestLog::EndMessage;
2661 
2662     generateAttributeData();
2663 
2664     BBoxRenderCase::init();
2665 }
2666 
deinit(void)2667 void PointRenderCase::deinit(void)
2668 {
2669     // clear data
2670     m_attribData = std::vector<tcu::Vec4>();
2671 
2672     // deinit parent
2673     BBoxRenderCase::deinit();
2674 }
2675 
genVertexSource(void) const2676 std::string PointRenderCase::genVertexSource(void) const
2677 {
2678     std::ostringstream buf;
2679 
2680     buf << "${GLSL_VERSION_DECL}\n"
2681            "in highp vec4 a_position;\n"
2682            "in highp vec4 a_color;\n"
2683            "out highp vec4 vtx_color;\n"
2684            "uniform highp vec4 u_posScale;\n"
2685            "\n";
2686     if (!m_hasTessellationStage)
2687     {
2688         DE_ASSERT(m_useGlobalState);
2689         buf << "uniform highp vec4 u_primitiveBBoxMin;\n"
2690                "uniform highp vec4 u_primitiveBBoxMax;\n"
2691                "\n"
2692                "flat out highp float v_"
2693             << (m_hasGeometryStage ? "geo_" : "")
2694             << "bbox_expansionSize;\n"
2695                "flat out highp vec3 v_"
2696             << (m_hasGeometryStage ? "geo_" : "")
2697             << "bbox_clipMin;\n"
2698                "flat out highp vec3 v_"
2699             << (m_hasGeometryStage ? "geo_" : "")
2700             << "bbox_clipMax;\n"
2701                "\n";
2702     }
2703 
2704     buf << "void main()\n"
2705            "{\n"
2706            "    highp vec2 patternOffset = u_posScale.xy;\n"
2707            "    highp vec2 patternScale = u_posScale.zw;\n"
2708            "    highp float pointSize = "
2709         << ((m_isWidePointCase && !m_hasTessellationStage && !m_hasGeometryStage) ?
2710                 ("(a_color.g > 0.0) ? (5.0) : (3.0)") :
2711                 ("1.0"))
2712         << ";\n"
2713         << "    gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n"
2714            "    gl_PointSize = pointSize;\n"
2715            "    vtx_color = a_color;\n";
2716 
2717     if (!m_hasTessellationStage)
2718     {
2719         DE_ASSERT(m_useGlobalState);
2720         buf << "\n"
2721                "    v_"
2722             << (m_hasGeometryStage ? "geo_" : "")
2723             << "bbox_expansionSize = pointSize;\n"
2724                "    v_"
2725             << (m_hasGeometryStage ? "geo_" : "")
2726             << "bbox_clipMin =\n"
2727                "        min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / "
2728                "u_primitiveBBoxMin.w,\n"
2729                "            vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / "
2730                "u_primitiveBBoxMax.w);\n"
2731                "    v_"
2732             << (m_hasGeometryStage ? "geo_" : "")
2733             << "bbox_clipMax =\n"
2734                "        min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / "
2735                "u_primitiveBBoxMin.w,\n"
2736                "            vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / "
2737                "u_primitiveBBoxMax.w);\n";
2738     }
2739 
2740     buf << "}\n";
2741     return buf.str();
2742 }
2743 
genFragmentSource(void) const2744 std::string PointRenderCase::genFragmentSource(void) const
2745 {
2746     const char *const colorInputName = (m_hasGeometryStage)     ? ("geo_color") :
2747                                        (m_hasTessellationStage) ? ("tess_color") :
2748                                                                   ("vtx_color");
2749     std::ostringstream buf;
2750 
2751     buf << "${GLSL_VERSION_DECL}\n"
2752            "in mediump vec4 "
2753         << colorInputName
2754         << ";\n"
2755            "layout(location = 0) out mediump vec4 o_color;\n"
2756         << genShaderFunction(SHADER_FUNC_INSIDE_BBOX)
2757         << "\n"
2758            "void main()\n"
2759            "{\n"
2760            "    mediump vec4 baseColor = "
2761         << colorInputName
2762         << ";\n"
2763            "    mediump float redChannel;\n"
2764            "    if (fragmentInsideTheBBox(gl_FragCoord.z))\n"
2765            "        redChannel = 0.0;\n"
2766            "    else\n"
2767            "        redChannel = 1.0;\n"
2768            "    o_color = vec4(redChannel, baseColor.g, baseColor.b, baseColor.a);\n"
2769            "}\n";
2770 
2771     return buf.str();
2772 }
2773 
genTessellationControlSource(void) const2774 std::string PointRenderCase::genTessellationControlSource(void) const
2775 {
2776     const bool tessellationWidePoints = (m_isWidePointCase) && (!m_hasGeometryStage);
2777     std::ostringstream buf;
2778 
2779     buf << "${GLSL_VERSION_DECL}\n"
2780            "${ARB_ES32_COMPATIBILITY_REQUIRE}\n"
2781            "${TESSELLATION_SHADER_REQUIRE}\n"
2782            "${PRIMITIVE_BOUNDING_BOX_REQUIRE}\n"
2783         << ((tessellationWidePoints) ? ("${TESSELLATION_POINT_SIZE_REQUIRE}\n") : (""))
2784         << "layout(vertices=1) out;"
2785            "\n"
2786            "in highp vec4 vtx_color[];\n"
2787            "out highp vec4 tess_ctrl_color[];\n"
2788            "uniform highp float u_tessellationLevel;\n"
2789            "uniform highp vec4 u_posScale;\n";
2790 
2791     if (!m_calcPerPrimitiveBBox)
2792     {
2793         buf << "uniform highp vec4 u_primitiveBBoxMin;\n"
2794                "uniform highp vec4 u_primitiveBBoxMax;\n";
2795     }
2796 
2797     buf << "patch out highp vec3 vp_bbox_clipMin;\n"
2798            "patch out highp vec3 vp_bbox_clipMax;\n";
2799 
2800     if (m_calcPerPrimitiveBBox)
2801     {
2802         buf << "\n";
2803         if (m_hasGeometryStage)
2804             buf << genShaderFunction(SHADER_FUNC_MIRROR_X);
2805         buf << genShaderFunction(SHADER_FUNC_MIRROR_Y);
2806 
2807         buf << "vec4 transformVec(in highp vec4 p)\n"
2808                "{\n"
2809                "    return "
2810             << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)"))
2811             << ";\n"
2812                "}\n";
2813     }
2814 
2815     buf << "\n"
2816            "void main()\n"
2817            "{\n"
2818            "    // convert to nonsensical coordinates, just in case\n"
2819            "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n"
2820            "    tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n"
2821            "\n"
2822            "    gl_TessLevelOuter[0] = u_tessellationLevel;\n"
2823            "    gl_TessLevelOuter[1] = u_tessellationLevel;\n"
2824            "    gl_TessLevelOuter[2] = u_tessellationLevel;\n"
2825            "    gl_TessLevelOuter[3] = u_tessellationLevel;\n"
2826            "    gl_TessLevelInner[0] = 0.8; // will be rounded up to 1\n"
2827            "    gl_TessLevelInner[1] = 0.8; // will be rounded up to 1\n";
2828 
2829     if (m_calcPerPrimitiveBBox)
2830     {
2831         buf << "\n";
2832 
2833         if (m_hasGeometryStage)
2834             buf << "    const vec2 minExpansion = vec2(0.07 + 0.05, 0.07 + 0.02); // eval and geometry shader\n"
2835                    "    const vec2 maxExpansion = vec2(0.07 + 0.05, 0.07 + 0.03); // eval and geometry shader\n";
2836         else
2837             buf << "    const vec2 minExpansion = vec2(0.07, 0.07); // eval shader\n"
2838                    "    const vec2 maxExpansion = vec2(0.07, 0.07); // eval shader\n";
2839 
2840         buf << "    highp vec2 patternScale = u_posScale.zw;\n"
2841                "    highp vec4 bboxMin = transformVec(gl_in[0].gl_Position) - vec4(minExpansion * patternScale, 0.0, "
2842                "0.0);\n"
2843                "    highp vec4 bboxMax = transformVec(gl_in[0].gl_Position) + vec4(maxExpansion * patternScale, 0.0, "
2844                "0.0);\n";
2845     }
2846     else
2847     {
2848         buf << "\n"
2849                "    highp vec4 bboxMin = u_primitiveBBoxMin;\n"
2850                "    highp vec4 bboxMax = u_primitiveBBoxMax;\n";
2851     }
2852     if (!m_useGlobalState)
2853         buf << "\n"
2854                "    ${PRIM_GL_BOUNDING_BOX}[0] = bboxMin;\n"
2855                "    ${PRIM_GL_BOUNDING_BOX}[1] = bboxMax;\n";
2856 
2857     buf << "    vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n"
2858            "                          vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n"
2859            "    vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n"
2860            "                          vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n"
2861            "}\n";
2862 
2863     return buf.str();
2864 }
2865 
genTessellationEvaluationSource(void) const2866 std::string PointRenderCase::genTessellationEvaluationSource(void) const
2867 {
2868     const bool tessellationWidePoints = (m_isWidePointCase) && (!m_hasGeometryStage);
2869     std::ostringstream buf;
2870 
2871     buf << "${GLSL_VERSION_DECL}\n"
2872            "${TESSELLATION_SHADER_REQUIRE}\n"
2873         << ((tessellationWidePoints) ? ("${TESSELLATION_POINT_SIZE_REQUIRE}\n") : (""))
2874         << "layout(quads, point_mode) in;"
2875            "\n"
2876            "in highp vec4 tess_ctrl_color[];\n"
2877            "out highp vec4 tess_color;\n"
2878            "uniform highp vec4 u_posScale;\n"
2879            "\n"
2880            "patch in highp vec3 vp_bbox_clipMin;\n"
2881            "patch in highp vec3 vp_bbox_clipMax;\n"
2882         << ((!m_hasGeometryStage) ? ("flat out highp float v_bbox_expansionSize;\n") : ("")) << "flat out highp vec3 v_"
2883         << (m_hasGeometryStage ? "geo_" : "")
2884         << "bbox_clipMin;\n"
2885            "flat out highp vec3 v_"
2886         << (m_hasGeometryStage ? "geo_" : "")
2887         << "bbox_clipMax;\n"
2888            "\n"
2889         << genShaderFunction(SHADER_FUNC_MIRROR_Y)
2890         << "void main()\n"
2891            "{\n"
2892            "    // non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n"
2893            "    highp vec2 patternScale = u_posScale.zw;\n"
2894            "    highp vec4 offset = vec4((gl_TessCoord.xy * 2.0 - vec2(1.0)) * 0.07 * patternScale, 0.0, 0.0);\n"
2895            "    highp float pointSize = "
2896         << ((tessellationWidePoints) ? ("(tess_ctrl_color[0].g > 0.0) ? (5.0) : (3.0)") : ("1.0"))
2897         << ";\n"
2898            "    gl_Position = mirrorY(gl_in[0].gl_Position.zwyx + offset);\n";
2899 
2900     if (tessellationWidePoints)
2901         buf << "    gl_PointSize = pointSize;\n";
2902 
2903     buf << "    tess_color = tess_ctrl_color[0];\n"
2904         << ((!m_hasGeometryStage) ? ("v_bbox_expansionSize = pointSize;\n") : ("")) << "    v_"
2905         << (m_hasGeometryStage ? "geo_" : "")
2906         << "bbox_clipMin = vp_bbox_clipMin;\n"
2907            "    v_"
2908         << (m_hasGeometryStage ? "geo_" : "")
2909         << "bbox_clipMax = vp_bbox_clipMax;\n"
2910            "}\n";
2911 
2912     return buf.str();
2913 }
2914 
genGeometrySource(void) const2915 std::string PointRenderCase::genGeometrySource(void) const
2916 {
2917     const char *const colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color");
2918     std::ostringstream buf;
2919 
2920     buf << "${GLSL_VERSION_DECL}\n"
2921            "${GEOMETRY_SHADER_REQUIRE}\n"
2922         << ((m_isWidePointCase) ? ("${GEOMETRY_POINT_SIZE}\n") : (""))
2923         << "layout(points) in;\n"
2924            "layout(max_vertices=3, points) out;\n"
2925            "\n"
2926            "in highp vec4 "
2927         << colorInputName
2928         << "[1];\n"
2929            "out highp vec4 geo_color;\n"
2930            "uniform highp vec4 u_posScale;\n"
2931            "\n"
2932            "flat in highp vec3 v_geo_bbox_clipMin[1];\n"
2933            "flat in highp vec3 v_geo_bbox_clipMax[1];\n"
2934            "flat out highp vec3 v_bbox_clipMin;\n"
2935            "flat out highp vec3 v_bbox_clipMax;\n"
2936            "flat out highp float v_bbox_expansionSize;\n"
2937            "\n"
2938         << genShaderFunction(SHADER_FUNC_MIRROR_X)
2939         << "\n"
2940            "void main()\n"
2941            "{\n"
2942            "    // Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n"
2943            "    highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n"
2944            "    highp vec4 pointColor = "
2945         << colorInputName
2946         << "[0];\n"
2947            "    highp vec2 patternScale = u_posScale.zw;\n"
2948            "    highp float pointSize = "
2949         << (m_isWidePointCase ? ("(pointColor.g > 0.0) ? (5.0) : (3.0)") : ("1.0"))
2950         << ";\n"
2951            "\n"
2952            "    highp vec4 offsets[3] =\n"
2953            "        vec4[3](\n"
2954            "            vec4( 0.05 * patternScale.x, 0.03 * patternScale.y, 0.0, 0.0),\n"
2955            "            vec4(-0.01 * patternScale.x,-0.02 * patternScale.y, 0.0, 0.0),\n"
2956            "            vec4(-0.05 * patternScale.x, 0.02 * patternScale.y, 0.0, 0.0)\n"
2957            "        );\n"
2958            "    for (int ndx = 0; ndx < 3; ++ndx)\n"
2959            "    {\n"
2960            "        gl_Position = p0 + offsets[ndx];\n";
2961 
2962     if (m_isWidePointCase)
2963         buf << "        gl_PointSize = pointSize;\n";
2964 
2965     buf << "        v_bbox_clipMin = v_geo_bbox_clipMin[0];\n"
2966            "        v_bbox_clipMax = v_geo_bbox_clipMax[0];\n"
2967            "        v_bbox_expansionSize = pointSize;\n"
2968            "        geo_color = pointColor;\n"
2969            "        EmitVertex();\n"
2970            "    }\n"
2971            "}\n";
2972 
2973     return buf.str();
2974 }
2975 
generateConfig(int iteration,const tcu::IVec2 & renderTargetSize) const2976 PointRenderCase::IterationConfig PointRenderCase::generateConfig(int iteration,
2977                                                                  const tcu::IVec2 &renderTargetSize) const
2978 {
2979     IterationConfig config = generateRandomConfig(0xDEDEDEu * (uint32_t)iteration, renderTargetSize);
2980 
2981     // equal or larger -> expand according to shader expansion
2982     if (m_bboxSize == BBOXSIZE_EQUAL || m_bboxSize == BBOXSIZE_LARGER)
2983     {
2984         const tcu::Vec2 patternScale = config.patternSize;
2985 
2986         if (m_hasTessellationStage)
2987         {
2988             config.bbox.min -= tcu::Vec4(0.07f * patternScale.x(), 0.07f * patternScale.y(), 0.0f, 0.0f);
2989             config.bbox.max += tcu::Vec4(0.07f * patternScale.x(), 0.07f * patternScale.y(), 0.0f, 0.0f);
2990         }
2991         if (m_hasGeometryStage)
2992         {
2993             config.bbox.min -= tcu::Vec4(0.05f * patternScale.x(), 0.02f * patternScale.y(), 0.0f, 0.0f);
2994             config.bbox.max += tcu::Vec4(0.05f * patternScale.x(), 0.03f * patternScale.y(), 0.0f, 0.0f);
2995         }
2996     }
2997 
2998     return config;
2999 }
3000 
generateAttributeData(void)3001 void PointRenderCase::generateAttributeData(void)
3002 {
3003     const tcu::Vec4 green(0.0f, 1.0f, 0.0f, 1.0f);
3004     const tcu::Vec4 blue(0.0f, 0.0f, 1.0f, 1.0f);
3005     std::vector<int> cellOrder(m_numStripes * m_numStripes * 2);
3006     de::Random rnd(0xDE22446);
3007 
3008     for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
3009         cellOrder[ndx] = ndx;
3010     rnd.shuffle(cellOrder.begin(), cellOrder.end());
3011 
3012     m_attribData.resize(cellOrder.size() * 2);
3013     for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
3014     {
3015         const int pointID    = cellOrder[ndx];
3016         const int direction  = pointID & 0x01;
3017         const int majorCoord = (pointID >> 1) / m_numStripes;
3018         const int minorCoord = (pointID >> 1) % m_numStripes;
3019 
3020         if (direction)
3021         {
3022             m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] =
3023                 tcu::Vec4(float(minorCoord) / float(m_numStripes), float(majorCoord) / float(m_numStripes), 0.0f, 1.0f);
3024             m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green;
3025         }
3026         else
3027         {
3028             m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] =
3029                 tcu::Vec4(((float)majorCoord + 0.5f) / float(m_numStripes),
3030                           ((float)minorCoord + 0.5f) / float(m_numStripes), 0.0f, 1.0f);
3031             m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue;
3032         }
3033     }
3034 }
3035 
getAttributeData(std::vector<tcu::Vec4> & data) const3036 void PointRenderCase::getAttributeData(std::vector<tcu::Vec4> &data) const
3037 {
3038     data = m_attribData;
3039 }
3040 
renderTestPattern(const IterationConfig & config)3041 void PointRenderCase::renderTestPattern(const IterationConfig &config)
3042 {
3043     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
3044 
3045     setupRender(config);
3046 
3047     if (m_hasTessellationStage)
3048     {
3049         const glw::GLint tessLevelPos = gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel");
3050         const glw::GLfloat tessLevel  = 0.8f; // will be rounded up
3051 
3052         TCU_CHECK(tessLevelPos != -1);
3053 
3054         m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel
3055                            << tcu::TestLog::EndMessage;
3056 
3057         gl.uniform1f(tessLevelPos, tessLevel);
3058         gl.patchParameteri(GL_PATCH_VERTICES, 1);
3059         GLU_EXPECT_NO_ERROR(gl.getError(), "patch param");
3060     }
3061 
3062     m_testCtx.getLog() << tcu::TestLog::Message << "Rendering pattern." << tcu::TestLog::EndMessage;
3063 
3064     gl.enable(GL_BLEND);
3065     gl.blendFunc(GL_ONE, GL_ONE);
3066     gl.blendEquation(GL_FUNC_ADD);
3067 
3068     gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_POINTS), 0, m_numStripes * m_numStripes * 2);
3069     GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
3070 }
3071 
verifyRenderResult(const IterationConfig & config)3072 void PointRenderCase::verifyRenderResult(const IterationConfig &config)
3073 {
3074     const glw::Functions &gl          = m_context.getRenderContext().getFunctions();
3075     const ProjectedBBox projectedBBox = projectBoundingBox(config.bbox);
3076     const tcu::IVec4 viewportBBoxArea = getViewportBoundingBoxArea(projectedBBox, config.viewportSize);
3077 
3078     tcu::Surface viewportSurface(config.viewportSize.x(), config.viewportSize.y());
3079     int logFloodCounter = 8;
3080     bool anyError;
3081     std::vector<GeneratedPoint> refPoints;
3082 
3083     if (!m_calcPerPrimitiveBBox)
3084         m_testCtx.getLog() << tcu::TestLog::Message << "Projected bounding box: (clip space)\n"
3085                            << "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n"
3086                            << "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n"
3087                            << "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n"
3088                            << "In viewport coordinates:\n"
3089                            << "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n"
3090                            << "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n"
3091                            << "Verifying render results within the bounding box:\n"
3092                            << tcu::TestLog::EndMessage;
3093     else
3094         m_testCtx.getLog() << tcu::TestLog::Message << "Verifying render result:" << tcu::TestLog::EndMessage;
3095 
3096     if (m_fbo)
3097         gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo);
3098     glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(),
3099                     viewportSurface.getAccess());
3100 
3101     genReferencePointData(config, refPoints);
3102 
3103     if (m_isWidePointCase)
3104         anyError = verifyWidePointPattern(viewportSurface, refPoints, projectedBBox, logFloodCounter);
3105     else
3106         anyError = verifyNarrowPointPattern(viewportSurface, refPoints, projectedBBox, logFloodCounter);
3107 
3108     if (anyError)
3109     {
3110         if (logFloodCounter < 0)
3111             m_testCtx.getLog() << tcu::TestLog::Message << "Omitted " << (-logFloodCounter) << " error descriptions."
3112                                << tcu::TestLog::EndMessage;
3113 
3114         m_testCtx.getLog() << tcu::TestLog::Message << "Image verification failed." << tcu::TestLog::EndMessage
3115                            << tcu::TestLog::ImageSet("Images", "Image verification")
3116                            << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
3117                            << tcu::TestLog::EndImageSet;
3118 
3119         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
3120     }
3121     else
3122     {
3123         m_testCtx.getLog() << tcu::TestLog::Message << "Result image ok." << tcu::TestLog::EndMessage
3124                            << tcu::TestLog::ImageSet("Images", "Image verification")
3125                            << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess())
3126                            << tcu::TestLog::EndImageSet;
3127     }
3128 }
3129 
3130 struct PointSorter
3131 {
operator ()deqp::gles31::Functional::__anon39b763e60111::PointSorter3132     bool operator()(const PointRenderCase::GeneratedPoint &a, const PointRenderCase::GeneratedPoint &b) const
3133     {
3134         if (a.center.y() < b.center.y())
3135             return true;
3136         else if (a.center.y() > b.center.y())
3137             return false;
3138         else
3139             return (a.center.x() < b.center.x());
3140     }
3141 };
3142 
genReferencePointData(const IterationConfig & config,std::vector<GeneratedPoint> & data) const3143 void PointRenderCase::genReferencePointData(const IterationConfig &config, std::vector<GeneratedPoint> &data) const
3144 {
3145     std::vector<GeneratedPoint> currentPoints;
3146 
3147     // vertex shader
3148     currentPoints.resize(m_attribData.size() / 2);
3149     for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
3150     {
3151         currentPoints[ndx].center = m_attribData[ndx * 2].swizzle(0, 1);
3152         currentPoints[ndx].even   = (m_attribData[ndx * 2 + 1].y() == 1.0f); // is green
3153         currentPoints[ndx].size   = ((m_isWidePointCase) ? ((currentPoints[ndx].even) ? 5 : 3) : 1);
3154     }
3155 
3156     // tessellation
3157     if (m_hasTessellationStage)
3158     {
3159         std::vector<GeneratedPoint> tessellatedPoints;
3160 
3161         tessellatedPoints.resize(currentPoints.size() * 4);
3162         for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
3163         {
3164             const tcu::Vec2 position =
3165                 tcu::Vec2(currentPoints[ndx].center.x(), 1.0f - currentPoints[ndx].center.y()); // mirror Y
3166 
3167             tessellatedPoints[4 * ndx + 0].center = position + tcu::Vec2(-0.07f, -0.07f);
3168             tessellatedPoints[4 * ndx + 0].size   = currentPoints[ndx].size;
3169             tessellatedPoints[4 * ndx + 0].even   = currentPoints[ndx].even;
3170 
3171             tessellatedPoints[4 * ndx + 1].center = position + tcu::Vec2(0.07f, -0.07f);
3172             tessellatedPoints[4 * ndx + 1].size   = currentPoints[ndx].size;
3173             tessellatedPoints[4 * ndx + 1].even   = currentPoints[ndx].even;
3174 
3175             tessellatedPoints[4 * ndx + 2].center = position + tcu::Vec2(0.07f, 0.07f);
3176             tessellatedPoints[4 * ndx + 2].size   = currentPoints[ndx].size;
3177             tessellatedPoints[4 * ndx + 2].even   = currentPoints[ndx].even;
3178 
3179             tessellatedPoints[4 * ndx + 3].center = position + tcu::Vec2(-0.07f, 0.07f);
3180             tessellatedPoints[4 * ndx + 3].size   = currentPoints[ndx].size;
3181             tessellatedPoints[4 * ndx + 3].even   = currentPoints[ndx].even;
3182         }
3183 
3184         currentPoints.swap(tessellatedPoints);
3185     }
3186 
3187     // geometry
3188     if (m_hasGeometryStage)
3189     {
3190         std::vector<GeneratedPoint> geometryShadedPoints;
3191 
3192         geometryShadedPoints.resize(currentPoints.size() * 3);
3193         for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
3194         {
3195             const tcu::Vec2 position =
3196                 tcu::Vec2(1.0f - currentPoints[ndx].center.x(), currentPoints[ndx].center.y()); // mirror X
3197 
3198             geometryShadedPoints[3 * ndx + 0].center = position + tcu::Vec2(0.05f, 0.03f);
3199             geometryShadedPoints[3 * ndx + 0].size   = currentPoints[ndx].size;
3200             geometryShadedPoints[3 * ndx + 0].even   = currentPoints[ndx].even;
3201 
3202             geometryShadedPoints[3 * ndx + 1].center = position + tcu::Vec2(-0.01f, -0.02f);
3203             geometryShadedPoints[3 * ndx + 1].size   = currentPoints[ndx].size;
3204             geometryShadedPoints[3 * ndx + 1].even   = currentPoints[ndx].even;
3205 
3206             geometryShadedPoints[3 * ndx + 2].center = position + tcu::Vec2(-0.05f, 0.02f);
3207             geometryShadedPoints[3 * ndx + 2].size   = currentPoints[ndx].size;
3208             geometryShadedPoints[3 * ndx + 2].even   = currentPoints[ndx].even;
3209         }
3210 
3211         currentPoints.swap(geometryShadedPoints);
3212     }
3213 
3214     // sort from left to right, top to bottom
3215     std::sort(currentPoints.begin(), currentPoints.end(), PointSorter());
3216 
3217     // map to pattern space
3218     for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx)
3219         currentPoints[ndx].center = currentPoints[ndx].center * config.patternSize + config.patternPos;
3220 
3221     currentPoints.swap(data);
3222 }
3223 
verifyNarrowPointPattern(const tcu::Surface & viewport,const std::vector<GeneratedPoint> & refPoints,const ProjectedBBox & bbox,int & logFloodCounter)3224 bool PointRenderCase::verifyNarrowPointPattern(const tcu::Surface &viewport,
3225                                                const std::vector<GeneratedPoint> &refPoints, const ProjectedBBox &bbox,
3226                                                int &logFloodCounter)
3227 {
3228     bool anyError = false;
3229 
3230     // check that there is something near each sample
3231     for (int pointNdx = 0; pointNdx < (int)refPoints.size(); ++pointNdx)
3232     {
3233         const float epsilon            = 1.0e-6f;
3234         const GeneratedPoint &refPoint = refPoints[pointNdx];
3235 
3236         // skip points not in the the bbox, treat boundary as "in"
3237         if (refPoint.center.x() < bbox.min.x() - epsilon || refPoint.center.y() < bbox.min.y() - epsilon ||
3238             refPoint.center.x() > bbox.max.x() + epsilon || refPoint.center.y() > bbox.max.y() + epsilon)
3239             continue;
3240         else
3241         {
3242             // transform to viewport coords
3243             const tcu::IVec2 pixelCenter(
3244                 deRoundFloatToInt32((refPoint.center.x() * 0.5f + 0.5f) * (float)viewport.getWidth()),
3245                 deRoundFloatToInt32((refPoint.center.y() * 0.5f + 0.5f) * (float)viewport.getHeight()));
3246 
3247             // find rasterized point in the result
3248             if (pixelCenter.x() < 1 || pixelCenter.y() < 1 || pixelCenter.x() >= viewport.getWidth() - 1 ||
3249                 pixelCenter.y() >= viewport.getHeight() - 1)
3250             {
3251                 // viewport boundary, assume point is fine
3252             }
3253             else
3254             {
3255                 const int componentNdx = (refPoint.even) ? (1) : (2); // analyze either green or blue channel
3256                 bool foundResult       = false;
3257 
3258                 // check neighborhood
3259                 for (int dy = -1; dy < 2 && !foundResult; ++dy)
3260                     for (int dx = -1; dx < 2 && !foundResult; ++dx)
3261                     {
3262                         const tcu::IVec2 testPos(pixelCenter.x() + dx, pixelCenter.y() + dy);
3263                         const tcu::RGBA color = viewport.getPixel(testPos.x(), testPos.y());
3264 
3265                         if (color.toIVec()[componentNdx] > 0)
3266                             foundResult = true;
3267                     }
3268 
3269                 if (!foundResult)
3270                 {
3271                     anyError = true;
3272 
3273                     if (--logFloodCounter >= 0)
3274                     {
3275                         m_testCtx.getLog() << tcu::TestLog::Message << "Missing point near " << pixelCenter
3276                                            << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "."
3277                                            << tcu::TestLog::EndMessage;
3278                     }
3279                 }
3280             }
3281         }
3282     }
3283 
3284     return anyError;
3285 }
3286 
verifyWidePointPattern(const tcu::Surface & viewport,const std::vector<GeneratedPoint> & refPoints,const ProjectedBBox & bbox,int & logFloodCounter)3287 bool PointRenderCase::verifyWidePointPattern(const tcu::Surface &viewport, const std::vector<GeneratedPoint> &refPoints,
3288                                              const ProjectedBBox &bbox, int &logFloodCounter)
3289 {
3290     bool anyError = false;
3291 
3292     // check that there is something near each sample
3293     for (int pointNdx = 0; pointNdx < (int)refPoints.size(); ++pointNdx)
3294     {
3295         const GeneratedPoint &refPoint = refPoints[pointNdx];
3296 
3297         if (refPoint.center.x() >= bbox.min.x() && refPoint.center.y() >= bbox.min.y() &&
3298             refPoint.center.x() <= bbox.max.x() && refPoint.center.y() <= bbox.max.y())
3299         {
3300             // point fully in the bounding box
3301             anyError |= !verifyWidePoint(viewport, refPoint, bbox, POINT_FULL, logFloodCounter);
3302         }
3303         else if (refPoint.center.x() >= bbox.min.x() + (float)refPoint.size / 2.0f &&
3304                  refPoint.center.y() >= bbox.min.y() - (float)refPoint.size / 2.0f &&
3305                  refPoint.center.x() <= bbox.max.x() + (float)refPoint.size / 2.0f &&
3306                  refPoint.center.y() <= bbox.max.y() - (float)refPoint.size / 2.0f)
3307         {
3308             // point leaks into bounding box
3309             anyError |= !verifyWidePoint(viewport, refPoint, bbox, POINT_PARTIAL, logFloodCounter);
3310         }
3311     }
3312 
3313     return anyError;
3314 }
3315 
verifyWidePoint(const tcu::Surface & viewport,const GeneratedPoint & refPoint,const ProjectedBBox & bbox,ResultPointType pointType,int & logFloodCounter)3316 bool PointRenderCase::verifyWidePoint(const tcu::Surface &viewport, const GeneratedPoint &refPoint,
3317                                       const ProjectedBBox &bbox, ResultPointType pointType, int &logFloodCounter)
3318 {
3319     const int componentNdx       = (refPoint.even) ? (1) : (2);
3320     const int halfPointSizeCeil  = (refPoint.size + 1) / 2;
3321     const int halfPointSizeFloor = (refPoint.size + 1) / 2;
3322     const tcu::IVec4 viewportBBoxArea =
3323         getViewportBoundingBoxArea(bbox, tcu::IVec2(viewport.getWidth(), viewport.getHeight()), (float)refPoint.size);
3324     const tcu::IVec4 verificationArea = tcu::IVec4(de::max(viewportBBoxArea.x(), 0), de::max(viewportBBoxArea.y(), 0),
3325                                                    de::min(viewportBBoxArea.z(), viewport.getWidth()),
3326                                                    de::min(viewportBBoxArea.w(), viewport.getHeight()));
3327     const tcu::IVec2 pointPos =
3328         tcu::IVec2(deRoundFloatToInt32((refPoint.center.x() * 0.5f + 0.5f) * (float)viewport.getWidth()),
3329                    deRoundFloatToInt32((refPoint.center.y() * 0.5f + 0.5f) * (float)viewport.getHeight()));
3330 
3331     // find any fragment within the point that is inside the bbox, start search at the center
3332 
3333     if (pointPos.x() >= verificationArea.x() && pointPos.y() >= verificationArea.y() &&
3334         pointPos.x() < verificationArea.z() && pointPos.y() < verificationArea.w())
3335     {
3336         if (viewport.getPixel(pointPos.x(), pointPos.y()).toIVec()[componentNdx])
3337             return verifyWidePointAt(pointPos, viewport, refPoint, verificationArea, pointType, componentNdx,
3338                                      logFloodCounter);
3339     }
3340 
3341     for (int dy = -halfPointSizeCeil; dy <= halfPointSizeCeil; ++dy)
3342         for (int dx = -halfPointSizeCeil; dx <= halfPointSizeCeil; ++dx)
3343         {
3344             const tcu::IVec2 testPos = pointPos + tcu::IVec2(dx, dy);
3345 
3346             if (dx == 0 && dy == 0)
3347                 continue;
3348 
3349             if (testPos.x() >= verificationArea.x() && testPos.y() >= verificationArea.y() &&
3350                 testPos.x() < verificationArea.z() && testPos.y() < verificationArea.w())
3351             {
3352                 if (viewport.getPixel(testPos.x(), testPos.y()).toIVec()[componentNdx])
3353                     return verifyWidePointAt(testPos, viewport, refPoint, verificationArea, pointType, componentNdx,
3354                                              logFloodCounter);
3355             }
3356         }
3357 
3358     // could not find point, this is only ok near boundaries
3359     if (pointPos.x() + halfPointSizeFloor < verificationArea.x() - 1 ||
3360         pointPos.y() + halfPointSizeFloor < verificationArea.y() - 1 ||
3361         pointPos.x() - halfPointSizeFloor >= verificationArea.z() - 1 ||
3362         pointPos.y() - halfPointSizeFloor >= verificationArea.w() - 1)
3363         return true;
3364 
3365     if (--logFloodCounter >= 0)
3366     {
3367         m_testCtx.getLog() << tcu::TestLog::Message << "Missing wide point near " << pointPos
3368                            << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "."
3369                            << tcu::TestLog::EndMessage;
3370     }
3371 
3372     return false;
3373 }
3374 
verifyWidePointAt(const tcu::IVec2 & pointPos,const tcu::Surface & viewport,const GeneratedPoint & refPoint,const tcu::IVec4 & bbox,ResultPointType pointType,int componentNdx,int & logFloodCounter)3375 bool PointRenderCase::verifyWidePointAt(const tcu::IVec2 &pointPos, const tcu::Surface &viewport,
3376                                         const GeneratedPoint &refPoint, const tcu::IVec4 &bbox,
3377                                         ResultPointType pointType, int componentNdx, int &logFloodCounter)
3378 {
3379     const int expectedPointSize = refPoint.size;
3380     bool viewportClippedTop     = false;
3381     bool viewportClippedBottom  = false;
3382     bool primitiveClippedTop    = false;
3383     bool primitiveClippedBottom = false;
3384     std::vector<tcu::IVec2> widthsUpwards;
3385     std::vector<tcu::IVec2> widthsDownwards;
3386     std::vector<tcu::IVec2> widths;
3387 
3388     // search upwards
3389     for (int y = pointPos.y();; --y)
3390     {
3391         if (y < bbox.y() || y < 0)
3392         {
3393             if (y < bbox.y())
3394                 primitiveClippedTop = true;
3395             if (y < 0)
3396                 viewportClippedTop = true;
3397             break;
3398         }
3399         else if (pointPos.y() - y > expectedPointSize)
3400         {
3401             // no need to go further than point height
3402             break;
3403         }
3404         else if (viewport.getPixel(pointPos.x(), y).toIVec()[componentNdx] == 0)
3405         {
3406             break;
3407         }
3408         else
3409         {
3410             widthsUpwards.push_back(
3411                 scanPointWidthAt(tcu::IVec2(pointPos.x(), y), viewport, expectedPointSize, componentNdx));
3412         }
3413     }
3414 
3415     // top is clipped
3416     if ((viewportClippedTop || (pointType == POINT_PARTIAL && primitiveClippedTop)) && !widthsUpwards.empty())
3417     {
3418         const tcu::IVec2 &range = widthsUpwards.back();
3419         const bool squareFits   = (range.y() - range.x() + 1) >= expectedPointSize;
3420         const bool widthClipped = (pointType == POINT_PARTIAL) && (range.x() <= bbox.x() || range.y() >= bbox.z());
3421 
3422         if (squareFits || widthClipped)
3423             return true;
3424     }
3425 
3426     // and downwards
3427     for (int y = pointPos.y() + 1;; ++y)
3428     {
3429         if (y >= bbox.w() || y >= viewport.getHeight())
3430         {
3431             if (y >= bbox.w())
3432                 primitiveClippedBottom = true;
3433             if (y >= viewport.getHeight())
3434                 viewportClippedBottom = true;
3435             break;
3436         }
3437         else if (y - pointPos.y() > expectedPointSize)
3438         {
3439             // no need to go further than point height
3440             break;
3441         }
3442         else if (viewport.getPixel(pointPos.x(), y).toIVec()[componentNdx] == 0)
3443         {
3444             break;
3445         }
3446         else
3447         {
3448             widthsDownwards.push_back(
3449                 scanPointWidthAt(tcu::IVec2(pointPos.x(), y), viewport, expectedPointSize, componentNdx));
3450         }
3451     }
3452 
3453     // bottom is clipped
3454     if ((viewportClippedBottom || (pointType == POINT_PARTIAL && primitiveClippedBottom)) &&
3455         !(widthsDownwards.empty() && widthsUpwards.empty()))
3456     {
3457         const tcu::IVec2 &range = (widthsDownwards.empty()) ? (widthsUpwards.front()) : (widthsDownwards.back());
3458         const bool squareFits   = (range.y() - range.x() + 1) >= expectedPointSize;
3459         const bool bboxClipped  = (pointType == POINT_PARTIAL) && (range.x() <= bbox.x() || range.y() >= bbox.z() - 1);
3460         const bool viewportClipped = range.x() <= 0 || range.y() >= viewport.getWidth() - 1;
3461 
3462         if (squareFits || bboxClipped || viewportClipped)
3463             return true;
3464     }
3465 
3466     // would square point would fit into the rasterized area
3467 
3468     for (int ndx = 0; ndx < (int)widthsUpwards.size(); ++ndx)
3469         widths.push_back(widthsUpwards[(int)widthsUpwards.size() - ndx - 1]);
3470     for (int ndx = 0; ndx < (int)widthsDownwards.size(); ++ndx)
3471         widths.push_back(widthsDownwards[ndx]);
3472     DE_ASSERT(!widths.empty());
3473 
3474     for (int y = 0; y < (int)widths.size() - expectedPointSize + 1; ++y)
3475     {
3476         tcu::IVec2 unionRange = widths[y];
3477 
3478         for (int dy = 1; dy < expectedPointSize; ++dy)
3479         {
3480             unionRange.x() = de::max(unionRange.x(), widths[y + dy].x());
3481             unionRange.y() = de::min(unionRange.y(), widths[y + dy].y());
3482         }
3483 
3484         // would a N x N block fit here?
3485         {
3486             const bool squareFits = (unionRange.y() - unionRange.x() + 1) >= expectedPointSize;
3487             const bool bboxClipped =
3488                 (pointType == POINT_PARTIAL) && (unionRange.x() <= bbox.x() || unionRange.y() >= bbox.z() - 1);
3489             const bool viewportClipped = unionRange.x() <= 0 || unionRange.y() >= viewport.getWidth() - 1;
3490 
3491             if (squareFits || bboxClipped || viewportClipped)
3492                 return true;
3493         }
3494     }
3495 
3496     if (--logFloodCounter >= 0)
3497     {
3498         m_testCtx.getLog() << tcu::TestLog::Message << "Missing " << expectedPointSize << "x" << expectedPointSize
3499                            << " point near " << pointPos << ", vertex coordinates=" << refPoint.center.swizzle(0, 1)
3500                            << "." << tcu::TestLog::EndMessage;
3501     }
3502     return false;
3503 }
3504 
scanPointWidthAt(const tcu::IVec2 & pointPos,const tcu::Surface & viewport,int expectedPointSize,int componentNdx) const3505 tcu::IVec2 PointRenderCase::scanPointWidthAt(const tcu::IVec2 &pointPos, const tcu::Surface &viewport,
3506                                              int expectedPointSize, int componentNdx) const
3507 {
3508     int minX = pointPos.x();
3509     int maxX = pointPos.x();
3510 
3511     // search horizontally for a point edges
3512     for (int x = pointPos.x() - 1; x >= 0; --x)
3513     {
3514         if (viewport.getPixel(x, pointPos.y()).toIVec()[componentNdx] == 0)
3515             break;
3516 
3517         // no need to go further than point width
3518         if (pointPos.x() - x > expectedPointSize)
3519             break;
3520 
3521         minX = x;
3522     }
3523     for (int x = pointPos.x() + 1; x < viewport.getWidth(); ++x)
3524     {
3525         if (viewport.getPixel(x, pointPos.y()).toIVec()[componentNdx] == 0)
3526             break;
3527 
3528         // no need to go further than point width
3529         if (x - pointPos.x() > expectedPointSize)
3530             break;
3531 
3532         maxX = x;
3533     }
3534 
3535     return tcu::IVec2(minX, maxX);
3536 }
3537 
3538 class BlitFboCase : public TestCase
3539 {
3540 public:
3541     enum RenderTarget
3542     {
3543         TARGET_DEFAULT = 0,
3544         TARGET_FBO,
3545 
3546         TARGET_LAST
3547     };
3548 
3549     BlitFboCase(Context &context, const char *name, const char *description, RenderTarget src, RenderTarget dst);
3550     ~BlitFboCase(void);
3551 
3552 private:
3553     enum
3554     {
3555         FBO_SIZE = 256,
3556     };
3557 
3558     struct BlitArgs
3559     {
3560         tcu::IVec4 src;
3561         tcu::IVec4 dst;
3562         tcu::Vec4 bboxMin;
3563         tcu::Vec4 bboxMax;
3564         bool linear;
3565     };
3566 
3567     void init(void);
3568     void deinit(void);
3569     IterateResult iterate(void);
3570 
3571     void fillSourceWithPattern(void);
3572     bool verifyImage(const BlitArgs &args);
3573 
3574     const RenderTarget m_src;
3575     const RenderTarget m_dst;
3576 
3577     std::vector<BlitArgs> m_iterations;
3578     int m_iteration;
3579     de::MovePtr<glu::Framebuffer> m_srcFbo;
3580     de::MovePtr<glu::Framebuffer> m_dstFbo;
3581     de::MovePtr<glu::Renderbuffer> m_srcRbo;
3582     de::MovePtr<glu::Renderbuffer> m_dstRbo;
3583     de::MovePtr<glu::ShaderProgram> m_program;
3584     de::MovePtr<glu::Buffer> m_vbo;
3585     glw::GLuint m_vao;
3586 };
3587 
BlitFboCase(Context & context,const char * name,const char * description,RenderTarget src,RenderTarget dst)3588 BlitFboCase::BlitFboCase(Context &context, const char *name, const char *description, RenderTarget src,
3589                          RenderTarget dst)
3590     : TestCase(context, name, description)
3591     , m_src(src)
3592     , m_dst(dst)
3593     , m_iteration(0)
3594     , m_vao(0)
3595 {
3596     DE_ASSERT(src < TARGET_LAST);
3597     DE_ASSERT(dst < TARGET_LAST);
3598 }
3599 
~BlitFboCase(void)3600 BlitFboCase::~BlitFboCase(void)
3601 {
3602     deinit();
3603 }
3604 
init(void)3605 void BlitFboCase::init(void)
3606 {
3607     const int numIterations          = 12;
3608     const bool defaultFBMultisampled = (m_context.getRenderTarget().getNumSamples() > 1);
3609     const glw::Functions &gl         = m_context.getRenderContext().getFunctions();
3610     de::Random rnd(0xABC123);
3611 
3612     m_testCtx.getLog() << tcu::TestLog::Message << "Using BlitFramebuffer to blit area from "
3613                        << ((m_src == TARGET_DEFAULT) ? ("default fb") : ("fbo")) << " to "
3614                        << ((m_dst == TARGET_DEFAULT) ? ("default fb") : ("fbo")) << ".\n"
3615                        << "Varying blit arguments and primitive bounding box between iterations.\n"
3616                        << "Expecting bounding box to have no effect on blitting.\n"
3617                        << "Source framebuffer is filled with green-yellow grid.\n"
3618                        << tcu::TestLog::EndMessage;
3619 
3620     if (!boundingBoxSupported(m_context))
3621         throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
3622 
3623     if (m_dst == TARGET_DEFAULT && defaultFBMultisampled)
3624         throw tcu::NotSupportedError("Test requires non-multisampled default framebuffer");
3625 
3626     // resources
3627 
3628     if (m_src == TARGET_FBO)
3629     {
3630         m_srcRbo = de::MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(m_context.getRenderContext()));
3631         gl.bindRenderbuffer(GL_RENDERBUFFER, **m_srcRbo);
3632         gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, FBO_SIZE, FBO_SIZE);
3633         GLU_EXPECT_NO_ERROR(gl.getError(), "src rbo");
3634 
3635         m_srcFbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext()));
3636         gl.bindFramebuffer(GL_FRAMEBUFFER, **m_srcFbo);
3637         gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **m_srcRbo);
3638         GLU_EXPECT_NO_ERROR(gl.getError(), "src fbo");
3639     }
3640 
3641     if (m_dst == TARGET_FBO)
3642     {
3643         m_dstRbo = de::MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(m_context.getRenderContext()));
3644         gl.bindRenderbuffer(GL_RENDERBUFFER, **m_dstRbo);
3645         gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, FBO_SIZE, FBO_SIZE);
3646         GLU_EXPECT_NO_ERROR(gl.getError(), "dst rbo");
3647 
3648         m_dstFbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext()));
3649         gl.bindFramebuffer(GL_FRAMEBUFFER, **m_dstFbo);
3650         gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **m_dstRbo);
3651         GLU_EXPECT_NO_ERROR(gl.getError(), "dst fbo");
3652     }
3653 
3654     {
3655         const char *const vertexSource   = "${GLSL_VERSION_DECL}\n"
3656                                            "in highp vec4 a_position;\n"
3657                                            "out highp vec4 v_position;\n"
3658                                            "void main()\n"
3659                                            "{\n"
3660                                            "    gl_Position = a_position;\n"
3661                                            "    v_position = a_position;\n"
3662                                            "}\n";
3663         const char *const fragmentSource = "${GLSL_VERSION_DECL}\n"
3664                                            "in mediump vec4 v_position;\n"
3665                                            "layout(location=0) out mediump vec4 dEQP_FragColor;\n"
3666                                            "void main()\n"
3667                                            "{\n"
3668                                            "    const mediump vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
3669                                            "    const mediump vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
3670                                            "    dEQP_FragColor = (step(0.1, mod(v_position.x, 0.2)) == step(0.1, "
3671                                            "mod(v_position.y, 0.2))) ? (green) : (yellow);\n"
3672                                            "}\n";
3673 
3674         m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(
3675             m_context.getRenderContext(), glu::ProgramSources()
3676                                               << glu::VertexSource(specializeShader(m_context, vertexSource))
3677                                               << glu::FragmentSource(specializeShader(m_context, fragmentSource))));
3678 
3679         if (!m_program->isOk())
3680         {
3681             m_testCtx.getLog() << *m_program;
3682             throw tcu::TestError("failed to build program");
3683         }
3684     }
3685 
3686     // Generate VAO for desktop OpenGL
3687     if (!glu::isContextTypeES(m_context.getRenderContext().getType()))
3688     {
3689         gl.genVertexArrays(1, &m_vao);
3690         gl.bindVertexArray(m_vao);
3691     }
3692 
3693     {
3694         static const tcu::Vec4 s_quadCoords[] = {
3695             tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f),
3696             tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f),
3697             tcu::Vec4(1.0f, -1.0f, 0.0f, 1.0f),
3698             tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f),
3699         };
3700 
3701         m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
3702 
3703         gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
3704         gl.bufferData(GL_ARRAY_BUFFER, sizeof(s_quadCoords), s_quadCoords, GL_STATIC_DRAW);
3705         GLU_EXPECT_NO_ERROR(gl.getError(), "set buf");
3706     }
3707 
3708     // gen iterations
3709 
3710     {
3711         const tcu::IVec2 srcSize =
3712             (m_src == TARGET_DEFAULT) ?
3713                 (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) :
3714                 (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3715         const tcu::IVec2 dstSize =
3716             (m_dst == TARGET_DEFAULT) ?
3717                 (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) :
3718                 (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3719 
3720         m_testCtx.getLog() << tcu::TestLog::Message << "srcSize = " << srcSize << "\n"
3721                            << "dstSize = " << dstSize << "\n"
3722                            << tcu::TestLog::EndMessage;
3723 
3724         for (int ndx = 0; ndx < numIterations; ++ndx)
3725         {
3726             BlitArgs args;
3727 
3728             if (m_src == TARGET_DEFAULT && defaultFBMultisampled)
3729             {
3730                 const tcu::IVec2 unionSize =
3731                     tcu::IVec2(de::min(srcSize.x(), dstSize.x()), de::min(srcSize.y(), dstSize.y()));
3732                 const int srcWidth  = rnd.getInt(1, unionSize.x());
3733                 const int srcHeight = rnd.getInt(1, unionSize.y());
3734                 const int srcX      = rnd.getInt(0, unionSize.x() - srcWidth);
3735                 const int srcY      = rnd.getInt(0, unionSize.y() - srcHeight);
3736 
3737                 args.src.x() = srcX;
3738                 args.src.y() = srcY;
3739                 args.src.z() = srcX + srcWidth;
3740                 args.src.w() = srcY + srcHeight;
3741 
3742                 args.dst = args.src;
3743             }
3744             else
3745             {
3746                 const int srcWidth  = rnd.getInt(1, srcSize.x());
3747                 const int srcHeight = rnd.getInt(1, srcSize.y());
3748                 const int srcX      = rnd.getInt(0, srcSize.x() - srcWidth);
3749                 const int srcY      = rnd.getInt(0, srcSize.y() - srcHeight);
3750                 const int dstWidth  = rnd.getInt(1, dstSize.x());
3751                 const int dstHeight = rnd.getInt(1, dstSize.y());
3752                 const int dstX =
3753                     rnd.getInt(-(dstWidth / 2), dstSize.x() - (dstWidth + 1) / 2); // allow dst go out of bounds
3754                 const int dstY = rnd.getInt(-(dstHeight / 2), dstSize.y() - (dstHeight + 1) / 2);
3755 
3756                 args.src.x() = srcX;
3757                 args.src.y() = srcY;
3758                 args.src.z() = srcX + srcWidth;
3759                 args.src.w() = srcY + srcHeight;
3760                 args.dst.x() = dstX;
3761                 args.dst.y() = dstY;
3762                 args.dst.z() = dstX + dstWidth;
3763                 args.dst.w() = dstY + dstHeight;
3764             }
3765 
3766             args.bboxMin.x() = rnd.getFloat(-1.1f, 1.1f);
3767             args.bboxMin.y() = rnd.getFloat(-1.1f, 1.1f);
3768             args.bboxMin.z() = rnd.getFloat(-1.1f, 1.1f);
3769             args.bboxMin.w() = rnd.getFloat(0.9f, 1.1f);
3770 
3771             args.bboxMax.x() = rnd.getFloat(-1.1f, 1.1f);
3772             args.bboxMax.y() = rnd.getFloat(-1.1f, 1.1f);
3773             args.bboxMax.z() = rnd.getFloat(-1.1f, 1.1f);
3774             args.bboxMax.w() = rnd.getFloat(0.9f, 1.1f);
3775 
3776             if (args.bboxMin.x() / args.bboxMin.w() > args.bboxMax.x() / args.bboxMax.w())
3777                 std::swap(args.bboxMin.x(), args.bboxMax.x());
3778             if (args.bboxMin.y() / args.bboxMin.w() > args.bboxMax.y() / args.bboxMax.w())
3779                 std::swap(args.bboxMin.y(), args.bboxMax.y());
3780             if (args.bboxMin.z() / args.bboxMin.w() > args.bboxMax.z() / args.bboxMax.w())
3781                 std::swap(args.bboxMin.z(), args.bboxMax.z());
3782 
3783             args.linear = rnd.getBool();
3784 
3785             m_iterations.push_back(args);
3786         }
3787     }
3788 }
3789 
deinit(void)3790 void BlitFboCase::deinit(void)
3791 {
3792     m_srcFbo.clear();
3793     m_srcRbo.clear();
3794     m_dstFbo.clear();
3795     m_dstRbo.clear();
3796     m_program.clear();
3797     m_vbo.clear();
3798 
3799     if (m_vao)
3800     {
3801         m_context.getRenderContext().getFunctions().deleteVertexArrays(1, &m_vao);
3802         m_vao = 0;
3803     }
3804 }
3805 
iterate(void)3806 BlitFboCase::IterateResult BlitFboCase::iterate(void)
3807 {
3808     const tcu::ScopedLogSection section(m_testCtx.getLog(), "Iteration" + de::toString(m_iteration),
3809                                         "Iteration " + de::toString(m_iteration + 1) + " / " +
3810                                             de::toString((int)m_iterations.size()));
3811     const BlitArgs &blitCfg  = m_iterations[m_iteration];
3812     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
3813 
3814     if (m_iteration == 0)
3815         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3816 
3817     // fill source with test pattern. Default fb must be filled for each iteration because contents might not survive the swap
3818     if (m_src == TARGET_DEFAULT || m_iteration == 0)
3819         fillSourceWithPattern();
3820 
3821     m_testCtx.getLog() << tcu::TestLog::Message << "Set bounding box:\n"
3822                        << "\tmin:" << blitCfg.bboxMin << "\n"
3823                        << "\tmax:" << blitCfg.bboxMax << "\n"
3824                        << "Blit:\n"
3825                        << "\tsrc: " << blitCfg.src << "\n"
3826                        << "\tdst: " << blitCfg.dst << "\n"
3827                        << "\tfilter: " << ((blitCfg.linear) ? ("linear") : ("nearest")) << tcu::TestLog::EndMessage;
3828 
3829     auto boundingBoxFunc = getBoundingBoxFunction(m_context);
3830 
3831     boundingBoxFunc(blitCfg.bboxMin.x(), blitCfg.bboxMin.y(), blitCfg.bboxMin.z(), blitCfg.bboxMin.w(),
3832                     blitCfg.bboxMax.x(), blitCfg.bboxMax.y(), blitCfg.bboxMax.z(), blitCfg.bboxMax.w());
3833     gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER,
3834                        (m_dst == TARGET_FBO) ? (**m_dstFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3835     gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3836     gl.clear(GL_COLOR_BUFFER_BIT);
3837 
3838     gl.bindFramebuffer(GL_READ_FRAMEBUFFER,
3839                        (m_src == TARGET_FBO) ? (**m_srcFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3840     gl.blitFramebuffer(blitCfg.src.x(), blitCfg.src.y(), blitCfg.src.z(), blitCfg.src.w(), blitCfg.dst.x(),
3841                        blitCfg.dst.y(), blitCfg.dst.z(), blitCfg.dst.w(), GL_COLOR_BUFFER_BIT,
3842                        ((blitCfg.linear) ? (GL_LINEAR) : (GL_NEAREST)));
3843     GLU_EXPECT_NO_ERROR(gl.getError(), "blit");
3844 
3845     if (!verifyImage(blitCfg))
3846         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected blit result");
3847 
3848     return (++m_iteration == (int)m_iterations.size()) ? (STOP) : (CONTINUE);
3849 }
3850 
verifyImage(const BlitArgs & args)3851 bool BlitFboCase::verifyImage(const BlitArgs &args)
3852 {
3853     const int colorThreshold = 4; //!< this test case is not about how color is preserved, allow almost anything
3854     const tcu::IVec2 dstSize =
3855         (m_dst == TARGET_DEFAULT) ?
3856             (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) :
3857             (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3858     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
3859     tcu::Surface viewport(dstSize.x(), dstSize.y());
3860     tcu::Surface errorMask(dstSize.x(), dstSize.y());
3861     bool anyError = false;
3862 
3863     m_testCtx.getLog() << tcu::TestLog::Message << "Verifying blit result" << tcu::TestLog::EndMessage;
3864 
3865     gl.bindFramebuffer(GL_READ_FRAMEBUFFER,
3866                        (m_dst == TARGET_FBO) ? (**m_dstFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3867     glu::readPixels(m_context.getRenderContext(), 0, 0, viewport.getAccess());
3868 
3869     tcu::clear(errorMask.getAccess(), tcu::IVec4(0, 0, 0, 255));
3870 
3871     for (int y = 0; y < dstSize.y(); ++y)
3872         for (int x = 0; x < dstSize.x(); ++x)
3873         {
3874             const tcu::RGBA color = viewport.getPixel(x, y);
3875             const bool inside     = (x >= args.dst.x() && x < args.dst.z() && y >= args.dst.y() && y < args.dst.w());
3876             const bool error      = (inside) ?
3877                                         (color.getGreen() < 255 - colorThreshold || color.getBlue() > colorThreshold) :
3878                                         (color.getRed() > colorThreshold || color.getGreen() > colorThreshold ||
3879                                     color.getBlue() > colorThreshold);
3880 
3881             if (error)
3882             {
3883                 anyError = true;
3884                 errorMask.setPixel(x, y, tcu::RGBA::red());
3885             }
3886         }
3887 
3888     if (anyError)
3889     {
3890         m_testCtx.getLog() << tcu::TestLog::Message << "Image verification failed." << tcu::TestLog::EndMessage
3891                            << tcu::TestLog::ImageSet("Images", "Image verification")
3892                            << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
3893                            << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
3894                            << tcu::TestLog::EndImageSet;
3895         return false;
3896     }
3897     else
3898     {
3899         m_testCtx.getLog() << tcu::TestLog::Message << "Result image ok." << tcu::TestLog::EndMessage
3900                            << tcu::TestLog::ImageSet("Images", "Image verification")
3901                            << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
3902                            << tcu::TestLog::EndImageSet;
3903         return true;
3904     }
3905 }
3906 
fillSourceWithPattern(void)3907 void BlitFboCase::fillSourceWithPattern(void)
3908 {
3909     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
3910     const tcu::IVec2 srcSize =
3911         (m_src == TARGET_DEFAULT) ?
3912             (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) :
3913             (tcu::IVec2(FBO_SIZE, FBO_SIZE));
3914     const int posLocation = gl.getAttribLocation(m_program->getProgram(), "a_position");
3915 
3916     gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER,
3917                        (m_src == TARGET_FBO) ? (**m_srcFbo) : (m_context.getRenderContext().getDefaultFramebuffer()));
3918     gl.viewport(0, 0, srcSize.x(), srcSize.y());
3919     gl.useProgram(m_program->getProgram());
3920 
3921     gl.clearColor(0.0f, 0.0f, 1.0f, 1.0f);
3922     gl.clear(GL_COLOR_BUFFER_BIT);
3923 
3924     gl.enableVertexAttribArray(posLocation);
3925     gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 4 * (int)sizeof(float), NULL);
3926     gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
3927     GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
3928 }
3929 
3930 class DepthDrawCase : public TestCase
3931 {
3932 public:
3933     enum DepthType
3934     {
3935         DEPTH_BUILTIN = 0,
3936         DEPTH_USER_DEFINED,
3937 
3938         DEPTH_LAST
3939     };
3940     enum BBoxState
3941     {
3942         STATE_GLOBAL = 0,
3943         STATE_PER_PRIMITIVE,
3944 
3945         STATE_LAST
3946     };
3947     enum BBoxSize
3948     {
3949         BBOX_EQUAL = 0,
3950         BBOX_LARGER,
3951 
3952         BBOX_LAST
3953     };
3954 
3955     DepthDrawCase(Context &context, const char *name, const char *description, DepthType depthType, BBoxState state,
3956                   BBoxSize bboxSize);
3957     ~DepthDrawCase(void);
3958 
3959 private:
3960     void init(void);
3961     void deinit(void);
3962     IterateResult iterate(void);
3963 
3964     std::string genVertexSource(void) const;
3965     std::string genFragmentSource(void) const;
3966     std::string genTessellationControlSource(void) const;
3967     std::string genTessellationEvaluationSource(void) const;
3968     void generateAttributeData(std::vector<tcu::Vec4> &data) const;
3969     bool verifyImage(const tcu::Surface &viewport) const;
3970 
3971     enum
3972     {
3973         RENDER_AREA_SIZE = 256,
3974     };
3975 
3976     struct LayerInfo
3977     {
3978         float zOffset;
3979         float zScale;
3980         tcu::Vec4 color1;
3981         tcu::Vec4 color2;
3982     };
3983 
3984     const int m_numLayers;
3985     const int m_gridSize;
3986 
3987     const DepthType m_depthType;
3988     const BBoxState m_state;
3989     const BBoxSize m_bboxSize;
3990 
3991     de::MovePtr<glu::ShaderProgram> m_program;
3992     de::MovePtr<glu::Buffer> m_vbo;
3993     glw::GLuint m_vao;
3994     std::vector<LayerInfo> m_layers;
3995 };
3996 
DepthDrawCase(Context & context,const char * name,const char * description,DepthType depthType,BBoxState state,BBoxSize bboxSize)3997 DepthDrawCase::DepthDrawCase(Context &context, const char *name, const char *description, DepthType depthType,
3998                              BBoxState state, BBoxSize bboxSize)
3999     : TestCase(context, name, description)
4000     , m_numLayers(14)
4001     , m_gridSize(24)
4002     , m_depthType(depthType)
4003     , m_state(state)
4004     , m_bboxSize(bboxSize)
4005     , m_vao(0)
4006 {
4007     DE_ASSERT(depthType < DEPTH_LAST);
4008     DE_ASSERT(state < STATE_LAST);
4009     DE_ASSERT(bboxSize < BBOX_LAST);
4010 }
4011 
~DepthDrawCase(void)4012 DepthDrawCase::~DepthDrawCase(void)
4013 {
4014     deinit();
4015 }
4016 
init(void)4017 void DepthDrawCase::init(void)
4018 {
4019     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
4020     const bool hasES32OrGL45 = supportsES32OrGL45(m_context);
4021 
4022     // requirements
4023     if (!boundingBoxSupported(m_context))
4024         throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
4025     if (m_state == STATE_PER_PRIMITIVE && !hasES32OrGL45 &&
4026         !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
4027         throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
4028     if (m_context.getRenderTarget().getDepthBits() == 0)
4029         throw tcu::NotSupportedError("Test requires depth buffer");
4030     if (m_context.getRenderTarget().getWidth() < RENDER_AREA_SIZE ||
4031         m_context.getRenderTarget().getHeight() < RENDER_AREA_SIZE)
4032         throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_AREA_SIZE) + "x" +
4033                                      de::toString<int>(RENDER_AREA_SIZE) + " viewport");
4034 
4035     // log
4036     m_testCtx.getLog() << tcu::TestLog::Message
4037                        << "Rendering multiple triangle grids with with different z coordinates.\n"
4038                        << "Topmost grid is green-yellow, other grids are blue-red.\n"
4039                        << "Expecting only the green-yellow grid to be visible.\n"
4040                        << "Setting primitive bounding box "
4041                        << ((m_bboxSize == BBOX_EQUAL) ? ("to exactly cover") : ("to cover"))
4042                        << ((m_state == STATE_GLOBAL) ? (" each grid") : (" each triangle"))
4043                        << ((m_bboxSize == BBOX_EQUAL) ? (".") : (" and include some padding.")) << "\n"
4044                        << "Set bounding box using "
4045                        << ((m_state == STATE_GLOBAL) ? ("PRIMITIVE_BOUNDING_BOX_EXT state") :
4046                                                        ("gl_BoundingBoxEXT output"))
4047                        << "\n"
4048                        << ((m_depthType == DEPTH_USER_DEFINED) ? ("Fragment depth is set in the fragment shader") :
4049                                                                  (""))
4050                        << tcu::TestLog::EndMessage;
4051 
4052     // resources
4053 
4054     {
4055         glu::ProgramSources sources;
4056         sources << glu::VertexSource(specializeShader(m_context, genVertexSource().c_str()));
4057         sources << glu::FragmentSource(specializeShader(m_context, genFragmentSource().c_str()));
4058 
4059         if (m_state == STATE_PER_PRIMITIVE)
4060             sources << glu::TessellationControlSource(
4061                            specializeShader(m_context, genTessellationControlSource().c_str()))
4062                     << glu::TessellationEvaluationSource(
4063                            specializeShader(m_context, genTessellationEvaluationSource().c_str()));
4064 
4065         m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), sources));
4066         GLU_EXPECT_NO_ERROR(gl.getError(), "build program");
4067 
4068         {
4069             const tcu::ScopedLogSection section(m_testCtx.getLog(), "ShaderProgram", "Shader program");
4070             m_testCtx.getLog() << *m_program;
4071         }
4072 
4073         if (!m_program->isOk())
4074             throw tcu::TestError("failed to build program");
4075     }
4076 
4077     // Generate VAO for desktop OpenGL
4078     if (!glu::isContextTypeES(m_context.getRenderContext().getType()))
4079     {
4080         gl.genVertexArrays(1, &m_vao);
4081         gl.bindVertexArray(m_vao);
4082     }
4083 
4084     {
4085         std::vector<tcu::Vec4> data;
4086 
4087         generateAttributeData(data);
4088 
4089         m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
4090         gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
4091         gl.bufferData(GL_ARRAY_BUFFER, (int)(sizeof(tcu::Vec4) * data.size()), &data[0], GL_STATIC_DRAW);
4092         GLU_EXPECT_NO_ERROR(gl.getError(), "buf upload");
4093     }
4094 
4095     // gen layers
4096     {
4097         de::Random rnd(0x12345);
4098 
4099         m_layers.resize(m_numLayers);
4100         for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx)
4101         {
4102             m_layers[layerNdx].zOffset = ((float)layerNdx / (float)m_numLayers) * 2.0f - 1.0f;
4103             m_layers[layerNdx].zScale  = (2.0f / (float)m_numLayers);
4104             m_layers[layerNdx].color1 =
4105                 (layerNdx == 0) ? (tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f)) : (tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
4106             m_layers[layerNdx].color2 =
4107                 (layerNdx == 0) ? (tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f)) : (tcu::Vec4(1.0f, 0.0f, 1.0f, 1.0f));
4108         }
4109         rnd.shuffle(m_layers.begin(), m_layers.end());
4110     }
4111 }
4112 
deinit(void)4113 void DepthDrawCase::deinit(void)
4114 {
4115     m_program.clear();
4116     m_vbo.clear();
4117 
4118     if (m_vao)
4119     {
4120         m_context.getRenderContext().getFunctions().deleteVertexArrays(1, &m_vao);
4121         m_vao = 0;
4122     }
4123 }
4124 
iterate(void)4125 DepthDrawCase::IterateResult DepthDrawCase::iterate(void)
4126 {
4127     const bool hasTessellation          = (m_state == STATE_PER_PRIMITIVE);
4128     const glw::Functions &gl            = m_context.getRenderContext().getFunctions();
4129     const glw::GLint posLocation        = gl.getAttribLocation(m_program->getProgram(), "a_position");
4130     const glw::GLint colLocation        = gl.getAttribLocation(m_program->getProgram(), "a_colorMix");
4131     const glw::GLint depthBiasLocation  = gl.getUniformLocation(m_program->getProgram(), "u_depthBias");
4132     const glw::GLint depthScaleLocation = gl.getUniformLocation(m_program->getProgram(), "u_depthScale");
4133     const glw::GLint color1Location     = gl.getUniformLocation(m_program->getProgram(), "u_color1");
4134     const glw::GLint color2Location     = gl.getUniformLocation(m_program->getProgram(), "u_color2");
4135 
4136     tcu::Surface viewport(RENDER_AREA_SIZE, RENDER_AREA_SIZE);
4137     de::Random rnd(0x213237);
4138 
4139     TCU_CHECK(posLocation != -1);
4140     TCU_CHECK(colLocation != -1);
4141     TCU_CHECK(depthBiasLocation != -1);
4142     TCU_CHECK(depthScaleLocation != -1);
4143     TCU_CHECK(color1Location != -1);
4144     TCU_CHECK(color2Location != -1);
4145 
4146     gl.viewport(0, 0, RENDER_AREA_SIZE, RENDER_AREA_SIZE);
4147     gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
4148     gl.clearDepthf(1.0f);
4149     gl.depthFunc(GL_LESS);
4150     gl.enable(GL_DEPTH_TEST);
4151     gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
4152     GLU_EXPECT_NO_ERROR(gl.getError(), "setup viewport");
4153 
4154     auto boundingBoxFunc = getBoundingBoxFunction(m_context);
4155 
4156     gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
4157     gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, (int)(8 * sizeof(float)),
4158                            glu::BufferOffsetAsPointer(0 * sizeof(float)));
4159     gl.vertexAttribPointer(colLocation, 4, GL_FLOAT, GL_FALSE, (int)(8 * sizeof(float)),
4160                            glu::BufferOffsetAsPointer(4 * sizeof(float)));
4161     gl.enableVertexAttribArray(posLocation);
4162     gl.enableVertexAttribArray(colLocation);
4163     gl.useProgram(m_program->getProgram());
4164     GLU_EXPECT_NO_ERROR(gl.getError(), "setup va");
4165 
4166     if (hasTessellation)
4167         gl.patchParameteri(GL_PATCH_VERTICES, 3);
4168 
4169     for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx)
4170     {
4171         gl.uniform1f(depthBiasLocation, m_layers[layerNdx].zOffset);
4172         gl.uniform1f(depthScaleLocation, m_layers[layerNdx].zScale);
4173         gl.uniform4fv(color1Location, 1, m_layers[layerNdx].color1.getPtr());
4174         gl.uniform4fv(color2Location, 1, m_layers[layerNdx].color2.getPtr());
4175 
4176         if (m_state == STATE_GLOBAL)
4177         {
4178             const float negPadding = (m_bboxSize == BBOX_EQUAL) ? (0.0f) : (rnd.getFloat() * 0.3f);
4179             const float posPadding = (m_bboxSize == BBOX_EQUAL) ? (0.0f) : (rnd.getFloat() * 0.3f);
4180 
4181             boundingBoxFunc(-1.0f, -1.0f, m_layers[layerNdx].zOffset - negPadding, 1.0f, 1.0f, 1.0f,
4182                             (m_layers[layerNdx].zOffset + m_layers[layerNdx].zScale + posPadding), 1.0f);
4183         }
4184 
4185         gl.drawArrays((hasTessellation) ? (GL_PATCHES) : (GL_TRIANGLES), 0, m_gridSize * m_gridSize * 6);
4186     }
4187 
4188     glu::readPixels(m_context.getRenderContext(), 0, 0, viewport.getAccess());
4189     GLU_EXPECT_NO_ERROR(gl.getError(), "render and read");
4190 
4191     if (verifyImage(viewport))
4192         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4193     else
4194         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
4195 
4196     return STOP;
4197 }
4198 
genVertexSource(void) const4199 std::string DepthDrawCase::genVertexSource(void) const
4200 {
4201     const bool hasTessellation = (m_state == STATE_PER_PRIMITIVE);
4202     std::ostringstream buf;
4203 
4204     buf << "${GLSL_VERSION_DECL}\n"
4205            "in highp vec4 a_position;\n"
4206            "in highp vec4 a_colorMix;\n"
4207            "out highp vec4 vtx_colorMix;\n";
4208 
4209     if (!hasTessellation && m_depthType == DEPTH_USER_DEFINED)
4210         buf << "out highp float v_fragDepth;\n";
4211 
4212     if (!hasTessellation)
4213         buf << "uniform highp float u_depthBias;\n"
4214                "uniform highp float u_depthScale;\n";
4215 
4216     buf << "\n"
4217            "void main()\n"
4218            "{\n";
4219 
4220     if (hasTessellation)
4221         buf << "    gl_Position = a_position;\n";
4222     else if (m_depthType == DEPTH_USER_DEFINED)
4223         buf << "    highp float unusedZ = a_position.z;\n"
4224                "    highp float writtenZ = a_position.w;\n"
4225                "    gl_Position = vec4(a_position.xy, unusedZ, 1.0);\n"
4226                "    v_fragDepth = writtenZ * u_depthScale + u_depthBias;\n";
4227     else
4228         buf << "    highp float writtenZ = a_position.w;\n"
4229                "    gl_Position = vec4(a_position.xy, writtenZ * u_depthScale + u_depthBias, 1.0);\n";
4230 
4231     buf << "    vtx_colorMix = a_colorMix;\n"
4232            "}\n";
4233 
4234     return buf.str();
4235 }
4236 
genFragmentSource(void) const4237 std::string DepthDrawCase::genFragmentSource(void) const
4238 {
4239     const bool hasTessellation     = (m_state == STATE_PER_PRIMITIVE);
4240     const char *const colorMixName = (hasTessellation) ? ("tess_eval_colorMix") : ("vtx_colorMix");
4241     std::ostringstream buf;
4242 
4243     buf << "${GLSL_VERSION_DECL}\n"
4244            "in mediump vec4 "
4245         << colorMixName << ";\n";
4246 
4247     if (m_depthType == DEPTH_USER_DEFINED)
4248         buf << "in mediump float v_fragDepth;\n";
4249 
4250     buf << "layout(location = 0) out mediump vec4 o_color;\n"
4251            "uniform highp vec4 u_color1;\n"
4252            "uniform highp vec4 u_color2;\n"
4253            "\n"
4254            "void main()\n"
4255            "{\n"
4256            "    o_color = mix(u_color1, u_color2, "
4257         << colorMixName << ");\n";
4258 
4259     if (m_depthType == DEPTH_USER_DEFINED)
4260         buf << "    gl_FragDepth = v_fragDepth * 0.5 + 0.5;\n";
4261 
4262     buf << "}\n";
4263 
4264     return buf.str();
4265 }
4266 
genTessellationControlSource(void) const4267 std::string DepthDrawCase::genTessellationControlSource(void) const
4268 {
4269     std::ostringstream buf;
4270 
4271     buf << "${GLSL_VERSION_DECL}\n"
4272            "${ARB_ES32_COMPATIBILITY_REQUIRE}\n"
4273            "${TESSELLATION_SHADER_REQUIRE}\n"
4274            "${PRIMITIVE_BOUNDING_BOX_REQUIRE}\n"
4275            "layout(vertices=3) out;\n"
4276            "\n"
4277            "uniform highp float u_depthBias;\n"
4278            "uniform highp float u_depthScale;\n"
4279            "\n"
4280            "in highp vec4 vtx_colorMix[];\n"
4281            "out highp vec4 tess_ctrl_colorMix[];\n"
4282            "\n"
4283            "void main()\n"
4284            "{\n"
4285            "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
4286            "    tess_ctrl_colorMix[gl_InvocationID] = vtx_colorMix[0];\n"
4287            "\n"
4288            "    gl_TessLevelOuter[0] = 2.8;\n"
4289            "    gl_TessLevelOuter[1] = 2.8;\n"
4290            "    gl_TessLevelOuter[2] = 2.8;\n"
4291            "    gl_TessLevelInner[0] = 2.8;\n"
4292            "\n"
4293            "    // real Z stored in w component\n"
4294            "    highp vec4 minBound = vec4(min(min(vec3(gl_in[0].gl_Position.xy, gl_in[0].gl_Position.w * u_depthScale "
4295            "+ u_depthBias),\n"
4296            "                                       vec3(gl_in[1].gl_Position.xy, gl_in[1].gl_Position.w * u_depthScale "
4297            "+ u_depthBias)),\n"
4298            "                                   vec3(gl_in[2].gl_Position.xy, gl_in[2].gl_Position.w * u_depthScale + "
4299            "u_depthBias)), 1.0);\n"
4300            "    highp vec4 maxBound = vec4(max(max(vec3(gl_in[0].gl_Position.xy, gl_in[0].gl_Position.w * u_depthScale "
4301            "+ u_depthBias),\n"
4302            "                                       vec3(gl_in[1].gl_Position.xy, gl_in[1].gl_Position.w * u_depthScale "
4303            "+ u_depthBias)),\n"
4304            "                                   vec3(gl_in[2].gl_Position.xy, gl_in[2].gl_Position.w * u_depthScale + "
4305            "u_depthBias)), 1.0);\n";
4306 
4307     if (m_bboxSize == BBOX_EQUAL)
4308         buf << "    ${PRIM_GL_BOUNDING_BOX}[0] = minBound;\n"
4309                "    ${PRIM_GL_BOUNDING_BOX}[1] = maxBound;\n";
4310     else
4311         buf << "    highp float nedPadding = mod(gl_in[0].gl_Position.z, 0.3);\n"
4312                "    highp float posPadding = mod(gl_in[1].gl_Position.z, 0.3);\n"
4313                "    ${PRIM_GL_BOUNDING_BOX}[0] = minBound - vec4(0.0, 0.0, nedPadding, 0.0);\n"
4314                "    ${PRIM_GL_BOUNDING_BOX}[1] = maxBound + vec4(0.0, 0.0, posPadding, 0.0);\n";
4315 
4316     buf << "}\n";
4317 
4318     return buf.str();
4319 }
4320 
genTessellationEvaluationSource(void) const4321 std::string DepthDrawCase::genTessellationEvaluationSource(void) const
4322 {
4323     std::ostringstream buf;
4324 
4325     buf << "${GLSL_VERSION_DECL}\n"
4326            "${TESSELLATION_SHADER_REQUIRE}\n"
4327            "${GPU_SHADER5_REQUIRE}\n"
4328            "layout(triangles) in;\n"
4329            "\n"
4330            "in highp vec4 tess_ctrl_colorMix[];\n"
4331            "out highp vec4 tess_eval_colorMix;\n";
4332 
4333     if (m_depthType == DEPTH_USER_DEFINED)
4334         buf << "out highp float v_fragDepth;\n";
4335 
4336     buf << "uniform highp float u_depthBias;\n"
4337            "uniform highp float u_depthScale;\n"
4338            "\n"
4339            "precise gl_Position;\n"
4340            "\n"
4341            "void main()\n"
4342            "{\n"
4343            "    highp vec4 tessellatedPos = gl_TessCoord.x * gl_in[0].gl_Position + gl_TessCoord.y * "
4344            "gl_in[1].gl_Position + gl_TessCoord.z * gl_in[2].gl_Position;\n";
4345 
4346     if (m_depthType == DEPTH_USER_DEFINED)
4347         buf << "    highp float unusedZ = tessellatedPos.z;\n"
4348                "    highp float writtenZ = tessellatedPos.w;\n"
4349                "    gl_Position = vec4(tessellatedPos.xy, unusedZ, 1.0);\n"
4350                "    v_fragDepth = writtenZ * u_depthScale + u_depthBias;\n";
4351     else
4352         buf << "    highp float writtenZ = tessellatedPos.w;\n"
4353                "    gl_Position = vec4(tessellatedPos.xy, writtenZ * u_depthScale + u_depthBias, 1.0);\n";
4354 
4355     buf << "    tess_eval_colorMix = tess_ctrl_colorMix[0];\n"
4356            "}\n";
4357 
4358     return buf.str();
4359 }
4360 
generateAttributeData(std::vector<tcu::Vec4> & data) const4361 void DepthDrawCase::generateAttributeData(std::vector<tcu::Vec4> &data) const
4362 {
4363     const tcu::Vec4 color1(0.0f, 0.0f, 0.0f, 0.0f); // mix weights
4364     const tcu::Vec4 color2(1.0f, 1.0f, 1.0f, 1.0f);
4365     std::vector<int> cellOrder(m_gridSize * m_gridSize);
4366     de::Random rnd(0xAB54321);
4367 
4368     // generate grid with cells in random order
4369     for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
4370         cellOrder[ndx] = ndx;
4371     rnd.shuffle(cellOrder.begin(), cellOrder.end());
4372 
4373     data.resize(m_gridSize * m_gridSize * 6 * 2);
4374     for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
4375     {
4376         const int cellNdx          = cellOrder[ndx];
4377         const int cellX            = cellNdx % m_gridSize;
4378         const int cellY            = cellNdx / m_gridSize;
4379         const tcu::Vec4 &cellColor = ((cellX + cellY) % 2 == 0) ? (color1) : (color2);
4380 
4381         data[ndx * 6 * 2 + 0]  = tcu::Vec4(float(cellX + 0) / float(m_gridSize) * 2.0f - 1.0f,
4382                                            float(cellY + 0) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f);
4383         data[ndx * 6 * 2 + 1]  = cellColor;
4384         data[ndx * 6 * 2 + 2]  = tcu::Vec4(float(cellX + 1) / float(m_gridSize) * 2.0f - 1.0f,
4385                                            float(cellY + 1) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f);
4386         data[ndx * 6 * 2 + 3]  = cellColor;
4387         data[ndx * 6 * 2 + 4]  = tcu::Vec4(float(cellX + 0) / float(m_gridSize) * 2.0f - 1.0f,
4388                                            float(cellY + 1) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f);
4389         data[ndx * 6 * 2 + 5]  = cellColor;
4390         data[ndx * 6 * 2 + 6]  = tcu::Vec4(float(cellX + 0) / float(m_gridSize) * 2.0f - 1.0f,
4391                                            float(cellY + 0) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f);
4392         data[ndx * 6 * 2 + 7]  = cellColor;
4393         data[ndx * 6 * 2 + 8]  = tcu::Vec4(float(cellX + 1) / float(m_gridSize) * 2.0f - 1.0f,
4394                                            float(cellY + 0) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f);
4395         data[ndx * 6 * 2 + 9]  = cellColor;
4396         data[ndx * 6 * 2 + 10] = tcu::Vec4(float(cellX + 1) / float(m_gridSize) * 2.0f - 1.0f,
4397                                            float(cellY + 1) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f);
4398         data[ndx * 6 * 2 + 11] = cellColor;
4399 
4400         // Fill Z with random values (fake Z)
4401         for (int vtxNdx = 0; vtxNdx < 6; ++vtxNdx)
4402             data[ndx * 6 * 2 + 2 * vtxNdx].z() = rnd.getFloat(0.0f, 1.0);
4403 
4404         // Fill W with other random values (written Z)
4405         for (int vtxNdx = 0; vtxNdx < 6; ++vtxNdx)
4406             data[ndx * 6 * 2 + 2 * vtxNdx].w() = rnd.getFloat(0.0f, 1.0);
4407     }
4408 }
4409 
verifyImage(const tcu::Surface & viewport) const4410 bool DepthDrawCase::verifyImage(const tcu::Surface &viewport) const
4411 {
4412     tcu::Surface errorMask(viewport.getWidth(), viewport.getHeight());
4413     bool anyError = false;
4414 
4415     tcu::clear(errorMask.getAccess(), tcu::IVec4(0, 0, 0, 255));
4416 
4417     for (int y = 0; y < viewport.getHeight(); ++y)
4418         for (int x = 0; x < viewport.getWidth(); ++x)
4419         {
4420             const tcu::RGBA pixel = viewport.getPixel(x, y);
4421             bool error            = false;
4422 
4423             // expect green, yellow or a combination of these
4424             if (pixel.getGreen() != 255 || pixel.getBlue() != 0)
4425                 error = true;
4426 
4427             if (error)
4428             {
4429                 errorMask.setPixel(x, y, tcu::RGBA::red());
4430                 anyError = true;
4431             }
4432         }
4433 
4434     if (anyError)
4435         m_testCtx.getLog() << tcu::TestLog::Message << "Image verification failed." << tcu::TestLog::EndMessage
4436                            << tcu::TestLog::ImageSet("Images", "Image verification")
4437                            << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
4438                            << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
4439                            << tcu::TestLog::EndImageSet;
4440     else
4441         m_testCtx.getLog() << tcu::TestLog::Message << "Result image ok." << tcu::TestLog::EndMessage
4442                            << tcu::TestLog::ImageSet("Images", "Image verification")
4443                            << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess())
4444                            << tcu::TestLog::EndImageSet;
4445 
4446     return !anyError;
4447 }
4448 
4449 class ClearCase : public TestCase
4450 {
4451 public:
4452     enum
4453     {
4454         SCISSOR_CLEAR_BIT      = 1 << 0,
4455         DRAW_TRIANGLE_BIT      = 1 << 1,
4456         PER_PRIMITIVE_BBOX_BIT = 1 << 2,
4457         FULLSCREEN_SCISSOR_BIT = 1 << 3,
4458     };
4459 
4460     ClearCase(Context &context, const char *name, const char *description, uint32_t flags);
4461     ~ClearCase(void);
4462 
4463 private:
4464     struct DrawObject
4465     {
4466         int firstNdx;
4467         int numVertices;
4468     };
4469 
4470     void init(void);
4471     void deinit(void);
4472     IterateResult iterate(void);
4473 
4474     void createVbo(void);
4475     void createProgram(void);
4476     void renderTo(tcu::Surface &dst, bool useBBox);
4477     bool verifyImagesEqual(const tcu::PixelBufferAccess &withoutBBox, const tcu::PixelBufferAccess &withBBox);
4478     bool verifyImageResultValid(const tcu::PixelBufferAccess &result);
4479 
4480     std::string genVertexSource(void) const;
4481     std::string genFragmentSource(void) const;
4482     std::string genTessellationControlSource(bool setBBox) const;
4483     std::string genTessellationEvaluationSource(void) const;
4484 
4485     const bool m_scissoredClear;
4486     const bool m_fullscreenScissor;
4487     const bool m_drawTriangles;
4488     const bool m_useGlobalState;
4489 
4490     de::MovePtr<glu::Buffer> m_vbo;
4491     de::MovePtr<glu::ShaderProgram> m_perPrimitiveProgram;
4492     de::MovePtr<glu::ShaderProgram> m_basicProgram;
4493     std::vector<DrawObject> m_drawObjects;
4494     std::vector<tcu::Vec4> m_objectVertices;
4495 };
4496 
ClearCase(Context & context,const char * name,const char * description,uint32_t flags)4497 ClearCase::ClearCase(Context &context, const char *name, const char *description, uint32_t flags)
4498     : TestCase(context, name, description)
4499     , m_scissoredClear((flags & SCISSOR_CLEAR_BIT) != 0)
4500     , m_fullscreenScissor((flags & FULLSCREEN_SCISSOR_BIT) != 0)
4501     , m_drawTriangles((flags & DRAW_TRIANGLE_BIT) != 0)
4502     , m_useGlobalState((flags & PER_PRIMITIVE_BBOX_BIT) == 0)
4503 {
4504     DE_ASSERT(m_useGlobalState || m_drawTriangles);      // per-triangle bbox requires triangles
4505     DE_ASSERT(!m_fullscreenScissor || m_scissoredClear); // fullscreenScissor requires scissoredClear
4506 }
4507 
~ClearCase(void)4508 ClearCase::~ClearCase(void)
4509 {
4510     deinit();
4511 }
4512 
init(void)4513 void ClearCase::init(void)
4514 {
4515     const bool hasES32OrGL45 = supportsES32OrGL45(m_context);
4516 
4517     if (!boundingBoxSupported(m_context))
4518         throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
4519     if (m_drawTriangles && !hasES32OrGL45 &&
4520         !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
4521         throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
4522 
4523     m_testCtx.getLog() << tcu::TestLog::Message << "Doing multiple" << ((m_scissoredClear) ? (" scissored") : (""))
4524                        << " color buffer clears"
4525                        << ((m_drawTriangles) ? (" and drawing some geometry between them") : ("")) << ".\n"
4526                        << ((m_scissoredClear && m_fullscreenScissor) ?
4527                                ("Setting scissor area to cover entire viewport.\n") :
4528                                (""))
4529                        << "Rendering with and without setting the bounding box.\n"
4530                        << "Expecting bounding box to have no effect on clears (i.e. results are constant).\n"
4531                        << "Set bounding box using "
4532                        << ((m_useGlobalState) ? ("PRIMITIVE_BOUNDING_BOX_EXT state") : ("gl_BoundingBoxEXT output"))
4533                        << ".\n"
4534                        << "Clear color is green with yellowish shades.\n"
4535                        << ((m_drawTriangles) ? ("Primitive color is yellow with greenish shades.\n") : (""))
4536                        << tcu::TestLog::EndMessage;
4537 
4538     if (m_drawTriangles)
4539     {
4540         createVbo();
4541         createProgram();
4542     }
4543 }
4544 
deinit(void)4545 void ClearCase::deinit(void)
4546 {
4547     m_vbo.clear();
4548     m_perPrimitiveProgram.clear();
4549     m_basicProgram.clear();
4550     m_drawObjects    = std::vector<DrawObject>();
4551     m_objectVertices = std::vector<tcu::Vec4>();
4552 }
4553 
iterate(void)4554 ClearCase::IterateResult ClearCase::iterate(void)
4555 {
4556     const tcu::IVec2 renderTargetSize(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight());
4557     tcu::Surface resultWithoutBBox(renderTargetSize.x(), renderTargetSize.y());
4558     tcu::Surface resultWithBBox(renderTargetSize.x(), renderTargetSize.y());
4559 
4560     // render with and without bbox set
4561     for (int passNdx = 0; passNdx < 2; ++passNdx)
4562     {
4563         const bool useBBox        = (passNdx == 1);
4564         tcu::Surface &destination = (useBBox) ? (resultWithBBox) : (resultWithoutBBox);
4565 
4566         renderTo(destination, useBBox);
4567     }
4568 
4569     // Verify images are equal and that the image does not contain (trivially detectable) garbage
4570 
4571     if (!verifyImagesEqual(resultWithoutBBox.getAccess(), resultWithBBox.getAccess()))
4572     {
4573         // verifyImagesEqual will print out the image and error mask
4574         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
4575     }
4576     else if (!verifyImageResultValid(resultWithBBox.getAccess()))
4577     {
4578         // verifyImageResultValid will print out the image and error mask
4579         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Result verification failed");
4580     }
4581     else
4582     {
4583         m_testCtx.getLog() << tcu::TestLog::Message << "Image comparison passed." << tcu::TestLog::EndMessage
4584                            << tcu::TestLog::ImageSet("Images", "Image verification")
4585                            << tcu::TestLog::Image("Result", "Result", resultWithBBox.getAccess())
4586                            << tcu::TestLog::EndImageSet;
4587 
4588         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4589     }
4590 
4591     return STOP;
4592 }
4593 
createVbo(void)4594 void ClearCase::createVbo(void)
4595 {
4596     const int numObjects     = 16;
4597     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
4598     de::Random rnd(deStringHash(getName()));
4599 
4600     m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
4601 
4602     for (int objectNdx = 0; objectNdx < numObjects; ++objectNdx)
4603     {
4604         const int numTriangles = rnd.getInt(1, 4);
4605         const float minX       = rnd.getFloat(-1.2f, 0.8f);
4606         const float minY       = rnd.getFloat(-1.2f, 0.8f);
4607         const float maxX       = minX + rnd.getFloat(0.2f, 1.0f);
4608         const float maxY       = minY + rnd.getFloat(0.2f, 1.0f);
4609 
4610         DrawObject drawObject;
4611         drawObject.firstNdx    = (int)m_objectVertices.size();
4612         drawObject.numVertices = numTriangles * 3;
4613 
4614         m_drawObjects.push_back(drawObject);
4615 
4616         for (int triangleNdx = 0; triangleNdx < numTriangles; ++triangleNdx)
4617             for (int vertexNdx = 0; vertexNdx < 3; ++vertexNdx)
4618             {
4619                 const float posX = rnd.getFloat(minX, maxX);
4620                 const float posY = rnd.getFloat(minY, maxY);
4621                 const float posZ = rnd.getFloat(-0.7f, 0.7f);
4622                 const float posW = rnd.getFloat(0.9f, 1.1f);
4623 
4624                 m_objectVertices.push_back(tcu::Vec4(posX, posY, posZ, posW));
4625             }
4626     }
4627 
4628     gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
4629     gl.bufferData(GL_ARRAY_BUFFER, (int)(m_objectVertices.size() * sizeof(tcu::Vec4)), &m_objectVertices[0],
4630                   GL_STATIC_DRAW);
4631     GLU_EXPECT_NO_ERROR(gl.getError(), "buffer upload");
4632 }
4633 
createProgram(void)4634 void ClearCase::createProgram(void)
4635 {
4636     m_basicProgram = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(
4637         m_context.getRenderContext(),
4638         glu::ProgramSources() << glu::VertexSource(specializeShader(m_context, genVertexSource().c_str()))
4639                               << glu::FragmentSource(specializeShader(m_context, genFragmentSource().c_str()))
4640                               << glu::TessellationControlSource(
4641                                      specializeShader(m_context, genTessellationControlSource(false).c_str()))
4642                               << glu::TessellationEvaluationSource(
4643                                      specializeShader(m_context, genTessellationEvaluationSource().c_str()))));
4644 
4645     m_testCtx.getLog() << tcu::TestLog::Section("Program", "Shader program") << *m_basicProgram
4646                        << tcu::TestLog::EndSection;
4647 
4648     if (!m_basicProgram->isOk())
4649         throw tcu::TestError("shader build failed");
4650 
4651     if (!m_useGlobalState)
4652     {
4653         m_perPrimitiveProgram = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(
4654             m_context.getRenderContext(),
4655             glu::ProgramSources() << glu::VertexSource(specializeShader(m_context, genVertexSource().c_str()))
4656                                   << glu::FragmentSource(specializeShader(m_context, genFragmentSource().c_str()))
4657                                   << glu::TessellationControlSource(
4658                                          specializeShader(m_context, genTessellationControlSource(true).c_str()))
4659                                   << glu::TessellationEvaluationSource(
4660                                          specializeShader(m_context, genTessellationEvaluationSource().c_str()))));
4661 
4662         m_testCtx.getLog() << tcu::TestLog::Section("PerPrimitiveProgram", "Shader program that sets the bounding box")
4663                            << *m_perPrimitiveProgram << tcu::TestLog::EndSection;
4664 
4665         if (!m_perPrimitiveProgram->isOk())
4666             throw tcu::TestError("shader build failed");
4667     }
4668 }
4669 
renderTo(tcu::Surface & dst,bool useBBox)4670 void ClearCase::renderTo(tcu::Surface &dst, bool useBBox)
4671 {
4672     const int numOps = 45;
4673     const tcu::Vec4 yellow(1.0f, 1.0f, 0.0f, 1.0f);
4674     const tcu::Vec4 green(0.0f, 1.0f, 0.0f, 1.0f);
4675     const tcu::IVec2 renderTargetSize(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight());
4676     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
4677     de::Random rnd(deStringHash(getName()));
4678     glu::VertexArray vao(m_context.getRenderContext());
4679 
4680     // always do the initial clear
4681     gl.disable(GL_SCISSOR_TEST);
4682     gl.viewport(0, 0, renderTargetSize.x(), renderTargetSize.y());
4683     gl.clearColor(yellow.x(), yellow.y(), yellow.z(), yellow.w());
4684     gl.clear(GL_COLOR_BUFFER_BIT);
4685     gl.finish();
4686 
4687     // prepare draw
4688     if (m_scissoredClear)
4689         gl.enable(GL_SCISSOR_TEST);
4690 
4691     if (m_drawTriangles)
4692     {
4693         const uint32_t programHandle =
4694             (m_useGlobalState || !useBBox) ? (m_basicProgram->getProgram()) : (m_perPrimitiveProgram->getProgram());
4695         const int positionAttribLoc = gl.getAttribLocation(programHandle, "a_position");
4696 
4697         TCU_CHECK(positionAttribLoc != -1);
4698 
4699         gl.useProgram(programHandle);
4700         gl.bindVertexArray(*vao);
4701         gl.enableVertexAttribArray(positionAttribLoc);
4702         gl.vertexAttribPointer(positionAttribLoc, 4, GL_FLOAT, GL_FALSE, (int)sizeof(tcu::Vec4), DE_NULL);
4703         gl.patchParameteri(GL_PATCH_VERTICES, 3);
4704     }
4705 
4706     // do random scissor/clearldraw operations
4707     for (int opNdx = 0; opNdx < numOps; ++opNdx)
4708     {
4709         const int drawObjNdx           = (m_drawTriangles) ? (rnd.getInt(0, (int)m_drawObjects.size() - 1)) : (0);
4710         const int objectVertexStartNdx = (m_drawTriangles) ? (m_drawObjects[drawObjNdx].firstNdx) : (0);
4711         const int objectVertexLength   = (m_drawTriangles) ? (m_drawObjects[drawObjNdx].numVertices) : (0);
4712         tcu::Vec4 bboxMin;
4713         tcu::Vec4 bboxMax;
4714 
4715         if (m_drawTriangles)
4716         {
4717             bboxMin = tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f);
4718             bboxMax = tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f);
4719 
4720             // calc bbox
4721             for (int vertexNdx = objectVertexStartNdx; vertexNdx < objectVertexStartNdx + objectVertexLength;
4722                  ++vertexNdx)
4723                 for (int componentNdx = 0; componentNdx < 4; ++componentNdx)
4724                 {
4725                     bboxMin[componentNdx] = de::min(bboxMin[componentNdx], m_objectVertices[vertexNdx][componentNdx]);
4726                     bboxMax[componentNdx] = de::max(bboxMax[componentNdx], m_objectVertices[vertexNdx][componentNdx]);
4727                 }
4728         }
4729         else
4730         {
4731             // no geometry, just random something
4732             bboxMin.x() = rnd.getFloat(-1.2f, 1.0f);
4733             bboxMin.y() = rnd.getFloat(-1.2f, 1.0f);
4734             bboxMin.z() = rnd.getFloat(-1.2f, 1.0f);
4735             bboxMin.w() = 1.0f;
4736             bboxMax.x() = bboxMin.x() + rnd.getFloat(0.2f, 1.0f);
4737             bboxMax.y() = bboxMin.y() + rnd.getFloat(0.2f, 1.0f);
4738             bboxMax.z() = bboxMin.z() + rnd.getFloat(0.2f, 1.0f);
4739             bboxMax.w() = 1.0f;
4740         }
4741 
4742         if (m_scissoredClear)
4743         {
4744             const int scissorX = (m_fullscreenScissor) ? (0) : rnd.getInt(0, renderTargetSize.x() - 1);
4745             const int scissorY = (m_fullscreenScissor) ? (0) : rnd.getInt(0, renderTargetSize.y() - 1);
4746             const int scissorW =
4747                 (m_fullscreenScissor) ? (renderTargetSize.x()) : rnd.getInt(0, renderTargetSize.x() - scissorX);
4748             const int scissorH =
4749                 (m_fullscreenScissor) ? (renderTargetSize.y()) : rnd.getInt(0, renderTargetSize.y() - scissorY);
4750 
4751             gl.scissor(scissorX, scissorY, scissorW, scissorH);
4752         }
4753 
4754         {
4755             const tcu::Vec4 color = tcu::mix(green, yellow, rnd.getFloat() * 0.4f); // greenish
4756             gl.clearColor(color.x(), color.y(), color.z(), color.w());
4757             gl.clear(GL_COLOR_BUFFER_BIT);
4758         }
4759 
4760         if (useBBox)
4761         {
4762             DE_ASSERT(m_useGlobalState || m_drawTriangles); // !m_useGlobalState -> m_drawTriangles
4763 
4764             auto boundingBoxFunc = getBoundingBoxFunction(m_context);
4765             if (m_useGlobalState)
4766                 boundingBoxFunc(bboxMin.x(), bboxMin.y(), bboxMin.z(), bboxMin.w(), bboxMax.x(), bboxMax.y(),
4767                                 bboxMax.z(), bboxMax.w());
4768         }
4769 
4770         if (m_drawTriangles)
4771             gl.drawArrays(GL_PATCHES, objectVertexStartNdx, objectVertexLength);
4772     }
4773 
4774     GLU_EXPECT_NO_ERROR(gl.getError(), "post draw");
4775     glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess());
4776 }
4777 
verifyImagesEqual(const tcu::PixelBufferAccess & withoutBBox,const tcu::PixelBufferAccess & withBBox)4778 bool ClearCase::verifyImagesEqual(const tcu::PixelBufferAccess &withoutBBox, const tcu::PixelBufferAccess &withBBox)
4779 {
4780     DE_ASSERT(withoutBBox.getWidth() == withBBox.getWidth());
4781     DE_ASSERT(withoutBBox.getHeight() == withBBox.getHeight());
4782 
4783     tcu::Surface errorMask(withoutBBox.getWidth(), withoutBBox.getHeight());
4784     bool anyError = false;
4785 
4786     tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec());
4787 
4788     for (int y = 0; y < withoutBBox.getHeight(); ++y)
4789         for (int x = 0; x < withoutBBox.getWidth(); ++x)
4790         {
4791             if (withoutBBox.getPixelInt(x, y) != withBBox.getPixelInt(x, y))
4792             {
4793                 errorMask.setPixel(x, y, tcu::RGBA::red());
4794                 anyError = true;
4795             }
4796         }
4797 
4798     if (anyError)
4799     {
4800         m_testCtx.getLog() << tcu::TestLog::Message << "Image comparison failed." << tcu::TestLog::EndMessage
4801                            << tcu::TestLog::ImageSet("Images", "Image comparison")
4802                            << tcu::TestLog::Image("WithoutBBox", "Result with bounding box not set", withoutBBox)
4803                            << tcu::TestLog::Image("WithBBox", "Result with bounding box set", withBBox)
4804                            << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
4805                            << tcu::TestLog::EndImageSet;
4806     }
4807 
4808     return !anyError;
4809 }
4810 
verifyImageResultValid(const tcu::PixelBufferAccess & result)4811 bool ClearCase::verifyImageResultValid(const tcu::PixelBufferAccess &result)
4812 {
4813     tcu::Surface errorMask(result.getWidth(), result.getHeight());
4814     bool anyError = false;
4815 
4816     tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec());
4817 
4818     for (int y = 0; y < result.getHeight(); ++y)
4819         for (int x = 0; x < result.getWidth(); ++x)
4820         {
4821             const tcu::IVec4 pixel = result.getPixelInt(x, y);
4822 
4823             // allow green, yellow and any shade between
4824             if (pixel[1] != 255 || pixel[2] != 0)
4825             {
4826                 errorMask.setPixel(x, y, tcu::RGBA::red());
4827                 anyError = true;
4828             }
4829         }
4830 
4831     if (anyError)
4832     {
4833         m_testCtx.getLog() << tcu::TestLog::Message << "Image verification failed." << tcu::TestLog::EndMessage
4834                            << tcu::TestLog::ImageSet("Images", "Image verification")
4835                            << tcu::TestLog::Image("ResultImage", "Result image", result)
4836                            << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask) << tcu::TestLog::EndImageSet;
4837     }
4838 
4839     return !anyError;
4840 }
4841 
4842 static const char *const s_yellowishPosOnlyVertexSource =
4843     "${GLSL_VERSION_DECL}\n"
4844     "in highp vec4 a_position;\n"
4845     "out highp vec4 v_vertex_color;\n"
4846     "void main()\n"
4847     "{\n"
4848     "    gl_Position = a_position;\n"
4849     "    // yellowish shade\n"
4850     "    highp float redComponent = 0.5 + float(gl_VertexID % 5) / 8.0;\n"
4851     "    v_vertex_color = vec4(redComponent, 1.0, 0.0, 1.0);\n"
4852     "}\n";
4853 
4854 static const char *const s_basicColorFragmentSource = "${GLSL_VERSION_DECL}\n"
4855                                                       "in mediump vec4 v_color;\n"
4856                                                       "layout(location = 0) out mediump vec4 o_color;\n"
4857                                                       "void main()\n"
4858                                                       "{\n"
4859                                                       "    o_color = v_color;\n"
4860                                                       "}\n";
4861 
4862 static const char *const s_basicColorTessEvalSource = "${GLSL_VERSION_DECL}\n"
4863                                                       "${TESSELLATION_SHADER_REQUIRE}\n"
4864                                                       "${GPU_SHADER5_REQUIRE}\n"
4865                                                       "layout(triangles) in;\n"
4866                                                       "in highp vec4 v_tess_eval_color[];\n"
4867                                                       "out highp vec4 v_color;\n"
4868                                                       "precise gl_Position;\n"
4869                                                       "void main()\n"
4870                                                       "{\n"
4871                                                       "    gl_Position = gl_TessCoord.x * gl_in[0].gl_Position\n"
4872                                                       "                + gl_TessCoord.y * gl_in[1].gl_Position\n"
4873                                                       "                + gl_TessCoord.z * gl_in[2].gl_Position;\n"
4874                                                       "    v_color = gl_TessCoord.x * v_tess_eval_color[0]\n"
4875                                                       "            + gl_TessCoord.y * v_tess_eval_color[1]\n"
4876                                                       "            + gl_TessCoord.z * v_tess_eval_color[2];\n"
4877                                                       "}\n";
4878 
genVertexSource(void) const4879 std::string ClearCase::genVertexSource(void) const
4880 {
4881     return s_yellowishPosOnlyVertexSource;
4882 }
4883 
genFragmentSource(void) const4884 std::string ClearCase::genFragmentSource(void) const
4885 {
4886     return s_basicColorFragmentSource;
4887 }
4888 
genTessellationControlSource(bool setBBox) const4889 std::string ClearCase::genTessellationControlSource(bool setBBox) const
4890 {
4891     std::ostringstream buf;
4892 
4893     buf << "${GLSL_VERSION_DECL}\n"
4894            "${ARB_ES32_COMPATIBILITY_REQUIRE}\n"
4895            "${TESSELLATION_SHADER_REQUIRE}\n";
4896 
4897     if (setBBox)
4898         buf << "${PRIMITIVE_BOUNDING_BOX_REQUIRE}\n";
4899 
4900     buf << "layout(vertices=3) out;\n"
4901            "in highp vec4 v_vertex_color[];\n"
4902            "out highp vec4 v_tess_eval_color[];\n"
4903            "void main()\n"
4904            "{\n"
4905            "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
4906            "    v_tess_eval_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
4907            "    gl_TessLevelOuter[0] = 2.8;\n"
4908            "    gl_TessLevelOuter[1] = 2.8;\n"
4909            "    gl_TessLevelOuter[2] = 2.8;\n"
4910            "    gl_TessLevelInner[0] = 2.8;\n";
4911 
4912     if (setBBox)
4913     {
4914         buf << "\n"
4915                "    ${PRIM_GL_BOUNDING_BOX}[0] = min(min(gl_in[0].gl_Position,\n"
4916                "                                   gl_in[1].gl_Position),\n"
4917                "                               gl_in[2].gl_Position);\n"
4918                "    ${PRIM_GL_BOUNDING_BOX}[1] = max(max(gl_in[0].gl_Position,\n"
4919                "                                   gl_in[1].gl_Position),\n"
4920                "                               gl_in[2].gl_Position);\n";
4921     }
4922 
4923     buf << "}\n";
4924     return buf.str();
4925 }
4926 
genTessellationEvaluationSource(void) const4927 std::string ClearCase::genTessellationEvaluationSource(void) const
4928 {
4929     return s_basicColorTessEvalSource;
4930 }
4931 
4932 class ViewportCallOrderCase : public TestCase
4933 {
4934 public:
4935     enum CallOrder
4936     {
4937         VIEWPORT_FIRST = 0,
4938         BBOX_FIRST,
4939 
4940         ORDER_LAST
4941     };
4942 
4943     ViewportCallOrderCase(Context &context, const char *name, const char *description, CallOrder callOrder);
4944     ~ViewportCallOrderCase(void);
4945 
4946 private:
4947     void init(void);
4948     void deinit(void);
4949     IterateResult iterate(void);
4950 
4951     void genVbo(void);
4952     void genProgram(void);
4953     bool verifyImage(const tcu::PixelBufferAccess &result);
4954 
4955     std::string genVertexSource(void) const;
4956     std::string genFragmentSource(void) const;
4957     std::string genTessellationControlSource(void) const;
4958     std::string genTessellationEvaluationSource(void) const;
4959 
4960     const CallOrder m_callOrder;
4961 
4962     de::MovePtr<glu::Buffer> m_vbo;
4963     glw::GLuint m_vao;
4964     de::MovePtr<glu::ShaderProgram> m_program;
4965     int m_numVertices;
4966 };
4967 
ViewportCallOrderCase(Context & context,const char * name,const char * description,CallOrder callOrder)4968 ViewportCallOrderCase::ViewportCallOrderCase(Context &context, const char *name, const char *description,
4969                                              CallOrder callOrder)
4970     : TestCase(context, name, description)
4971     , m_callOrder(callOrder)
4972     , m_vao(0)
4973     , m_numVertices(-1)
4974 {
4975     DE_ASSERT(m_callOrder < ORDER_LAST);
4976 }
4977 
~ViewportCallOrderCase(void)4978 ViewportCallOrderCase::~ViewportCallOrderCase(void)
4979 {
4980     deinit();
4981 }
4982 
init(void)4983 void ViewportCallOrderCase::init(void)
4984 {
4985     const bool hasES32OrGL45 = supportsES32OrGL45(m_context);
4986 
4987     if (!boundingBoxSupported(m_context))
4988         throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension");
4989 
4990     if (!hasES32OrGL45 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
4991         throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension");
4992 
4993     m_testCtx.getLog() << tcu::TestLog::Message
4994                        << "Testing call order of state setting functions have no effect on the rendering.\n"
4995                        << "Setting viewport and bounding box in the following order:\n"
4996                        << ((m_callOrder == VIEWPORT_FIRST) ?
4997                                ("\tFirst viewport with glViewport function.\n") :
4998                                ("\tFirst bounding box with glPrimitiveBoundingBoxEXT function.\n"))
4999                        << ((m_callOrder == VIEWPORT_FIRST) ?
5000                                ("\tThen bounding box with glPrimitiveBoundingBoxEXT function.\n") :
5001                                ("\tThen viewport with glViewport function.\n"))
5002                        << "Verifying rendering result." << tcu::TestLog::EndMessage;
5003 
5004     // resources
5005     genVbo();
5006     genProgram();
5007 }
5008 
deinit(void)5009 void ViewportCallOrderCase::deinit(void)
5010 {
5011     m_vbo.clear();
5012     m_program.clear();
5013 
5014     if (m_vao)
5015     {
5016         m_context.getRenderContext().getFunctions().deleteVertexArrays(1, &m_vao);
5017         m_vao = 0;
5018     }
5019 }
5020 
iterate(void)5021 ViewportCallOrderCase::IterateResult ViewportCallOrderCase::iterate(void)
5022 {
5023     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
5024     const tcu::IVec2 viewportSize =
5025         tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight());
5026     const glw::GLint posLocation = gl.getAttribLocation(m_program->getProgram(), "a_position");
5027     tcu::Surface resultSurface(viewportSize.x(), viewportSize.y());
5028 
5029     gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
5030     gl.clear(GL_COLOR_BUFFER_BIT);
5031 
5032     // set state
5033     for (int orderNdx = 0; orderNdx < 2; ++orderNdx)
5034     {
5035         if ((orderNdx == 0 && m_callOrder == VIEWPORT_FIRST) || (orderNdx == 1 && m_callOrder == BBOX_FIRST))
5036         {
5037             m_testCtx.getLog() << tcu::TestLog::Message
5038                                << "Setting viewport to cover the left half of the render target.\n"
5039                                << "\t(0, 0, " << (viewportSize.x() / 2) << ", " << viewportSize.y() << ")"
5040                                << tcu::TestLog::EndMessage;
5041 
5042             gl.viewport(0, 0, viewportSize.x() / 2, viewportSize.y());
5043         }
5044         else
5045         {
5046             m_testCtx.getLog() << tcu::TestLog::Message
5047                                << "Setting bounding box to cover the right half of the clip space.\n"
5048                                << "\t(0.0, -1.0, -1.0, 1.0) .. (1.0, 1.0, 1.0f, 1.0)" << tcu::TestLog::EndMessage;
5049 
5050             auto boundingBoxFunc = getBoundingBoxFunction(m_context);
5051 
5052             boundingBoxFunc(0.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f);
5053         }
5054     }
5055 
5056     m_testCtx.getLog() << tcu::TestLog::Message << "Rendering mesh covering the right half of the clip space."
5057                        << tcu::TestLog::EndMessage;
5058 
5059     gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
5060     gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, sizeof(float[4]), (const float *)DE_NULL);
5061     gl.enableVertexAttribArray(posLocation);
5062     gl.useProgram(m_program->getProgram());
5063     gl.patchParameteri(GL_PATCH_VERTICES, 3);
5064     gl.drawArrays(GL_PATCHES, 0, m_numVertices);
5065     GLU_EXPECT_NO_ERROR(gl.getError(), "post-draw");
5066 
5067     m_testCtx.getLog() << tcu::TestLog::Message << "Verifying image" << tcu::TestLog::EndMessage;
5068     glu::readPixels(m_context.getRenderContext(), 0, 0, resultSurface.getAccess());
5069 
5070     if (!verifyImage(resultSurface.getAccess()))
5071         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
5072     else
5073     {
5074         m_testCtx.getLog() << tcu::TestLog::Message << "Result ok." << tcu::TestLog::EndMessage
5075                            << tcu::TestLog::ImageSet("Images", "Image verification")
5076                            << tcu::TestLog::Image("Result", "Result", resultSurface.getAccess())
5077                            << tcu::TestLog::EndImageSet;
5078 
5079         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
5080     }
5081     return STOP;
5082 }
5083 
genVbo(void)5084 void ViewportCallOrderCase::genVbo(void)
5085 {
5086     const int gridSize       = 6;
5087     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
5088     std::vector<tcu::Vec4> data(gridSize * gridSize * 2 * 3);
5089     std::vector<int> cellOrder(gridSize * gridSize * 2);
5090     de::Random rnd(0x55443322);
5091 
5092     // generate grid with triangles in random order
5093     for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
5094         cellOrder[ndx] = ndx;
5095     rnd.shuffle(cellOrder.begin(), cellOrder.end());
5096 
5097     // generate grid filling the right half of the clip space: (x: 0.0, y: -1.0) .. (x: 1.0, y: 1.0)
5098     for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx)
5099     {
5100         const int cellNdx   = cellOrder[ndx];
5101         const bool cellSide = ((cellNdx % 2) == 0);
5102         const int cellX     = (cellNdx / 2) % gridSize;
5103         const int cellY     = (cellNdx / 2) / gridSize;
5104 
5105         if (cellSide)
5106         {
5107             data[ndx * 3 + 0] = tcu::Vec4(float(cellX + 0) / float(gridSize),
5108                                           (float(cellY + 0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
5109             data[ndx * 3 + 1] = tcu::Vec4(float(cellX + 1) / float(gridSize),
5110                                           (float(cellY + 1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
5111             data[ndx * 3 + 2] = tcu::Vec4(float(cellX + 0) / float(gridSize),
5112                                           (float(cellY + 1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
5113         }
5114         else
5115         {
5116             data[ndx * 3 + 0] = tcu::Vec4(float(cellX + 0) / float(gridSize),
5117                                           (float(cellY + 0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
5118             data[ndx * 3 + 1] = tcu::Vec4(float(cellX + 1) / float(gridSize),
5119                                           (float(cellY + 0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
5120             data[ndx * 3 + 2] = tcu::Vec4(float(cellX + 1) / float(gridSize),
5121                                           (float(cellY + 1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f);
5122         }
5123     }
5124 
5125     // Generate VAO for desktop OpenGL
5126     if (!glu::isContextTypeES(m_context.getRenderContext().getType()))
5127     {
5128         gl.genVertexArrays(1, &m_vao);
5129         gl.bindVertexArray(m_vao);
5130     }
5131 
5132     m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext()));
5133     gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo);
5134     gl.bufferData(GL_ARRAY_BUFFER, (int)(data.size() * sizeof(tcu::Vec4)), &data[0], GL_STATIC_DRAW);
5135     GLU_EXPECT_NO_ERROR(gl.getError(), "create vbo");
5136 
5137     m_numVertices = (int)data.size();
5138 }
5139 
genProgram(void)5140 void ViewportCallOrderCase::genProgram(void)
5141 {
5142     m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(
5143         m_context.getRenderContext(),
5144         glu::ProgramSources() << glu::VertexSource(specializeShader(m_context, genVertexSource().c_str()))
5145                               << glu::FragmentSource(specializeShader(m_context, genFragmentSource().c_str()))
5146                               << glu::TessellationControlSource(
5147                                      specializeShader(m_context, genTessellationControlSource().c_str()))
5148                               << glu::TessellationEvaluationSource(
5149                                      specializeShader(m_context, genTessellationEvaluationSource().c_str()))));
5150 
5151     m_testCtx.getLog() << tcu::TestLog::Section("Program", "Shader program") << *m_program << tcu::TestLog::EndSection;
5152 
5153     if (!m_program->isOk())
5154         throw tcu::TestError("shader build failed");
5155 }
5156 
verifyImage(const tcu::PixelBufferAccess & result)5157 bool ViewportCallOrderCase::verifyImage(const tcu::PixelBufferAccess &result)
5158 {
5159     const tcu::IVec2 insideBorder(deCeilFloatToInt32(0.25f * (float)result.getWidth()) + 1,
5160                                   deFloorFloatToInt32(0.5f * (float)result.getWidth()) - 1);
5161     const tcu::IVec2 outsideBorder(deFloorFloatToInt32(0.25f * (float)result.getWidth()) - 1,
5162                                    deCeilFloatToInt32(0.5f * (float)result.getWidth()) + 1);
5163     tcu::Surface errorMask(result.getWidth(), result.getHeight());
5164     bool anyError = false;
5165 
5166     tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec());
5167 
5168     for (int y = 0; y < result.getHeight(); ++y)
5169         for (int x = 0; x < result.getWidth(); ++x)
5170         {
5171             /*
5172                 Test stores the xmin,xmax values in the insideBorder and outsideBorder vectors.
5173                 Thus the "y" in the vector is xmax.
5174             */
5175 
5176             const tcu::IVec4 pixel     = result.getPixelInt(x, y);
5177             const bool insideMeshArea  = x >= insideBorder.x() && x <= insideBorder.y();
5178             const bool outsideMeshArea = x <= outsideBorder.x() && x >= outsideBorder.y();
5179 
5180             // inside mesh, allow green, yellow and any shade between
5181             // outside mesh, allow background (black) only
5182             // in the border area, allow anything
5183             if ((insideMeshArea && (pixel[1] != 255 || pixel[2] != 0)) ||
5184                 (outsideMeshArea && (pixel[0] != 0 || pixel[1] != 0 || pixel[2] != 0)))
5185             {
5186                 errorMask.setPixel(x, y, tcu::RGBA::red());
5187                 anyError = true;
5188             }
5189         }
5190 
5191     if (anyError)
5192     {
5193         m_testCtx.getLog() << tcu::TestLog::Message << "Image verification failed." << tcu::TestLog::EndMessage
5194                            << tcu::TestLog::ImageSet("Images", "Image verification")
5195                            << tcu::TestLog::Image("ResultImage", "Result image", result)
5196                            << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask) << tcu::TestLog::EndImageSet;
5197     }
5198 
5199     return !anyError;
5200 }
5201 
genVertexSource(void) const5202 std::string ViewportCallOrderCase::genVertexSource(void) const
5203 {
5204     return s_yellowishPosOnlyVertexSource;
5205 }
5206 
genFragmentSource(void) const5207 std::string ViewportCallOrderCase::genFragmentSource(void) const
5208 {
5209     return s_basicColorFragmentSource;
5210 }
5211 
genTessellationControlSource(void) const5212 std::string ViewportCallOrderCase::genTessellationControlSource(void) const
5213 {
5214     return "${GLSL_VERSION_DECL}\n"
5215            "${ARB_ES32_COMPATIBILITY_REQUIRE}\n"
5216            "${TESSELLATION_SHADER_REQUIRE}\n"
5217            "layout(vertices=3) out;\n"
5218            "in highp vec4 v_vertex_color[];\n"
5219            "out highp vec4 v_tess_eval_color[];\n"
5220            "void main()\n"
5221            "{\n"
5222            "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
5223            "    v_tess_eval_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
5224            "    gl_TessLevelOuter[0] = 2.8;\n"
5225            "    gl_TessLevelOuter[1] = 2.8;\n"
5226            "    gl_TessLevelOuter[2] = 2.8;\n"
5227            "    gl_TessLevelInner[0] = 2.8;\n"
5228            "}\n";
5229 }
5230 
genTessellationEvaluationSource(void) const5231 std::string ViewportCallOrderCase::genTessellationEvaluationSource(void) const
5232 {
5233     return s_basicColorTessEvalSource;
5234 }
5235 
5236 } // namespace
5237 
PrimitiveBoundingBoxTests(Context & context)5238 PrimitiveBoundingBoxTests::PrimitiveBoundingBoxTests(Context &context)
5239     : TestCaseGroup(context, "primitive_bounding_box", "Tests for EXT_primitive_bounding_box")
5240 {
5241 }
5242 
~PrimitiveBoundingBoxTests(void)5243 PrimitiveBoundingBoxTests::~PrimitiveBoundingBoxTests(void)
5244 {
5245 }
5246 
init(void)5247 void PrimitiveBoundingBoxTests::init(void)
5248 {
5249     static const struct
5250     {
5251         const char *name;
5252         const char *description;
5253         uint32_t methodFlags;
5254     } stateSetMethods[] = {
5255         {
5256             "global_state",
5257             "Set bounding box using PRIMITIVE_BOUNDING_BOX_EXT state",
5258             BBoxRenderCase::FLAG_SET_BBOX_STATE,
5259         },
5260         {
5261             "tessellation_set_per_draw",
5262             "Set bounding box using gl_BoundingBoxEXT, use same value for all primitives",
5263             BBoxRenderCase::FLAG_SET_BBOX_OUTPUT,
5264         },
5265         {
5266             "tessellation_set_per_primitive",
5267             "Set bounding box using gl_BoundingBoxEXT, use per-primitive bounding box",
5268             BBoxRenderCase::FLAG_SET_BBOX_OUTPUT | BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX,
5269         },
5270     };
5271     static const struct
5272     {
5273         const char *name;
5274         const char *description;
5275         uint32_t stageFlags;
5276     } pipelineConfigs[] = {
5277         {"vertex_fragment", "Render with vertex-fragment program", 0u},
5278         {"vertex_tessellation_fragment", "Render with vertex-tessellation{ctrl,eval}-fragment program",
5279          BBoxRenderCase::FLAG_TESSELLATION},
5280         {"vertex_geometry_fragment", "Render with vertex-tessellation{ctrl,eval}-geometry-fragment program",
5281          BBoxRenderCase::FLAG_GEOMETRY},
5282         {"vertex_tessellation_geometry_fragment", "Render with vertex-geometry-fragment program",
5283          BBoxRenderCase::FLAG_TESSELLATION | BBoxRenderCase::FLAG_GEOMETRY},
5284     };
5285     static const struct
5286     {
5287         const char *name;
5288         const char *description;
5289         uint32_t flags;
5290         uint32_t invalidFlags;
5291         uint32_t requiredFlags;
5292     } usageConfigs[] = {
5293         {"default_framebuffer_bbox_equal", "Render to default framebuffer, set tight bounding box",
5294          BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
5295          BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX, 0},
5296         {"default_framebuffer_bbox_larger", "Render to default framebuffer, set padded bounding box",
5297          BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_LARGER,
5298          BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX, 0},
5299         {"default_framebuffer_bbox_smaller", "Render to default framebuffer, set too small bounding box",
5300          BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_SMALLER,
5301          BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX, 0},
5302         {"fbo_bbox_equal", "Render to texture, set tight bounding box",
5303          BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL,
5304          BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX, 0},
5305         {"fbo_bbox_larger", "Render to texture, set padded bounding box",
5306          BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_LARGER,
5307          BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX, 0},
5308         {"fbo_bbox_smaller", "Render to texture, set too small bounding box",
5309          BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_SMALLER,
5310          BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX, 0},
5311         {"default_framebuffer", "Render to default framebuffer, set tight bounding box",
5312          BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL, 0,
5313          BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX},
5314         {"fbo", "Render to texture, set tight bounding box",
5315          BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL, 0,
5316          BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX},
5317     };
5318     enum PrimitiveRenderType
5319     {
5320         TYPE_TRIANGLE,
5321         TYPE_LINE,
5322         TYPE_POINT,
5323     };
5324     const struct
5325     {
5326         const char *name;
5327         const char *description;
5328         PrimitiveRenderType type;
5329         uint32_t flags;
5330     } primitiveTypes[] = {
5331         {"triangles", "Triangle render tests", TYPE_TRIANGLE, 0},
5332         {"lines", "Line render tests", TYPE_LINE, 0},
5333         {"points", "Point render tests", TYPE_POINT, 0},
5334         {"wide_lines", "Wide line render tests", TYPE_LINE, LineRenderCase::LINEFLAG_WIDE},
5335         {"wide_points", "Wide point render tests", TYPE_POINT, PointRenderCase::POINTFLAG_WIDE},
5336     };
5337 
5338     // .state_query
5339     {
5340         tcu::TestCaseGroup *const stateQueryGroup = new tcu::TestCaseGroup(m_testCtx, "state_query", "State queries");
5341         addChild(stateQueryGroup);
5342 
5343         stateQueryGroup->addChild(new InitialValueCase(m_context, "initial_value", "Initial value case"));
5344         stateQueryGroup->addChild(new QueryCase(m_context, "getfloat", "getFloatv", QueryCase::QUERY_FLOAT));
5345         stateQueryGroup->addChild(new QueryCase(m_context, "getboolean", "getBooleanv", QueryCase::QUERY_BOOLEAN));
5346         stateQueryGroup->addChild(new QueryCase(m_context, "getinteger", "getIntegerv", QueryCase::QUERY_INT));
5347         stateQueryGroup->addChild(new QueryCase(m_context, "getinteger64", "getInteger64v", QueryCase::QUERY_INT64));
5348     }
5349 
5350     // .triangles
5351     // .(wide_)lines
5352     // .(wide_)points
5353     for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); ++primitiveTypeNdx)
5354     {
5355         tcu::TestCaseGroup *const primitiveGroup = new tcu::TestCaseGroup(
5356             m_testCtx, primitiveTypes[primitiveTypeNdx].name, primitiveTypes[primitiveTypeNdx].description);
5357         addChild(primitiveGroup);
5358 
5359         for (int stateSetMethodNdx = 0; stateSetMethodNdx < DE_LENGTH_OF_ARRAY(stateSetMethods); ++stateSetMethodNdx)
5360         {
5361             tcu::TestCaseGroup *const methodGroup = new tcu::TestCaseGroup(
5362                 m_testCtx, stateSetMethods[stateSetMethodNdx].name, stateSetMethods[stateSetMethodNdx].description);
5363             primitiveGroup->addChild(methodGroup);
5364 
5365             for (int pipelineConfigNdx = 0; pipelineConfigNdx < DE_LENGTH_OF_ARRAY(pipelineConfigs);
5366                  ++pipelineConfigNdx)
5367             {
5368                 if ((stateSetMethods[stateSetMethodNdx].methodFlags & BBoxRenderCase::FLAG_SET_BBOX_OUTPUT) != 0 &&
5369                     (pipelineConfigs[pipelineConfigNdx].stageFlags & BBoxRenderCase::FLAG_TESSELLATION) == 0)
5370                 {
5371                     // invalid config combination
5372                 }
5373                 else
5374                 {
5375                     tcu::TestCaseGroup *const pipelineGroup =
5376                         new tcu::TestCaseGroup(m_testCtx, pipelineConfigs[pipelineConfigNdx].name,
5377                                                pipelineConfigs[pipelineConfigNdx].description);
5378                     methodGroup->addChild(pipelineGroup);
5379 
5380                     for (int usageNdx = 0; usageNdx < DE_LENGTH_OF_ARRAY(usageConfigs); ++usageNdx)
5381                     {
5382                         const uint32_t flags =
5383                             primitiveTypes[primitiveTypeNdx].flags | stateSetMethods[stateSetMethodNdx].methodFlags |
5384                             pipelineConfigs[pipelineConfigNdx].stageFlags | usageConfigs[usageNdx].flags;
5385 
5386                         if (usageConfigs[usageNdx].invalidFlags && (flags & usageConfigs[usageNdx].invalidFlags) != 0)
5387                             continue;
5388                         if (usageConfigs[usageNdx].requiredFlags && (flags & usageConfigs[usageNdx].requiredFlags) == 0)
5389                             continue;
5390 
5391                         switch (primitiveTypes[primitiveTypeNdx].type)
5392                         {
5393                         case TYPE_TRIANGLE:
5394                             pipelineGroup->addChild(new GridRenderCase(m_context, usageConfigs[usageNdx].name,
5395                                                                        usageConfigs[usageNdx].description, flags));
5396                             break;
5397                         case TYPE_LINE:
5398                             pipelineGroup->addChild(new LineRenderCase(m_context, usageConfigs[usageNdx].name,
5399                                                                        usageConfigs[usageNdx].description, flags));
5400                             break;
5401                         case TYPE_POINT:
5402                             pipelineGroup->addChild(new PointRenderCase(m_context, usageConfigs[usageNdx].name,
5403                                                                         usageConfigs[usageNdx].description, flags));
5404                             break;
5405                         default:
5406                             DE_ASSERT(false);
5407                         }
5408                     }
5409                 }
5410             }
5411         }
5412     }
5413 
5414     // .depth
5415     {
5416         static const struct
5417         {
5418             const char *name;
5419             const char *description;
5420             DepthDrawCase::DepthType depthMethod;
5421         } depthMethods[] = {
5422             {"builtin_depth", "Fragment depth not modified in fragment shader", DepthDrawCase::DEPTH_BUILTIN},
5423             {"user_defined_depth", "Fragment depth is defined in the fragment shader",
5424              DepthDrawCase::DEPTH_USER_DEFINED},
5425         };
5426         static const struct
5427         {
5428             const char *name;
5429             const char *description;
5430             DepthDrawCase::BBoxState bboxState;
5431             DepthDrawCase::BBoxSize bboxSize;
5432         } depthCases[] = {
5433             {
5434                 "global_state_bbox_equal",
5435                 "Test tight bounding box with global bbox state",
5436                 DepthDrawCase::STATE_GLOBAL,
5437                 DepthDrawCase::BBOX_EQUAL,
5438             },
5439             {
5440                 "global_state_bbox_larger",
5441                 "Test padded bounding box with global bbox state",
5442                 DepthDrawCase::STATE_GLOBAL,
5443                 DepthDrawCase::BBOX_LARGER,
5444             },
5445             {
5446                 "per_primitive_bbox_equal",
5447                 "Test tight bounding box with tessellation output bbox",
5448                 DepthDrawCase::STATE_PER_PRIMITIVE,
5449                 DepthDrawCase::BBOX_EQUAL,
5450             },
5451             {
5452                 "per_primitive_bbox_larger",
5453                 "Test padded bounding box with tessellation output bbox",
5454                 DepthDrawCase::STATE_PER_PRIMITIVE,
5455                 DepthDrawCase::BBOX_LARGER,
5456             },
5457         };
5458 
5459         tcu::TestCaseGroup *const depthGroup =
5460             new tcu::TestCaseGroup(m_testCtx, "depth", "Test bounding box depth component");
5461         addChild(depthGroup);
5462 
5463         // .builtin_depth
5464         // .user_defined_depth
5465         for (int depthNdx = 0; depthNdx < DE_LENGTH_OF_ARRAY(depthMethods); ++depthNdx)
5466         {
5467             tcu::TestCaseGroup *const group =
5468                 new tcu::TestCaseGroup(m_testCtx, depthMethods[depthNdx].name, depthMethods[depthNdx].description);
5469             depthGroup->addChild(group);
5470 
5471             for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(depthCases); ++caseNdx)
5472                 group->addChild(new DepthDrawCase(m_context, depthCases[caseNdx].name, depthCases[caseNdx].description,
5473                                                   depthMethods[depthNdx].depthMethod, depthCases[caseNdx].bboxState,
5474                                                   depthCases[caseNdx].bboxSize));
5475         }
5476     }
5477 
5478     // .blit_fbo
5479     {
5480         tcu::TestCaseGroup *const blitFboGroup =
5481             new tcu::TestCaseGroup(m_testCtx, "blit_fbo", "Test bounding box does not affect blitting");
5482         addChild(blitFboGroup);
5483 
5484         blitFboGroup->addChild(new BlitFboCase(m_context, "blit_default_to_fbo", "Blit from default fb to fbo",
5485                                                BlitFboCase::TARGET_DEFAULT, BlitFboCase::TARGET_FBO));
5486         blitFboGroup->addChild(new BlitFboCase(m_context, "blit_fbo_to_default", "Blit from fbo to default fb",
5487                                                BlitFboCase::TARGET_FBO, BlitFboCase::TARGET_DEFAULT));
5488         blitFboGroup->addChild(new BlitFboCase(m_context, "blit_fbo_to_fbo", "Blit from fbo to fbo",
5489                                                BlitFboCase::TARGET_FBO, BlitFboCase::TARGET_FBO));
5490     }
5491 
5492     // .clear
5493     {
5494         tcu::TestCaseGroup *const clearGroup =
5495             new tcu::TestCaseGroup(m_testCtx, "clear", "Test bounding box does not clears");
5496         addChild(clearGroup);
5497 
5498         clearGroup->addChild(new ClearCase(m_context, "full_clear", "Do full clears", 0));
5499         clearGroup->addChild(new ClearCase(m_context, "full_clear_with_triangles",
5500                                            "Do full clears and render some geometry", ClearCase::DRAW_TRIANGLE_BIT));
5501         clearGroup->addChild(new ClearCase(m_context, "full_clear_with_triangles_per_primitive_bbox",
5502                                            "Do full clears and render some geometry",
5503                                            ClearCase::DRAW_TRIANGLE_BIT | ClearCase::PER_PRIMITIVE_BBOX_BIT));
5504         clearGroup->addChild(
5505             new ClearCase(m_context, "scissored_clear", "Do scissored clears", ClearCase::SCISSOR_CLEAR_BIT));
5506         clearGroup->addChild(new ClearCase(m_context, "scissored_clear_with_triangles",
5507                                            "Do scissored clears and render some geometry",
5508                                            ClearCase::SCISSOR_CLEAR_BIT | ClearCase::DRAW_TRIANGLE_BIT));
5509         clearGroup->addChild(new ClearCase(m_context, "scissored_clear_with_triangles_per_primitive_bbox",
5510                                            "Do scissored clears and render some geometry",
5511                                            ClearCase::SCISSOR_CLEAR_BIT | ClearCase::DRAW_TRIANGLE_BIT |
5512                                                ClearCase::PER_PRIMITIVE_BBOX_BIT));
5513         clearGroup->addChild(new ClearCase(m_context, "scissored_full_clear", "Do full clears with enabled scissor",
5514                                            ClearCase::FULLSCREEN_SCISSOR_BIT | ClearCase::SCISSOR_CLEAR_BIT));
5515         clearGroup->addChild(new ClearCase(m_context, "scissored_full_clear_with_triangles",
5516                                            "Do full clears with enabled scissor and render some geometry",
5517                                            ClearCase::FULLSCREEN_SCISSOR_BIT | ClearCase::SCISSOR_CLEAR_BIT |
5518                                                ClearCase::DRAW_TRIANGLE_BIT));
5519         clearGroup->addChild(new ClearCase(m_context, "scissored_full_clear_with_triangles_per_primitive_bbox",
5520                                            "Do full clears with enabled scissor and render some geometry",
5521                                            ClearCase::FULLSCREEN_SCISSOR_BIT | ClearCase::SCISSOR_CLEAR_BIT |
5522                                                ClearCase::DRAW_TRIANGLE_BIT | ClearCase::PER_PRIMITIVE_BBOX_BIT));
5523     }
5524 
5525     // .call_order (Khronos bug #13262)
5526     {
5527         tcu::TestCaseGroup *const callOrderGroup =
5528             new tcu::TestCaseGroup(m_testCtx, "call_order", "Test viewport and bounding box calls have no effect");
5529         addChild(callOrderGroup);
5530 
5531         callOrderGroup->addChild(new ViewportCallOrderCase(m_context, "viewport_first_bbox_second",
5532                                                            "Set up viewport first and bbox after",
5533                                                            ViewportCallOrderCase::VIEWPORT_FIRST));
5534         callOrderGroup->addChild(new ViewportCallOrderCase(m_context, "bbox_first_viewport_second",
5535                                                            "Set up bbox first and viewport after",
5536                                                            ViewportCallOrderCase::BBOX_FIRST));
5537     }
5538 }
5539 
5540 } // namespace Functional
5541 } // namespace gles31
5542 } // namespace deqp
5543