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