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 Choose config reference implementation.
22 *//*--------------------------------------------------------------------*/
23
24#include "teglChooseConfigReference.hpp"
25
26#include "egluUtil.hpp"
27#include "egluConfigInfo.hpp"
28#include "egluStrUtil.hpp"
29#include "eglwLibrary.hpp"
30#include "eglwEnums.hpp"
31
32#include <algorithm>
33#include <vector>
34#include <map>
35
36namespace deqp
37{
38namespace egl
39{
40
41using namespace eglw;
42using eglu::ConfigInfo;
43
44enum Criteria
45{
46	CRITERIA_AT_LEAST = 0,
47	CRITERIA_EXACT,
48	CRITERIA_MASK,
49	CRITERIA_SPECIAL,
50
51	CRITERIA_LAST
52};
53
54enum SortOrder
55{
56	SORTORDER_NONE	= 0,
57	SORTORDER_SMALLER,
58	SORTORDER_SPECIAL,
59
60	SORTORDER_LAST
61};
62
63struct AttribRule
64{
65	EGLenum		name;
66	EGLint		value;
67	Criteria	criteria;
68	SortOrder	sortOrder;
69
70	AttribRule (void)
71		: name			(EGL_NONE)
72		, value			(EGL_NONE)
73		, criteria		(CRITERIA_LAST)
74		, sortOrder		(SORTORDER_LAST)
75	{
76	}
77
78	AttribRule (EGLenum name_, EGLint value_, Criteria criteria_, SortOrder sortOrder_)
79		: name			(name_)
80		, value			(value_)
81		, criteria		(criteria_)
82		, sortOrder		(sortOrder_)
83	{
84	}
85};
86
87class SurfaceConfig
88{
89private:
90	static int getCaveatRank (EGLenum caveat)
91	{
92		switch (caveat)
93		{
94			case EGL_NONE:					return 0;
95			case EGL_SLOW_CONFIG:			return 1;
96			case EGL_NON_CONFORMANT_CONFIG:	return 2;
97			default:
98				TCU_THROW(TestError, (std::string("Unknown config caveat: ") + eglu::getConfigCaveatStr(caveat).toString()).c_str());
99		}
100	}
101
102	static int getColorBufferTypeRank (EGLenum type)
103	{
104		switch (type)
105		{
106			case EGL_RGB_BUFFER:			return 0;
107			case EGL_LUMINANCE_BUFFER:		return 1;
108			case EGL_YUV_BUFFER_EXT:		return 2;
109			default:
110				TCU_THROW(TestError, (std::string("Unknown color buffer type: ") + eglu::getColorBufferTypeStr(type).toString()).c_str());
111		}
112	}
113
114	typedef bool (*CompareFunc) (const SurfaceConfig& a, const SurfaceConfig& b);
115
116	static bool compareCaveat (const SurfaceConfig& a, const SurfaceConfig& b)
117	{
118		return getCaveatRank((EGLenum)a.m_info.configCaveat) < getCaveatRank((EGLenum)b.m_info.configCaveat);
119	}
120
121	static bool compareColorBufferType (const SurfaceConfig& a, const SurfaceConfig& b)
122	{
123		return getColorBufferTypeRank((EGLenum)a.m_info.colorBufferType) < getColorBufferTypeRank((EGLenum)b.m_info.colorBufferType);
124	}
125
126	static bool compareColorBufferBits (const SurfaceConfig& a, const SurfaceConfig& b, const tcu::BVec4& specifiedRGBColors, const tcu::BVec2& specifiedLuminanceColors)
127	{
128		DE_ASSERT(a.m_info.colorBufferType == b.m_info.colorBufferType);
129		switch (a.m_info.colorBufferType)
130		{
131			case EGL_RGB_BUFFER:
132			{
133				const tcu::IVec4	mask	(specifiedRGBColors.cast<deInt32>());
134
135				return (a.m_info.redSize * mask[0] + a.m_info.greenSize * mask[1] + a.m_info.blueSize * mask[2] + a.m_info.alphaSize * mask[3])
136						> (b.m_info.redSize * mask[0] + b.m_info.greenSize * mask[1] + b.m_info.blueSize * mask[2] + b.m_info.alphaSize * mask[3]);
137			}
138
139			case EGL_LUMINANCE_BUFFER:
140			{
141				const tcu::IVec2	mask	(specifiedLuminanceColors.cast<deInt32>());
142
143				return (a.m_info.luminanceSize * mask[0] + a.m_info.alphaSize * mask[1]) > (b.m_info.luminanceSize * mask[0] + b.m_info.alphaSize * mask[1]);
144			}
145
146			case EGL_YUV_BUFFER_EXT:
147				// \todo [mika 2015-05-05] Sort YUV configs correctly. Currently all YUV configs are non-conformant and ordering can be relaxed.
148				return true;
149
150			default:
151				DE_ASSERT(DE_FALSE);
152				return true;
153		}
154	}
155
156	template <EGLenum Attribute>
157	static bool compareAttributeSmaller (const SurfaceConfig& a, const SurfaceConfig& b)
158	{
159		return a.getAttribute(Attribute) < b.getAttribute(Attribute);
160	}
161public:
162	SurfaceConfig (EGLConfig config, ConfigInfo &info)
163		: m_config(config)
164		, m_info(info)
165	{
166	}
167
168	EGLConfig getEglConfig (void) const
169	{
170		return m_config;
171	}
172
173	EGLint getAttribute (const EGLenum attribute) const
174	{
175		return m_info.getAttribute(attribute);
176	}
177
178	friend bool operator== (const SurfaceConfig& a, const SurfaceConfig& b)
179	{
180		for (std::map<EGLenum, AttribRule>::const_iterator iter = SurfaceConfig::defaultRules.begin(); iter != SurfaceConfig::defaultRules.end(); iter++)
181		{
182			const EGLenum attribute = iter->first;
183
184			if (a.getAttribute(attribute) != b.getAttribute(attribute)) return false;
185		}
186		return true;
187	}
188
189	bool compareTo (const SurfaceConfig& b, const tcu::BVec4& specifiedRGBColors, const tcu::BVec2& specifiedLuminanceColors) const
190	{
191		static const SurfaceConfig::CompareFunc compareFuncs[] =
192		{
193			SurfaceConfig::compareCaveat,
194			SurfaceConfig::compareColorBufferType,
195			DE_NULL, // SurfaceConfig::compareColorBufferBits,
196			SurfaceConfig::compareAttributeSmaller<EGL_BUFFER_SIZE>,
197			SurfaceConfig::compareAttributeSmaller<EGL_SAMPLE_BUFFERS>,
198			SurfaceConfig::compareAttributeSmaller<EGL_SAMPLES>,
199			SurfaceConfig::compareAttributeSmaller<EGL_DEPTH_SIZE>,
200			SurfaceConfig::compareAttributeSmaller<EGL_STENCIL_SIZE>,
201			SurfaceConfig::compareAttributeSmaller<EGL_ALPHA_MASK_SIZE>,
202			SurfaceConfig::compareAttributeSmaller<EGL_CONFIG_ID>
203		};
204
205		if (*this == b)
206			return false; // std::sort() can compare object to itself.
207
208		for (int ndx = 0; ndx < (int)DE_LENGTH_OF_ARRAY(compareFuncs); ndx++)
209		{
210			if (!compareFuncs[ndx])
211			{
212				if (compareColorBufferBits(*this, b, specifiedRGBColors, specifiedLuminanceColors))
213					return true;
214				else if (compareColorBufferBits(b, *this, specifiedRGBColors, specifiedLuminanceColors))
215					return false;
216
217				continue;
218			}
219
220			if (compareFuncs[ndx](*this, b))
221				return true;
222			else if (compareFuncs[ndx](b, *this))
223				return false;
224		}
225
226		TCU_FAIL("Unable to compare configs - duplicate ID?");
227	}
228
229	static const std::map<EGLenum, AttribRule> defaultRules;
230
231	static std::map<EGLenum, AttribRule> initAttribRules (void)
232	{
233		// \todo [2011-03-24 pyry] From EGL 1.4 spec - check that this is valid for other versions as well
234		std::map<EGLenum, AttribRule> rules;
235
236		//									Attribute									Default				Selection Criteria	Sort Order			Sort Priority
237		rules[EGL_BUFFER_SIZE]				= AttribRule(EGL_BUFFER_SIZE,				0,					CRITERIA_AT_LEAST,	SORTORDER_SMALLER);	//	4
238		rules[EGL_RED_SIZE]					= AttribRule(EGL_RED_SIZE,					0,					CRITERIA_AT_LEAST,	SORTORDER_SPECIAL);	//	3
239		rules[EGL_GREEN_SIZE]				= AttribRule(EGL_GREEN_SIZE,				0,					CRITERIA_AT_LEAST,	SORTORDER_SPECIAL);	//	3
240		rules[EGL_BLUE_SIZE]				= AttribRule(EGL_BLUE_SIZE,					0,					CRITERIA_AT_LEAST,	SORTORDER_SPECIAL);	//	3
241		rules[EGL_LUMINANCE_SIZE]			= AttribRule(EGL_LUMINANCE_SIZE,			0,					CRITERIA_AT_LEAST,	SORTORDER_SPECIAL);	//	3
242		rules[EGL_ALPHA_SIZE]				= AttribRule(EGL_ALPHA_SIZE,				0,					CRITERIA_AT_LEAST,	SORTORDER_SPECIAL);	//	3
243		rules[EGL_ALPHA_MASK_SIZE]			= AttribRule(EGL_ALPHA_MASK_SIZE,			0,					CRITERIA_AT_LEAST,	SORTORDER_SMALLER);	//	9
244		rules[EGL_BIND_TO_TEXTURE_RGB]		= AttribRule(EGL_BIND_TO_TEXTURE_RGB,		EGL_DONT_CARE,		CRITERIA_EXACT,		SORTORDER_NONE);
245		rules[EGL_BIND_TO_TEXTURE_RGBA]		= AttribRule(EGL_BIND_TO_TEXTURE_RGBA,		EGL_DONT_CARE,		CRITERIA_EXACT,		SORTORDER_NONE);
246		rules[EGL_COLOR_BUFFER_TYPE]		= AttribRule(EGL_COLOR_BUFFER_TYPE,			EGL_RGB_BUFFER,		CRITERIA_EXACT,		SORTORDER_NONE);	//	2
247		rules[EGL_CONFIG_CAVEAT]			= AttribRule(EGL_CONFIG_CAVEAT,				EGL_DONT_CARE,		CRITERIA_EXACT,		SORTORDER_SPECIAL);	//	1
248		rules[EGL_CONFIG_ID]				= AttribRule(EGL_CONFIG_ID,					EGL_DONT_CARE,		CRITERIA_EXACT,		SORTORDER_SMALLER);	//	11
249		rules[EGL_CONFORMANT]				= AttribRule(EGL_CONFORMANT,				0,					CRITERIA_MASK,		SORTORDER_NONE);
250		rules[EGL_DEPTH_SIZE]				= AttribRule(EGL_DEPTH_SIZE,				0,					CRITERIA_AT_LEAST,	SORTORDER_SMALLER);	//	7
251		rules[EGL_LEVEL]					= AttribRule(EGL_LEVEL,						0,					CRITERIA_EXACT,		SORTORDER_NONE);
252		rules[EGL_MATCH_NATIVE_PIXMAP]		= AttribRule(EGL_MATCH_NATIVE_PIXMAP,		EGL_NONE,			CRITERIA_SPECIAL,	SORTORDER_NONE);
253		rules[EGL_MAX_SWAP_INTERVAL]		= AttribRule(EGL_MAX_SWAP_INTERVAL,			EGL_DONT_CARE,		CRITERIA_EXACT,		SORTORDER_NONE);
254		rules[EGL_MIN_SWAP_INTERVAL]		= AttribRule(EGL_MIN_SWAP_INTERVAL,			EGL_DONT_CARE,		CRITERIA_EXACT,		SORTORDER_NONE);
255		rules[EGL_NATIVE_RENDERABLE]		= AttribRule(EGL_NATIVE_RENDERABLE,			EGL_DONT_CARE,		CRITERIA_EXACT,		SORTORDER_NONE);
256		rules[EGL_NATIVE_VISUAL_TYPE]		= AttribRule(EGL_NATIVE_VISUAL_TYPE,		EGL_DONT_CARE,		CRITERIA_EXACT,		SORTORDER_SPECIAL);	//	10
257		rules[EGL_RENDERABLE_TYPE]			= AttribRule(EGL_RENDERABLE_TYPE,			EGL_OPENGL_ES_BIT,	CRITERIA_MASK,		SORTORDER_NONE);
258		rules[EGL_SAMPLE_BUFFERS]			= AttribRule(EGL_SAMPLE_BUFFERS,			0,					CRITERIA_AT_LEAST,	SORTORDER_SMALLER);	//	5
259		rules[EGL_SAMPLES]					= AttribRule(EGL_SAMPLES,					0,					CRITERIA_AT_LEAST,	SORTORDER_SMALLER);	//	6
260		rules[EGL_STENCIL_SIZE]				= AttribRule(EGL_STENCIL_SIZE,				0,					CRITERIA_AT_LEAST,	SORTORDER_SMALLER);	//	8
261		rules[EGL_SURFACE_TYPE]				= AttribRule(EGL_SURFACE_TYPE,				EGL_WINDOW_BIT,		CRITERIA_MASK,		SORTORDER_NONE);
262		rules[EGL_TRANSPARENT_TYPE]			= AttribRule(EGL_TRANSPARENT_TYPE,			EGL_NONE,			CRITERIA_EXACT,		SORTORDER_NONE);
263		rules[EGL_TRANSPARENT_RED_VALUE]	= AttribRule(EGL_TRANSPARENT_RED_VALUE,		EGL_DONT_CARE,		CRITERIA_EXACT,		SORTORDER_NONE);
264		rules[EGL_TRANSPARENT_GREEN_VALUE]	= AttribRule(EGL_TRANSPARENT_GREEN_VALUE,	EGL_DONT_CARE,		CRITERIA_EXACT,		SORTORDER_NONE);
265		rules[EGL_TRANSPARENT_BLUE_VALUE]	= AttribRule(EGL_TRANSPARENT_BLUE_VALUE,	EGL_DONT_CARE,		CRITERIA_EXACT,		SORTORDER_NONE);
266
267		return rules;
268	}
269private:
270	EGLConfig m_config;
271	ConfigInfo m_info;
272};
273
274const std::map<EGLenum, AttribRule> SurfaceConfig::defaultRules = SurfaceConfig::initAttribRules();
275
276class CompareConfigs
277{
278public:
279	CompareConfigs (const tcu::BVec4& specifiedRGBColors, const tcu::BVec2& specifiedLuminanceColors)
280		: m_specifiedRGBColors			(specifiedRGBColors)
281		, m_specifiedLuminanceColors	(specifiedLuminanceColors)
282	{
283	}
284
285	bool operator() (const SurfaceConfig& a, const SurfaceConfig& b)
286	{
287		return a.compareTo(b, m_specifiedRGBColors, m_specifiedLuminanceColors);
288	}
289
290private:
291	const tcu::BVec4	m_specifiedRGBColors;
292	const tcu::BVec2	m_specifiedLuminanceColors;
293};
294
295class ConfigFilter
296{
297private:
298	std::map<EGLenum, AttribRule> m_rules;
299public:
300	ConfigFilter ()
301		: m_rules(SurfaceConfig::defaultRules)
302	{
303	}
304
305	void setValue (EGLenum name, EGLint value)
306	{
307		DE_ASSERT(SurfaceConfig::defaultRules.find(name) != SurfaceConfig::defaultRules.end());
308		m_rules[name].value = value;
309	}
310
311	void setValues (std::vector<std::pair<EGLenum, EGLint> > values)
312	{
313		for (size_t ndx = 0; ndx < values.size(); ndx++)
314		{
315			const EGLenum	name	= values[ndx].first;
316			const EGLint	value	= values[ndx].second;
317
318			setValue(name, value);
319		}
320	}
321
322	AttribRule getAttribute (EGLenum name)
323	{
324		DE_ASSERT(SurfaceConfig::defaultRules.find(name) != SurfaceConfig::defaultRules.end());
325		return m_rules[name];
326	}
327
328	bool isMatch (const SurfaceConfig& config)
329	{
330		for (std::map<EGLenum, AttribRule>::const_iterator iter = m_rules.begin(); iter != m_rules.end(); iter++)
331		{
332			const AttribRule rule = iter->second;
333
334			if (rule.value == EGL_DONT_CARE)
335				continue;
336			else if (rule.name == EGL_MATCH_NATIVE_PIXMAP)
337				TCU_CHECK(rule.value == EGL_NONE); // Not supported
338			else if (rule.name == EGL_TRANSPARENT_RED_VALUE || rule.name == EGL_TRANSPARENT_GREEN_VALUE || rule.name == EGL_TRANSPARENT_BLUE_VALUE)
339				continue;
340			else
341			{
342				const EGLint cfgValue = config.getAttribute(rule.name);
343
344				switch (rule.criteria)
345				{
346					case CRITERIA_EXACT:
347						if (rule.value != cfgValue)
348							return false;
349						break;
350
351					case CRITERIA_AT_LEAST:
352						if (rule.value > cfgValue)
353							return false;
354						break;
355
356					case CRITERIA_MASK:
357						if ((rule.value & cfgValue) != rule.value)
358							return false;
359						break;
360
361					default:
362						TCU_FAIL("Unknown criteria");
363				}
364			}
365		}
366
367		return true;
368	}
369
370	tcu::BVec4 getSpecifiedRGBColors (void)
371	{
372		const EGLenum bitAttribs[] =
373		{
374			EGL_RED_SIZE,
375			EGL_GREEN_SIZE,
376			EGL_BLUE_SIZE,
377			EGL_ALPHA_SIZE
378		};
379
380		tcu::BVec4 result;
381
382		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(bitAttribs); ndx++)
383		{
384			const EGLenum	attrib	= bitAttribs[ndx];
385			const EGLint	value	= getAttribute(attrib).value;
386
387			if (value != 0 && value != EGL_DONT_CARE)
388				result[ndx] = true;
389			else
390				result[ndx] = false;
391		}
392
393		return result;
394	}
395
396	tcu::BVec2 getSpecifiedLuminanceColors (void)
397	{
398		const EGLenum bitAttribs[] =
399		{
400			EGL_LUMINANCE_SIZE,
401			EGL_ALPHA_SIZE
402		};
403
404		tcu::BVec2 result;
405
406		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(bitAttribs); ndx++)
407		{
408			const EGLenum	attrib	= bitAttribs[ndx];
409			const EGLint	value	= getAttribute(attrib).value;
410
411			if (value != 0 && value != EGL_DONT_CARE)
412				result[ndx] = true;
413			else
414				result[ndx] = false;
415		}
416
417		return result;
418	}
419
420	std::vector<SurfaceConfig> filter (const std::vector<SurfaceConfig>& configs)
421	{
422		std::vector<SurfaceConfig> out;
423
424		for (std::vector<SurfaceConfig>::const_iterator iter = configs.begin(); iter != configs.end(); iter++)
425		{
426			if (isMatch(*iter)) out.push_back(*iter);
427		}
428
429		return out;
430	}
431};
432
433void chooseConfigReference (const Library& egl, EGLDisplay display, std::vector<EGLConfig>& dst, const std::vector<std::pair<EGLenum, EGLint> >& attributes)
434{
435	// Get all configs
436	std::vector<EGLConfig> eglConfigs = eglu::getConfigs(egl, display);
437
438	// Config infos
439	std::vector<ConfigInfo> configInfos;
440	configInfos.resize(eglConfigs.size());
441	for (size_t ndx = 0; ndx < eglConfigs.size(); ndx++)
442		eglu::queryConfigInfo(egl, display, eglConfigs[ndx], &configInfos[ndx]);
443
444	// Pair configs with info
445	std::vector<SurfaceConfig> configs;
446	for (size_t ndx = 0; ndx < eglConfigs.size(); ndx++)
447		configs.push_back(SurfaceConfig(eglConfigs[ndx], configInfos[ndx]));
448
449	// Filter configs
450	ConfigFilter configFilter;
451	configFilter.setValues(attributes);
452
453	std::vector<SurfaceConfig> filteredConfigs = configFilter.filter(configs);
454
455	// Sort configs
456	std::sort(filteredConfigs.begin(), filteredConfigs.end(), CompareConfigs(configFilter.getSpecifiedRGBColors(), configFilter.getSpecifiedLuminanceColors()));
457
458	// Write to dst list
459	dst.resize(filteredConfigs.size());
460	for (size_t ndx = 0; ndx < filteredConfigs.size(); ndx++)
461		dst[ndx] = filteredConfigs[ndx].getEglConfig();
462}
463
464} // egl
465} // deqp
466