Home | History | Annotate | Download | only in glshared
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program OpenGL (ES) 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 State change performance tests.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "glsStateChangePerfTestCases.hpp"
     25 
     26 #include "tcuTestLog.hpp"
     27 
     28 #include "gluDefs.hpp"
     29 #include "gluRenderContext.hpp"
     30 #include "gluShaderProgram.hpp"
     31 
     32 #include "glwFunctions.hpp"
     33 #include "glwEnums.hpp"
     34 
     35 #include "deStringUtil.hpp"
     36 
     37 #include "deClock.h"
     38 
     39 #include <vector>
     40 #include <algorithm>
     41 
     42 using std::vector;
     43 using std::string;
     44 using tcu::TestLog;
     45 using namespace glw;
     46 
     47 namespace deqp
     48 {
     49 namespace gls
     50 {
     51 
     52 namespace
     53 {
     54 
     55 struct ResultStats
     56 {
     57 	double		median;
     58 	double		mean;
     59 	double		variance;
     60 
     61 	deUint64	min;
     62 	deUint64	max;
     63 };
     64 
     65 ResultStats calculateStats (const vector<deUint64>& values)
     66 {
     67 	ResultStats result = { 0.0, 0.0, 0.0, 0xFFFFFFFFFFFFFFFFu, 0 };
     68 
     69 	deUint64 sum = 0;
     70 
     71 	for (int i = 0; i < (int)values.size(); i++)
     72 		sum += values[i];
     73 
     74 	result.mean = ((double)sum) / (double)values.size();
     75 
     76 	for (int i = 0; i < (int)values.size(); i++)
     77 	{
     78 		const double val = (double)values[i];
     79 		result.variance += (val - result.mean) * (val - result.mean);
     80 	}
     81 
     82 	result.variance /= (double)values.size();
     83 
     84 	{
     85 		const int n = (int)(values.size()/2);
     86 
     87 		vector<deUint64> sortedValues = values;
     88 
     89 		std::sort(sortedValues.begin(), sortedValues.end());
     90 
     91 		result.median = (double)sortedValues[n];
     92 	}
     93 
     94 	for (int i = 0; i < (int)values.size(); i++)
     95 	{
     96 		result.min = std::min(result.min, values[i]);
     97 		result.max = std::max(result.max, values[i]);
     98 	}
     99 
    100 	return result;
    101 }
    102 
    103 
    104 void genIndices (vector<GLushort>& indices, int triangleCount)
    105 {
    106 	indices.reserve(triangleCount*3);
    107 
    108 	for (int triangleNdx = 0; triangleNdx < triangleCount; triangleNdx++)
    109 	{
    110 		indices.push_back((GLushort)(triangleNdx*3));
    111 		indices.push_back((GLushort)(triangleNdx*3+1));
    112 		indices.push_back((GLushort)(triangleNdx*3+2));
    113 	}
    114 }
    115 
    116 void genCoords (vector<GLfloat>& coords, int triangleCount)
    117 {
    118 	coords.reserve(triangleCount * 3 * 2);
    119 
    120 	for (int triangleNdx = 0; triangleNdx < triangleCount; triangleNdx++)
    121 	{
    122 		if ((triangleNdx % 2) == 0)
    123 		{
    124 			// CW
    125 			coords.push_back(-1.0f);
    126 			coords.push_back(-1.0f);
    127 
    128 			coords.push_back( 1.0f);
    129 			coords.push_back(-1.0f);
    130 
    131 			coords.push_back( 1.0f);
    132 			coords.push_back( 1.0f);
    133 		}
    134 		else
    135 		{
    136 			// CCW
    137 			coords.push_back(-1.0f);
    138 			coords.push_back(-1.0f);
    139 
    140 			coords.push_back(-1.0f);
    141 			coords.push_back( 1.0f);
    142 
    143 			coords.push_back( 1.0f);
    144 			coords.push_back( 1.0f);
    145 		}
    146 	}
    147 }
    148 
    149 void genTextureData (vector<deUint8>& data, int width, int height)
    150 {
    151 	data.clear();
    152 	data.reserve(width*height*4);
    153 
    154 	for (int x = 0; x < width; x++)
    155 	{
    156 		for (int y = 0; y < height; y++)
    157 		{
    158 			data.push_back((deUint8)((255*x)/width));
    159 			data.push_back((deUint8)((255*y)/width));
    160 			data.push_back((deUint8)((255*x*y)/(width*height)));
    161 			data.push_back(255);
    162 		}
    163 	}
    164 }
    165 
    166 double calculateVariance (const vector<deUint64>& values, double avg)
    167 {
    168 	double sum = 0.0;
    169 
    170 	for (int valueNdx = 0; valueNdx < (int)values.size(); valueNdx++)
    171 	{
    172 		double value = (double)values[valueNdx];
    173 		sum += (value - avg) * (value - avg);
    174 	}
    175 
    176 	return sum / (double)values.size();
    177 }
    178 
    179 deUint64 findMin (const vector<deUint64>& values)
    180 {
    181 	deUint64 min = ~0ull;
    182 
    183 	for (int valueNdx = 0; valueNdx < (int)values.size(); valueNdx++)
    184 		min = std::min(values[valueNdx], min);
    185 
    186 	return min;
    187 }
    188 
    189 deUint64 findMax (const vector<deUint64>& values)
    190 {
    191 	deUint64 max = 0;
    192 
    193 	for (int valueNdx = 0; valueNdx < (int)values.size(); valueNdx++)
    194 		max = std::max(values[valueNdx], max);
    195 
    196 	return max;
    197 }
    198 
    199 deUint64 findMedian (const vector<deUint64>& v)
    200 {
    201 	vector<deUint64> values = v;
    202 	size_t n = values.size() / 2;
    203 
    204 	std::nth_element(values.begin(), values.begin() + n, values.end());
    205 
    206 	return values[n];
    207 }
    208 
    209 } // anonymous
    210 
    211 StateChangePerformanceCase::StateChangePerformanceCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* description, DrawType drawType, int drawCallCount, int triangleCount)
    212 	: tcu::TestCase		(testCtx, tcu::NODETYPE_PERFORMANCE, name, description)
    213 	, m_renderCtx		(renderCtx)
    214 	, m_drawType		(drawType)
    215 	, m_iterationCount	(100)
    216 	, m_callCount		(drawCallCount)
    217 	, m_triangleCount	(triangleCount)
    218 {
    219 }
    220 
    221 StateChangePerformanceCase::~StateChangePerformanceCase (void)
    222 {
    223 	StateChangePerformanceCase::deinit();
    224 }
    225 
    226 void StateChangePerformanceCase::init (void)
    227 {
    228 	if (m_drawType == DRAWTYPE_INDEXED_USER_PTR)
    229 		genIndices(m_indices, m_triangleCount);
    230 }
    231 
    232 void StateChangePerformanceCase::requireIndexBuffers (int count)
    233 {
    234 	const glw::Functions& gl = m_renderCtx.getFunctions();
    235 
    236 	if ((int)m_indexBuffers.size() >= count)
    237 		return;
    238 
    239 	m_indexBuffers.reserve(count);
    240 
    241 	vector<GLushort> indices;
    242 	genIndices(indices, m_triangleCount);
    243 
    244 	while ((int)m_indexBuffers.size() < count)
    245 	{
    246 		GLuint buffer;
    247 
    248 		gl.genBuffers(1, &buffer);
    249 		GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers()");
    250 
    251 		gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
    252 		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer()");
    253 		gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)(indices.size() * sizeof(GLushort)), &(indices[0]), GL_STATIC_DRAW);
    254 		GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData()");
    255 		gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    256 		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer()");
    257 
    258 		m_indexBuffers.push_back(buffer);
    259 	}
    260 }
    261 
    262 void StateChangePerformanceCase::requireCoordBuffers (int count)
    263 {
    264 	const glw::Functions& gl = m_renderCtx.getFunctions();
    265 
    266 	if ((int)m_coordBuffers.size() >= count)
    267 		return;
    268 
    269 	m_coordBuffers.reserve(count);
    270 
    271 	vector<GLfloat> coords;
    272 	genCoords(coords, m_triangleCount);
    273 
    274 	while ((int)m_coordBuffers.size() < count)
    275 	{
    276 		GLuint buffer;
    277 
    278 		gl.genBuffers(1, &buffer);
    279 		GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers()");
    280 
    281 		gl.bindBuffer(GL_ARRAY_BUFFER, buffer);
    282 		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer()");
    283 		gl.bufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(coords.size() * sizeof(GLfloat)), &(coords[0]), GL_STATIC_DRAW);
    284 		GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData()");
    285 		gl.bindBuffer(GL_ARRAY_BUFFER, 0);
    286 		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer()");
    287 
    288 		m_coordBuffers.push_back(buffer);
    289 	}
    290 }
    291 
    292 void StateChangePerformanceCase::requirePrograms (int count)
    293 {
    294 	if ((int)m_programs.size() >= count)
    295 		return;
    296 
    297 	m_programs.reserve(count);
    298 
    299 	while ((int)m_programs.size() < count)
    300 	{
    301 		string vertexShaderSource =
    302 			"attribute mediump vec2 a_coord;\n"
    303 			"varying mediump vec2 v_texCoord;\n"
    304 			"void main (void)\n"
    305 			"{\n"
    306 			"\tv_texCoord = vec2(0.5) + 0.5" + de::toString(m_programs.size()) + " * a_coord.xy;\n"
    307 			"\tgl_Position = vec4(a_coord, 0.5, 1.0);\n"
    308 			"}";
    309 
    310 		string fragmentShaderSource =
    311 			"uniform sampler2D u_sampler;\n"
    312 			"varying mediump vec2 v_texCoord;\n"
    313 			"void main (void)\n"
    314 			"{\n"
    315 			"\tgl_FragColor = vec4(1.0" + de::toString(m_programs.size()) + " * texture2D(u_sampler, v_texCoord).xyz, 1.0);\n"
    316 			"}";
    317 
    318 		glu::ShaderProgram* program = new glu::ShaderProgram(m_renderCtx, glu::ProgramSources() << glu::VertexSource(vertexShaderSource) << glu::FragmentSource(fragmentShaderSource));
    319 
    320 		if (!program->isOk())
    321 		{
    322 			m_testCtx.getLog() << *program;
    323 			delete program;
    324 			TCU_FAIL("Compile failed");
    325 		}
    326 
    327 		m_programs.push_back(program);
    328 	}
    329 }
    330 
    331 void StateChangePerformanceCase::requireTextures (int count)
    332 {
    333 	const glw::Functions& gl = m_renderCtx.getFunctions();
    334 
    335 	const int textureWidth	= 64;
    336 	const int textureHeight	= 64;
    337 
    338 	if ((int)m_textures.size() >= count)
    339 		return;
    340 
    341 	m_textures.reserve(count);
    342 
    343 	vector<deUint8> textureData;
    344 	genTextureData(textureData, textureWidth, textureHeight);
    345 
    346 	DE_ASSERT(textureData.size() == textureWidth * textureHeight * 4);
    347 
    348 	while ((int)m_textures.size() < count)
    349 	{
    350 		GLuint texture;
    351 
    352 		gl.genTextures(1, &texture);
    353 		GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures()");
    354 
    355 		gl.bindTexture(GL_TEXTURE_2D, texture);
    356 		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture()");
    357 
    358 		gl.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, &(textureData[0]));
    359 		GLU_EXPECT_NO_ERROR(gl.getError(), "glTexImage2D()");
    360 
    361 		gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,	GL_NEAREST);
    362 		GLU_EXPECT_NO_ERROR(gl.getError(), "glTexParameteri()");
    363 		gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,	GL_NEAREST);
    364 		GLU_EXPECT_NO_ERROR(gl.getError(), "glTexParameteri()");
    365 		gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,		GL_REPEAT);
    366 		GLU_EXPECT_NO_ERROR(gl.getError(), "glTexParameteri()");
    367 		gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,		GL_REPEAT);
    368 		GLU_EXPECT_NO_ERROR(gl.getError(), "glTexParameteri()");
    369 
    370 		gl.bindTexture(GL_TEXTURE_2D, 0);
    371 		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture()");
    372 
    373 		m_textures.push_back(texture);
    374 	}
    375 }
    376 
    377 void StateChangePerformanceCase::requireFramebuffers (int count)
    378 {
    379 	const glw::Functions& gl = m_renderCtx.getFunctions();
    380 
    381 	if ((int)m_framebuffers.size() >= count)
    382 		return;
    383 
    384 	m_framebuffers.reserve(count);
    385 
    386 	requireRenderbuffers(count);
    387 
    388 	while ((int)m_framebuffers.size() < count)
    389 	{
    390 		GLuint framebuffer;
    391 
    392 		gl.genFramebuffers(1, &framebuffer);
    393 		GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers()");
    394 
    395 		gl.bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
    396 		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer()");
    397 
    398 		gl.bindRenderbuffer(GL_RENDERBUFFER, m_renderbuffers[m_framebuffers.size()]);
    399 		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer()");
    400 
    401 		gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_renderbuffers[m_framebuffers.size()]);
    402 		GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferRenderbuffer()");
    403 
    404 		gl.bindRenderbuffer(GL_RENDERBUFFER, 0);
    405 		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer()");
    406 
    407 		gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
    408 		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer()");
    409 
    410 		m_framebuffers.push_back(framebuffer);
    411 	}
    412 }
    413 
    414 void StateChangePerformanceCase::requireRenderbuffers (int count)
    415 {
    416 	const glw::Functions& gl = m_renderCtx.getFunctions();
    417 
    418 	if ((int)m_renderbuffers.size() >= count)
    419 		return;
    420 
    421 	m_renderbuffers.reserve(count);
    422 
    423 	while ((int)m_renderbuffers.size() < count)
    424 	{
    425 		GLuint renderbuffer;
    426 
    427 		gl.genRenderbuffers(1, &renderbuffer);
    428 		GLU_EXPECT_NO_ERROR(gl.getError(), "glGenRenderbuffers()");
    429 
    430 		gl.bindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
    431 		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer()");
    432 
    433 		gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGB565, 24, 24);
    434 		GLU_EXPECT_NO_ERROR(gl.getError(), "glRenderbufferStorage()");
    435 
    436 		gl.bindRenderbuffer(GL_RENDERBUFFER, 0);
    437 		GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer()");
    438 
    439 		m_renderbuffers.push_back(renderbuffer);
    440 	}
    441 }
    442 
    443 void StateChangePerformanceCase::requireSamplers (int count)
    444 {
    445 	const glw::Functions& gl = m_renderCtx.getFunctions();
    446 
    447 	if ((int)m_samplers.size() >= count)
    448 		return;
    449 
    450 	m_samplers.reserve(count);
    451 
    452 	while ((int)m_samplers.size() < count)
    453 	{
    454 		GLuint sampler;
    455 		gl.genSamplers(1, &sampler);
    456 		GLU_EXPECT_NO_ERROR(gl.getError(), "glGenSamplers()");
    457 		m_samplers.push_back(sampler);
    458 	}
    459 }
    460 
    461 void StateChangePerformanceCase::requireVertexArrays (int count)
    462 {
    463 	const glw::Functions& gl = m_renderCtx.getFunctions();
    464 
    465 	if ((int)m_vertexArrays.size() >= count)
    466 		return;
    467 
    468 	m_vertexArrays.reserve(count);
    469 
    470 	while ((int)m_vertexArrays.size() < count)
    471 	{
    472 		GLuint vertexArray;
    473 		gl.genVertexArrays(1, &vertexArray);
    474 		GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays()");
    475 		m_vertexArrays.push_back(vertexArray);
    476 	}
    477 }
    478 
    479 void StateChangePerformanceCase::deinit (void)
    480 {
    481 	m_indices.clear();
    482 	m_interleavedResults.clear();
    483 	m_batchedResults.clear();
    484 
    485 	{
    486 		const glw::Functions& gl = m_renderCtx.getFunctions();
    487 
    488 		if (!m_indexBuffers.empty())
    489 		{
    490 			gl.deleteBuffers((GLsizei)m_indexBuffers.size(), &(m_indexBuffers[0]));
    491 			m_indexBuffers.clear();
    492 		}
    493 
    494 		if (!m_coordBuffers.empty())
    495 		{
    496 			gl.deleteBuffers((GLsizei)m_coordBuffers.size(), &(m_coordBuffers[0]));
    497 			m_coordBuffers.clear();
    498 		}
    499 
    500 		if (!m_textures.empty())
    501 		{
    502 			gl.deleteTextures((GLsizei)m_textures.size(), &(m_textures[0]));
    503 			m_textures.clear();
    504 		}
    505 
    506 		if (!m_framebuffers.empty())
    507 		{
    508 			gl.deleteFramebuffers((GLsizei)m_framebuffers.size(), &(m_framebuffers[0]));
    509 			m_framebuffers.clear();
    510 		}
    511 
    512 		if (!m_renderbuffers.empty())
    513 		{
    514 			gl.deleteRenderbuffers((GLsizei)m_renderbuffers.size(), &(m_renderbuffers[0]));
    515 			m_renderbuffers.clear();
    516 		}
    517 
    518 		if (!m_samplers.empty())
    519 		{
    520 			gl.deleteSamplers((GLsizei)m_samplers.size(), &m_samplers[0]);
    521 			m_samplers.clear();
    522 		}
    523 
    524 		if (!m_vertexArrays.empty())
    525 		{
    526 			gl.deleteVertexArrays((GLsizei)m_vertexArrays.size(), &m_vertexArrays[0]);
    527 			m_vertexArrays.clear();
    528 		}
    529 
    530 		for (int programNdx = 0; programNdx < (int)m_programs.size(); programNdx++)
    531 		{
    532 			delete m_programs[programNdx];
    533 			m_programs[programNdx] = NULL;
    534 		}
    535 		m_programs.clear();
    536 	}
    537 }
    538 
    539 void StateChangePerformanceCase::logAndSetTestResult (void)
    540 {
    541 	TestLog&	log			= m_testCtx.getLog();
    542 
    543 	ResultStats interleaved	= calculateStats(m_interleavedResults);
    544 	ResultStats batched		= calculateStats(m_batchedResults);
    545 
    546 	log << TestLog::Message << "Interleaved mean: "					<< interleaved.mean						<< TestLog::EndMessage;
    547 	log << TestLog::Message << "Interleaved median: "				<< interleaved.median					<< TestLog::EndMessage;
    548 	log << TestLog::Message << "Interleaved variance: "				<< interleaved.variance					<< TestLog::EndMessage;
    549 	log << TestLog::Message << "Interleaved min: "					<< interleaved.min						<< TestLog::EndMessage;
    550 	log << TestLog::Message << "Interleaved max: "					<< interleaved.max						<< TestLog::EndMessage;
    551 
    552 	log << TestLog::Message << "Batched mean: "						<< batched.mean							<< TestLog::EndMessage;
    553 	log << TestLog::Message << "Batched median: "					<< batched.median						<< TestLog::EndMessage;
    554 	log << TestLog::Message << "Batched variance: "					<< batched.variance						<< TestLog::EndMessage;
    555 	log << TestLog::Message << "Batched min: "						<< batched.min							<< TestLog::EndMessage;
    556 	log << TestLog::Message << "Batched max: "						<< batched.max							<< TestLog::EndMessage;
    557 
    558 	log << TestLog::Message << "Batched/Interleaved mean ratio: "	<< (interleaved.mean/batched.mean)		<< TestLog::EndMessage;
    559 	log << TestLog::Message << "Batched/Interleaved median ratio: "	<< (interleaved.median/batched.median)	<< TestLog::EndMessage;
    560 
    561 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, de::floatToString((float)(((double)interleaved.median) / batched.median), 2).c_str());
    562 }
    563 
    564 tcu::TestCase::IterateResult StateChangePerformanceCase::iterate (void)
    565 {
    566 	if (m_interleavedResults.empty() && m_batchedResults.empty())
    567 	{
    568 		TestLog& log = m_testCtx.getLog();
    569 
    570 		log << TestLog::Message << "Draw call count: " << m_callCount << TestLog::EndMessage;
    571 		log << TestLog::Message << "Per call triangle count: " << m_triangleCount << TestLog::EndMessage;
    572 	}
    573 
    574 	// \note [mika] Interleave sampling to balance effects of powerstate etc.
    575 	if ((int)m_interleavedResults.size() < m_iterationCount && m_batchedResults.size() >= m_interleavedResults.size())
    576 	{
    577 		const glw::Functions&	gl			= m_renderCtx.getFunctions();
    578 		deUint64				resBeginUs	= 0;
    579 		deUint64				resEndUs	= 0;
    580 
    581 		setupInitialState(gl);
    582 		gl.finish();
    583 		GLU_EXPECT_NO_ERROR(gl.getError(), "glFinish()");
    584 
    585 		// Render result
    586 		resBeginUs = deGetMicroseconds();
    587 
    588 		renderTest(gl);
    589 
    590 		gl.finish();
    591 		resEndUs = deGetMicroseconds();
    592 		GLU_EXPECT_NO_ERROR(gl.getError(), "glFinish()");
    593 
    594 		m_interleavedResults.push_back(resEndUs - resBeginUs);
    595 
    596 		return CONTINUE;
    597 	}
    598 	else if ((int)m_batchedResults.size() < m_iterationCount)
    599 	{
    600 		const glw::Functions&	gl			= m_renderCtx.getFunctions();
    601 		deUint64				refBeginUs	= 0;
    602 		deUint64				refEndUs	= 0;
    603 
    604 		setupInitialState(gl);
    605 		gl.finish();
    606 		GLU_EXPECT_NO_ERROR(gl.getError(), "glFinish()");
    607 
    608 		// Render reference
    609 		refBeginUs = deGetMicroseconds();
    610 
    611 		renderReference(gl);
    612 
    613 		gl.finish();
    614 		refEndUs = deGetMicroseconds();
    615 		GLU_EXPECT_NO_ERROR(gl.getError(), "glFinish()");
    616 
    617 		m_batchedResults.push_back(refEndUs - refBeginUs);
    618 
    619 		return CONTINUE;
    620 	}
    621 	else
    622 	{
    623 		logAndSetTestResult();
    624 		return STOP;
    625 	}
    626 }
    627 
    628 void StateChangePerformanceCase::callDraw (const glw::Functions& gl)
    629 {
    630 	switch (m_drawType)
    631 	{
    632 		case DRAWTYPE_NOT_INDEXED:		gl.drawArrays(GL_TRIANGLES, 0, m_triangleCount * 3);									break;
    633 		case DRAWTYPE_INDEXED_USER_PTR:	gl.drawElements(GL_TRIANGLES, m_triangleCount * 3, GL_UNSIGNED_SHORT, &m_indices[0]);	break;
    634 		case DRAWTYPE_INDEXED_BUFFER:	gl.drawElements(GL_TRIANGLES, m_triangleCount * 3, GL_UNSIGNED_SHORT, NULL);			break;
    635 		default:
    636 			DE_ASSERT(false);
    637 	}
    638 }
    639 
    640 // StateChangeCallPerformanceCase
    641 
    642 StateChangeCallPerformanceCase::StateChangeCallPerformanceCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* description)
    643 	: tcu::TestCase		(testCtx, tcu::NODETYPE_PERFORMANCE, name, description)
    644 	, m_renderCtx		(renderCtx)
    645 	, m_iterationCount	(100)
    646 	, m_callCount		(1000)
    647 {
    648 }
    649 
    650 StateChangeCallPerformanceCase::~StateChangeCallPerformanceCase (void)
    651 {
    652 }
    653 
    654 void StateChangeCallPerformanceCase::executeTest (void)
    655 {
    656 	const glw::Functions&	gl				= m_renderCtx.getFunctions();
    657 	deUint64				beginTimeUs		= 0;
    658 	deUint64				endTimeUs		= 0;
    659 
    660 	beginTimeUs = deGetMicroseconds();
    661 
    662 	execCalls(gl, (int)m_results.size(), m_callCount);
    663 
    664 	endTimeUs = deGetMicroseconds();
    665 
    666 	m_results.push_back(endTimeUs - beginTimeUs);
    667 }
    668 
    669 void StateChangeCallPerformanceCase::logTestCase (void)
    670 {
    671 	TestLog& log = m_testCtx.getLog();
    672 
    673 	log << TestLog::Message << "Iteration count: " << m_iterationCount << TestLog::EndMessage;
    674 	log << TestLog::Message << "Per iteration call count: " << m_callCount << TestLog::EndMessage;
    675 }
    676 
    677 double calculateAverage (const vector<deUint64>& values)
    678 {
    679 	deUint64 sum = 0;
    680 
    681 	for (int valueNdx = 0; valueNdx < (int)values.size(); valueNdx++)
    682 		sum += values[valueNdx];
    683 
    684 	return ((double)sum) / (double)values.size();
    685 }
    686 
    687 void StateChangeCallPerformanceCase::logAndSetTestResult (void)
    688 {
    689 	TestLog&	log				= m_testCtx.getLog();
    690 
    691 	deUint64	minUs			= findMin(m_results);
    692 	deUint64	maxUs			= findMax(m_results);
    693 	deUint64	medianUs		= findMedian(m_results);
    694 	double		avgIterationUs	= calculateAverage(m_results);
    695 	double		avgCallUs		= avgIterationUs / m_callCount;
    696 	double		varIteration	= calculateVariance(m_results, avgIterationUs);
    697 	double		avgMedianCallUs	= ((double)medianUs)/m_callCount;
    698 
    699 	log << TestLog::Message << "Min iteration time: "						<< minUs << "us" << TestLog::EndMessage;
    700 	log << TestLog::Message << "Max iteration time: "						<< maxUs << "us" << TestLog::EndMessage;
    701 	log << TestLog::Message << "Average iteration time: "					<< avgIterationUs << "us" << TestLog::EndMessage;
    702 	log << TestLog::Message << "Iteration variance time: "					<< varIteration << TestLog::EndMessage;
    703 	log << TestLog::Message << "Median iteration time: "					<< medianUs << "us" << TestLog::EndMessage;
    704 	log << TestLog::Message << "Average call time: "						<< avgCallUs << "us" << TestLog::EndMessage;
    705 	log << TestLog::Message << "Average call time for median iteration: "	<< avgMedianCallUs << "us" << TestLog::EndMessage;
    706 
    707 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, de::floatToString((float)avgMedianCallUs, 3).c_str());
    708 }
    709 
    710 tcu::TestCase::IterateResult StateChangeCallPerformanceCase::iterate (void)
    711 {
    712 	if (m_results.empty())
    713 		logTestCase();
    714 
    715 	if ((int)m_results.size() < m_iterationCount)
    716 	{
    717 		executeTest();
    718 		GLU_EXPECT_NO_ERROR(m_renderCtx.getFunctions().getError(), "Unexpected error");
    719 		return CONTINUE;
    720 	}
    721 	else
    722 	{
    723 		logAndSetTestResult();
    724 		return STOP;
    725 	}
    726 }
    727 
    728 } // gls
    729 } // deqp
    730