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