• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
ResizeTest(EglTestContext & eglTestCtx,const ResizeParams & params)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:
surfaceType(void) const109 	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 
getEGLConfig(const EGLDisplay eglDisplay,EGLenum surfaceType)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 
init(void)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 
deinit(void)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 
resize(IVec2 size)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:
ChangeSurfaceSizeCase(EglTestContext & eglTestCtx,const ResizeParams & params)205 					ChangeSurfaceSizeCase	(EglTestContext&		eglTestCtx,
206 											 const ResizeParams&	params)
207 						: ResizeTest(eglTestCtx, params) {}
208 
209 	IterateResult	iterate					(void);
210 };
211 
drawRectangle(const glw::Functions & gl,IVec2 pos,IVec2 size,Vec3 color)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 
initSurface(const glw::Functions & gl,IVec2 oldSize)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 
compareRectangles(const ConstPixelBufferAccess & rectA,const ConstPixelBufferAccess & rectB)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.
compareCorners(const Surface & oldSurface,const Surface & newSurface)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 
readSurface(const glw::Functions & gl,IVec2 size)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>
hasBits(T bitSet,T requiredBits)300 inline bool hasBits (T bitSet, T requiredBits)
301 {
302 	return (bitSet & requiredBits) == requiredBits;
303 }
304 
getNativeSurfaceSize(const NativeWindow & nativeWindow,IVec2 reqSize)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 
checkSurfaceSize(EGLDisplay eglDisplay,EGLSurface eglSurface,const NativeWindow & nativeWindow,IVec2 reqSize,ResultCollector & status)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 
iterate(void)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:
PreserveBackBufferCase(EglTestContext & eglTestCtx,const ResizeParams & params)356 					PreserveBackBufferCase	(EglTestContext&		eglTestCtx,
357 											 const ResizeParams&	params)
358 						: ResizeTest(eglTestCtx, params) {}
359 
360 	IterateResult	iterate					(void);
361 	EGLenum			surfaceType				(void) const;
362 };
363 
surfaceType(void) const364 EGLenum PreserveBackBufferCase::surfaceType (void) const
365 {
366 	return EGL_WINDOW_BIT | EGL_SWAP_BEHAVIOR_PRESERVED_BIT;
367 }
368 
iterate(void)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:
UpdateResolutionCase(EglTestContext & eglTestCtx,const ResizeParams & params)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 
ivVec2(const IVec2 & vec)428 IvVec2 ivVec2 (const IVec2& vec)
429 {
430 	return IvVec2(double(vec.x()), double(vec.y()));
431 }
432 
approximateInt(int i)433 Interval approximateInt (int i)
434 {
435 	return (Interval(i) + Interval(-1.0, 1.0)) & Interval(0.0, TCU_INFINITY);
436 }
437 
getNativePixelsPerInch(void)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 
iterate(void)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 
ResizeTests(EglTestContext & eglTestCtx)494 ResizeTests::ResizeTests (EglTestContext& eglTestCtx)
495 	: TestCaseGroup(eglTestCtx, "resize", "Tests resizing the native surface")
496 {
497 }
498 
499 template <class Case>
createCaseGroup(EglTestContext & eglTestCtx,const string & name,const string & desc)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 
init(void)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