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