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 
46 namespace deqp
47 {
48 namespace egl
49 {
50 
51 using tcu::TestLog;
52 using tcu::RGBA;
53 using std::vector;
54 using namespace eglw;
55 
56 // Utilities.
57 
58 struct ClearOp
59 {
ClearOpdeqp::egl::ClearOp60 	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 
ClearOpdeqp::egl::ClearOp69 	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 
85 struct ApiFunctions
86 {
87 	glw::Functions	gl;
88 };
89 
computeRandomClear(de::Random & rnd,int width,int height)90 static 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 
renderReference(tcu::Surface & dst,const vector<ClearOp> & clears,const tcu::PixelFormat & pixelFormat)101 static 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 
renderClear(EGLint api,const ApiFunctions & func,const ClearOp & clear)110 static 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 
finish(EGLint api,const ApiFunctions & func)123 static 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 
readPixels(EGLint api,const ApiFunctions & func,tcu::Surface & dst)136 static 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 
getPixelFormat(const Library & egl,EGLDisplay display,EGLConfig config)149 static 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 
SingleThreadColorClearCase(EglTestContext & eglTestCtx,const char * name,const char * description,EGLint api,EGLint surfaceType,const eglu::FilterList & filters,int numContextsPerApi)163 SingleThreadColorClearCase::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 
executeForContexts(EGLDisplay display,EGLSurface surface,const Config & config,const std::vector<std::pair<EGLint,EGLContext>> & contexts)168 void 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 		bool imagesOk = tcu::pixelThresholdCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, RGBA(1,1,1,1) + pixelFmt.getColorThreshold(), tcu::COMPARE_LOG_RESULT);
248 
249 		if (!imagesOk)
250 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
251 	}
252 }
253 
254 // MultiThreadColorClearCase
255 
256 enum
257 {
258 	NUM_CLEARS_PER_PACKET	= 2 //!< Number of clears performed in one context activation in one thread.
259 };
260 
261 class ColorClearThread;
262 
263 typedef de::SharedPtr<ColorClearThread>	ColorClearThreadSp;
264 typedef de::SharedPtr<de::Semaphore>	SemaphoreSp;
265 
266 struct ClearPacket
267 {
ClearPacketdeqp::egl::ClearPacket268 	ClearPacket (void)
269 	{
270 	}
271 
272 	ClearOp			clears[NUM_CLEARS_PER_PACKET];
273 	SemaphoreSp		wait;
274 	SemaphoreSp		signal;
275 };
276 
277 class ColorClearThread : public de::Thread
278 {
279 public:
ColorClearThread(const Library & egl,EGLDisplay display,EGLSurface surface,EGLContext context,EGLint api,const ApiFunctions & funcs,const std::vector<ClearPacket> & packets)280 	ColorClearThread (const Library& egl, EGLDisplay display, EGLSurface surface, EGLContext context, EGLint api, const ApiFunctions& funcs, const std::vector<ClearPacket>& packets)
281 		: m_egl		(egl)
282 		, m_display	(display)
283 		, m_surface	(surface)
284 		, m_context	(context)
285 		, m_api		(api)
286 		, m_funcs	(funcs)
287 		, m_packets	(packets)
288 	{
289 	}
290 
run(void)291 	void run (void)
292 	{
293 		for (std::vector<ClearPacket>::const_iterator packetIter = m_packets.begin(); packetIter != m_packets.end(); packetIter++)
294 		{
295 			// Wait until it is our turn.
296 			packetIter->wait->decrement();
297 
298 			// Acquire context.
299 			m_egl.makeCurrent(m_display, m_surface, m_surface, m_context);
300 
301 			// Execute clears.
302 			for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(packetIter->clears); ndx++)
303 				renderClear(m_api, m_funcs, packetIter->clears[ndx]);
304 
305 			finish(m_api, m_funcs);
306 			// Release context.
307 			m_egl.makeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
308 
309 			// Signal completion.
310 			packetIter->signal->increment();
311 		}
312 	}
313 
314 private:
315 	const Library&					m_egl;
316 	EGLDisplay						m_display;
317 	EGLSurface						m_surface;
318 	EGLContext						m_context;
319 	EGLint							m_api;
320 	const ApiFunctions&				m_funcs;
321 	const std::vector<ClearPacket>&	m_packets;
322 };
323 
MultiThreadColorClearCase(EglTestContext & eglTestCtx,const char * name,const char * description,EGLint api,EGLint surfaceType,const eglu::FilterList & filters,int numContextsPerApi)324 MultiThreadColorClearCase::MultiThreadColorClearCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi)
325 	: MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi)
326 {
327 }
328 
executeForContexts(EGLDisplay display,EGLSurface surface,const Config & config,const std::vector<std::pair<EGLint,EGLContext>> & contexts)329 void MultiThreadColorClearCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts)
330 {
331 	const Library&		egl			= m_eglTestCtx.getLibrary();
332 
333 	const tcu::IVec2	surfaceSize	= eglu::getSurfaceSize(egl, display, surface);
334 	const int			width		= surfaceSize.x();
335 	const int			height		= surfaceSize.y();
336 
337 	TestLog&			log			= m_testCtx.getLog();
338 
339 	tcu::Surface		refFrame	(width, height);
340 	tcu::Surface		frame		(width, height);
341 	tcu::PixelFormat	pixelFmt	= getPixelFormat(egl, display, config.config);
342 
343 	de::Random			rnd			(deStringHash(getName()));
344 
345 	ApiFunctions		funcs;
346 
347 	m_eglTestCtx.initGLFunctions(&funcs.gl, glu::ApiType::es(2,0));
348 
349 	// Create clear packets.
350 	const int						numPacketsPerThread		= 2;
351 	int								numThreads				= (int)contexts.size();
352 	int								numPackets				= numThreads * numPacketsPerThread;
353 
354 	vector<SemaphoreSp>				semaphores				(numPackets+1);
355 	vector<vector<ClearPacket> >	packets					(numThreads);
356 	vector<ColorClearThreadSp>		threads					(numThreads);
357 
358 	// Initialize semaphores.
359 	for (vector<SemaphoreSp>::iterator sem = semaphores.begin(); sem != semaphores.end(); ++sem)
360 		*sem = SemaphoreSp(new de::Semaphore(0));
361 
362 	// Create packets.
363 	for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
364 	{
365 		packets[threadNdx].resize(numPacketsPerThread);
366 
367 		for (int packetNdx = 0; packetNdx < numPacketsPerThread; packetNdx++)
368 		{
369 			ClearPacket& packet = packets[threadNdx][packetNdx];
370 
371 			// Threads take turns with packets.
372 			packet.wait		= semaphores[packetNdx*numThreads + threadNdx];
373 			packet.signal	= semaphores[packetNdx*numThreads + threadNdx + 1];
374 
375 			for (int clearNdx = 0; clearNdx < DE_LENGTH_OF_ARRAY(packet.clears); clearNdx++)
376 			{
377 				// First clear is always full-screen black.
378 				if (threadNdx == 0 && packetNdx == 0 && clearNdx == 0)
379 					packet.clears[clearNdx] = ClearOp(0, 0, width, height, RGBA::black());
380 				else
381 					packet.clears[clearNdx] = computeRandomClear(rnd, width, height);
382 			}
383 		}
384 	}
385 
386 	// Create and launch threads (actual rendering starts once first semaphore is signaled).
387 	for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
388 	{
389 		threads[threadNdx] = ColorClearThreadSp(new ColorClearThread(egl, display, surface, contexts[threadNdx].second, contexts[threadNdx].first, funcs, packets[threadNdx]));
390 		threads[threadNdx]->start();
391 	}
392 
393 	// Signal start and wait until complete.
394 	semaphores.front()->increment();
395 	semaphores.back()->decrement();
396 
397 	// Read pixels using first context. \todo [pyry] Randomize?
398 	{
399 		EGLint		api		= contexts[0].first;
400 		EGLContext	context	= contexts[0].second;
401 
402 		egl.makeCurrent(display, surface, surface, context);
403 		EGLU_CHECK_MSG(egl, "eglMakeCurrent");
404 
405 		readPixels(api, funcs, frame);
406 	}
407 
408 	egl.makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
409 	EGLU_CHECK_MSG(egl, "eglMakeCurrent");
410 
411 	// Join threads.
412 	for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
413 		threads[threadNdx]->join();
414 
415 	// Render reference.
416 	for (int packetNdx = 0; packetNdx < numPacketsPerThread; packetNdx++)
417 	{
418 		for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
419 		{
420 			const ClearPacket& packet = packets[threadNdx][packetNdx];
421 			for (int clearNdx = 0; clearNdx < DE_LENGTH_OF_ARRAY(packet.clears); clearNdx++)
422 			{
423 				tcu::PixelBufferAccess access = tcu::getSubregion(refFrame.getAccess(),
424 																  packet.clears[clearNdx].x, packet.clears[clearNdx].y, 0,
425 																  packet.clears[clearNdx].width, packet.clears[clearNdx].height, 1);
426 				tcu::clear(access, pixelFmt.convertColor(packet.clears[clearNdx].color).toIVec());
427 			}
428 		}
429 	}
430 
431 	// Compare images
432 	{
433 		bool imagesOk = tcu::pixelThresholdCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, RGBA(1,1,1,1) + pixelFmt.getColorThreshold(), tcu::COMPARE_LOG_RESULT);
434 
435 		if (!imagesOk)
436 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
437 	}
438 }
439 
440 } // egl
441 } // deqp
442