1/*-------------------------------------------------------------------------
2 * drawElements Quality Program EGL 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 Color clear case.
22 *//*--------------------------------------------------------------------*/
23
24#include "teglColorClearCase.hpp"
25#include "tcuTestLog.hpp"
26#include "eglwLibrary.hpp"
27#include "eglwEnums.hpp"
28#include "egluUtil.hpp"
29#include "deRandom.hpp"
30#include "deString.h"
31#include "tcuImageCompare.hpp"
32#include "tcuVector.hpp"
33#include "tcuTextureUtil.hpp"
34#include "tcuPixelFormat.hpp"
35#include "glwFunctions.hpp"
36#include "deThread.hpp"
37#include "deSemaphore.hpp"
38#include "deSharedPtr.hpp"
39#include "teglGLES1RenderUtil.hpp"
40#include "teglGLES2RenderUtil.hpp"
41#include "teglVGRenderUtil.hpp"
42
43#include <memory>
44#include <iterator>
45
46namespace deqp
47{
48namespace egl
49{
50
51using tcu::TestLog;
52using tcu::RGBA;
53using std::vector;
54using namespace eglw;
55
56// Utilities.
57
58struct ClearOp
59{
60	ClearOp (int x_, int y_, int width_, int height_, const tcu::RGBA& color_)
61		: x			(x_)
62		, y			(y_)
63		, width		(width_)
64		, height	(height_)
65		, color		(color_)
66	{
67	}
68
69	ClearOp (void)
70		: x			(0)
71		, y			(0)
72		, width		(0)
73		, height	(0)
74		, color		(0)
75	{
76	}
77
78	int			x;
79	int			y;
80	int			width;
81	int			height;
82	tcu::RGBA	color;
83};
84
85struct ApiFunctions
86{
87	glw::Functions	gl;
88};
89
90static ClearOp computeRandomClear (de::Random& rnd, int width, int height)
91{
92	int			w		= rnd.getInt(1, width);
93	int			h		= rnd.getInt(1, height);
94	int			x		= rnd.getInt(0, width-w);
95	int			y		= rnd.getInt(0, height-h);
96	tcu::RGBA	col		(rnd.getUint32());
97
98	return ClearOp(x, y, w, h, col);
99}
100
101static void renderReference (tcu::Surface& dst, const vector<ClearOp>& clears, const tcu::PixelFormat& pixelFormat)
102{
103	for (vector<ClearOp>::const_iterator clearIter = clears.begin(); clearIter != clears.end(); clearIter++)
104	{
105		tcu::PixelBufferAccess access = tcu::getSubregion(dst.getAccess(), clearIter->x, clearIter->y, 0, clearIter->width, clearIter->height, 1);
106		tcu::clear(access, pixelFormat.convertColor(clearIter->color).toIVec());
107	}
108}
109
110static void renderClear (EGLint api, const ApiFunctions& func, const ClearOp& clear)
111{
112	switch (api)
113	{
114		case EGL_OPENGL_ES_BIT:			gles1::clear(clear.x, clear.y, clear.width, clear.height, clear.color.toVec());				break;
115		case EGL_OPENGL_ES2_BIT:		gles2::clear(func.gl, clear.x, clear.y, clear.width, clear.height, clear.color.toVec());	break;
116		case EGL_OPENGL_ES3_BIT_KHR:	gles2::clear(func.gl, clear.x, clear.y, clear.width, clear.height, clear.color.toVec());	break;
117		case EGL_OPENVG_BIT:			vg::clear	(clear.x, clear.y, clear.width, clear.height, clear.color.toVec());				break;
118		default:
119			DE_ASSERT(DE_FALSE);
120	}
121}
122
123static void finish (EGLint api, const ApiFunctions& func)
124{
125	switch (api)
126	{
127		case EGL_OPENGL_ES_BIT:			gles1::finish();		break;
128		case EGL_OPENGL_ES2_BIT:		gles2::finish(func.gl);	break;
129		case EGL_OPENGL_ES3_BIT_KHR:	gles2::finish(func.gl);	break;
130		case EGL_OPENVG_BIT:			vg::finish();			break;
131		default:
132			DE_ASSERT(DE_FALSE);
133	}
134}
135
136static void readPixels (EGLint api, const ApiFunctions& func, tcu::Surface& dst)
137{
138	switch (api)
139	{
140		case EGL_OPENGL_ES_BIT:			gles1::readPixels	(dst, 0, 0, dst.getWidth(), dst.getHeight());			break;
141		case EGL_OPENGL_ES2_BIT:		gles2::readPixels	(func.gl, dst, 0, 0, dst.getWidth(), dst.getHeight());	break;
142		case EGL_OPENGL_ES3_BIT_KHR:	gles2::readPixels	(func.gl, dst, 0, 0, dst.getWidth(), dst.getHeight());	break;
143		case EGL_OPENVG_BIT:			vg::readPixels		(dst, 0, 0, dst.getWidth(), dst.getHeight());			break;
144		default:
145			DE_ASSERT(DE_FALSE);
146	}
147}
148
149static tcu::PixelFormat getPixelFormat (const Library& egl, EGLDisplay display, EGLConfig config)
150{
151	tcu::PixelFormat pixelFmt;
152
153	egl.getConfigAttrib(display, config, EGL_RED_SIZE,		&pixelFmt.redBits);
154	egl.getConfigAttrib(display, config, EGL_GREEN_SIZE,	&pixelFmt.greenBits);
155	egl.getConfigAttrib(display, config, EGL_BLUE_SIZE,		&pixelFmt.blueBits);
156	egl.getConfigAttrib(display, config, EGL_ALPHA_SIZE,	&pixelFmt.alphaBits);
157
158	return pixelFmt;
159}
160
161// SingleThreadColorClearCase
162
163SingleThreadColorClearCase::SingleThreadColorClearCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi)
164	: MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi)
165{
166}
167
168void SingleThreadColorClearCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts)
169{
170	const Library&		egl			= m_eglTestCtx.getLibrary();
171
172	const tcu::IVec2	surfaceSize	= eglu::getSurfaceSize(egl, display, surface);
173	const int			width		= surfaceSize.x();
174	const int			height		= surfaceSize.y();
175
176	TestLog&			log			= m_testCtx.getLog();
177
178	tcu::Surface		refFrame	(width, height);
179	tcu::Surface		frame		(width, height);
180	tcu::PixelFormat	pixelFmt	= getPixelFormat(egl, display, config.config);
181
182	de::Random			rnd			(deStringHash(getName()));
183	vector<ClearOp>		clears;
184	const int			ctxClears	= 2;
185	const int			numIters	= 3;
186
187	ApiFunctions		funcs;
188
189	m_eglTestCtx.initGLFunctions(&funcs.gl, glu::ApiType::es(2,0));
190
191	// Clear to black using first context.
192	{
193		EGLint		api			= contexts[0].first;
194		EGLContext	context		= contexts[0].second;
195		ClearOp		clear		(0, 0, width, height, RGBA::black());
196
197		egl.makeCurrent(display, surface, surface, context);
198		EGLU_CHECK_MSG(egl, "eglMakeCurrent");
199
200		renderClear(api, funcs, clear);
201		finish(api, funcs);
202		clears.push_back(clear);
203	}
204
205	// Render.
206	for (int iterNdx = 0; iterNdx < numIters; iterNdx++)
207	{
208		for (vector<std::pair<EGLint, EGLContext> >::const_iterator ctxIter = contexts.begin(); ctxIter != contexts.end(); ctxIter++)
209		{
210			EGLint		api			= ctxIter->first;
211			EGLContext	context		= ctxIter->second;
212
213			egl.makeCurrent(display, surface, surface, context);
214			EGLU_CHECK_MSG(egl, "eglMakeCurrent");
215
216			for (int clearNdx = 0; clearNdx < ctxClears; clearNdx++)
217			{
218				ClearOp clear = computeRandomClear(rnd, width, height);
219
220				renderClear(api, funcs, clear);
221				clears.push_back(clear);
222			}
223
224			finish(api, funcs);
225		}
226	}
227
228	// Read pixels using first context. \todo [pyry] Randomize?
229	{
230		EGLint		api		= contexts[0].first;
231		EGLContext	context	= contexts[0].second;
232
233		egl.makeCurrent(display, surface, surface, context);
234		EGLU_CHECK_MSG(egl, "eglMakeCurrent");
235
236		readPixels(api, funcs, frame);
237	}
238
239	egl.makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
240	EGLU_CHECK_MSG(egl, "eglMakeCurrent");
241
242	// Render reference.
243	renderReference(refFrame, clears, pixelFmt);
244
245	// Compare images
246	{
247		tcu::RGBA eps = pixelFmt.alphaBits == 1 ? RGBA(1,1,1,127) : RGBA(1,1,1,1);
248		bool imagesOk = tcu::pixelThresholdCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, eps + pixelFmt.getColorThreshold(), tcu::COMPARE_LOG_RESULT);
249
250		if (!imagesOk)
251			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
252	}
253}
254
255// MultiThreadColorClearCase
256
257enum
258{
259	NUM_CLEARS_PER_PACKET	= 2 //!< Number of clears performed in one context activation in one thread.
260};
261
262class ColorClearThread;
263
264typedef de::SharedPtr<ColorClearThread>	ColorClearThreadSp;
265typedef de::SharedPtr<de::Semaphore>	SemaphoreSp;
266
267struct ClearPacket
268{
269	ClearPacket (void)
270	{
271	}
272
273	ClearOp			clears[NUM_CLEARS_PER_PACKET];
274	SemaphoreSp		wait;
275	SemaphoreSp		signal;
276};
277
278class ColorClearThread : public de::Thread
279{
280public:
281	ColorClearThread (const Library& egl, EGLDisplay display, EGLSurface surface, EGLContext context, EGLint api, const ApiFunctions& funcs, const std::vector<ClearPacket>& packets)
282		: m_egl		(egl)
283		, m_display	(display)
284		, m_surface	(surface)
285		, m_context	(context)
286		, m_api		(api)
287		, m_funcs	(funcs)
288		, m_packets	(packets)
289	{
290	}
291
292	void run (void)
293	{
294		for (std::vector<ClearPacket>::const_iterator packetIter = m_packets.begin(); packetIter != m_packets.end(); packetIter++)
295		{
296			// Wait until it is our turn.
297			packetIter->wait->decrement();
298
299			// Acquire context.
300			m_egl.makeCurrent(m_display, m_surface, m_surface, m_context);
301
302			// Execute clears.
303			for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(packetIter->clears); ndx++)
304				renderClear(m_api, m_funcs, packetIter->clears[ndx]);
305
306			finish(m_api, m_funcs);
307			// Release context.
308			m_egl.makeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
309
310			// Signal completion.
311			packetIter->signal->increment();
312		}
313		m_egl.releaseThread();
314	}
315
316private:
317	const Library&					m_egl;
318	EGLDisplay						m_display;
319	EGLSurface						m_surface;
320	EGLContext						m_context;
321	EGLint							m_api;
322	const ApiFunctions&				m_funcs;
323	const std::vector<ClearPacket>&	m_packets;
324};
325
326MultiThreadColorClearCase::MultiThreadColorClearCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi)
327	: MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi)
328{
329}
330
331void MultiThreadColorClearCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts)
332{
333	const Library&		egl			= m_eglTestCtx.getLibrary();
334
335	const tcu::IVec2	surfaceSize	= eglu::getSurfaceSize(egl, display, surface);
336	const int			width		= surfaceSize.x();
337	const int			height		= surfaceSize.y();
338
339	TestLog&			log			= m_testCtx.getLog();
340
341	tcu::Surface		refFrame	(width, height);
342	tcu::Surface		frame		(width, height);
343	tcu::PixelFormat	pixelFmt	= getPixelFormat(egl, display, config.config);
344
345	de::Random			rnd			(deStringHash(getName()));
346
347	ApiFunctions		funcs;
348
349	m_eglTestCtx.initGLFunctions(&funcs.gl, glu::ApiType::es(2,0));
350
351	// Create clear packets.
352	const int						numPacketsPerThread		= 2;
353	int								numThreads				= (int)contexts.size();
354	int								numPackets				= numThreads * numPacketsPerThread;
355
356	vector<SemaphoreSp>				semaphores				(numPackets+1);
357	vector<vector<ClearPacket> >	packets					(numThreads);
358	vector<ColorClearThreadSp>		threads					(numThreads);
359
360	// Initialize semaphores.
361	for (vector<SemaphoreSp>::iterator sem = semaphores.begin(); sem != semaphores.end(); ++sem)
362		*sem = SemaphoreSp(new de::Semaphore(0));
363
364	// Create packets.
365	for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
366	{
367		packets[threadNdx].resize(numPacketsPerThread);
368
369		for (int packetNdx = 0; packetNdx < numPacketsPerThread; packetNdx++)
370		{
371			ClearPacket& packet = packets[threadNdx][packetNdx];
372
373			// Threads take turns with packets.
374			packet.wait		= semaphores[packetNdx*numThreads + threadNdx];
375			packet.signal	= semaphores[packetNdx*numThreads + threadNdx + 1];
376
377			for (int clearNdx = 0; clearNdx < DE_LENGTH_OF_ARRAY(packet.clears); clearNdx++)
378			{
379				// First clear is always full-screen black.
380				if (threadNdx == 0 && packetNdx == 0 && clearNdx == 0)
381					packet.clears[clearNdx] = ClearOp(0, 0, width, height, RGBA::black());
382				else
383					packet.clears[clearNdx] = computeRandomClear(rnd, width, height);
384			}
385		}
386	}
387
388	// Create and launch threads (actual rendering starts once first semaphore is signaled).
389	for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
390	{
391		threads[threadNdx] = ColorClearThreadSp(new ColorClearThread(egl, display, surface, contexts[threadNdx].second, contexts[threadNdx].first, funcs, packets[threadNdx]));
392		threads[threadNdx]->start();
393	}
394
395	// Signal start and wait until complete.
396	semaphores.front()->increment();
397	semaphores.back()->decrement();
398
399	// Read pixels using first context. \todo [pyry] Randomize?
400	{
401		EGLint		api		= contexts[0].first;
402		EGLContext	context	= contexts[0].second;
403
404		egl.makeCurrent(display, surface, surface, context);
405		EGLU_CHECK_MSG(egl, "eglMakeCurrent");
406
407		readPixels(api, funcs, frame);
408	}
409
410	egl.makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
411	EGLU_CHECK_MSG(egl, "eglMakeCurrent");
412
413	// Join threads.
414	for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
415		threads[threadNdx]->join();
416
417	// Render reference.
418	for (int packetNdx = 0; packetNdx < numPacketsPerThread; packetNdx++)
419	{
420		for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
421		{
422			const ClearPacket& packet = packets[threadNdx][packetNdx];
423			for (int clearNdx = 0; clearNdx < DE_LENGTH_OF_ARRAY(packet.clears); clearNdx++)
424			{
425				tcu::PixelBufferAccess access = tcu::getSubregion(refFrame.getAccess(),
426																  packet.clears[clearNdx].x, packet.clears[clearNdx].y, 0,
427																  packet.clears[clearNdx].width, packet.clears[clearNdx].height, 1);
428				tcu::clear(access, pixelFmt.convertColor(packet.clears[clearNdx].color).toIVec());
429			}
430		}
431	}
432
433	// Compare images
434	{
435		tcu::RGBA eps = pixelFmt.alphaBits == 1 ? RGBA(1,1,1,127) : RGBA(1,1,1,1);
436		bool imagesOk = tcu::pixelThresholdCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, eps + pixelFmt.getColorThreshold(), tcu::COMPARE_LOG_RESULT);
437
438		if (!imagesOk)
439			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
440	}
441}
442
443} // egl
444} // deqp
445