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 Texture upload performance tests.
22  *
23  * \todo [2012-10-01 pyry]
24  *  - Test different pixel unpack alignments
25  *  - Use multiple textures
26  *  - Trash cache prior to uploading from data ptr
27  *//*--------------------------------------------------------------------*/
28 
29 #include "es2pTextureUploadTests.hpp"
30 #include "tcuTexture.hpp"
31 #include "tcuTextureUtil.hpp"
32 #include "tcuTestLog.hpp"
33 #include "tcuSurface.hpp"
34 #include "gluTextureUtil.hpp"
35 #include "gluShaderProgram.hpp"
36 #include "gluPixelTransfer.hpp"
37 #include "deStringUtil.hpp"
38 #include "deRandom.hpp"
39 #include "deClock.h"
40 #include "deString.h"
41 
42 #include "glsCalibration.hpp"
43 
44 #include "glwEnums.hpp"
45 #include "glwFunctions.hpp"
46 
47 #include <algorithm>
48 #include <vector>
49 
50 namespace deqp
51 {
52 namespace gles2
53 {
54 namespace Performance
55 {
56 
57 using tcu::Vec2;
58 using tcu::Vec3;
59 using tcu::Vec4;
60 using tcu::IVec4;
61 using std::string;
62 using std::vector;
63 using tcu::TestLog;
64 using tcu::TextureFormat;
65 using namespace glw; // GL types
66 
67 static const int	VIEWPORT_SIZE	= 64;
68 static const float	quadCoords[] =
69 {
70 	-1.0f, -1.0f,
71 	 1.0f, -1.0f,
72 	-1.0f,  1.0f,
73 	 1.0f,  1.0f
74 };
75 
76 class TextureUploadCase : public TestCase
77 {
78 public:
79 								TextureUploadCase	(Context& context, const char* name, const char* description, UploadFunction uploadFunction, deUint32 format, deUint32 type, int texSize);
80 								~TextureUploadCase	(void);
81 
82 	virtual void				init				(void);
83 	void						deinit				(void);
84 
85 	virtual IterateResult		iterate				(void) = 0;
86 	void						logResults			(void);
87 
88 protected:
89 	UploadFunction				m_uploadFunction;
90 	deUint32					m_format;
91 	deUint32					m_type;
92 	int							m_texSize;
93 	int							m_alignment;
94 
95 	gls::TheilSenCalibrator		m_calibrator;
96 	glu::ShaderProgram*			m_program;
97 	deUint32					m_texture;
98 	de::Random					m_rnd;
99 	TestLog&					m_log;
100 
101 	vector<deUint8>				m_texData;
102 };
103 
TextureUploadCase(Context & context,const char * name,const char * description,UploadFunction uploadFunction,deUint32 format,deUint32 type,int texSize)104 TextureUploadCase::TextureUploadCase (Context& context, const char* name, const char* description, UploadFunction uploadFunction, deUint32 format, deUint32 type, int texSize)
105 	: TestCase			(context, tcu::NODETYPE_PERFORMANCE, name, description)
106 	, m_uploadFunction	(uploadFunction)
107 	, m_format			(format)
108 	, m_type			(type)
109 	, m_texSize			(texSize)
110 	, m_alignment		(4)
111 	, m_calibrator		()
112 	, m_program			(DE_NULL)
113 	, m_texture			(0)
114 	, m_rnd				(deStringHash(name))
115 	, m_log				(context.getTestContext().getLog())
116 {
117 }
118 
~TextureUploadCase(void)119 TextureUploadCase::~TextureUploadCase (void)
120 {
121 	TextureUploadCase::deinit();
122 }
123 
deinit(void)124 void TextureUploadCase::deinit (void)
125 {
126 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
127 
128 	if (m_program)
129 	{
130 		delete m_program;
131 		m_program = DE_NULL;
132 	}
133 
134 	gl.deleteTextures(1, &m_texture);
135 	m_texture = 0;
136 
137 	m_texData.clear();
138 }
139 
init(void)140 void TextureUploadCase::init (void)
141 {
142 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
143 	int maxTextureSize;
144 	gl.getIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
145 
146 	if (m_texSize > maxTextureSize)
147 	{
148 		m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Unsupported texture size");
149 		return;
150 	}
151 
152 	// Create program
153 
154 	string vertexShaderSource = "";
155 	string fragmentShaderSource = "";
156 
157 	vertexShaderSource.append(	"precision mediump	float;\n"
158 								"attribute vec2		a_pos;\n"
159 								"varying   vec2		v_texCoord;\n"
160 								"\n"
161 								"void main (void)\n"
162 								"{\n"
163 								"	v_texCoord	= a_pos;\n"
164 								"	gl_Position = vec4(a_pos, 0.5, 1.0);\n"
165 								"}\n");
166 
167 	fragmentShaderSource.append("precision	mediump	float;\n"
168 								"uniform	lowp sampler2D	u_sampler;\n"
169 								"varying	vec2			v_texCoord;\n"
170 								"\n"
171 								"void main (void)\n"
172 								"{\n"
173 								"	gl_FragColor = texture2D(u_sampler, v_texCoord.xy);\n"
174 								"}\n");
175 
176 	DE_ASSERT(!m_program);
177 	m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
178 
179 	if (!m_program->isOk())
180 	{
181 		m_log << *m_program;
182 		TCU_FAIL("Failed to create shader program (m_programRender)");
183 	}
184 
185 	gl.useProgram (m_program->getProgram());
186 
187 	// Init GL state
188 
189 	gl.viewport		(0, 0, VIEWPORT_SIZE, VIEWPORT_SIZE);
190 	gl.disable		(GL_DEPTH_TEST);
191 	gl.disable		(GL_CULL_FACE);
192 	gl.enable		(GL_BLEND);
193 	gl.blendFunc	(GL_ONE, GL_ONE);
194 	gl.clearColor	(0.0f, 0.0f, 0.0f, 1.0f);
195 	gl.clear		(GL_COLOR_BUFFER_BIT);
196 
197 	deUint32 uSampler	= gl.getUniformLocation(m_program->getProgram(), "u_sampler");
198 	deUint32 aPos		= gl.getAttribLocation (m_program->getProgram(), "a_pos");
199 	gl.enableVertexAttribArray	(aPos);
200 	gl.vertexAttribPointer		(aPos,	2, GL_FLOAT, GL_FALSE, 0, &quadCoords[0]);
201 	gl.uniform1i				(uSampler, 0);
202 
203 	// Create texture
204 
205 	gl.activeTexture	(GL_TEXTURE0);
206 	gl.genTextures		(1, &m_texture);
207 	gl.bindTexture		(GL_TEXTURE_2D, m_texture);
208 	gl.pixelStorei		(GL_UNPACK_ALIGNMENT, m_alignment);
209 	gl.texParameteri	(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
210 	gl.texParameteri	(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
211 	gl.texParameteri	(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
212 	gl.texParameteri	(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
213 
214 	// Prepare texture data
215 
216 	{
217 		const tcu::TextureFormat&	texFmt		= glu::mapGLTransferFormat(m_format, m_type);
218 		int							pixelSize	= texFmt.getPixelSize();
219 		int							stride		= deAlign32(pixelSize*m_texSize, m_alignment);
220 
221 		m_texData.resize(stride*m_texSize);
222 
223 		tcu::PixelBufferAccess		access		(texFmt, m_texSize, m_texSize, 1, stride, 0, &m_texData[0]);
224 
225 		tcu::fillWithComponentGradients(access, tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
226 	}
227 
228 	// Do a dry-run to ensure the pipes are hot
229 
230 	gl.texImage2D	(GL_TEXTURE_2D, 0, m_format, m_texSize, m_texSize, 0, m_format, m_type, &m_texData[0]);
231 	gl.drawArrays	(GL_TRIANGLE_STRIP, 0, 4);
232 	gl.finish		();
233 }
234 
logResults(void)235 void TextureUploadCase::logResults (void)
236 {
237 	const gls::MeasureState& measureState = m_calibrator.getMeasureState();
238 
239 	// Log measurement details
240 
241 	m_log << TestLog::Section("Measurement details", "Measurement details");
242 	m_log << TestLog::Message << "Uploading texture with " << (m_uploadFunction == UPLOAD_TEXIMAGE2D ? "glTexImage2D" : "glTexSubImage2D") << "." << TestLog::EndMessage; // \todo [arttu] Change enum to struct with name included
243 	m_log << TestLog::Message << "Texture size = "	<< m_texSize	 << "x" << m_texSize	 << "." << TestLog::EndMessage;
244 	m_log << TestLog::Message << "Viewport size = " << VIEWPORT_SIZE << "x" << VIEWPORT_SIZE << "." << TestLog::EndMessage;
245 	m_log << TestLog::Message << measureState.numDrawCalls << " upload calls / iteration" << TestLog::EndMessage;
246 	m_log << TestLog::EndSection;
247 
248 	// Log results
249 
250 	TestLog& log = m_testCtx.getLog();
251 	log << TestLog::Section("Results", "Results");
252 
253 	// Log individual frame durations
254 	//for (int i = 0; i < m_calibrator.measureState.numFrames; i++)
255 	//	m_log << TestLog::Message	<< "Frame "	<< i+1 << " duration: \t" << m_calibrator.measureState.frameTimes[i] << " us."<< TestLog::EndMessage;
256 
257 	std::vector<deUint64> sortedFrameTimes(measureState.frameTimes.begin(), measureState.frameTimes.end());
258 	std::sort(sortedFrameTimes.begin(), sortedFrameTimes.end());
259 	vector<deUint64>::const_iterator first	= sortedFrameTimes.begin();
260 	vector<deUint64>::const_iterator last	= sortedFrameTimes.end();
261 	vector<deUint64>::const_iterator middle	= first + (last - first) / 2;
262 
263 	deUint64 medianFrameTime			=  *middle;
264 	double medianMTexelsPerSeconds		= (double)(m_texSize*m_texSize*measureState.numDrawCalls) / (double)medianFrameTime;
265 	double medianTexelDrawDurationNs	= (double)medianFrameTime * 1000.0 / (double)(m_texSize*m_texSize*measureState.numDrawCalls);
266 
267 	deUint64	totalTime			= measureState.getTotalTime();
268 	int			numFrames			= (int)measureState.frameTimes.size();
269 	deInt64		numTexturesDrawn	= measureState.numDrawCalls * numFrames;
270 	deInt64		numPixels			= (deInt64)m_texSize * (deInt64)m_texSize * numTexturesDrawn;
271 
272 	double		framesPerSecond			= (double)numFrames / ((double)totalTime / 1000000.0);
273 	double		avgFrameTime			= (double)totalTime / (double)numFrames;
274 	double		avgMTexelsPerSeconds	= (double)numPixels / (double)totalTime;
275 	double		avgTexelDrawDurationNs	= (double)totalTime * 1000.0 / (double)numPixels;
276 
277 	log << TestLog::Float("FramesPerSecond",	"Frames per second in measurement\t\t",		"Frames/s",		QP_KEY_TAG_PERFORMANCE,	(float)framesPerSecond);
278 	log << TestLog::Float("AverageFrameTime",	"Average frame duration in measurement\t",	"us",			QP_KEY_TAG_PERFORMANCE,	(float)avgFrameTime);
279 	log << TestLog::Float("AverageTexelPerf",	"Average texel upload performance\t\t",		"MTex/s",		QP_KEY_TAG_PERFORMANCE,	(float)avgMTexelsPerSeconds);
280 	log << TestLog::Float("AverageTexelTime",	"Average texel upload duration\t\t",		"ns",			QP_KEY_TAG_PERFORMANCE,	(float)avgTexelDrawDurationNs);
281 	log << TestLog::Float("MedianTexelPerf",	"Median texel upload performance\t\t",		"MTex/s",		QP_KEY_TAG_PERFORMANCE,	(float)medianMTexelsPerSeconds);
282 	log << TestLog::Float("MedianTexelTime",	"Median texel upload duration\t\t",			"ns",			QP_KEY_TAG_PERFORMANCE,	(float)medianTexelDrawDurationNs);
283 
284 	log << TestLog::EndSection;
285 
286 	gls::logCalibrationInfo(log, m_calibrator);	// Log calibration details
287 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, de::floatToString((float)avgMTexelsPerSeconds, 2).c_str());
288 }
289 
290 // Texture upload call case
291 
292 class TextureUploadCallCase : public TextureUploadCase
293 {
294 public:
295 							TextureUploadCallCase	(Context& context, const char* name, const char* description, UploadFunction uploadFunction, deUint32 format, deUint32 type, int texSize);
296 							~TextureUploadCallCase	(void);
297 
298 	IterateResult			iterate					(void);
299 	void					render					(void);
300 };
301 
TextureUploadCallCase(Context & context,const char * name,const char * description,UploadFunction uploadFunction,deUint32 format,deUint32 type,int texSize)302 TextureUploadCallCase::TextureUploadCallCase (Context& context, const char* name, const char* description, UploadFunction uploadFunction, deUint32 format, deUint32 type, int texSize)
303 	: TextureUploadCase (context, name, description, uploadFunction, format, type, texSize)
304 {
305 }
306 
~TextureUploadCallCase(void)307 TextureUploadCallCase::~TextureUploadCallCase (void)
308 {
309 	TextureUploadCase::deinit();
310 }
311 
render(void)312 void TextureUploadCallCase::render (void)
313 {
314 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
315 
316 	// Draw multiple quads to ensure enough workload
317 
318 	switch (m_uploadFunction)
319 	{
320 		case UPLOAD_TEXIMAGE2D:
321 			gl.texImage2D(GL_TEXTURE_2D, 0, m_format, m_texSize, m_texSize, 0, m_format, m_type, &m_texData[0]);
322 			break;
323 		case UPLOAD_TEXSUBIMAGE2D:
324 			gl.texSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_texSize, m_texSize, m_format, m_type, &m_texData[0]);
325 			break;
326 		default:
327 			DE_ASSERT(false);
328 	}
329 }
330 
iterate(void)331 tcu::TestNode::IterateResult TextureUploadCallCase::iterate (void)
332 {
333 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
334 
335 	if (m_testCtx.getTestResult() == QP_TEST_RESULT_NOT_SUPPORTED)
336 		return STOP;
337 
338 	for (;;)
339 	{
340 		gls::TheilSenCalibrator::State state = m_calibrator.getState();
341 
342 		if (state == gls::TheilSenCalibrator::STATE_MEASURE)
343 		{
344 			int			numCalls	= m_calibrator.getCallCount();
345 			deUint64	startTime	= deGetMicroseconds();
346 
347 			for (int i = 0; i < numCalls; i++)
348 				render();
349 
350 			gl.finish();
351 
352 			deUint64	endTime		= deGetMicroseconds();
353 			deUint64	duration	= endTime-startTime;
354 
355 			m_calibrator.recordIteration(duration);
356 		}
357 		else if (state == gls::TheilSenCalibrator::STATE_RECOMPUTE_PARAMS)
358 		{
359 			m_calibrator.recomputeParameters();
360 		}
361 		else
362 		{
363 			DE_ASSERT(state == gls::TheilSenCalibrator::STATE_FINISHED);
364 			break;
365 		}
366 
367 		// Touch watchdog between iterations to avoid timeout.
368 		{
369 			qpWatchDog* dog = m_testCtx.getWatchDog();
370 			if (dog)
371 				qpWatchDog_touch(dog);
372 		}
373 	}
374 
375 	GLU_EXPECT_NO_ERROR(gl.getError(), "iterate");
376 	logResults();
377 	return STOP;
378 }
379 
380 // Texture upload and draw case
381 
382 class TextureUploadAndDrawCase : public TextureUploadCase
383 {
384 public:
385 								TextureUploadAndDrawCase	(Context& context, const char* name, const char* description, UploadFunction uploadFunction, deUint32 format, deUint32 type, int texSize);
386 								~TextureUploadAndDrawCase	(void);
387 
388 	IterateResult				iterate						(void);
389 	void						render						(void);
390 
391 private:
392 	bool						m_lastIterationRender;
393 	deUint64					m_renderStart;
394 };
395 
TextureUploadAndDrawCase(Context & context,const char * name,const char * description,UploadFunction uploadFunction,deUint32 format,deUint32 type,int texSize)396 TextureUploadAndDrawCase::TextureUploadAndDrawCase (Context& context, const char* name, const char* description, UploadFunction uploadFunction, deUint32 format, deUint32 type, int texSize)
397 	: TextureUploadCase		(context, name, description, uploadFunction, format, type, texSize)
398 	, m_lastIterationRender	(false)
399 	, m_renderStart			(0)
400 {
401 }
402 
~TextureUploadAndDrawCase(void)403 TextureUploadAndDrawCase::~TextureUploadAndDrawCase (void)
404 {
405 	TextureUploadCase::deinit();
406 }
407 
render(void)408 void TextureUploadAndDrawCase::render (void)
409 {
410 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
411 
412 	// Draw multiple quads to ensure enough workload
413 
414 	switch (m_uploadFunction)
415 	{
416 		case UPLOAD_TEXIMAGE2D:
417 			gl.texImage2D(GL_TEXTURE_2D, 0, m_format, m_texSize, m_texSize, 0, m_format, m_type, &m_texData[0]);
418 			break;
419 		case UPLOAD_TEXSUBIMAGE2D:
420 			gl.texSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_texSize, m_texSize, m_format, m_type, &m_texData[0]);
421 			break;
422 		default:
423 			DE_ASSERT(false);
424 	}
425 
426 	gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
427 }
428 
iterate(void)429 tcu::TestNode::IterateResult TextureUploadAndDrawCase::iterate (void)
430 {
431 	if (m_testCtx.getTestResult() == QP_TEST_RESULT_NOT_SUPPORTED)
432 		return STOP;
433 
434 	if (m_lastIterationRender && (m_calibrator.getState() == gls::TheilSenCalibrator::STATE_MEASURE))
435 	{
436 		deUint64 curTime = deGetMicroseconds();
437 		m_calibrator.recordIteration(curTime - m_renderStart);
438 	}
439 
440 	gls::TheilSenCalibrator::State state = m_calibrator.getState();
441 
442 	if (state == gls::TheilSenCalibrator::STATE_MEASURE)
443 	{
444 		// Render
445 		int	numCalls = m_calibrator.getCallCount();
446 
447 		m_renderStart			= deGetMicroseconds();
448 		m_lastIterationRender	= true;
449 
450 		for (int i = 0; i < numCalls; i++)
451 			render();
452 
453 		return CONTINUE;
454 	}
455 	else if (state == gls::TheilSenCalibrator::STATE_RECOMPUTE_PARAMS)
456 	{
457 		m_calibrator.recomputeParameters();
458 		m_lastIterationRender = false;
459 		return CONTINUE;
460 	}
461 	else
462 	{
463 		DE_ASSERT(state == gls::TheilSenCalibrator::STATE_FINISHED);
464 		GLU_EXPECT_NO_ERROR(m_context.getRenderContext().getFunctions().getError(), "finish");
465 		logResults();
466 		return STOP;
467 	}
468 }
469 
470 // Texture upload tests
471 
TextureUploadTests(Context & context)472 TextureUploadTests::TextureUploadTests (Context& context)
473 	: TestCaseGroup(context, "upload", "Texture upload tests")
474 {
475 }
476 
~TextureUploadTests(void)477 TextureUploadTests::~TextureUploadTests (void)
478 {
479 	TextureUploadTests::deinit();
480 }
481 
deinit(void)482 void TextureUploadTests::deinit (void)
483 {
484 }
485 
init(void)486 void TextureUploadTests::init (void)
487 {
488 	TestCaseGroup* uploadCall		= new TestCaseGroup(m_context, "upload",			"Texture upload");
489 	TestCaseGroup* uploadAndDraw	= new TestCaseGroup(m_context, "upload_draw_swap",	"Texture upload, draw & buffer swap");
490 
491 	addChild(uploadCall);
492 	addChild(uploadAndDraw);
493 
494 	static const struct
495 	{
496 		const char*		name;
497 		const char*		nameLower;
498 		UploadFunction	func;
499 	} uploadFunctions[] =
500 	{
501 		{ "texImage2D",		"teximage2d",		UPLOAD_TEXIMAGE2D },
502 		{ "texSubImage2D",	"texsubimage2d",	UPLOAD_TEXSUBIMAGE2D }
503 	};
504 
505 	static const struct
506 	{
507 		const char*	name;
508 		deUint32	format;
509 		deUint32	type;
510 	} textureCombinations[] =
511 	{
512 		{ "rgb_ubyte",				GL_RGB,				GL_UNSIGNED_BYTE },
513 		{ "rgba_ubyte",				GL_RGBA,			GL_UNSIGNED_BYTE },
514 		{ "alpha_ubyte",			GL_ALPHA,			GL_UNSIGNED_BYTE },
515 		{ "luminance_ubyte",		GL_LUMINANCE,		GL_UNSIGNED_BYTE },
516 		{ "luminance-alpha_ubyte",	GL_LUMINANCE_ALPHA,	GL_UNSIGNED_BYTE },
517 		{ "rgb_ushort565",			GL_RGB,				GL_UNSIGNED_SHORT_5_6_5 },
518 		{ "rgba_ushort4444",		GL_RGBA,			GL_UNSIGNED_SHORT_4_4_4_4 },
519 		{ "rgba_ushort5551",		GL_RGBA,			GL_UNSIGNED_SHORT_5_5_5_1 },
520 	};
521 
522 	static const struct
523 	{
524 		int				size;
525 		TestCaseGroup*	uploadCallGroup;
526 		TestCaseGroup*	uploadAndDrawGroup;
527 	} textureSizes[] =
528 	{
529 		{ 16,	new TestCaseGroup(m_context, "16x16",		"Texture size 16x16"),		new TestCaseGroup(m_context, "16x16",		"Texture size 16x16") },
530 		{ 256,	new TestCaseGroup(m_context, "256x256",		"Texture size 256x256"),	new TestCaseGroup(m_context, "256x256",		"Texture size 256x256") },
531 		{ 257,	new TestCaseGroup(m_context, "257x257",		"Texture size 257x257"),	new TestCaseGroup(m_context, "257x257",		"Texture size 257x257") },
532 		{ 1024,	new TestCaseGroup(m_context, "1024x1024",	"Texture size 1024x1024"),	new TestCaseGroup(m_context, "1024x1024",	"Texture size 1024x1024") },
533 		{ 2048,	new TestCaseGroup(m_context, "2048x2048",	"Texture size 2048x2048"),	new TestCaseGroup(m_context, "2048x2048",	"Texture size 2048x2048") },
534 	};
535 
536 #define FOR_EACH(ITERATOR, ARRAY, BODY)	\
537 	for (int ITERATOR = 0; ITERATOR < DE_LENGTH_OF_ARRAY(ARRAY); ITERATOR++)	\
538 		BODY
539 
540 	FOR_EACH(uploadFunc,	 uploadFunctions,
541 	FOR_EACH(texSize,		 textureSizes,
542 	FOR_EACH(texCombination, textureCombinations,
543 	{
544 		string			caseName	= string("") + uploadFunctions[uploadFunc].nameLower + "_" + textureCombinations[texCombination].name;
545 		UploadFunction	function	= uploadFunctions[uploadFunc].func;
546 		deUint32		format		= textureCombinations[texCombination].format;
547 		deUint32		type		= textureCombinations[texCombination].type;
548 		int				size		= textureSizes[texSize].size;
549 
550 		textureSizes[texSize].uploadCallGroup->addChild		(new TextureUploadCallCase		(m_context, caseName.c_str(), "", function, format, type, size));
551 		textureSizes[texSize].uploadAndDrawGroup->addChild	(new TextureUploadAndDrawCase	(m_context, caseName.c_str(), "", function, format, type, size));
552 	})));
553 
554 	for (int i = 0; i < DE_LENGTH_OF_ARRAY(textureSizes); i++)
555 	{
556 		uploadCall->addChild	(textureSizes[i].uploadCallGroup);
557 		uploadAndDraw->addChild	(textureSizes[i].uploadAndDrawGroup);
558 	}
559 }
560 
561 
562 } // Performance
563 } // gles2
564 } // deqp
565