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