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