1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 3.0 Module
3  * -------------------------------------------------
4  *
5  * Copyright 2014 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 Default vertex attribute test
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es3fDefaultVertexAttributeTests.hpp"
25 #include "tcuVector.hpp"
26 #include "tcuRenderTarget.hpp"
27 #include "tcuSurface.hpp"
28 #include "tcuTextureUtil.hpp"
29 #include "gluRenderContext.hpp"
30 #include "gluCallLogWrapper.hpp"
31 #include "gluShaderProgram.hpp"
32 #include "gluObjectWrapper.hpp"
33 #include "gluPixelTransfer.hpp"
34 #include "glwEnums.hpp"
35 #include "glwFunctions.hpp"
36 #include "deMath.h"
37 #include "deStringUtil.hpp"
38 #include "deString.h"
39 
40 #include <limits>
41 
42 namespace deqp
43 {
44 namespace gles3
45 {
46 namespace Functional
47 {
48 namespace
49 {
50 
51 static const int s_valueRange = 10;
52 
53 static const char* const s_passThroughFragmentShaderSource =	"#version 300 es\n"
54 																"layout(location = 0) out mediump vec4 fragColor;\n"
55 																"in mediump vec4 v_color;\n"
56 																"void main (void)\n"
57 																"{\n"
58 																"	fragColor = v_color;\n"
59 																"}\n";
60 
61 template <typename T1, int S1, typename T2, int S2>
convertToTypeVec(const tcu::Vector<T2,S2> & v)62 tcu::Vector<T1, S1> convertToTypeVec (const tcu::Vector<T2, S2>& v)
63 {
64 	tcu::Vector<T1, S1> retVal;
65 
66 	for (int ndx = 0; ndx < S1; ++ndx)
67 		retVal[ndx] = T1(0);
68 
69 	if (S1 == 4)
70 		retVal[3] = T1(1);
71 
72 	for (int ndx = 0; ndx < de::min(S1, S2); ++ndx)
73 		retVal[ndx] = T1(v[ndx]);
74 
75 	return retVal;
76 }
77 
78 class FloatLoader
79 {
80 public:
~FloatLoader(void)81 	virtual				~FloatLoader	(void) {};
82 
83 	// returns the value loaded
84 	virtual tcu::Vec4	load			(glu::CallLogWrapper& gl, int index, const tcu::Vec4& v) const = 0;
85 };
86 
87 #define GEN_DIRECT_FLOAT_LOADER(TYPE, COMPS, TYPECODE, CASENAME, VALUES)				\
88 	class LoaderVertexAttrib##COMPS##TYPECODE : public FloatLoader						\
89 	{																					\
90 	public:																				\
91 		enum																			\
92 		{																				\
93 			NORMALIZING = 0,															\
94 		};																				\
95 		enum																			\
96 		{																				\
97 			COMPONENTS = COMPS															\
98 		};																				\
99 		typedef TYPE Type;																\
100 																						\
101 		tcu::Vec4 load (glu::CallLogWrapper& gl, int index, const tcu::Vec4& v) const	\
102 		{																				\
103 			tcu::Vector<TYPE, COMPONENTS> value;										\
104 			value = convertToTypeVec<Type, COMPONENTS>(v);								\
105 																						\
106 			gl.glVertexAttrib ##COMPS ##TYPECODE VALUES;								\
107 			return convertToTypeVec<float, 4>(value);									\
108 		}																				\
109 																						\
110 		static const char* getCaseName (void)											\
111 		{																				\
112 			return CASENAME;															\
113 		}																				\
114 																						\
115 		static const char* getName (void)												\
116 		{																				\
117 			return "VertexAttrib" #COMPS #TYPECODE;										\
118 		}																				\
119 	}
120 
121 #define GEN_INDIRECT_FLOAT_LOADER(TYPE, COMPS, TYPECODE, CASENAME)						\
122 	class LoaderVertexAttrib##COMPS##TYPECODE : public FloatLoader						\
123 	{																					\
124 	public:																				\
125 		enum																			\
126 		{																				\
127 			NORMALIZING = 0,															\
128 		};																				\
129 		enum																			\
130 		{																				\
131 			COMPONENTS = COMPS															\
132 		};																				\
133 		typedef TYPE Type;																\
134 																						\
135 		tcu::Vec4 load (glu::CallLogWrapper& gl, int index, const tcu::Vec4& v) const	\
136 		{																				\
137 			tcu::Vector<TYPE, COMPONENTS> value;										\
138 			value = convertToTypeVec<Type, COMPONENTS>(v);								\
139 																						\
140 			gl.glVertexAttrib ##COMPS ##TYPECODE (index, value.getPtr());				\
141 			return convertToTypeVec<float, 4>(value);									\
142 		}																				\
143 																						\
144 		static const char* getCaseName (void)											\
145 		{																				\
146 			return CASENAME;															\
147 		}																				\
148 																						\
149 		static const char* getName (void)												\
150 		{																				\
151 			return "VertexAttrib" #COMPS #TYPECODE;										\
152 		}																				\
153 	}
154 
155 #define GEN_DIRECT_INTEGER_LOADER(TYPE, COMPS, TYPECODE, CASENAME, VALUES)				\
156 	class LoaderVertexAttribI##COMPS##TYPECODE : public FloatLoader						\
157 	{																					\
158 	public:																				\
159 		enum																			\
160 		{																				\
161 			NORMALIZING = 0,															\
162 		};																				\
163 		enum																			\
164 		{																				\
165 			COMPONENTS = COMPS															\
166 		};																				\
167 		typedef TYPE Type;																\
168 																						\
169 		tcu::Vec4 load (glu::CallLogWrapper& gl, int index, const tcu::Vec4& v) const	\
170 		{																				\
171 			tcu::Vector<TYPE, COMPONENTS> value;										\
172 			value = convertToTypeVec<Type, COMPONENTS>(v);								\
173 																						\
174 			gl.glVertexAttribI ##COMPS ##TYPECODE VALUES;								\
175 			return convertToTypeVec<float, 4>(value);									\
176 		}																				\
177 																						\
178 		static const char* getCaseName (void)											\
179 		{																				\
180 			return CASENAME;															\
181 		}																				\
182 																						\
183 		static const char* getName (void)												\
184 		{																				\
185 			return "VertexAttrib" #COMPS #TYPECODE;										\
186 		}																				\
187 	}
188 
189 #define GEN_INDIRECT_INTEGER_LOADER(TYPE, COMPS, TYPECODE, CASENAME)					\
190 	class LoaderVertexAttribI##COMPS##TYPECODE : public FloatLoader						\
191 	{																					\
192 	public:																				\
193 		enum																			\
194 		{																				\
195 			NORMALIZING = 0,															\
196 		};																				\
197 		enum																			\
198 		{																				\
199 			COMPONENTS = COMPS															\
200 		};																				\
201 		typedef TYPE Type;																\
202 																						\
203 		tcu::Vec4 load (glu::CallLogWrapper& gl, int index, const tcu::Vec4& v) const	\
204 		{																				\
205 			tcu::Vector<TYPE, COMPONENTS> value;										\
206 			value = convertToTypeVec<Type, COMPONENTS>(v);								\
207 																						\
208 			gl.glVertexAttribI ##COMPS ##TYPECODE (index, value.getPtr());				\
209 			return convertToTypeVec<float, 4>(value);									\
210 		}																				\
211 																						\
212 		static const char* getCaseName (void)											\
213 		{																				\
214 			return CASENAME;															\
215 		}																				\
216 																						\
217 		static const char* getName (void)												\
218 		{																				\
219 			return "VertexAttrib" #COMPS #TYPECODE;										\
220 		}																				\
221 	}
222 
223 GEN_DIRECT_FLOAT_LOADER(float, 1, f, "vertex_attrib_1f", (index, value.x()));
224 GEN_DIRECT_FLOAT_LOADER(float, 2, f, "vertex_attrib_2f", (index, value.x(), value.y()));
225 GEN_DIRECT_FLOAT_LOADER(float, 3, f, "vertex_attrib_3f", (index, value.x(), value.y(), value.z()));
226 GEN_DIRECT_FLOAT_LOADER(float, 4, f, "vertex_attrib_4f", (index, value.x(), value.y(), value.z(), value.w()));
227 
228 GEN_INDIRECT_FLOAT_LOADER(float, 1, fv, "vertex_attrib_1fv");
229 GEN_INDIRECT_FLOAT_LOADER(float, 2, fv, "vertex_attrib_2fv");
230 GEN_INDIRECT_FLOAT_LOADER(float, 3, fv, "vertex_attrib_3fv");
231 GEN_INDIRECT_FLOAT_LOADER(float, 4, fv, "vertex_attrib_4fv");
232 
233 GEN_DIRECT_INTEGER_LOADER(deInt32, 4, i, "vertex_attribi_4i", (index, value.x(), value.y(), value.z(), value.w()));
234 GEN_INDIRECT_INTEGER_LOADER(deInt32, 4, iv, "vertex_attribi_4iv");
235 
236 GEN_DIRECT_INTEGER_LOADER(deUint32, 4, ui, "vertex_attribi_4ui", (index, value.x(), value.y(), value.z(), value.w()));
237 GEN_INDIRECT_INTEGER_LOADER(deUint32, 4, uiv, "vertex_attribi_4uiv");
238 
239 class AttributeCase : public TestCase
240 {
241 									AttributeCase			(Context& ctx, const char* name, const char* desc, const char* funcName, bool normalizing, bool useNegative, glu::DataType dataType);
242 public:
243 	template<typename LoaderType>
244 	static AttributeCase*			create					(Context& ctx, glu::DataType dataType);
245 									~AttributeCase			(void);
246 
247 private:
248 	void							init					(void);
249 	void							deinit					(void);
250 	IterateResult					iterate					(void);
251 
252 	glu::DataType					getTargetType			(void) const;
253 	std::string						genVertexSource			(void) const;
254 	bool							renderWithValue			(const tcu::Vec4& v);
255 	tcu::Vec4						computeColor			(const tcu::Vec4& value);
256 	bool							verifyUnicoloredBuffer	(const tcu::Surface& scene, const tcu::Vec4& refValue);
257 
258 	const bool						m_normalizing;
259 	const bool						m_useNegativeValues;
260 	const char* const				m_funcName;
261 	const glu::DataType				m_dataType;
262 	const FloatLoader*				m_loader;
263 	glu::ShaderProgram*				m_program;
264 	deUint32						m_bufID;
265 	bool							m_allIterationsPassed;
266 	int								m_iteration;
267 
268 	enum
269 	{
270 		RENDER_SIZE = 32
271 	};
272 };
273 
AttributeCase(Context & ctx,const char * name,const char * desc,const char * funcName,bool normalizing,bool useNegative,glu::DataType dataType)274 AttributeCase::AttributeCase (Context& ctx, const char* name, const char* desc, const char* funcName, bool normalizing, bool useNegative, glu::DataType dataType)
275 	: TestCase				(ctx, name, desc)
276 	, m_normalizing			(normalizing)
277 	, m_useNegativeValues	(useNegative)
278 	, m_funcName			(funcName)
279 	, m_dataType			(dataType)
280 	, m_loader				(DE_NULL)
281 	, m_program				(DE_NULL)
282 	, m_bufID				(0)
283 	, m_allIterationsPassed	(true)
284 	, m_iteration			(0)
285 {
286 }
287 
288 template<typename LoaderType>
create(Context & ctx,glu::DataType dataType)289 AttributeCase* AttributeCase::create (Context& ctx, glu::DataType dataType)
290 {
291 	AttributeCase* retVal = new AttributeCase(ctx,
292 											  LoaderType::getCaseName(),
293 											  (std::string("Test ") + LoaderType::getName()).c_str(),
294 											  LoaderType::getName(),
295 											  LoaderType::NORMALIZING != 0,
296 											  std::numeric_limits<typename LoaderType::Type>::is_signed,
297 											  dataType);
298 	retVal->m_loader = new LoaderType();
299 	return retVal;
300 }
301 
~AttributeCase(void)302 AttributeCase::~AttributeCase (void)
303 {
304 	deinit();
305 }
306 
init(void)307 void AttributeCase::init (void)
308 {
309 	if (m_context.getRenderTarget().getWidth() < RENDER_SIZE || m_context.getRenderTarget().getHeight() < RENDER_SIZE)
310 		throw tcu::NotSupportedError("Render target must be at least " + de::toString<int>(RENDER_SIZE) + "x" + de::toString<int>(RENDER_SIZE));
311 
312 	// log test info
313 
314 	{
315 		const float			maxRange		= (m_normalizing) ? (1.0f) : (s_valueRange);
316 		const float			minRange		= (m_useNegativeValues) ? (-maxRange) : (0.0f);
317 
318 		m_testCtx.getLog()
319 			<< tcu::TestLog::Message
320 			<< "Loading attribute values using " << m_funcName << "\n"
321 			<< "Attribute type: " << glu::getDataTypeName(m_dataType) << "\n"
322 			<< "Attribute value range: [" << minRange << ", " << maxRange << "]"
323 			<< tcu::TestLog::EndMessage;
324 	}
325 
326 	// gen shader and base quad
327 
328 	m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(genVertexSource()) << glu::FragmentSource(s_passThroughFragmentShaderSource));
329 	m_testCtx.getLog() << *m_program;
330 	if (!m_program->isOk())
331 		throw tcu::TestError("could not build program");
332 
333 	{
334 		const tcu::Vec4 fullscreenQuad[] =
335 		{
336 			tcu::Vec4( 1.0f,  1.0f, 0.0f, 1.0f),
337 			tcu::Vec4( 1.0f, -1.0f, 0.0f, 1.0f),
338 			tcu::Vec4(-1.0f,  1.0f, 0.0f, 1.0f),
339 			tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f),
340 		};
341 
342 		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
343 
344 		gl.genBuffers(1, &m_bufID);
345 		gl.bindBuffer(GL_ARRAY_BUFFER, m_bufID);
346 		gl.bufferData(GL_ARRAY_BUFFER, sizeof(fullscreenQuad), fullscreenQuad, GL_STATIC_DRAW);
347 		GLU_EXPECT_NO_ERROR(gl.getError(), "fill buffer");
348 	}
349 }
350 
deinit(void)351 void AttributeCase::deinit (void)
352 {
353 	delete m_loader;
354 	m_loader = DE_NULL;
355 
356 	delete m_program;
357 	m_program = DE_NULL;
358 
359 	if (m_bufID)
360 	{
361 		m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_bufID);
362 		m_bufID = 0;
363 	}
364 }
365 
iterate(void)366 AttributeCase::IterateResult AttributeCase::iterate (void)
367 {
368 	static const tcu::Vec4 testValues[] =
369 	{
370 		tcu::Vec4(0.0f, 0.5f, 0.2f, 1.0f),
371 		tcu::Vec4(0.1f, 0.7f, 1.0f, 0.6f),
372 		tcu::Vec4(0.4f, 0.2f, 0.0f, 0.5f),
373 		tcu::Vec4(0.5f, 0.0f, 0.9f, 0.1f),
374 		tcu::Vec4(0.6f, 0.2f, 0.2f, 0.9f),
375 		tcu::Vec4(0.9f, 1.0f, 0.0f, 0.0f),
376 		tcu::Vec4(1.0f, 0.5f, 0.3f, 0.8f),
377 	};
378 
379 	const tcu::ScopedLogSection section(m_testCtx.getLog(), "Iteration", "Iteration " + de::toString(m_iteration+1) + "/" + de::toString(DE_LENGTH_OF_ARRAY(testValues)));
380 
381 	// Test normalizing transfers with whole range, non-normalizing with up to s_valueRange
382 	const tcu::Vec4 testValue = ((m_useNegativeValues) ? (testValues[m_iteration] * 2.0f - tcu::Vec4(1.0f)) : (testValues[m_iteration])) * ((m_normalizing) ? (1.0f) : ((float)s_valueRange));
383 
384 	if (!renderWithValue(testValue))
385 		m_allIterationsPassed = false;
386 
387 	// continue
388 
389 	if (++m_iteration < DE_LENGTH_OF_ARRAY(testValues))
390 		return CONTINUE;
391 
392 	if (m_allIterationsPassed)
393 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
394 	else
395 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected values");
396 
397 	return STOP;
398 }
399 
genVertexSource(void) const400 std::string AttributeCase::genVertexSource (void) const
401 {
402 	const int			vectorSize	= (glu::isDataTypeMatrix(m_dataType)) ? (glu::getDataTypeMatrixNumRows(m_dataType)) : (glu::isDataTypeVector(m_dataType)) ? (glu::getDataTypeScalarSize(m_dataType)) : (-1);
403 	const char* const	vectorType	= glu::getDataTypeName((glu::isDataTypeMatrix(m_dataType)) ? (glu::getDataTypeVector(glu::TYPE_FLOAT, vectorSize)) : (glu::isDataTypeVector(m_dataType)) ? (glu::getDataTypeVector(glu::TYPE_FLOAT, vectorSize)) : (glu::TYPE_FLOAT));
404 	const int			components	= (glu::isDataTypeMatrix(m_dataType)) ? (glu::getDataTypeMatrixNumRows(m_dataType)) : (glu::getDataTypeScalarSize(m_dataType));
405 	std::ostringstream	buf;
406 
407 	buf <<	"#version 300 es\n"
408 			"in highp vec4 a_position;\n"
409 			"in highp " << glu::getDataTypeName(m_dataType) << " a_value;\n"
410 			"out highp vec4 v_color;\n"
411 			"void main (void)\n"
412 			"{\n"
413 			"	gl_Position = a_position;\n"
414 			"\n";
415 
416 	if (m_normalizing)
417 		buf << "	highp " << vectorType << " normalizedValue = " << ((glu::getDataTypeScalarType(m_dataType) == glu::TYPE_FLOAT) ? ("") : (vectorType)) << "(a_value" << ((glu::isDataTypeMatrix(m_dataType)) ? ("[1]") : ("")) << ");\n";
418 	else
419 		buf << "	highp " << vectorType << " normalizedValue = " << ((glu::getDataTypeScalarType(m_dataType) == glu::TYPE_FLOAT) ? ("") : (vectorType)) << "(a_value" << ((glu::isDataTypeMatrix(m_dataType)) ? ("[1]") : ("")) << ") / float(" << s_valueRange << ");\n";
420 
421 	if (m_useNegativeValues)
422 		buf << "	highp " << vectorType << " positiveNormalizedValue = (normalizedValue + " << vectorType << "(1.0)) / 2.0;\n";
423 	else
424 		buf << "	highp " << vectorType << " positiveNormalizedValue = normalizedValue;\n";
425 
426 	if (components == 1)
427 		buf << "	v_color = vec4(positiveNormalizedValue, 0.0, 0.0, 1.0);\n";
428 	else if (components == 2)
429 		buf << "	v_color = vec4(positiveNormalizedValue.xy, 0.0, 1.0);\n";
430 	else if (components == 3)
431 		buf << "	v_color = vec4(positiveNormalizedValue.xyz, 1.0);\n";
432 	else if (components == 4)
433 		buf << "	v_color = vec4((positiveNormalizedValue.xy + positiveNormalizedValue.zz) / 2.0, positiveNormalizedValue.w, 1.0);\n";
434 	else
435 		DE_ASSERT(DE_FALSE);
436 
437 	buf << "}\n";
438 
439 	return buf.str();
440 }
441 
renderWithValue(const tcu::Vec4 & v)442 bool AttributeCase::renderWithValue (const tcu::Vec4& v)
443 {
444 	glu::CallLogWrapper	gl				(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
445 
446 	gl.enableLogging(true);
447 
448 	const int			positionIndex	= gl.glGetAttribLocation(m_program->getProgram(), "a_position");
449 	const int			valueIndex		= gl.glGetAttribLocation(m_program->getProgram(), "a_value");
450 	tcu::Surface		dest			(RENDER_SIZE, RENDER_SIZE);
451 	tcu::Vec4			loadedValue;
452 
453 	gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
454 	gl.glClear(GL_COLOR_BUFFER_BIT);
455 	gl.glViewport(0, 0, RENDER_SIZE, RENDER_SIZE);
456 	GLU_EXPECT_NO_ERROR(gl.glGetError(), "setup");
457 
458 	gl.glBindBuffer(GL_ARRAY_BUFFER, m_bufID);
459 	gl.glVertexAttribPointer(positionIndex, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
460 	gl.glEnableVertexAttribArray(positionIndex);
461 	GLU_EXPECT_NO_ERROR(gl.glGetError(), "position va");
462 
463 	// transfer test value. Load to the second column in the matrix case
464 	loadedValue = m_loader->load(gl, (glu::isDataTypeMatrix(m_dataType)) ? (valueIndex + 1) : (valueIndex), v);
465 	GLU_EXPECT_NO_ERROR(gl.glGetError(), "default va");
466 
467 	gl.glUseProgram(m_program->getProgram());
468 	gl.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
469 	gl.glUseProgram(0);
470 	GLU_EXPECT_NO_ERROR(gl.glGetError(), "draw");
471 
472 	glu::readPixels(m_context.getRenderContext(), 0, 0, dest.getAccess());
473 
474 	// check whole result is colored correctly
475 	return verifyUnicoloredBuffer(dest, computeColor(loadedValue));
476 }
477 
computeColor(const tcu::Vec4 & value)478 tcu::Vec4 AttributeCase::computeColor (const tcu::Vec4& value)
479 {
480 	const tcu::Vec4 normalizedValue			= value / ((m_normalizing) ? (1.0f) : ((float)s_valueRange));
481 	const tcu::Vec4 positiveNormalizedValue = ((m_useNegativeValues) ? ((normalizedValue + tcu::Vec4(1.0f)) / 2.0f) : (normalizedValue));
482 	const int		components				= (glu::isDataTypeMatrix(m_dataType)) ? (glu::getDataTypeMatrixNumRows(m_dataType)) : (glu::getDataTypeScalarSize(m_dataType));
483 
484 	if (components == 1)
485 		return tcu::Vec4(positiveNormalizedValue.x(), 0.0f, 0.0f, 1.0f);
486 	else if (components == 2)
487 		return tcu::Vec4(positiveNormalizedValue.x(), positiveNormalizedValue.y(), 0.0f, 1.0f);
488 	else if (components == 3)
489 		return tcu::Vec4(positiveNormalizedValue.x(), positiveNormalizedValue.y(), positiveNormalizedValue.z(), 1.0f);
490 	else if (components == 4)
491 		return tcu::Vec4((positiveNormalizedValue.x() + positiveNormalizedValue.z()) / 2.0f, (positiveNormalizedValue.y() + positiveNormalizedValue.z()) / 2.0f, positiveNormalizedValue.w(), 1.0f);
492 	else
493 		DE_ASSERT(DE_FALSE);
494 
495 	return tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f);
496 }
497 
verifyUnicoloredBuffer(const tcu::Surface & scene,const tcu::Vec4 & refValue)498 bool AttributeCase::verifyUnicoloredBuffer (const tcu::Surface& scene, const tcu::Vec4& refValue)
499 {
500 	tcu::Surface	errorMask		(RENDER_SIZE, RENDER_SIZE);
501 	const tcu::RGBA	refColor		(refValue);
502 	const int		resultThreshold	= 2;
503 	const tcu::RGBA	colorThreshold	= m_context.getRenderTarget().getPixelFormat().getColorThreshold() * resultThreshold;
504 	bool			error			= false;
505 
506 	tcu::RGBA		exampleColor;
507 	tcu::IVec2		examplePos;
508 
509 	tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec());
510 
511 	m_testCtx.getLog() << tcu::TestLog::Message << "Verifying rendered image. Expecting color " << refColor << ", threshold " << colorThreshold << tcu::TestLog::EndMessage;
512 
513 	for (int y = 0; y < RENDER_SIZE; ++y)
514 	for (int x = 0; x < RENDER_SIZE; ++x)
515 	{
516 		const tcu::RGBA color = scene.getPixel(x, y);
517 
518 		if (de::abs(color.getRed()   - refColor.getRed())   > colorThreshold.getRed()   ||
519 			de::abs(color.getGreen() - refColor.getGreen()) > colorThreshold.getGreen() ||
520 			de::abs(color.getBlue()  - refColor.getBlue())  > colorThreshold.getBlue())
521 		{
522 			// first error
523 			if (!error)
524 			{
525 				exampleColor = color;
526 				examplePos = tcu::IVec2(x, y);
527 			}
528 
529 			error = true;
530 			errorMask.setPixel(x, y, tcu::RGBA::red());
531 		}
532 	}
533 
534 	if (!error)
535 		m_testCtx.getLog() << tcu::TestLog::Message << "Rendered image is valid." << tcu::TestLog::EndMessage;
536 	else
537 	{
538 		m_testCtx.getLog()	<< tcu::TestLog::Message
539 							<< "Found invalid pixel(s).\n"
540 							<< "Pixel at (" << examplePos.x() << ", " << examplePos.y() << ") color: " << exampleColor
541 							<< tcu::TestLog::EndMessage
542 							<< tcu::TestLog::ImageSet("Result", "Render result")
543 							<< tcu::TestLog::Image("Result", "Result", scene)
544 							<< tcu::TestLog::Image("ErrorMask", "Error Mask", errorMask)
545 							<< tcu::TestLog::EndImageSet;
546 	}
547 
548 	return !error;
549 }
550 
551 } // anonymous
552 
DefaultVertexAttributeTests(Context & context)553 DefaultVertexAttributeTests::DefaultVertexAttributeTests (Context& context)
554 	: TestCaseGroup(context, "default_vertex_attrib", "Test default vertex attributes")
555 {
556 }
557 
~DefaultVertexAttributeTests(void)558 DefaultVertexAttributeTests::~DefaultVertexAttributeTests (void)
559 {
560 }
561 
init(void)562 void DefaultVertexAttributeTests::init (void)
563 {
564 	struct Target
565 	{
566 		const char*		name;
567 		glu::DataType	dataType;
568 		bool			reducedTestSets;	// !< use reduced coverage
569 	};
570 
571 	static const Target floatTargets[] =
572 	{
573 		{ "float",	glu::TYPE_FLOAT,		false	},
574 		{ "vec2",	glu::TYPE_FLOAT_VEC2,	true	},
575 		{ "vec3",	glu::TYPE_FLOAT_VEC3,	true	},
576 		{ "vec4",	glu::TYPE_FLOAT_VEC4,	false	},
577 		{ "mat2",	glu::TYPE_FLOAT_MAT2,	true	},
578 		{ "mat2x3",	glu::TYPE_FLOAT_MAT2X3,	true	},
579 		{ "mat2x4",	glu::TYPE_FLOAT_MAT2X4,	true	},
580 		{ "mat3",	glu::TYPE_FLOAT_MAT3,	true	},
581 		{ "mat3x2",	glu::TYPE_FLOAT_MAT3X2,	true	},
582 		{ "mat3x4",	glu::TYPE_FLOAT_MAT3X4,	true	},
583 		{ "mat4",	glu::TYPE_FLOAT_MAT4,	false	},
584 		{ "mat4x2",	glu::TYPE_FLOAT_MAT4X2,	true	},
585 		{ "mat4x3",	glu::TYPE_FLOAT_MAT4X3,	true	},
586 	};
587 
588 	static const Target intTargets[] =
589 	{
590 		{ "int",	glu::TYPE_INT,			false	},
591 		{ "ivec2",	glu::TYPE_INT_VEC2,		true	},
592 		{ "ivec3",	glu::TYPE_INT_VEC3,		true	},
593 		{ "ivec4",	glu::TYPE_INT_VEC4,		false	},
594 	};
595 
596 	static const Target uintTargets[] =
597 	{
598 		{ "uint",	glu::TYPE_UINT,			false	},
599 		{ "uvec2",	glu::TYPE_UINT_VEC2,	true	},
600 		{ "uvec3",	glu::TYPE_UINT_VEC3,	true	},
601 		{ "uvec4",	glu::TYPE_UINT_VEC4,	false	},
602 	};
603 
604 	// float targets
605 
606 	for (int targetNdx = 0; targetNdx < DE_LENGTH_OF_ARRAY(floatTargets); ++targetNdx)
607 	{
608 		tcu::TestCaseGroup* const	group	= new tcu::TestCaseGroup(m_testCtx, floatTargets[targetNdx].name, (std::string("test with ") + floatTargets[targetNdx].name).c_str());
609 		const bool					fullSet	= !floatTargets[targetNdx].reducedTestSets;
610 
611 #define ADD_CASE(X) group->addChild(AttributeCase::create<X>(m_context, floatTargets[targetNdx].dataType))
612 #define ADD_REDUCED_CASE(X)	if (fullSet) ADD_CASE(X)
613 
614 		ADD_CASE		(LoaderVertexAttrib1f);
615 		ADD_REDUCED_CASE(LoaderVertexAttrib2f);
616 		ADD_REDUCED_CASE(LoaderVertexAttrib3f);
617 		ADD_CASE		(LoaderVertexAttrib4f);
618 
619 		ADD_CASE		(LoaderVertexAttrib1fv);
620 		ADD_REDUCED_CASE(LoaderVertexAttrib2fv);
621 		ADD_REDUCED_CASE(LoaderVertexAttrib3fv);
622 		ADD_CASE		(LoaderVertexAttrib4fv);
623 
624 #undef ADD_CASE
625 #undef ADD_REDUCED_CASE
626 
627 		addChild(group);
628 	}
629 
630 	// int targets
631 
632 	for (int targetNdx = 0; targetNdx < DE_LENGTH_OF_ARRAY(intTargets); ++targetNdx)
633 	{
634 		tcu::TestCaseGroup* const	group	= new tcu::TestCaseGroup(m_testCtx, intTargets[targetNdx].name, (std::string("test with ") + intTargets[targetNdx].name).c_str());
635 
636 #define ADD_CASE(X) group->addChild(AttributeCase::create<X>(m_context, intTargets[targetNdx].dataType))
637 
638 		ADD_CASE		(LoaderVertexAttribI4i);
639 		ADD_CASE		(LoaderVertexAttribI4iv);
640 
641 #undef ADD_CASE
642 
643 		addChild(group);
644 	}
645 
646 	// uint targets
647 
648 	for (int targetNdx = 0; targetNdx < DE_LENGTH_OF_ARRAY(uintTargets); ++targetNdx)
649 	{
650 		tcu::TestCaseGroup* const	group	= new tcu::TestCaseGroup(m_testCtx, uintTargets[targetNdx].name, (std::string("test with ") + uintTargets[targetNdx].name).c_str());
651 
652 #define ADD_CASE(X) group->addChild(AttributeCase::create<X>(m_context, uintTargets[targetNdx].dataType))
653 
654 		ADD_CASE		(LoaderVertexAttribI4ui);
655 		ADD_CASE		(LoaderVertexAttribI4uiv);
656 
657 #undef ADD_CASE
658 
659 		addChild(group);
660 	}
661 }
662 
663 } // Functional
664 } // gles3
665 } // deqp
666