Home | History | Annotate | Download | only in functional
      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 Flush and finish tests.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "es3fFlushFinishTests.hpp"
     25 
     26 #include "gluRenderContext.hpp"
     27 #include "gluObjectWrapper.hpp"
     28 #include "gluShaderProgram.hpp"
     29 #include "gluDrawUtil.hpp"
     30 
     31 #include "glsCalibration.hpp"
     32 
     33 #include "tcuTestLog.hpp"
     34 #include "tcuRenderTarget.hpp"
     35 #include "tcuCPUWarmup.hpp"
     36 
     37 #include "glwEnums.hpp"
     38 #include "glwFunctions.hpp"
     39 
     40 #include "deRandom.hpp"
     41 #include "deStringUtil.hpp"
     42 #include "deClock.h"
     43 #include "deThread.h"
     44 #include "deMath.h"
     45 
     46 #include <algorithm>
     47 
     48 namespace deqp
     49 {
     50 namespace gles3
     51 {
     52 namespace Functional
     53 {
     54 
     55 using std::vector;
     56 using std::string;
     57 using tcu::TestLog;
     58 using tcu::Vec2;
     59 using deqp::gls::theilSenLinearRegression;
     60 using deqp::gls::LineParameters;
     61 
     62 namespace
     63 {
     64 
     65 enum
     66 {
     67 	MAX_VIEWPORT_SIZE			= 256,
     68 	MAX_SAMPLE_DURATION_US		= 150*1000,
     69 	WAIT_TIME_MS				= 200,
     70 	MIN_DRAW_CALL_COUNT			= 10,
     71 	MAX_DRAW_CALL_COUNT			= 1<<20,
     72 	MAX_SHADER_ITER_COUNT		= 1<<10,
     73 	NUM_SAMPLES					= 50,
     74 	NUM_VERIFICATION_SAMPLES	= 3,
     75 	MAX_CALIBRATION_ATTEMPTS	= 5
     76 };
     77 
     78 DE_STATIC_ASSERT(MAX_SAMPLE_DURATION_US < 1000*WAIT_TIME_MS);
     79 
     80 const float		NO_CORR_COEF_THRESHOLD				= 0.1f;
     81 const float		FLUSH_COEF_THRESHOLD				= 0.2f;
     82 const float		CORRELATED_COEF_THRESHOLD			= 0.5f;
     83 const float		CALIBRATION_VERIFICATION_THRESHOLD	= 0.10f;	// Rendering time needs to be within 10% of MAX_SAMPLE_DURATION_US
     84 
     85 static void busyWait (int milliseconds)
     86 {
     87 	const deUint64	startTime	= deGetMicroseconds();
     88 	float			v			= 2.0f;
     89 
     90 	for (;;)
     91 	{
     92 		for (int i = 0; i < 10; i++)
     93 			v = deFloatSin(v);
     94 
     95 		if (deGetMicroseconds()-startTime >= deUint64(1000*milliseconds))
     96 			break;
     97 	}
     98 }
     99 
    100 class CalibrationFailedException : public std::runtime_error
    101 {
    102 public:
    103 	CalibrationFailedException (const std::string& reason) : std::runtime_error(reason) {}
    104 };
    105 
    106 class FlushFinishCase : public TestCase
    107 {
    108 public:
    109 	enum ExpectedBehavior
    110 	{
    111 		EXPECT_COEF_LESS_THAN = 0,
    112 		EXPECT_COEF_GREATER_THAN,
    113 	};
    114 
    115 							FlushFinishCase		(Context&			context,
    116 												 const char*		name,
    117 												 const char*		description,
    118 												 ExpectedBehavior	waitBehavior,
    119 												 float				waitThreshold,
    120 												 ExpectedBehavior	readBehavior,
    121 												 float				readThreshold);
    122 							~FlushFinishCase	(void);
    123 
    124 	void					init				(void);
    125 	void					deinit				(void);
    126 	IterateResult			iterate				(void);
    127 
    128 	struct Sample
    129 	{
    130 		int			numDrawCalls;
    131 		deUint64	submitTime;
    132 		deUint64	waitTime;
    133 		deUint64	readPixelsTime;
    134 	};
    135 
    136 	struct CalibrationParams
    137 	{
    138 		int			numItersInShader;
    139 		int			maxDrawCalls;
    140 	};
    141 
    142 protected:
    143 	virtual void			waitForGL			(void) = 0;
    144 
    145 private:
    146 							FlushFinishCase		(const FlushFinishCase&);
    147 	FlushFinishCase&		operator=			(const FlushFinishCase&);
    148 
    149 	CalibrationParams		calibrate			(void);
    150 	void					verifyCalibration	(const CalibrationParams& params);
    151 
    152 	void					analyzeResults		(const std::vector<Sample>& samples, const CalibrationParams& calibrationParams);
    153 
    154 	void					setupRenderState	(void);
    155 	void					setShaderIterCount	(int numIters);
    156 	void					render				(int numDrawCalls);
    157 	void					readPixels			(void);
    158 
    159 	const ExpectedBehavior	m_waitBehavior;
    160 	const float				m_waitThreshold;
    161 	const ExpectedBehavior	m_readBehavior;
    162 	const float				m_readThreshold;
    163 
    164 	glu::ShaderProgram*		m_program;
    165 	int						m_iterCountLoc;
    166 };
    167 
    168 FlushFinishCase::FlushFinishCase (Context& context, const char* name, const char* description, ExpectedBehavior waitBehavior, float waitThreshold, ExpectedBehavior readBehavior, float readThreshold)
    169 	: TestCase			(context, name, description)
    170 	, m_waitBehavior	(waitBehavior)
    171 	, m_waitThreshold	(waitThreshold)
    172 	, m_readBehavior	(readBehavior)
    173 	, m_readThreshold	(readThreshold)
    174 	, m_program			(DE_NULL)
    175 	, m_iterCountLoc	(0)
    176 {
    177 }
    178 
    179 FlushFinishCase::~FlushFinishCase (void)
    180 {
    181 	FlushFinishCase::deinit();
    182 }
    183 
    184 void FlushFinishCase::init (void)
    185 {
    186 	DE_ASSERT(!m_program);
    187 
    188 	m_program = new glu::ShaderProgram(m_context.getRenderContext(),
    189 		glu::ProgramSources()
    190 			<< glu::VertexSource(
    191 				"#version 300 es\n"
    192 				"in highp vec4 a_position;\n"
    193 				"out highp vec4 v_coord;\n"
    194 				"void main (void)\n"
    195 				"{\n"
    196 				"	gl_Position = a_position;\n"
    197 				"	v_coord = a_position;\n"
    198 				"}\n")
    199 			<< glu::FragmentSource(
    200 				"#version 300 es\n"
    201 				"uniform highp int u_numIters;\n"
    202 				"in highp vec4 v_coord;\n"
    203 				"out mediump vec4 o_color;\n"
    204 				"void main (void)\n"
    205 				"{\n"
    206 				"	highp vec4 color = v_coord;\n"
    207 				"	for (int i = 0; i < u_numIters; i++)\n"
    208 				"		color = sin(color);\n"
    209 				"	o_color = color;\n"
    210 				"}\n"));
    211 
    212 	if (!m_program->isOk())
    213 	{
    214 		m_testCtx.getLog() << *m_program;
    215 		delete m_program;
    216 		m_program = DE_NULL;
    217 		TCU_FAIL("Compile failed");
    218 	}
    219 
    220 	m_iterCountLoc = m_context.getRenderContext().getFunctions().getUniformLocation(m_program->getProgram(), "u_numIters");
    221 	TCU_CHECK(m_iterCountLoc >= 0);
    222 }
    223 
    224 void FlushFinishCase::deinit (void)
    225 {
    226 	delete m_program;
    227 	m_program = DE_NULL;
    228 }
    229 
    230 tcu::TestLog& operator<< (tcu::TestLog& log, const FlushFinishCase::Sample& sample)
    231 {
    232 	log << TestLog::Message << sample.numDrawCalls << " calls:\t" << sample.submitTime << " us submit,\t" << sample.waitTime << " us wait,\t" << sample.readPixelsTime << " us read" << TestLog::EndMessage;
    233 	return log;
    234 }
    235 
    236 void FlushFinishCase::setupRenderState (void)
    237 {
    238 	const glw::Functions&	gl				= m_context.getRenderContext().getFunctions();
    239 	const int				posLoc			= gl.getAttribLocation(m_program->getProgram(), "a_position");
    240 	const int				viewportW		= de::min<int>(m_context.getRenderTarget().getWidth(), MAX_VIEWPORT_SIZE);
    241 	const int				viewportH		= de::min<int>(m_context.getRenderTarget().getHeight(), MAX_VIEWPORT_SIZE);
    242 
    243 	static const float s_positions[] =
    244 	{
    245 		-1.0f, -1.0f,
    246 		+1.0f, -1.0f,
    247 		-1.0f, +1.0f,
    248 		+1.0f, +1.0f
    249 	};
    250 
    251 	TCU_CHECK(posLoc >= 0);
    252 
    253 	gl.viewport(0, 0, viewportW, viewportH);
    254 	gl.useProgram(m_program->getProgram());
    255 	gl.enableVertexAttribArray(posLoc);
    256 	gl.vertexAttribPointer(posLoc, 2, GL_FLOAT, GL_FALSE, 0, &s_positions[0]);
    257 	gl.enable(GL_BLEND);
    258 	gl.blendFunc(GL_ONE, GL_ONE);
    259 	gl.blendEquation(GL_FUNC_ADD);
    260 	GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to set up render state");
    261 }
    262 
    263 void FlushFinishCase::setShaderIterCount (int numIters)
    264 {
    265 	const glw::Functions&	gl	= m_context.getRenderContext().getFunctions();
    266 	gl.uniform1i(m_iterCountLoc, numIters);
    267 }
    268 
    269 void FlushFinishCase::render (int numDrawCalls)
    270 {
    271 	const glw::Functions&	gl	= m_context.getRenderContext().getFunctions();
    272 
    273 	const deUint8 indices[] = { 0, 1, 2, 2, 1, 3 };
    274 
    275 	gl.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
    276 
    277 	for (int ndx = 0; ndx < numDrawCalls; ndx++)
    278 		gl.drawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(indices), GL_UNSIGNED_BYTE, &indices[0]);
    279 }
    280 
    281 void FlushFinishCase::readPixels (void)
    282 {
    283 	const glw::Functions&	gl		= m_context.getRenderContext().getFunctions();
    284 	deUint8					tmp[4];
    285 
    286 	gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &tmp);
    287 }
    288 
    289 FlushFinishCase::CalibrationParams FlushFinishCase::calibrate (void)
    290 {
    291 	tcu::ScopedLogSection		section				(m_testCtx.getLog(), "CalibrationInfo", "Calibration info");
    292 	CalibrationParams			params;
    293 
    294 	// Step 1: find iteration count that results in rougly 1/10th of target maximum sample duration.
    295 	{
    296 		const deUint64		targetDurationUs		= MAX_SAMPLE_DURATION_US/100;
    297 		deUint64			prevDuration			= 0;
    298 		int					prevIterCount			= 1;
    299 		int					curIterCount			= 1;
    300 
    301 		m_testCtx.getLog() << TestLog::Message << "Calibrating shader iteration count, target duration = " << targetDurationUs << " us" << TestLog::EndMessage;
    302 
    303 		for (;;)
    304 		{
    305 			deUint64 curDuration;
    306 
    307 			setShaderIterCount(curIterCount);
    308 			render(1); // \note Submit time is ignored
    309 
    310 			{
    311 				const deUint64 startTime = deGetMicroseconds();
    312 				readPixels();
    313 				curDuration = deGetMicroseconds()-startTime;
    314 			}
    315 
    316 			m_testCtx.getLog() << TestLog::Message << "Duration with " << curIterCount << " iterations = " << curDuration << " us" << TestLog::EndMessage;
    317 
    318 			if (curDuration > targetDurationUs)
    319 			{
    320 				if (curIterCount > 1)
    321 				{
    322 					// Compute final count by using linear estimation.
    323 					const float		a		= float(curDuration - prevDuration) / float(curIterCount - prevIterCount);
    324 					const float		b		= float(prevDuration) - a*float(prevIterCount);
    325 					const float		est		= (float(targetDurationUs) - b) / a;
    326 
    327 					curIterCount = de::clamp(deFloorFloatToInt32(est), 1, int(MAX_SHADER_ITER_COUNT));
    328 				}
    329 				// else: Settle on 1.
    330 
    331 				break;
    332 			}
    333 			else if (curIterCount >= MAX_SHADER_ITER_COUNT)
    334 				break; // Settle on maximum.
    335 			else
    336 			{
    337 				prevIterCount	= curIterCount;
    338 				prevDuration	= curDuration;
    339 				curIterCount	= curIterCount*2;
    340 			}
    341 		}
    342 
    343 		params.numItersInShader = curIterCount;
    344 
    345 		m_testCtx.getLog() << TestLog::Integer("ShaderIterCount", "Shader iteration count", "", QP_KEY_TAG_NONE, params.numItersInShader);
    346 	}
    347 
    348 	// Step 2: Find draw call count that results in desired maximum time.
    349 	{
    350 		deUint64			prevDuration			= 0;
    351 		int					prevDrawCount			= 1;
    352 		int					curDrawCount			= 1;
    353 
    354 		m_testCtx.getLog() << TestLog::Message << "Calibrating maximum draw call count, target duration = " << int(MAX_SAMPLE_DURATION_US) << " us" << TestLog::EndMessage;
    355 
    356 		setShaderIterCount(params.numItersInShader);
    357 
    358 		for (;;)
    359 		{
    360 			deUint64 curDuration;
    361 
    362 			render(curDrawCount); // \note Submit time is ignored
    363 
    364 			{
    365 				const deUint64 startTime = deGetMicroseconds();
    366 				readPixels();
    367 				curDuration = deGetMicroseconds()-startTime;
    368 			}
    369 
    370 			m_testCtx.getLog() << TestLog::Message << "Duration with " << curDrawCount << " draw calls = " << curDuration << " us" << TestLog::EndMessage;
    371 
    372 			if (curDuration > MAX_SAMPLE_DURATION_US)
    373 			{
    374 				if (curDrawCount > 1)
    375 				{
    376 					// Compute final count by using linear estimation.
    377 					const float		a		= float(curDuration - prevDuration) / float(curDrawCount - prevDrawCount);
    378 					const float		b		= float(prevDuration) - a*float(prevDrawCount);
    379 					const float		est		= (float(MAX_SAMPLE_DURATION_US) - b) / a;
    380 
    381 					curDrawCount = de::clamp(deFloorFloatToInt32(est), 1, int(MAX_DRAW_CALL_COUNT));
    382 				}
    383 				// else: Settle on 1.
    384 
    385 				break;
    386 			}
    387 			else if (curDrawCount >= MAX_DRAW_CALL_COUNT)
    388 				break; // Settle on maximum.
    389 			else
    390 			{
    391 				prevDrawCount	= curDrawCount;
    392 				prevDuration	= curDuration;
    393 				curDrawCount	= curDrawCount*2;
    394 			}
    395 		}
    396 
    397 		params.maxDrawCalls = curDrawCount;
    398 
    399 		m_testCtx.getLog() << TestLog::Integer("MaxDrawCalls", "Maximum number of draw calls", "", QP_KEY_TAG_NONE, params.maxDrawCalls);
    400 	}
    401 
    402 	// Sanity check.
    403 	if (params.maxDrawCalls < MIN_DRAW_CALL_COUNT)
    404 		throw CalibrationFailedException("Calibration failed, maximum draw call count is too low");
    405 
    406 	return params;
    407 }
    408 
    409 void FlushFinishCase::verifyCalibration (const CalibrationParams& params)
    410 {
    411 	setShaderIterCount(params.numItersInShader);
    412 
    413 	for (int sampleNdx = 0; sampleNdx < NUM_VERIFICATION_SAMPLES; sampleNdx++)
    414 	{
    415 		deUint64 readStartTime;
    416 
    417 		render(params.maxDrawCalls);
    418 
    419 		readStartTime = deGetMicroseconds();
    420 		readPixels();
    421 
    422 		{
    423 			const deUint64	renderDuration	= deGetMicroseconds()-readStartTime;
    424 			const float		relativeDelta	= float(double(renderDuration) / double(MAX_SAMPLE_DURATION_US)) - 1.0f;
    425 
    426 			if (!de::inBounds(relativeDelta, -CALIBRATION_VERIFICATION_THRESHOLD, CALIBRATION_VERIFICATION_THRESHOLD))
    427 			{
    428 				std::ostringstream msg;
    429 				msg << "ERROR: Unstable performance, got " << renderDuration << " us read time, "
    430 					<< de::floatToString(relativeDelta*100.0f, 1) << "% diff to estimated " << (int)MAX_SAMPLE_DURATION_US << " us";
    431 				throw CalibrationFailedException(msg.str());
    432 			}
    433 		}
    434 	}
    435 }
    436 
    437 struct CompareSampleDrawCount
    438 {
    439 	bool operator() (const FlushFinishCase::Sample& a, const FlushFinishCase::Sample& b) const { return a.numDrawCalls < b.numDrawCalls; }
    440 };
    441 
    442 std::vector<Vec2> getPointsFromSamples (const std::vector<FlushFinishCase::Sample>& samples, const deUint64 FlushFinishCase::Sample::*field)
    443 {
    444 	vector<Vec2> points(samples.size());
    445 
    446 	for (size_t ndx = 0; ndx < samples.size(); ndx++)
    447 		points[ndx] = Vec2(float(samples[ndx].numDrawCalls), float(samples[ndx].*field));
    448 
    449 	return points;
    450 }
    451 
    452 template<typename T>
    453 T getMaximumValue (const std::vector<FlushFinishCase::Sample>& samples, const T FlushFinishCase::Sample::*field)
    454 {
    455 	DE_ASSERT(!samples.empty());
    456 
    457 	T maxVal = samples[0].*field;
    458 
    459 	for (size_t ndx = 1; ndx < samples.size(); ndx++)
    460 		maxVal = de::max(maxVal, samples[ndx].*field);
    461 
    462 	return maxVal;
    463 }
    464 
    465 void FlushFinishCase::analyzeResults (const std::vector<Sample>& samples, const CalibrationParams& calibrationParams)
    466 {
    467 	const vector<Vec2>		waitTimes		= getPointsFromSamples(samples, &Sample::waitTime);
    468 	const vector<Vec2>		readTimes		= getPointsFromSamples(samples, &Sample::readPixelsTime);
    469 	const LineParameters	waitLine		= theilSenLinearRegression(waitTimes);
    470 	const LineParameters	readLine		= theilSenLinearRegression(readTimes);
    471 	const float				normWaitCoef	= waitLine.coefficient * float(calibrationParams.maxDrawCalls) / float(MAX_SAMPLE_DURATION_US);
    472 	const float				normReadCoef	= readLine.coefficient * float(calibrationParams.maxDrawCalls) / float(MAX_SAMPLE_DURATION_US);
    473 	bool					allOk			= true;
    474 
    475 	{
    476 		tcu::ScopedLogSection	section			(m_testCtx.getLog(), "Samples", "Samples");
    477 		vector<Sample>			sortedSamples	(samples.begin(), samples.end());
    478 
    479 		std::sort(sortedSamples.begin(), sortedSamples.end(), CompareSampleDrawCount());
    480 
    481 		for (vector<Sample>::const_iterator iter = sortedSamples.begin(); iter != sortedSamples.end(); ++iter)
    482 			m_testCtx.getLog() << *iter;
    483 	}
    484 
    485 	m_testCtx.getLog() << TestLog::Float("WaitCoefficient",				"Wait coefficient", "", QP_KEY_TAG_NONE, waitLine.coefficient)
    486 					   << TestLog::Float("ReadCoefficient",				"Read coefficient", "", QP_KEY_TAG_NONE, readLine.coefficient)
    487 					   << TestLog::Float("NormalizedWaitCoefficient",	"Normalized wait coefficient", "", QP_KEY_TAG_NONE, normWaitCoef)
    488 					   << TestLog::Float("NormalizedReadCoefficient",	"Normalized read coefficient", "", QP_KEY_TAG_NONE, normReadCoef);
    489 
    490 	{
    491 		const bool		waitCorrelated		= normWaitCoef > CORRELATED_COEF_THRESHOLD;
    492 		const bool		readCorrelated		= normReadCoef > CORRELATED_COEF_THRESHOLD;
    493 		const bool		waitNotCorr			= normWaitCoef < NO_CORR_COEF_THRESHOLD;
    494 		const bool		readNotCorr			= normReadCoef < NO_CORR_COEF_THRESHOLD;
    495 
    496 		if (waitCorrelated || waitNotCorr)
    497 			m_testCtx.getLog() << TestLog::Message << "Wait time is" << (waitCorrelated ? "" : " NOT") << " correlated to rendering workload size." << TestLog::EndMessage;
    498 		else
    499 			m_testCtx.getLog() << TestLog::Message << "Warning: Wait time correlation to rendering workload size is unclear." << TestLog::EndMessage;
    500 
    501 		if (readCorrelated || readNotCorr)
    502 			m_testCtx.getLog() << TestLog::Message << "Read time is" << (readCorrelated ? "" : " NOT") << " correlated to rendering workload size." << TestLog::EndMessage;
    503 		else
    504 			m_testCtx.getLog() << TestLog::Message << "Warning: Read time correlation to rendering workload size is unclear." << TestLog::EndMessage;
    505 	}
    506 
    507 	for (int ndx = 0; ndx < 2; ndx++)
    508 	{
    509 		const float				coef		= ndx == 0 ? normWaitCoef : normReadCoef;
    510 		const char*				name		= ndx == 0 ? "wait" : "read";
    511 		const ExpectedBehavior	behavior	= ndx == 0 ? m_waitBehavior : m_readBehavior;
    512 		const float				threshold	= ndx == 0 ? m_waitThreshold : m_readThreshold;
    513 		const bool				isOk		= behavior == EXPECT_COEF_GREATER_THAN	? coef > threshold :
    514 											  behavior == EXPECT_COEF_LESS_THAN		? coef < threshold : false;
    515 		const char*				cmpName		= behavior == EXPECT_COEF_GREATER_THAN	? "greater than" :
    516 											  behavior == EXPECT_COEF_LESS_THAN		? "less than" : DE_NULL;
    517 
    518 		if (!isOk)
    519 		{
    520 			m_testCtx.getLog() << TestLog::Message << "ERROR: Expected " << name << " coefficient to be " << cmpName << " " << threshold << TestLog::EndMessage;
    521 			allOk = false;
    522 		}
    523 	}
    524 
    525 	m_testCtx.setTestResult(allOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
    526 							allOk ? "Pass"				: "Suspicious performance behavior");
    527 }
    528 
    529 FlushFinishCase::IterateResult FlushFinishCase::iterate (void)
    530 {
    531 	vector<Sample>		samples		(NUM_SAMPLES);
    532 	CalibrationParams	params;
    533 
    534 	tcu::warmupCPU();
    535 
    536 	setupRenderState();
    537 
    538 	// Do one full render cycle.
    539 	{
    540 		setShaderIterCount(1);
    541 		render(1);
    542 		readPixels();
    543 	}
    544 
    545 	// Calibrate.
    546 	for (int calibrationRoundNdx = 0; /* until done */; calibrationRoundNdx++)
    547 	{
    548 		try
    549 		{
    550 			m_testCtx.touchWatchdog();
    551 			params = calibrate();
    552 			verifyCalibration(params);
    553 			break;
    554 		}
    555 		catch (const CalibrationFailedException& e)
    556 		{
    557 			m_testCtx.getLog() << e;
    558 
    559 			if (calibrationRoundNdx < MAX_CALIBRATION_ATTEMPTS)
    560 			{
    561 				m_testCtx.getLog() << TestLog::Message
    562 								   << "Retrying calibration (" << (calibrationRoundNdx+1) << " / " << (int)MAX_CALIBRATION_ATTEMPTS << ")"
    563 								   << TestLog::EndMessage;
    564 			}
    565 			else
    566 			{
    567 				m_testCtx.setTestResult(QP_TEST_RESULT_COMPATIBILITY_WARNING, e.what());
    568 				return STOP;
    569 			}
    570 		}
    571 	}
    572 
    573 	// Do measurement.
    574 	{
    575 		de::Random	rnd		(123);
    576 
    577 		setShaderIterCount(params.numItersInShader);
    578 
    579 		for (size_t ndx = 0; ndx < samples.size(); ndx++)
    580 		{
    581 			const int		drawCallCount	= rnd.getInt(1, params.maxDrawCalls);
    582 			const deUint64	submitStartTime	= deGetMicroseconds();
    583 			deUint64		waitStartTime;
    584 			deUint64		readStartTime;
    585 			deUint64		readFinishTime;
    586 
    587 			render(drawCallCount);
    588 
    589 			waitStartTime = deGetMicroseconds();
    590 			waitForGL();
    591 
    592 			readStartTime = deGetMicroseconds();
    593 			readPixels();
    594 			readFinishTime = deGetMicroseconds();
    595 
    596 			samples[ndx].numDrawCalls	= drawCallCount;
    597 			samples[ndx].submitTime		= waitStartTime-submitStartTime;
    598 			samples[ndx].waitTime		= readStartTime-waitStartTime;
    599 			samples[ndx].readPixelsTime	= readFinishTime-readStartTime;
    600 
    601 			m_testCtx.touchWatchdog();
    602 		}
    603 	}
    604 
    605 	// Analyze - sets test case result.
    606 	analyzeResults(samples, params);
    607 
    608 	return STOP;
    609 }
    610 
    611 class WaitOnlyCase : public FlushFinishCase
    612 {
    613 public:
    614 	WaitOnlyCase (Context& context)
    615 		: FlushFinishCase(context, "wait", "Wait only", EXPECT_COEF_LESS_THAN, NO_CORR_COEF_THRESHOLD, EXPECT_COEF_GREATER_THAN, -1000.0f /* practically nothing is expected */)
    616 	{
    617 	}
    618 
    619 	void init (void)
    620 	{
    621 		m_testCtx.getLog() << TestLog::Message << int(WAIT_TIME_MS) << " ms busy wait" << TestLog::EndMessage;
    622 		FlushFinishCase::init();
    623 	}
    624 
    625 protected:
    626 	void waitForGL (void)
    627 	{
    628 		busyWait(WAIT_TIME_MS);
    629 	}
    630 };
    631 
    632 class FlushOnlyCase : public FlushFinishCase
    633 {
    634 public:
    635 	FlushOnlyCase (Context& context)
    636 		: FlushFinishCase(context, "flush", "Flush only", EXPECT_COEF_LESS_THAN, FLUSH_COEF_THRESHOLD, EXPECT_COEF_GREATER_THAN, CORRELATED_COEF_THRESHOLD)
    637 	{
    638 	}
    639 
    640 	void init (void)
    641 	{
    642 		m_testCtx.getLog() << TestLog::Message << "Single call to glFlush()" << TestLog::EndMessage;
    643 		FlushFinishCase::init();
    644 	}
    645 
    646 protected:
    647 	void waitForGL (void)
    648 	{
    649 		m_context.getRenderContext().getFunctions().flush();
    650 	}
    651 };
    652 
    653 class FlushWaitCase : public FlushFinishCase
    654 {
    655 public:
    656 	FlushWaitCase (Context& context)
    657 		: FlushFinishCase(context, "flush_wait", "Wait after flushing", EXPECT_COEF_LESS_THAN, FLUSH_COEF_THRESHOLD, EXPECT_COEF_LESS_THAN, NO_CORR_COEF_THRESHOLD)
    658 	{
    659 	}
    660 
    661 	void init (void)
    662 	{
    663 		m_testCtx.getLog() << TestLog::Message << "glFlush() followed by " << int(WAIT_TIME_MS) << " ms busy wait" << TestLog::EndMessage;
    664 		FlushFinishCase::init();
    665 	}
    666 
    667 protected:
    668 	void waitForGL (void)
    669 	{
    670 		m_context.getRenderContext().getFunctions().flush();
    671 		busyWait(WAIT_TIME_MS);
    672 	}
    673 };
    674 
    675 class FinishOnlyCase : public FlushFinishCase
    676 {
    677 public:
    678 	FinishOnlyCase (Context& context)
    679 		: FlushFinishCase(context, "finish", "Finish only", EXPECT_COEF_GREATER_THAN, CORRELATED_COEF_THRESHOLD, EXPECT_COEF_LESS_THAN, NO_CORR_COEF_THRESHOLD)
    680 	{
    681 	}
    682 
    683 	void init (void)
    684 	{
    685 		m_testCtx.getLog() << TestLog::Message << "Single call to glFinish()" << TestLog::EndMessage;
    686 		FlushFinishCase::init();
    687 	}
    688 
    689 protected:
    690 	void waitForGL (void)
    691 	{
    692 		m_context.getRenderContext().getFunctions().finish();
    693 	}
    694 };
    695 
    696 class FinishWaitCase : public FlushFinishCase
    697 {
    698 public:
    699 	FinishWaitCase (Context& context)
    700 		: FlushFinishCase(context, "finish_wait", "Finish and wait", EXPECT_COEF_GREATER_THAN, CORRELATED_COEF_THRESHOLD, EXPECT_COEF_LESS_THAN, NO_CORR_COEF_THRESHOLD)
    701 	{
    702 	}
    703 
    704 	void init (void)
    705 	{
    706 		m_testCtx.getLog() << TestLog::Message << "glFinish() followed by " << int(WAIT_TIME_MS) << " ms busy wait" << TestLog::EndMessage;
    707 		FlushFinishCase::init();
    708 	}
    709 
    710 protected:
    711 	void waitForGL (void)
    712 	{
    713 		m_context.getRenderContext().getFunctions().finish();
    714 		busyWait(WAIT_TIME_MS);
    715 	}
    716 };
    717 
    718 } // anonymous
    719 
    720 FlushFinishTests::FlushFinishTests (Context& context)
    721 	: TestCaseGroup(context, "flush_finish", "Flush and Finish tests")
    722 {
    723 }
    724 
    725 FlushFinishTests::~FlushFinishTests (void)
    726 {
    727 }
    728 
    729 void FlushFinishTests::init (void)
    730 {
    731 	addChild(new WaitOnlyCase	(m_context));
    732 	addChild(new FlushOnlyCase	(m_context));
    733 	addChild(new FlushWaitCase	(m_context));
    734 	addChild(new FinishOnlyCase	(m_context));
    735 	addChild(new FinishWaitCase	(m_context));
    736 }
    737 
    738 } // Functional
    739 } // gles3
    740 } // deqp
    741