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