teglResizeTests.cpp revision ab44fc6da25bb126919615ad2ded101695251161
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 Tests for resizing the native window of a surface.
22 *//*--------------------------------------------------------------------*/
23
24#include "teglResizeTests.hpp"
25
26#include "teglSimpleConfigCase.hpp"
27
28#include "tcuImageCompare.hpp"
29#include "tcuSurface.hpp"
30#include "tcuPlatform.hpp"
31#include "tcuTestLog.hpp"
32#include "tcuInterval.hpp"
33#include "tcuTextureUtil.hpp"
34#include "tcuResultCollector.hpp"
35
36#include "egluNativeDisplay.hpp"
37#include "egluNativeWindow.hpp"
38#include "egluNativePixmap.hpp"
39#include "egluUnique.hpp"
40#include "egluUtil.hpp"
41
42#include "eglwLibrary.hpp"
43#include "eglwEnums.hpp"
44
45#include "gluDefs.hpp"
46#include "glwFunctions.hpp"
47#include "glwEnums.hpp"
48
49#include "tcuTestLog.hpp"
50#include "tcuVector.hpp"
51
52#include "deThread.h"
53#include "deUniquePtr.hpp"
54
55#include <sstream>
56
57namespace deqp
58{
59namespace egl
60{
61
62using std::vector;
63using std::string;
64using std::ostringstream;
65using de::MovePtr;
66using tcu::CommandLine;
67using tcu::ConstPixelBufferAccess;
68using tcu::Interval;
69using tcu::IVec2;
70using tcu::Vec3;
71using tcu::Vec4;
72using tcu::UVec4;
73using tcu::ResultCollector;
74using tcu::Surface;
75using tcu::TestLog;
76using eglu::AttribMap;
77using eglu::NativeDisplay;
78using eglu::NativeWindow;
79using eglu::ScopedCurrentContext;
80using eglu::UniqueSurface;
81using eglu::UniqueContext;
82using eglu::NativeWindowFactory;
83using eglu::WindowParams;
84using namespace eglw;
85
86typedef	eglu::WindowParams::Visibility	Visibility;
87typedef	TestCase::IterateResult			IterateResult;
88
89struct ResizeParams
90{
91	string	name;
92	string	description;
93	IVec2	oldSize;
94	IVec2	newSize;
95};
96
97class ResizeTest : public TestCase
98{
99public:
100								ResizeTest	(EglTestContext&		eglTestCtx,
101											 const ResizeParams&	params)
102									: TestCase	(eglTestCtx,
103												 params.name.c_str(),
104												 params.description.c_str())
105									, m_oldSize	(params.oldSize)
106									, m_newSize	(params.newSize)
107									, m_display	(EGL_NO_DISPLAY)
108									, m_config	(DE_NULL)
109									, m_log		(m_testCtx.getLog())
110									, m_status	(m_log) {}
111
112	void						init		(void);
113	void						deinit		(void);
114
115protected:
116	virtual EGLenum				surfaceType	(void) const { return EGL_WINDOW_BIT; }
117	void						resize		(IVec2 size);
118
119	const IVec2					m_oldSize;
120	const IVec2					m_newSize;
121	EGLDisplay					m_display;
122	EGLConfig					m_config;
123	MovePtr<NativeWindow>		m_nativeWindow;
124	MovePtr<UniqueSurface>		m_surface;
125	MovePtr<UniqueContext>		m_context;
126	TestLog&					m_log;
127	ResultCollector				m_status;
128	glw::Functions				m_gl;
129};
130
131EGLConfig getEGLConfig (const Library& egl, const EGLDisplay eglDisplay, EGLenum surfaceType)
132{
133	AttribMap attribMap;
134
135	attribMap[EGL_SURFACE_TYPE]		= surfaceType;
136	attribMap[EGL_RENDERABLE_TYPE]	= EGL_OPENGL_ES2_BIT;
137
138	return eglu::chooseSingleConfig(egl, eglDisplay, attribMap);
139}
140
141void ResizeTest::init (void)
142{
143	TestCase::init();
144
145	const Library&				egl				= m_eglTestCtx.getLibrary();
146	const CommandLine&			cmdLine			= m_testCtx.getCommandLine();
147	const EGLDisplay			eglDisplay		= eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay());
148	const EGLConfig				eglConfig		= getEGLConfig(egl, eglDisplay, surfaceType());
149	const EGLint				ctxAttribs[]	=
150	{
151		EGL_CONTEXT_CLIENT_VERSION, 2,
152		EGL_NONE
153	};
154	EGLContext					eglContext		= egl.createContext(eglDisplay,
155																   eglConfig,
156																   EGL_NO_CONTEXT,
157																   ctxAttribs);
158	EGLU_CHECK_MSG(egl, "eglCreateContext()");
159	MovePtr<UniqueContext>		context			(new UniqueContext(egl, eglDisplay, eglContext));
160	const EGLint				configId		= eglu::getConfigAttribInt(egl,
161																		   eglDisplay,
162																		   eglConfig,
163																		   EGL_CONFIG_ID);
164	const Visibility			visibility		= eglu::parseWindowVisibility(cmdLine);
165	NativeDisplay&				nativeDisplay	= m_eglTestCtx.getNativeDisplay();
166	const NativeWindowFactory*	windowFactory	= eglu::selectNativeWindowFactory(m_eglTestCtx.getNativeDisplayFactory(),
167																				  cmdLine);
168
169	if (!windowFactory)
170		TCU_THROW(NotSupportedError, "Windows not supported");
171
172	const WindowParams			windowParams	(m_oldSize.x(), m_oldSize.y(), visibility);
173	MovePtr<NativeWindow>		nativeWindow	(windowFactory->createWindow(&nativeDisplay,
174																			 eglDisplay,
175																			 eglConfig,
176																			 DE_NULL,
177																			 windowParams));
178	const EGLSurface			eglSurface		= eglu::createWindowSurface(nativeDisplay,
179																			*nativeWindow,
180																			eglDisplay,
181																			eglConfig,
182																			DE_NULL);
183	MovePtr<UniqueSurface>		surface			(new UniqueSurface(egl, eglDisplay, eglSurface));
184
185	m_log << TestLog::Message
186		  << "Chose EGLConfig with id " << configId << ".\n"
187		  << "Created initial surface with size " << m_oldSize
188		  << TestLog::EndMessage;
189
190	m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2, 0));
191	m_config		= eglConfig;
192	m_surface		= surface;
193	m_context		= context;
194	m_display		= eglDisplay;
195	m_nativeWindow	= nativeWindow;
196	EGLU_CHECK_MSG(egl, "init");
197}
198
199void ResizeTest::deinit (void)
200{
201	if (m_display != EGL_NO_DISPLAY)
202		m_eglTestCtx.getLibrary().terminate(m_display);
203
204	m_config		= DE_NULL;
205	m_display		= EGL_NO_DISPLAY;
206	m_context.clear();
207	m_surface.clear();
208	m_nativeWindow.clear();
209}
210
211void ResizeTest::resize (IVec2 size)
212{
213	m_nativeWindow->setSurfaceSize(size);
214	m_testCtx.getPlatform().processEvents();
215	m_log << TestLog::Message
216		  << "Resized surface to size " << size
217		  << TestLog::EndMessage;
218}
219
220class ChangeSurfaceSizeCase : public ResizeTest
221{
222public:
223					ChangeSurfaceSizeCase	(EglTestContext&		eglTestCtx,
224											 const ResizeParams&	params)
225						: ResizeTest(eglTestCtx, params) {}
226
227	IterateResult	iterate					(void);
228};
229
230void drawRectangle (const glw::Functions& gl, IVec2 pos, IVec2 size, Vec3 color)
231{
232	gl.clearColor(color.x(), color.y(), color.z(), 1.0);
233	gl.scissor(pos.x(), pos.y(), size.x(), size.y());
234	gl.enable(GL_SCISSOR_TEST);
235	gl.clear(GL_COLOR_BUFFER_BIT);
236	gl.disable(GL_SCISSOR_TEST);
237	GLU_EXPECT_NO_ERROR(gl.getError(),
238						"Rectangle drawing with glScissor and glClear failed.");
239}
240
241void initSurface (const glw::Functions& gl, IVec2 oldSize)
242{
243	const Vec3	frameColor	(0.0f, 0.0f, 1.0f);
244	const Vec3	fillColor	(1.0f, 0.0f, 0.0f);
245	const Vec3	markColor	(0.0f, 1.0f, 0.0f);
246
247	drawRectangle(gl, IVec2(0, 0), oldSize, frameColor);
248	drawRectangle(gl, IVec2(2, 2), oldSize - IVec2(4, 4), fillColor);
249
250	drawRectangle(gl, IVec2(0, 0), IVec2(8, 4), markColor);
251	drawRectangle(gl, oldSize - IVec2(16, 16), IVec2(8, 4), markColor);
252	drawRectangle(gl, IVec2(0, oldSize.y() - 16), IVec2(8, 4), markColor);
253	drawRectangle(gl, IVec2(oldSize.x() - 16, 0), IVec2(8, 4), markColor);
254}
255
256bool compareRectangles (const ConstPixelBufferAccess& rectA,
257						const ConstPixelBufferAccess& rectB)
258{
259	const int width		= rectA.getWidth();
260	const int height	= rectA.getHeight();
261	const int depth		= rectA.getDepth();
262
263	if (rectB.getWidth() != width || rectB.getHeight() != height || rectB.getDepth() != depth)
264		return false;
265
266	for (int z = 0; z < depth; ++z)
267		for (int y = 0; y < height; ++y)
268			for (int x = 0; x < width; ++x)
269				if (rectA.getPixel(x, y, z) != rectB.getPixel(x, y, z))
270					return false;
271
272	return true;
273}
274
275// Check whether `oldSurface` and `newSurface` share a common corner.
276bool compareCorners (const Surface& oldSurface, const Surface& newSurface)
277{
278	const int	oldWidth	= oldSurface.getWidth();
279	const int	oldHeight	= oldSurface.getHeight();
280	const int	newWidth	= newSurface.getWidth();
281	const int	newHeight	= newSurface.getHeight();
282	const int	minWidth	= de::min(oldWidth, newWidth);
283	const int	minHeight	= de::min(oldHeight, newHeight);
284
285	for (int xCorner = 0; xCorner < 2; ++xCorner)
286	{
287		const int oldX = xCorner == 0 ? 0 : oldWidth - minWidth;
288		const int newX = xCorner == 0 ? 0 : newWidth - minWidth;
289
290		for (int yCorner = 0; yCorner < 2; ++yCorner)
291		{
292			const int				oldY		= yCorner == 0 ? 0 : oldHeight - minHeight;
293			const int				newY		= yCorner == 0 ? 0 : newHeight - minHeight;
294			ConstPixelBufferAccess	oldAccess	=
295				getSubregion(oldSurface.getAccess(), oldX, oldY, minWidth, minHeight);
296			ConstPixelBufferAccess	newAccess	=
297				getSubregion(newSurface.getAccess(), newX, newY, minWidth, minHeight);
298
299			if (compareRectangles(oldAccess, newAccess))
300				return true;
301		}
302	}
303
304	return false;
305}
306
307Surface readSurface (const glw::Functions& gl, IVec2 size)
308{
309	Surface ret (size.x(), size.y());
310	gl.readPixels(0, 0, size.x(), size.y(), GL_RGBA, GL_UNSIGNED_BYTE,
311				  ret.getAccess().getDataPtr());
312
313	GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels() failed");
314	return ret;
315}
316
317template <typename T>
318inline bool hasBits (T bitSet, T requiredBits)
319{
320	return (bitSet & requiredBits) == requiredBits;
321}
322
323IVec2 getNativeSurfaceSize (const NativeWindow& nativeWindow,
324							IVec2				reqSize)
325{
326	if (hasBits(nativeWindow.getCapabilities(), NativeWindow::CAPABILITY_GET_SURFACE_SIZE))
327		return nativeWindow.getSurfaceSize();
328	return reqSize; // assume we got the requested size
329}
330
331IVec2 checkSurfaceSize (const Library&		egl,
332						EGLDisplay			eglDisplay,
333						EGLSurface			eglSurface,
334						const NativeWindow&	nativeWindow,
335						IVec2				reqSize,
336						ResultCollector&	status)
337{
338	const IVec2		nativeSize	= getNativeSurfaceSize(nativeWindow, reqSize);
339	IVec2			eglSize		= eglu::getSurfaceSize(egl, eglDisplay, eglSurface);
340	ostringstream	oss;
341
342	oss << "Size of EGL surface " << eglSize
343		<< " differs from size of native window " << nativeSize;
344	status.check(eglSize == nativeSize, oss.str());
345
346	return eglSize;
347}
348
349IterateResult ChangeSurfaceSizeCase::iterate (void)
350{
351	const Library&			egl			= m_eglTestCtx.getLibrary();
352	Surface					oldSurface;
353	Surface					newSurface;
354	ScopedCurrentContext	currentCtx	(egl, m_display, **m_surface, **m_surface, **m_context);
355	IVec2					oldEglSize	= checkSurfaceSize(egl,
356														   m_display,
357														   **m_surface,
358														   *m_nativeWindow,
359														   m_oldSize,
360														   m_status);
361
362	initSurface(m_gl, oldEglSize);
363
364	this->resize(m_newSize);
365
366	egl.swapBuffers(m_display, **m_surface);
367	EGLU_CHECK_MSG(egl, "eglSwapBuffers()");
368	checkSurfaceSize(egl, m_display, **m_surface, *m_nativeWindow, m_newSize, m_status);
369
370	m_status.setTestContextResult(m_testCtx);
371	return STOP;
372}
373
374class PreserveBackBufferCase : public ResizeTest
375{
376public:
377					PreserveBackBufferCase	(EglTestContext&		eglTestCtx,
378											 const ResizeParams&	params)
379						: ResizeTest(eglTestCtx, params) {}
380
381	IterateResult	iterate					(void);
382	EGLenum			surfaceType				(void) const;
383};
384
385EGLenum PreserveBackBufferCase::surfaceType (void) const
386{
387	return EGL_WINDOW_BIT | EGL_SWAP_BEHAVIOR_PRESERVED_BIT;
388}
389
390IterateResult PreserveBackBufferCase::iterate (void)
391{
392	const Library&			egl			= m_eglTestCtx.getLibrary();
393	ScopedCurrentContext	currentCtx	(egl, m_display, **m_surface, **m_surface, **m_context);
394
395	EGLU_CHECK_CALL(egl, surfaceAttrib(m_display, **m_surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED));
396
397	GLU_EXPECT_NO_ERROR(m_gl.getError(), "GL state erroneous upon initialization!");
398
399	{
400		const IVec2 oldEglSize = eglu::getSurfaceSize(egl, m_display, **m_surface);
401		initSurface(m_gl, oldEglSize);
402
403		m_gl.finish();
404		GLU_EXPECT_NO_ERROR(m_gl.getError(), "glFinish() failed");
405
406		{
407			const Surface oldSurface = readSurface(m_gl, oldEglSize);
408
409			egl.swapBuffers(m_display, **m_surface);
410			this->resize(m_newSize);
411			egl.swapBuffers(m_display, **m_surface);
412			EGLU_CHECK_MSG(egl, "eglSwapBuffers()");
413
414			{
415				const IVec2		newEglSize	= eglu::getSurfaceSize(egl, m_display, **m_surface);
416				const Surface	newSurface	= readSurface(m_gl, newEglSize);
417
418				m_log << TestLog::ImageSet("Corner comparison",
419										   "Comparing old and new surfaces at all corners")
420					  << TestLog::Image("Before resizing", "Before resizing", oldSurface)
421					  << TestLog::Image("After resizing", "After resizing", newSurface)
422					  << TestLog::EndImageSet;
423
424				m_status.check(compareCorners(oldSurface, newSurface),
425							   "Resizing the native window changed the contents of "
426							   "the EGL surface");
427			}
428		}
429	}
430
431	m_status.setTestContextResult(m_testCtx);
432	return STOP;
433}
434
435typedef tcu::Vector<Interval, 2> IvVec2;
436
437class UpdateResolutionCase : public ResizeTest
438{
439public:
440					UpdateResolutionCase	(EglTestContext&		eglTestCtx,
441											 const ResizeParams&	params)
442						: ResizeTest(eglTestCtx, params) {}
443
444	IterateResult	iterate					(void);
445
446private:
447	IvVec2			getNativePixelsPerInch	(void);
448};
449
450IvVec2 ivVec2 (const IVec2& vec)
451{
452	return IvVec2(double(vec.x()), double(vec.y()));
453}
454
455Interval approximateInt (int i)
456{
457	return (Interval(i) + Interval(-1.0, 1.0)) & Interval(0.0, TCU_INFINITY);
458}
459
460IvVec2 UpdateResolutionCase::getNativePixelsPerInch	(void)
461{
462	const Library&	egl			= m_eglTestCtx.getLibrary();
463	const int		inchPer10km	= 254 * EGL_DISPLAY_SCALING;
464	const IVec2		bufSize		= eglu::getSurfaceSize(egl, m_display, **m_surface);
465	const IVec2		winSize		= m_nativeWindow->getScreenSize();
466	const IVec2		bufPp10km	= eglu::getSurfaceResolution(egl, m_display, **m_surface);
467	const Interval	margin		(-1.0, 1.0); // The resolution may be rounded
468	const IvVec2	bufPpiI		= (IvVec2(approximateInt(bufPp10km.x()),
469										  approximateInt(bufPp10km.y()))
470								   / Interval(inchPer10km));
471	const IvVec2	winPpiI		= ivVec2(winSize) * bufPpiI / ivVec2(bufSize);
472	const IVec2		winPpi		(int(winPpiI.x().midpoint()), int(winPpiI.y().midpoint()));
473
474	m_log << TestLog::Message
475		  << "EGL surface size: "							<< bufSize		<< "\n"
476		  << "EGL surface pixel density (pixels / 10 km): "	<< bufPp10km	<< "\n"
477		  << "Native window size: "							<< winSize		<< "\n"
478		  << "Native pixel density (ppi): "					<< winPpi		<< "\n"
479		  << TestLog::EndMessage;
480
481	m_status.checkResult(bufPp10km.x() >= 1 && bufPp10km.y() >= 1,
482						 QP_TEST_RESULT_QUALITY_WARNING,
483						 "Surface pixel density is less than one pixel per 10 km. "
484						 "Is the surface really visible from space?");
485
486	return winPpiI;
487}
488
489IterateResult UpdateResolutionCase::iterate (void)
490{
491	const Library&			egl			= m_eglTestCtx.getLibrary();
492	ScopedCurrentContext	currentCtx	(egl, m_display, **m_surface, **m_surface, **m_context);
493
494	if (!hasBits(m_nativeWindow->getCapabilities(),
495				 NativeWindow::CAPABILITY_GET_SCREEN_SIZE))
496		TCU_THROW(NotSupportedError, "Unable to determine surface size in screen pixels");
497
498	{
499		const IVec2 oldEglSize = eglu::getSurfaceSize(egl, m_display, **m_surface);
500		initSurface(m_gl, oldEglSize);
501	}
502	{
503		const IvVec2 oldPpi = this->getNativePixelsPerInch();
504		this->resize(m_newSize);
505		EGLU_CHECK_CALL(egl, swapBuffers(m_display, **m_surface));
506		{
507			const IvVec2 newPpi = this->getNativePixelsPerInch();
508			m_status.check(oldPpi.x().intersects(newPpi.x()) &&
509						   oldPpi.y().intersects(newPpi.y()),
510						   "Window PPI differs after resizing");
511		}
512	}
513
514	m_status.setTestContextResult(m_testCtx);
515	return STOP;
516}
517
518ResizeTests::ResizeTests (EglTestContext& eglTestCtx)
519	: TestCaseGroup(eglTestCtx, "resize", "Tests resizing the native surface")
520{
521}
522
523template <class Case>
524TestCaseGroup* createCaseGroup(EglTestContext&	eglTestCtx,
525							   const string&	name,
526							   const string&	desc)
527{
528	const ResizeParams		params[]	=
529	{
530		{ "shrink",			"Shrink in both dimensions",
531		  IVec2(128, 128),	IVec2(32, 32) },
532		{ "grow",			"Grow in both dimensions",
533		  IVec2(32, 32),	IVec2(128, 128) },
534		{ "stretch_width",	"Grow horizontally, shrink vertically",
535		  IVec2(32, 128),	IVec2(128, 32) },
536		{ "stretch_height",	"Grow vertically, shrink horizontally",
537		  IVec2(128, 32),	IVec2(32, 128) },
538	};
539	TestCaseGroup* const	group		=
540		new TestCaseGroup(eglTestCtx, name.c_str(), desc.c_str());
541
542	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(params); ++ndx)
543		group->addChild(new Case(eglTestCtx, params[ndx]));
544
545	return group;
546}
547
548void ResizeTests::init (void)
549{
550	addChild(createCaseGroup<ChangeSurfaceSizeCase>(m_eglTestCtx,
551													"surface_size",
552													"EGL surface size update"));
553	addChild(createCaseGroup<PreserveBackBufferCase>(m_eglTestCtx,
554													 "back_buffer",
555													 "Back buffer contents"));
556	addChild(createCaseGroup<UpdateResolutionCase>(m_eglTestCtx,
557												   "pixel_density",
558												   "Pixel density"));
559}
560
561} // egl
562} // deqp
563