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 Base class for rendering tests.
22 *//*--------------------------------------------------------------------*/
23
24#include "teglRenderCase.hpp"
25
26#include "teglSimpleConfigCase.hpp"
27
28#include "egluNativeDisplay.hpp"
29#include "egluNativeWindow.hpp"
30#include "egluNativePixmap.hpp"
31#include "egluUtil.hpp"
32
33#include "tcuRenderTarget.hpp"
34#include "tcuTestLog.hpp"
35#include "tcuCommandLine.hpp"
36
37#include "deStringUtil.hpp"
38#include "deUniquePtr.hpp"
39
40#include <algorithm>
41#include <iterator>
42#include <memory>
43#include <set>
44
45#include <EGL/eglext.h>
46
47#if !defined(EGL_OPENGL_ES3_BIT_KHR)
48#	define EGL_OPENGL_ES3_BIT_KHR	0x0040
49#endif
50#if !defined(EGL_CONTEXT_MAJOR_VERSION_KHR)
51#	define EGL_CONTEXT_MAJOR_VERSION_KHR EGL_CONTEXT_CLIENT_VERSION
52#endif
53
54using std::string;
55using std::vector;
56using std::set;
57
58using tcu::TestLog;
59
60namespace deqp
61{
62namespace egl
63{
64
65// \todo [2013-04-24 pyry] Should we instead store surface bit somewhere?
66template<class Derived, class Base>
67inline bool instanceOf (Base& obj)
68{
69	return dynamic_cast<Derived*>(&obj) != DE_NULL;
70}
71
72static void postSurface (tcu::egl::Surface& surface)
73{
74	const bool	isWindow	= instanceOf<tcu::egl::WindowSurface>(surface);
75	const bool	isPixmap	= instanceOf<tcu::egl::PixmapSurface>(surface);
76	const bool	isPbuffer	= instanceOf<tcu::egl::PbufferSurface>(surface);
77
78	DE_ASSERT((isWindow?1:0) + (isPixmap?1:0) + (isPbuffer?1:0) == 1);
79
80	if (isWindow)
81	{
82		tcu::egl::WindowSurface& window = static_cast<tcu::egl::WindowSurface&>(surface);
83		window.swapBuffers();
84	}
85	else if (isPixmap)
86	{
87		TCU_CHECK_EGL_CALL(eglWaitClient());
88	}
89	else
90	{
91		DE_ASSERT(isPbuffer);
92		DE_UNREF(isPbuffer);
93		TCU_CHECK_EGL_CALL(eglWaitClient());
94	}
95}
96
97// RenderCase
98
99RenderCase::RenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint apiMask, EGLint surfaceTypeMask, const vector<EGLint>& configIds)
100	: SimpleConfigCase	(eglTestCtx, name, description, configIds)
101	, m_apiMask			(apiMask)
102	, m_surfaceTypeMask	(surfaceTypeMask)
103{
104}
105
106RenderCase::~RenderCase (void)
107{
108}
109
110EGLint RenderCase::getSupportedApis (void)
111{
112	EGLint apiMask = 0;
113
114#if defined(DEQP_SUPPORT_GLES2)
115	apiMask |= EGL_OPENGL_ES2_BIT;
116#endif
117
118#if defined(DEQP_SUPPORT_GLES3)
119	apiMask |= EGL_OPENGL_ES3_BIT_KHR;
120#endif
121
122#if defined(DEQP_SUPPORT_GLES1)
123	apiMask |= EGL_OPENGL_ES_BIT;
124#endif
125
126#if defined(DEQP_SUPPORT_VG)
127	apiMask |= EGL_OPENVG_BIT;
128#endif
129
130	return apiMask;
131}
132
133void RenderCase::executeForConfig (tcu::egl::Display& defaultDisplay, EGLConfig config)
134{
135	tcu::TestLog&			log				= m_testCtx.getLog();
136	int						width			= 128;
137	int						height			= 128;
138	EGLint					configId		= defaultDisplay.getConfigAttrib(config, EGL_CONFIG_ID);
139	bool					isOk			= true;
140	string					failReason		= "";
141
142	if (m_surfaceTypeMask & EGL_WINDOW_BIT)
143	{
144		tcu::ScopedLogSection(log, (string("Config") + de::toString(configId) + "-Window").c_str(),
145										(string("Config ID ") + de::toString(configId) + ", window surface").c_str());
146
147		try
148		{
149			tcu::egl::Display&					display		= m_eglTestCtx.getDisplay();
150			de::UniquePtr<eglu::NativeWindow>	window		(m_eglTestCtx.createNativeWindow(display.getEGLDisplay(), config, DE_NULL, width, height, eglu::parseWindowVisibility(m_testCtx.getCommandLine())));
151			EGLSurface							eglSurface	= createWindowSurface(m_eglTestCtx.getNativeDisplay(), *window, display.getEGLDisplay(), config, DE_NULL);
152			tcu::egl::WindowSurface				surface		(display, eglSurface);
153
154			executeForSurface(display, surface, config);
155		}
156		catch (const tcu::TestError& e)
157		{
158			log << e;
159			isOk = false;
160			failReason = e.what();
161		}
162	}
163
164	if (m_surfaceTypeMask & EGL_PIXMAP_BIT)
165	{
166		tcu::ScopedLogSection(log, (string("Config") + de::toString(configId) + "-Pixmap").c_str(),
167										(string("Config ID ") + de::toString(configId) + ", pixmap surface").c_str());
168
169		try
170		{
171			tcu::egl::Display&					display		= m_eglTestCtx.getDisplay();
172			std::auto_ptr<eglu::NativePixmap>	pixmap		(m_eglTestCtx.createNativePixmap(display.getEGLDisplay(), config, DE_NULL, width, height));
173			EGLSurface							eglSurface	= createPixmapSurface(m_eglTestCtx.getNativeDisplay(), *pixmap, display.getEGLDisplay(), config, DE_NULL);
174			tcu::egl::PixmapSurface				surface		(display, eglSurface);
175
176			executeForSurface(display, surface, config);
177		}
178		catch (const tcu::TestError& e)
179		{
180			log << e;
181			isOk = false;
182			failReason = e.what();
183		}
184	}
185
186	if (m_surfaceTypeMask & EGL_PBUFFER_BIT)
187	{
188		tcu::ScopedLogSection(log, (string("Config") + de::toString(configId) + "-Pbuffer").c_str(),
189										(string("Config ID ") + de::toString(configId) + ", pbuffer surface").c_str());
190		try
191		{
192			EGLint surfaceAttribs[] =
193			{
194				EGL_WIDTH,	width,
195				EGL_HEIGHT,	height,
196				EGL_NONE
197			};
198
199			tcu::egl::PbufferSurface surface(defaultDisplay, config, surfaceAttribs);
200
201			executeForSurface(defaultDisplay, surface, config);
202		}
203		catch (const tcu::TestError& e)
204		{
205			log << e;
206			isOk = false;
207			failReason = e.what();
208		}
209	}
210
211	if (!isOk && m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
212		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, failReason.c_str());
213}
214
215// SingleContextRenderCase
216
217SingleContextRenderCase::SingleContextRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint apiMask, EGLint surfaceTypeMask, const std::vector<EGLint>& configIds)
218	: RenderCase(eglTestCtx, name, description, apiMask, surfaceTypeMask, configIds)
219{
220}
221
222SingleContextRenderCase::~SingleContextRenderCase (void)
223{
224}
225
226void SingleContextRenderCase::executeForSurface (tcu::egl::Display& display, tcu::egl::Surface& surface, EGLConfig config)
227{
228	EGLint				supportedApis	= getSupportedApis();
229	const EGLint		apis[]			= { EGL_OPENGL_ES2_BIT, EGL_OPENGL_ES3_BIT_KHR, EGL_OPENGL_ES_BIT, EGL_OPENVG_BIT };
230	tcu::TestLog&		log				= m_testCtx.getLog();
231
232	// Check if case is supported
233	if ((m_apiMask & supportedApis) != m_apiMask)
234		throw tcu::NotSupportedError("Client APIs not supported", "", __FILE__, __LINE__);
235
236	for (int apiNdx = 0; apiNdx < DE_LENGTH_OF_ARRAY(apis); apiNdx++)
237	{
238		EGLint apiBit = apis[apiNdx];
239
240		if ((apiBit & m_apiMask) == 0)
241			continue; // Skip this api.
242
243		EGLint			api		= EGL_NONE;
244		const char*		apiName	= DE_NULL;
245		vector<EGLint>	contextAttribs;
246
247		// Select api enum and build context attributes.
248		switch (apiBit)
249		{
250			case EGL_OPENGL_ES2_BIT:
251				api		= EGL_OPENGL_ES_API;
252				apiName	= "OpenGL ES 2.x";
253				contextAttribs.push_back(EGL_CONTEXT_CLIENT_VERSION);
254				contextAttribs.push_back(2);
255				break;
256
257			case EGL_OPENGL_ES3_BIT_KHR:
258				api		= EGL_OPENGL_ES_API;
259				apiName	= "OpenGL ES 3.x";
260				contextAttribs.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR);
261				contextAttribs.push_back(3);
262				break;
263
264			case EGL_OPENGL_ES_BIT:
265				api		= EGL_OPENGL_ES_API;
266				apiName	= "OpenGL ES 1.x";
267				contextAttribs.push_back(EGL_CONTEXT_CLIENT_VERSION);
268				contextAttribs.push_back(1);
269				break;
270
271			case EGL_OPENVG_BIT:
272				api		= EGL_OPENVG_API;
273				apiName	= "OpenVG";
274				break;
275
276			default:
277				DE_ASSERT(DE_FALSE);
278		}
279
280		contextAttribs.push_back(EGL_NONE);
281
282		log << TestLog::Message << apiName << TestLog::EndMessage;
283
284		tcu::egl::Context context(display, config, &contextAttribs[0], api);
285
286		context.makeCurrent(surface, surface);
287		executeForContext(display, context, surface, apiBit);
288
289		// Call SwapBuffers() / WaitClient() to finish rendering
290		postSurface(surface);
291	}
292}
293
294// MultiContextRenderCase
295
296MultiContextRenderCase::MultiContextRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const vector<EGLint>& configIds, int numContextsPerApi)
297	: RenderCase			(eglTestCtx, name, description, api, surfaceType, configIds)
298	, m_numContextsPerApi	(numContextsPerApi)
299{
300}
301
302MultiContextRenderCase::~MultiContextRenderCase (void)
303{
304}
305
306void MultiContextRenderCase::executeForSurface (tcu::egl::Display& display, tcu::egl::Surface& surface, EGLConfig config)
307{
308	vector<std::pair<EGLint, tcu::egl::Context*> > contexts;
309	contexts.reserve(3*m_numContextsPerApi); // 3 types of contexts at maximum.
310
311	try
312	{
313		// Create contexts that will participate in rendering.
314		for (int ndx = 0; ndx < m_numContextsPerApi; ndx++)
315		{
316			if (m_apiMask & EGL_OPENGL_ES2_BIT)
317			{
318				static const EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
319				contexts.push_back(std::make_pair(EGL_OPENGL_ES2_BIT, new tcu::egl::Context(display, config, &attribs[0], EGL_OPENGL_ES_API)));
320			}
321
322			if (m_apiMask & EGL_OPENGL_ES3_BIT_KHR)
323			{
324				static const EGLint attribs[] = { EGL_CONTEXT_MAJOR_VERSION_KHR, 3, EGL_NONE };
325				contexts.push_back(std::make_pair(EGL_OPENGL_ES3_BIT_KHR, new tcu::egl::Context(display, config, &attribs[0], EGL_OPENGL_ES_API)));
326			}
327
328			if (m_apiMask & EGL_OPENGL_ES_BIT)
329			{
330				static const EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 1, EGL_NONE };
331				contexts.push_back(std::make_pair(EGL_OPENGL_ES_BIT, new tcu::egl::Context(display, config, &attribs[0], EGL_OPENGL_ES_API)));
332			}
333
334			if (m_apiMask & EGL_OPENVG_BIT)
335			{
336				static const EGLint attribs[] = { EGL_NONE };
337				contexts.push_back(std::make_pair(EGL_OPENVG_BIT, new tcu::egl::Context(display, config, &attribs[0], EGL_OPENVG_API)));
338			}
339		}
340
341		// Execute for contexts.
342		executeForContexts(display, surface, config, contexts);
343	}
344	catch (const std::exception&)
345	{
346		// Make sure all contexts have been destroyed.
347		for (vector<std::pair<EGLint, tcu::egl::Context*> >::iterator i = contexts.begin(); i != contexts.end(); i++)
348			delete i->second;
349		throw;
350	}
351
352	// Destroy contexts.
353	for (vector<std::pair<EGLint, tcu::egl::Context*> >::iterator i = contexts.begin(); i != contexts.end(); i++)
354		delete i->second;
355}
356
357// Utilities
358
359void addRenderConfigIdSet (
360	vector<RenderConfigIdSet>&			configSets,
361	const vector<eglu::ConfigInfo>&		configInfos,
362	const eglu::FilterList&				baseFilters,
363	const char*							name,
364	tcu::RGBA							colorBits,
365	EGLint								surfaceType)
366{
367	eglu::FilterList filters = baseFilters;
368	filters << (eglu::ConfigColorBits() == colorBits) << (eglu::ConfigSurfaceType() & surfaceType);
369
370	vector<EGLint> matchingConfigs;
371
372	for (vector<eglu::ConfigInfo>::const_iterator configIter = configInfos.begin(); configIter != configInfos.end(); configIter++)
373	{
374		if (!filters.match(*configIter))
375			continue;
376
377		matchingConfigs.push_back(configIter->configId);
378	}
379
380	configSets.push_back(RenderConfigIdSet(name, "", matchingConfigs, surfaceType));
381}
382
383void addRenderConfigIdSet (
384	vector<RenderConfigIdSet>&			configSets,
385	const vector<eglu::ConfigInfo>&		configInfos,
386	const eglu::FilterList&				baseFilters,
387	const char*							name,
388	tcu::RGBA							colorBits)
389{
390	addRenderConfigIdSet(configSets, configInfos, baseFilters, (string(name) + "_window").c_str(),	colorBits, EGL_WINDOW_BIT);
391	addRenderConfigIdSet(configSets, configInfos, baseFilters, (string(name) + "_pixmap").c_str(),	colorBits, EGL_PIXMAP_BIT);
392	addRenderConfigIdSet(configSets, configInfos, baseFilters, (string(name) + "_pbuffer").c_str(),	colorBits, EGL_PBUFFER_BIT);
393}
394
395void getDefaultRenderConfigIdSets (vector<RenderConfigIdSet>& configSets, const vector<eglu::ConfigInfo>& configInfos, const eglu::FilterList& baseFilters)
396{
397	using tcu::RGBA;
398
399	addRenderConfigIdSet(configSets, configInfos, baseFilters, "rgb565",	RGBA(5, 6, 5, 0));
400	addRenderConfigIdSet(configSets, configInfos, baseFilters, "rgb888",	RGBA(8, 8, 8, 0));
401	addRenderConfigIdSet(configSets, configInfos, baseFilters, "rgba4444",	RGBA(4, 4, 4, 4));
402	addRenderConfigIdSet(configSets, configInfos, baseFilters, "rgba5551",	RGBA(5, 5, 5, 1));
403	addRenderConfigIdSet(configSets, configInfos, baseFilters, "rgba8888",	RGBA(8, 8, 8, 8));
404
405	// Add other config ids to "other" set
406	{
407		set<EGLint>		usedConfigs;
408		vector<EGLint>	otherCfgSet;
409
410		for (vector<RenderConfigIdSet>::const_iterator setIter = configSets.begin(); setIter != configSets.end(); setIter++)
411		{
412			const vector<EGLint>& setCfgs = setIter->getConfigIds();
413			for (vector<EGLint>::const_iterator i = setCfgs.begin(); i != setCfgs.end(); i++)
414				usedConfigs.insert(*i);
415		}
416
417		for (vector<eglu::ConfigInfo>::const_iterator cfgIter = configInfos.begin(); cfgIter != configInfos.end(); cfgIter++)
418		{
419			if (!baseFilters.match(*cfgIter))
420				continue;
421
422			EGLint id = cfgIter->configId;
423
424			if (usedConfigs.find(id) == usedConfigs.end())
425				otherCfgSet.push_back(id);
426		}
427
428		configSets.push_back(RenderConfigIdSet("other", "", otherCfgSet, EGL_WINDOW_BIT|EGL_PIXMAP_BIT|EGL_PBUFFER_BIT));
429	}
430}
431
432} // egl
433} // deqp
434