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