1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 2.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 "es2fDefaultVertexAttributeTests.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 gles2
45 {
46 namespace Functional
47 {
48 namespace
49 {
50 
51 static const int s_valueRange = 10;
52 
53 static const char* const s_passThroughFragmentShaderSource =	"varying mediump vec4 v_color;\n"
54 																"void main (void)\n"
55 																"{\n"
56 																"	gl_FragColor = v_color;\n"
57 																"}\n";
58 
59 template <typename T1, int S1, typename T2, int S2>
convertToTypeVec(const tcu::Vector<T2,S2> & v)60 tcu::Vector<T1, S1> convertToTypeVec (const tcu::Vector<T2, S2>& v)
61 {
62 	tcu::Vector<T1, S1> retVal;
63 
64 	for (int ndx = 0; ndx < S1; ++ndx)
65 		retVal[ndx] = T1(0);
66 
67 	if (S1 == 4)
68 		retVal[3] = T1(1);
69 
70 	for (int ndx = 0; ndx < de::min(S1, S2); ++ndx)
71 		retVal[ndx] = T1(v[ndx]);
72 
73 	return retVal;
74 }
75 
76 class FloatLoader
77 {
78 public:
~FloatLoader(void)79 	virtual				~FloatLoader	(void) {};
80 
81 	// returns the value loaded
82 	virtual tcu::Vec4	load			(glu::CallLogWrapper& gl, int index, const tcu::Vec4& v) const = 0;
83 };
84 
85 #define GEN_DIRECT_FLOAT_LOADER(TYPE, COMPS, TYPECODE, CASENAME, VALUES)				\
86 	class LoaderVertexAttrib##COMPS##TYPECODE : public FloatLoader						\
87 	{																					\
88 	public:																				\
89 		enum																			\
90 		{																				\
91 			NORMALIZING = 0,															\
92 		};																				\
93 		enum																			\
94 		{																				\
95 			COMPONENTS = (COMPS)														\
96 		};																				\
97 		typedef TYPE Type;																\
98 																						\
99 		tcu::Vec4 load (glu::CallLogWrapper& gl, int index, const tcu::Vec4& v) const	\
100 		{																				\
101 			tcu::Vector<TYPE, COMPONENTS> value;										\
102 			value = convertToTypeVec<Type, COMPONENTS>(v);								\
103 																						\
104 			gl.glVertexAttrib ##COMPS ##TYPECODE VALUES;								\
105 			return convertToTypeVec<float, 4>(value);									\
106 		}																				\
107 																						\
108 		static const char* getCaseName (void)											\
109 		{																				\
110 			return CASENAME;															\
111 		}																				\
112 																						\
113 		static const char* getName (void)												\
114 		{																				\
115 			return "VertexAttrib" #COMPS #TYPECODE;										\
116 		}																				\
117 	}
118 
119 #define GEN_INDIRECT_FLOAT_LOADER(TYPE, COMPS, TYPECODE, CASENAME)						\
120 	class LoaderVertexAttrib##COMPS##TYPECODE : public FloatLoader						\
121 	{																					\
122 	public:																				\
123 		enum																			\
124 		{																				\
125 			NORMALIZING = 0,															\
126 		};																				\
127 		enum																			\
128 		{																				\
129 			COMPONENTS = (COMPS)														\
130 		};																				\
131 		typedef TYPE Type;																\
132 																						\
133 		tcu::Vec4 load (glu::CallLogWrapper& gl, int index, const tcu::Vec4& v) const	\
134 		{																				\
135 			tcu::Vector<TYPE, COMPONENTS> value;										\
136 			value = convertToTypeVec<Type, COMPONENTS>(v);								\
137 																						\
138 			gl.glVertexAttrib ##COMPS ##TYPECODE (index, value.getPtr());				\
139 			return convertToTypeVec<float, 4>(value);									\
140 		}																				\
141 																						\
142 		static const char* getCaseName (void)											\
143 		{																				\
144 			return CASENAME;															\
145 		}																				\
146 																						\
147 		static const char* getName (void)												\
148 		{																				\
149 			return "VertexAttrib" #COMPS #TYPECODE;										\
150 		}																				\
151 	}
152 
153 GEN_DIRECT_FLOAT_LOADER(float, 1, f, "vertex_attrib_1f", (index, value.x()));
154 GEN_DIRECT_FLOAT_LOADER(float, 2, f, "vertex_attrib_2f", (index, value.x(), value.y()));
155 GEN_DIRECT_FLOAT_LOADER(float, 3, f, "vertex_attrib_3f", (index, value.x(), value.y(), value.z()));
156 GEN_DIRECT_FLOAT_LOADER(float, 4, f, "vertex_attrib_4f", (index, value.x(), value.y(), value.z(), value.w()));
157 
158 GEN_INDIRECT_FLOAT_LOADER(float, 1, fv, "vertex_attrib_1fv");
159 GEN_INDIRECT_FLOAT_LOADER(float, 2, fv, "vertex_attrib_2fv");
160 GEN_INDIRECT_FLOAT_LOADER(float, 3, fv, "vertex_attrib_3fv");
161 GEN_INDIRECT_FLOAT_LOADER(float, 4, fv, "vertex_attrib_4fv");
162 
163 class AttributeCase : public TestCase
164 {
165 									AttributeCase			(Context& ctx, const char* name, const char* desc, const char* funcName, bool normalizing, bool useNegative, glu::DataType dataType);
166 public:
167 	template<typename LoaderType>
168 	static AttributeCase*			create					(Context& ctx, glu::DataType dataType);
169 									~AttributeCase			(void);
170 
171 private:
172 	void							init					(void);
173 	void							deinit					(void);
174 	IterateResult					iterate					(void);
175 
176 	glu::DataType					getTargetType			(void) const;
177 	std::string						genVertexSource			(void) const;
178 	bool							renderWithValue			(const tcu::Vec4& v);
179 	tcu::Vec4						computeColor			(const tcu::Vec4& value);
180 	bool							verifyUnicoloredBuffer	(const tcu::Surface& scene, const tcu::Vec4& refValue);
181 
182 	const bool						m_normalizing;
183 	const bool						m_useNegativeValues;
184 	const char* const				m_funcName;
185 	const glu::DataType				m_dataType;
186 	const FloatLoader*				m_loader;
187 	glu::ShaderProgram*				m_program;
188 	deUint32						m_bufID;
189 	bool							m_allIterationsPassed;
190 	int								m_iteration;
191 
192 	enum
193 	{
194 		RENDER_SIZE = 32
195 	};
196 };
197 
AttributeCase(Context & ctx,const char * name,const char * desc,const char * funcName,bool normalizing,bool useNegative,glu::DataType dataType)198 AttributeCase::AttributeCase (Context& ctx, const char* name, const char* desc, const char* funcName, bool normalizing, bool useNegative, glu::DataType dataType)
199 	: TestCase				(ctx, name, desc)
200 	, m_normalizing			(normalizing)
201 	, m_useNegativeValues	(useNegative)
202 	, m_funcName			(funcName)
203 	, m_dataType			(dataType)
204 	, m_loader				(DE_NULL)
205 	, m_program				(DE_NULL)
206 	, m_bufID				(0)
207 	, m_allIterationsPassed	(true)
208 	, m_iteration			(0)
209 {
210 }
211 
212 template<typename LoaderType>
create(Context & ctx,glu::DataType dataType)213 AttributeCase* AttributeCase::create (Context& ctx, glu::DataType dataType)
214 {
215 	AttributeCase* retVal = new AttributeCase(ctx,
216 											  LoaderType::getCaseName(),
217 											  (std::string("Test ") + LoaderType::getName()).c_str(),
218 											  LoaderType::getName(),
219 											  LoaderType::NORMALIZING != 0,
220 											  std::numeric_limits<typename LoaderType::Type>::is_signed,
221 											  dataType);
222 	retVal->m_loader = new LoaderType();
223 	return retVal;
224 }
225 
~AttributeCase(void)226 AttributeCase::~AttributeCase (void)
227 {
228 	deinit();
229 }
230 
init(void)231 void AttributeCase::init (void)
232 {
233 	if (m_context.getRenderTarget().getWidth() < RENDER_SIZE || m_context.getRenderTarget().getHeight() < RENDER_SIZE)
234 		throw tcu::NotSupportedError("Render target must be at least " + de::toString<int>(RENDER_SIZE) + "x" + de::toString<int>(RENDER_SIZE));
235 
236 	// log test info
237 
238 	{
239 		const float			maxRange		= (m_normalizing) ? (1.0f) : (s_valueRange);
240 		const float			minRange		= (m_useNegativeValues) ? (-maxRange) : (0.0f);
241 
242 		m_testCtx.getLog()
243 			<< tcu::TestLog::Message
244 			<< "Loading attribute values using " << m_funcName << "\n"
245 			<< "Attribute type: " << glu::getDataTypeName(m_dataType) << "\n"
246 			<< "Attribute value range: [" << minRange << ", " << maxRange << "]"
247 			<< tcu::TestLog::EndMessage;
248 	}
249 
250 	// gen shader and base quad
251 
252 	m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(genVertexSource()) << glu::FragmentSource(s_passThroughFragmentShaderSource));
253 	m_testCtx.getLog() << *m_program;
254 	if (!m_program->isOk())
255 		throw tcu::TestError("could not build program");
256 
257 	{
258 		const tcu::Vec4 fullscreenQuad[] =
259 		{
260 			tcu::Vec4( 1.0f,  1.0f, 0.0f, 1.0f),
261 			tcu::Vec4( 1.0f, -1.0f, 0.0f, 1.0f),
262 			tcu::Vec4(-1.0f,  1.0f, 0.0f, 1.0f),
263 			tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f),
264 		};
265 
266 		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
267 
268 		gl.genBuffers(1, &m_bufID);
269 		gl.bindBuffer(GL_ARRAY_BUFFER, m_bufID);
270 		gl.bufferData(GL_ARRAY_BUFFER, sizeof(fullscreenQuad), fullscreenQuad, GL_STATIC_DRAW);
271 		GLU_EXPECT_NO_ERROR(gl.getError(), "fill buffer");
272 	}
273 }
274 
deinit(void)275 void AttributeCase::deinit (void)
276 {
277 	delete m_loader;
278 	m_loader = DE_NULL;
279 
280 	delete m_program;
281 	m_program = DE_NULL;
282 
283 	if (m_bufID)
284 	{
285 		m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_bufID);
286 		m_bufID = 0;
287 	}
288 }
289 
iterate(void)290 AttributeCase::IterateResult AttributeCase::iterate (void)
291 {
292 	static const tcu::Vec4 testValues[] =
293 	{
294 		tcu::Vec4(0.0f, 0.5f, 0.2f, 1.0f),
295 		tcu::Vec4(0.1f, 0.7f, 1.0f, 0.6f),
296 		tcu::Vec4(0.4f, 0.2f, 0.0f, 0.5f),
297 		tcu::Vec4(0.5f, 0.0f, 0.9f, 0.1f),
298 		tcu::Vec4(0.6f, 0.2f, 0.2f, 0.9f),
299 		tcu::Vec4(0.9f, 1.0f, 0.0f, 0.0f),
300 		tcu::Vec4(1.0f, 0.5f, 0.3f, 0.8f),
301 	};
302 
303 	const tcu::ScopedLogSection section(m_testCtx.getLog(), "Iteration", "Iteration " + de::toString(m_iteration+1) + "/" + de::toString(DE_LENGTH_OF_ARRAY(testValues)));
304 
305 	// Test normalizing transfers with whole range, non-normalizing with up to s_valueRange
306 	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));
307 
308 	if (!renderWithValue(testValue))
309 		m_allIterationsPassed = false;
310 
311 	// continue
312 
313 	if (++m_iteration < DE_LENGTH_OF_ARRAY(testValues))
314 		return CONTINUE;
315 
316 	if (m_allIterationsPassed)
317 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
318 	else
319 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected values");
320 
321 	return STOP;
322 }
323 
genVertexSource(void) const324 std::string AttributeCase::genVertexSource (void) const
325 {
326 	const int			vectorSize	= (glu::isDataTypeMatrix(m_dataType)) ? (glu::getDataTypeMatrixNumRows(m_dataType)) : (glu::isDataTypeVector(m_dataType)) ? (glu::getDataTypeScalarSize(m_dataType)) : (-1);
327 	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));
328 	const int			components	= (glu::isDataTypeMatrix(m_dataType)) ? (glu::getDataTypeMatrixNumRows(m_dataType)) : (glu::getDataTypeScalarSize(m_dataType));
329 	std::ostringstream	buf;
330 
331 	buf <<	"attribute highp vec4 a_position;\n"
332 			"attribute highp " << glu::getDataTypeName(m_dataType) << " a_value;\n"
333 			"varying highp vec4 v_color;\n"
334 			"void main (void)\n"
335 			"{\n"
336 			"	gl_Position = a_position;\n"
337 			"\n";
338 
339 	if (m_normalizing)
340 		buf << "	highp " << vectorType << " normalizedValue = " << ((glu::getDataTypeScalarType(m_dataType) == glu::TYPE_FLOAT) ? ("") : (vectorType)) << "(a_value" << ((glu::isDataTypeMatrix(m_dataType)) ? ("[1]") : ("")) << ");\n";
341 	else
342 		buf << "	highp " << vectorType << " normalizedValue = " << ((glu::getDataTypeScalarType(m_dataType) == glu::TYPE_FLOAT) ? ("") : (vectorType)) << "(a_value" << ((glu::isDataTypeMatrix(m_dataType)) ? ("[1]") : ("")) << ") / float(" << s_valueRange << ");\n";
343 
344 	if (m_useNegativeValues)
345 		buf << "	highp " << vectorType << " positiveNormalizedValue = (normalizedValue + " << vectorType << "(1.0)) / 2.0;\n";
346 	else
347 		buf << "	highp " << vectorType << " positiveNormalizedValue = normalizedValue;\n";
348 
349 	if (components == 1)
350 		buf << "	v_color = vec4(positiveNormalizedValue, 0.0, 0.0, 1.0);\n";
351 	else if (components == 2)
352 		buf << "	v_color = vec4(positiveNormalizedValue.xy, 0.0, 1.0);\n";
353 	else if (components == 3)
354 		buf << "	v_color = vec4(positiveNormalizedValue.xyz, 1.0);\n";
355 	else if (components == 4)
356 		buf << "	v_color = vec4((positiveNormalizedValue.xy + positiveNormalizedValue.zz) / 2.0, positiveNormalizedValue.w, 1.0);\n";
357 	else
358 		DE_ASSERT(DE_FALSE);
359 
360 	buf << "}\n";
361 
362 	return buf.str();
363 }
364 
renderWithValue(const tcu::Vec4 & v)365 bool AttributeCase::renderWithValue (const tcu::Vec4& v)
366 {
367 	glu::CallLogWrapper	gl				(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
368 
369 	gl.enableLogging(true);
370 
371 	const int			positionIndex	= gl.glGetAttribLocation(m_program->getProgram(), "a_position");
372 	const int			valueIndex		= gl.glGetAttribLocation(m_program->getProgram(), "a_value");
373 	tcu::Surface		dest			(RENDER_SIZE, RENDER_SIZE);
374 	tcu::Vec4			loadedValue;
375 
376 	gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
377 	gl.glClear(GL_COLOR_BUFFER_BIT);
378 	gl.glViewport(0, 0, RENDER_SIZE, RENDER_SIZE);
379 	GLU_EXPECT_NO_ERROR(gl.glGetError(), "setup");
380 
381 	gl.glBindBuffer(GL_ARRAY_BUFFER, m_bufID);
382 	gl.glVertexAttribPointer(positionIndex, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
383 	gl.glEnableVertexAttribArray(positionIndex);
384 	GLU_EXPECT_NO_ERROR(gl.glGetError(), "position va");
385 
386 	// transfer test value. Load to the second column in the matrix case
387 	loadedValue = m_loader->load(gl, (glu::isDataTypeMatrix(m_dataType)) ? (valueIndex + 1) : (valueIndex), v);
388 	GLU_EXPECT_NO_ERROR(gl.glGetError(), "default va");
389 
390 	gl.glUseProgram(m_program->getProgram());
391 	gl.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
392 	gl.glUseProgram(0);
393 	GLU_EXPECT_NO_ERROR(gl.glGetError(), "draw");
394 
395 	glu::readPixels(m_context.getRenderContext(), 0, 0, dest.getAccess());
396 
397 	// check whole result is colored correctly
398 	return verifyUnicoloredBuffer(dest, computeColor(loadedValue));
399 }
400 
computeColor(const tcu::Vec4 & value)401 tcu::Vec4 AttributeCase::computeColor (const tcu::Vec4& value)
402 {
403 	const tcu::Vec4 normalizedValue			= value / ((m_normalizing) ? (1.0f) : ((float)s_valueRange));
404 	const tcu::Vec4 positiveNormalizedValue = ((m_useNegativeValues) ? ((normalizedValue + tcu::Vec4(1.0f)) / 2.0f) : (normalizedValue));
405 	const int		components				= (glu::isDataTypeMatrix(m_dataType)) ? (glu::getDataTypeMatrixNumRows(m_dataType)) : (glu::getDataTypeScalarSize(m_dataType));
406 
407 	if (components == 1)
408 		return tcu::Vec4(positiveNormalizedValue.x(), 0.0f, 0.0f, 1.0f);
409 	else if (components == 2)
410 		return tcu::Vec4(positiveNormalizedValue.x(), positiveNormalizedValue.y(), 0.0f, 1.0f);
411 	else if (components == 3)
412 		return tcu::Vec4(positiveNormalizedValue.x(), positiveNormalizedValue.y(), positiveNormalizedValue.z(), 1.0f);
413 	else if (components == 4)
414 		return tcu::Vec4((positiveNormalizedValue.x() + positiveNormalizedValue.z()) / 2.0f, (positiveNormalizedValue.y() + positiveNormalizedValue.z()) / 2.0f, positiveNormalizedValue.w(), 1.0f);
415 	else
416 		DE_ASSERT(DE_FALSE);
417 
418 	return tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f);
419 }
420 
verifyUnicoloredBuffer(const tcu::Surface & scene,const tcu::Vec4 & refValue)421 bool AttributeCase::verifyUnicoloredBuffer (const tcu::Surface& scene, const tcu::Vec4& refValue)
422 {
423 	tcu::Surface	errorMask		(RENDER_SIZE, RENDER_SIZE);
424 	const tcu::RGBA	refColor		(refValue);
425 	const int		resultThreshold	= 2;
426 	const tcu::RGBA	colorThreshold	= m_context.getRenderTarget().getPixelFormat().getColorThreshold() * resultThreshold;
427 	bool			error			= false;
428 
429 	tcu::RGBA		exampleColor;
430 	tcu::IVec2		examplePos;
431 
432 	tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec());
433 
434 	m_testCtx.getLog() << tcu::TestLog::Message << "Verifying rendered image. Expecting color " << refColor << ", threshold " << colorThreshold << tcu::TestLog::EndMessage;
435 
436 	for (int y = 0; y < RENDER_SIZE; ++y)
437 	for (int x = 0; x < RENDER_SIZE; ++x)
438 	{
439 		const tcu::RGBA color = scene.getPixel(x, y);
440 
441 		if (de::abs(color.getRed()   - refColor.getRed())   > colorThreshold.getRed()   ||
442 			de::abs(color.getGreen() - refColor.getGreen()) > colorThreshold.getGreen() ||
443 			de::abs(color.getBlue()  - refColor.getBlue())  > colorThreshold.getBlue())
444 		{
445 			// first error
446 			if (!error)
447 			{
448 				exampleColor = color;
449 				examplePos = tcu::IVec2(x, y);
450 			}
451 
452 			error = true;
453 			errorMask.setPixel(x, y, tcu::RGBA::red());
454 		}
455 	}
456 
457 	if (!error)
458 		m_testCtx.getLog() << tcu::TestLog::Message << "Rendered image is valid." << tcu::TestLog::EndMessage;
459 	else
460 	{
461 		m_testCtx.getLog()	<< tcu::TestLog::Message
462 							<< "Found invalid pixel(s).\n"
463 							<< "Pixel at (" << examplePos.x() << ", " << examplePos.y() << ") color: " << exampleColor
464 							<< tcu::TestLog::EndMessage
465 							<< tcu::TestLog::ImageSet("Result", "Render result")
466 							<< tcu::TestLog::Image("Result", "Result", scene)
467 							<< tcu::TestLog::Image("ErrorMask", "Error Mask", errorMask)
468 							<< tcu::TestLog::EndImageSet;
469 	}
470 
471 	return !error;
472 }
473 
474 } // anonymous
475 
DefaultVertexAttributeTests(Context & context)476 DefaultVertexAttributeTests::DefaultVertexAttributeTests (Context& context)
477 	: TestCaseGroup(context, "default_vertex_attrib", "Test default vertex attributes")
478 {
479 }
480 
~DefaultVertexAttributeTests(void)481 DefaultVertexAttributeTests::~DefaultVertexAttributeTests (void)
482 {
483 }
484 
init(void)485 void DefaultVertexAttributeTests::init (void)
486 {
487 	struct Target
488 	{
489 		const char*		name;
490 		glu::DataType	dataType;
491 		bool			reducedTestSets;	// !< use reduced coverage
492 	};
493 
494 	static const Target floatTargets[] =
495 	{
496 		{ "float",	glu::TYPE_FLOAT,		false	},
497 		{ "vec2",	glu::TYPE_FLOAT_VEC2,	true	},
498 		{ "vec3",	glu::TYPE_FLOAT_VEC3,	true	},
499 		{ "vec4",	glu::TYPE_FLOAT_VEC4,	false	},
500 		{ "mat2",	glu::TYPE_FLOAT_MAT2,	true	},
501 		{ "mat3",	glu::TYPE_FLOAT_MAT3,	true	},
502 		{ "mat4",	glu::TYPE_FLOAT_MAT4,	false	},
503 	};
504 
505 	// float targets
506 
507 	for (int targetNdx = 0; targetNdx < DE_LENGTH_OF_ARRAY(floatTargets); ++targetNdx)
508 	{
509 		tcu::TestCaseGroup* const	group	= new tcu::TestCaseGroup(m_testCtx, floatTargets[targetNdx].name, (std::string("test with ") + floatTargets[targetNdx].name).c_str());
510 		const bool					fullSet	= !floatTargets[targetNdx].reducedTestSets;
511 
512 #define ADD_CASE(X) group->addChild(AttributeCase::create<X>(m_context, floatTargets[targetNdx].dataType))
513 #define ADD_REDUCED_CASE(X)	if (fullSet) ADD_CASE(X)
514 
515 		ADD_CASE		(LoaderVertexAttrib1f);
516 		ADD_REDUCED_CASE(LoaderVertexAttrib2f);
517 		ADD_REDUCED_CASE(LoaderVertexAttrib3f);
518 		ADD_CASE		(LoaderVertexAttrib4f);
519 
520 		ADD_CASE		(LoaderVertexAttrib1fv);
521 		ADD_REDUCED_CASE(LoaderVertexAttrib2fv);
522 		ADD_REDUCED_CASE(LoaderVertexAttrib3fv);
523 		ADD_CASE		(LoaderVertexAttrib4fv);
524 
525 #undef ADD_CASE
526 #undef ADD_REDUCED_CASE
527 
528 		addChild(group);
529 	}
530 }
531 
532 } // Functional
533 } // gles2
534 } // deqp
535