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