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