es2pTextureUploadTests.cpp revision 3c827367444ee418f129b2c238299f49d3264554
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
50namespace deqp
51{
52namespace gles2
53{
54namespace Performance
55{
56
57using tcu::Vec2;
58using tcu::Vec3;
59using tcu::Vec4;
60using tcu::IVec4;
61using std::string;
62using std::vector;
63using tcu::TestLog;
64using tcu::TextureFormat;
65using namespace glw; // GL types
66
67static const int	VIEWPORT_SIZE	= 64;
68static 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
76class TextureUploadCase : public TestCase
77{
78public:
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
88protected:
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
104TextureUploadCase::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
119TextureUploadCase::~TextureUploadCase (void)
120{
121	TextureUploadCase::deinit();
122}
123
124void 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
140void 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
235void 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) / 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
292class TextureUploadCallCase : public TextureUploadCase
293{
294public:
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
302TextureUploadCallCase::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
307TextureUploadCallCase::~TextureUploadCallCase (void)
308{
309	TextureUploadCase::deinit();
310}
311
312void 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
331tcu::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
382class TextureUploadAndDrawCase : public TextureUploadCase
383{
384public:
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
391private:
392	bool						m_lastIterationRender;
393	deUint64					m_renderStart;
394};
395
396TextureUploadAndDrawCase::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
403TextureUploadAndDrawCase::~TextureUploadAndDrawCase (void)
404{
405	TextureUploadCase::deinit();
406}
407
408void 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
429tcu::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
472TextureUploadTests::TextureUploadTests (Context& context)
473	: TestCaseGroup(context, "upload", "Texture upload tests")
474{
475}
476
477TextureUploadTests::~TextureUploadTests (void)
478{
479	TextureUploadTests::deinit();
480}
481
482void TextureUploadTests::deinit (void)
483{
484}
485
486void 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