• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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