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