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