summaryrefslogtreecommitdiff
path: root/modules/egl/teglResizeTests.cpp
blob: fd250e4f51c1f864a629c307d49dcfbe25a4a405 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
/*-------------------------------------------------------------------------
 * drawElements Quality Program EGL Module
 * ---------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Tests for resizing the native window of a surface.
 *//*--------------------------------------------------------------------*/

#include "teglResizeTests.hpp"

#include "teglSimpleConfigCase.hpp"

#include "tcuImageCompare.hpp"
#include "tcuSurface.hpp"
#include "tcuPlatform.hpp"
#include "tcuTestLog.hpp"
#include "tcuInterval.hpp"
#include "tcuTextureUtil.hpp"
#include "tcuResultCollector.hpp"

#include "egluNativeDisplay.hpp"
#include "egluNativeWindow.hpp"
#include "egluNativePixmap.hpp"
#include "egluUnique.hpp"
#include "egluUtil.hpp"

#include "eglwLibrary.hpp"
#include "eglwEnums.hpp"

#include "gluDefs.hpp"
#include "glwFunctions.hpp"
#include "glwEnums.hpp"

#include "tcuTestLog.hpp"
#include "tcuVector.hpp"

#include "deThread.h"
#include "deUniquePtr.hpp"

#include <sstream>

namespace deqp
{
namespace egl
{

using std::vector;
using std::string;
using std::ostringstream;
using de::MovePtr;
using tcu::CommandLine;
using tcu::ConstPixelBufferAccess;
using tcu::Interval;
using tcu::IVec2;
using tcu::Vec3;
using tcu::Vec4;
using tcu::UVec4;
using tcu::ResultCollector;
using tcu::Surface;
using tcu::TestLog;
using eglu::AttribMap;
using eglu::NativeDisplay;
using eglu::NativeWindow;
using eglu::ScopedCurrentContext;
using eglu::UniqueSurface;
using eglu::UniqueContext;
using eglu::NativeWindowFactory;
using eglu::WindowParams;
using namespace eglw;

typedef	eglu::WindowParams::Visibility	Visibility;
typedef	TestCase::IterateResult			IterateResult;

struct ResizeParams
{
	string	name;
	string	description;
	IVec2	oldSize;
	IVec2	newSize;
};

class ResizeTest : public TestCase
{
public:
								ResizeTest	(EglTestContext&		eglTestCtx,
											 const ResizeParams&	params)
									: TestCase	(eglTestCtx,
												 params.name.c_str(),
												 params.description.c_str())
									, m_oldSize	(params.oldSize)
									, m_newSize	(params.newSize)
									, m_display	(EGL_NO_DISPLAY)
									, m_config	(DE_NULL)
									, m_log		(m_testCtx.getLog())
									, m_status	(m_log) {}

	void						init		(void);
	void						deinit		(void);

protected:
	virtual EGLenum				surfaceType	(void) const { return EGL_WINDOW_BIT; }
	void						resize		(IVec2 size);

	const IVec2					m_oldSize;
	const IVec2					m_newSize;
	EGLDisplay					m_display;
	EGLConfig					m_config;
	MovePtr<NativeWindow>		m_nativeWindow;
	MovePtr<UniqueSurface>		m_surface;
	MovePtr<UniqueContext>		m_context;
	TestLog&					m_log;
	ResultCollector				m_status;
	glw::Functions				m_gl;
};

EGLConfig getEGLConfig (const Library& egl, const EGLDisplay eglDisplay, EGLenum surfaceType)
{
	AttribMap attribMap;

	attribMap[EGL_SURFACE_TYPE]		= surfaceType;
	attribMap[EGL_RENDERABLE_TYPE]	= EGL_OPENGL_ES2_BIT;

	return eglu::chooseSingleConfig(egl, eglDisplay, attribMap);
}

void ResizeTest::init (void)
{
	TestCase::init();

	const Library&				egl				= m_eglTestCtx.getLibrary();
	const CommandLine&			cmdLine			= m_testCtx.getCommandLine();
	const EGLDisplay			eglDisplay		= eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay());
	const EGLConfig				eglConfig		= getEGLConfig(egl, eglDisplay, surfaceType());
	const EGLint				ctxAttribs[]	=
	{
		EGL_CONTEXT_CLIENT_VERSION, 2,
		EGL_NONE
	};
	EGLContext					eglContext		= egl.createContext(eglDisplay,
																   eglConfig,
																   EGL_NO_CONTEXT,
																   ctxAttribs);
	EGLU_CHECK_MSG(egl, "eglCreateContext()");
	MovePtr<UniqueContext>		context			(new UniqueContext(egl, eglDisplay, eglContext));
	const EGLint				configId		= eglu::getConfigAttribInt(egl,
																		   eglDisplay,
																		   eglConfig,
																		   EGL_CONFIG_ID);
	const Visibility			visibility		= eglu::parseWindowVisibility(cmdLine);
	NativeDisplay&				nativeDisplay	= m_eglTestCtx.getNativeDisplay();
	const NativeWindowFactory&	windowFactory	= eglu::selectNativeWindowFactory(m_eglTestCtx.getNativeDisplayFactory(),
																				  cmdLine);

	const WindowParams			windowParams	(m_oldSize.x(), m_oldSize.y(), visibility);
	MovePtr<NativeWindow>		nativeWindow	(windowFactory.createWindow(&nativeDisplay,
																			 eglDisplay,
																			 eglConfig,
																			 DE_NULL,
																			 windowParams));
	const EGLSurface			eglSurface		= eglu::createWindowSurface(nativeDisplay,
																			*nativeWindow,
																			eglDisplay,
																			eglConfig,
																			DE_NULL);
	MovePtr<UniqueSurface>		surface			(new UniqueSurface(egl, eglDisplay, eglSurface));

	m_log << TestLog::Message
		  << "Chose EGLConfig with id " << configId << ".\n"
		  << "Created initial surface with size " << m_oldSize
		  << TestLog::EndMessage;

	m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2, 0));
	m_config		= eglConfig;
	m_surface		= surface;
	m_context		= context;
	m_display		= eglDisplay;
	m_nativeWindow	= nativeWindow;
	EGLU_CHECK_MSG(egl, "init");
}

void ResizeTest::deinit (void)
{
	if (m_display != EGL_NO_DISPLAY)
		m_eglTestCtx.getLibrary().terminate(m_display);

	m_config		= DE_NULL;
	m_display		= EGL_NO_DISPLAY;
	m_context.clear();
	m_surface.clear();
	m_nativeWindow.clear();
}

void ResizeTest::resize (IVec2 size)
{
	m_nativeWindow->setSurfaceSize(size);
	m_testCtx.getPlatform().processEvents();
	m_log << TestLog::Message
		  << "Resized surface to size " << size
		  << TestLog::EndMessage;
}

class ChangeSurfaceSizeCase : public ResizeTest
{
public:
					ChangeSurfaceSizeCase	(EglTestContext&		eglTestCtx,
											 const ResizeParams&	params)
						: ResizeTest(eglTestCtx, params) {}

	IterateResult	iterate					(void);
};

void drawRectangle (const glw::Functions& gl, IVec2 pos, IVec2 size, Vec3 color)
{
	gl.clearColor(color.x(), color.y(), color.z(), 1.0);
	gl.scissor(pos.x(), pos.y(), size.x(), size.y());
	gl.enable(GL_SCISSOR_TEST);
	gl.clear(GL_COLOR_BUFFER_BIT);
	gl.disable(GL_SCISSOR_TEST);
	GLU_EXPECT_NO_ERROR(gl.getError(),
						"Rectangle drawing with glScissor and glClear failed.");
}

void initSurface (const glw::Functions& gl, IVec2 oldSize)
{
	const Vec3	frameColor	(0.0f, 0.0f, 1.0f);
	const Vec3	fillColor	(1.0f, 0.0f, 0.0f);
	const Vec3	markColor	(0.0f, 1.0f, 0.0f);

	drawRectangle(gl, IVec2(0, 0), oldSize, frameColor);
	drawRectangle(gl, IVec2(2, 2), oldSize - IVec2(4, 4), fillColor);

	drawRectangle(gl, IVec2(0, 0), IVec2(8, 4), markColor);
	drawRectangle(gl, oldSize - IVec2(16, 16), IVec2(8, 4), markColor);
	drawRectangle(gl, IVec2(0, oldSize.y() - 16), IVec2(8, 4), markColor);
	drawRectangle(gl, IVec2(oldSize.x() - 16, 0), IVec2(8, 4), markColor);
}

bool compareRectangles (const ConstPixelBufferAccess& rectA,
						const ConstPixelBufferAccess& rectB)
{
	const int width		= rectA.getWidth();
	const int height	= rectA.getHeight();
	const int depth		= rectA.getDepth();

	if (rectB.getWidth() != width || rectB.getHeight() != height || rectB.getDepth() != depth)
		return false;

	for (int z = 0; z < depth; ++z)
		for (int y = 0; y < height; ++y)
			for (int x = 0; x < width; ++x)
				if (rectA.getPixel(x, y, z) != rectB.getPixel(x, y, z))
					return false;

	return true;
}

// Check whether `oldSurface` and `newSurface` share a common corner.
bool compareCorners (const Surface& oldSurface, const Surface& newSurface)
{
	const int	oldWidth	= oldSurface.getWidth();
	const int	oldHeight	= oldSurface.getHeight();
	const int	newWidth	= newSurface.getWidth();
	const int	newHeight	= newSurface.getHeight();
	const int	minWidth	= de::min(oldWidth, newWidth);
	const int	minHeight	= de::min(oldHeight, newHeight);

	for (int xCorner = 0; xCorner < 2; ++xCorner)
	{
		const int oldX = xCorner == 0 ? 0 : oldWidth - minWidth;
		const int newX = xCorner == 0 ? 0 : newWidth - minWidth;

		for (int yCorner = 0; yCorner < 2; ++yCorner)
		{
			const int				oldY		= yCorner == 0 ? 0 : oldHeight - minHeight;
			const int				newY		= yCorner == 0 ? 0 : newHeight - minHeight;
			ConstPixelBufferAccess	oldAccess	=
				getSubregion(oldSurface.getAccess(), oldX, oldY, minWidth, minHeight);
			ConstPixelBufferAccess	newAccess	=
				getSubregion(newSurface.getAccess(), newX, newY, minWidth, minHeight);

			if (compareRectangles(oldAccess, newAccess))
				return true;
		}
	}

	return false;
}

Surface readSurface (const glw::Functions& gl, IVec2 size)
{
	Surface ret (size.x(), size.y());
	gl.readPixels(0, 0, size.x(), size.y(), GL_RGBA, GL_UNSIGNED_BYTE,
				  ret.getAccess().getDataPtr());

	GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels() failed");
	return ret;
}

template <typename T>
inline bool hasBits (T bitSet, T requiredBits)
{
	return (bitSet & requiredBits) == requiredBits;
}

IVec2 getNativeSurfaceSize (const NativeWindow& nativeWindow,
							IVec2				reqSize)
{
	if (hasBits(nativeWindow.getCapabilities(), NativeWindow::CAPABILITY_GET_SURFACE_SIZE))
		return nativeWindow.getSurfaceSize();
	return reqSize; // assume we got the requested size
}

IVec2 checkSurfaceSize (const Library&		egl,
						EGLDisplay			eglDisplay,
						EGLSurface			eglSurface,
						const NativeWindow&	nativeWindow,
						IVec2				reqSize,
						ResultCollector&	status)
{
	const IVec2		nativeSize	= getNativeSurfaceSize(nativeWindow, reqSize);
	IVec2			eglSize		= eglu::getSurfaceSize(egl, eglDisplay, eglSurface);
	ostringstream	oss;

	oss << "Size of EGL surface " << eglSize
		<< " differs from size of native window " << nativeSize;
	status.check(eglSize == nativeSize, oss.str());

	return eglSize;
}

IterateResult ChangeSurfaceSizeCase::iterate (void)
{
	const Library&			egl			= m_eglTestCtx.getLibrary();
	ScopedCurrentContext	currentCtx	(egl, m_display, **m_surface, **m_surface, **m_context);
	IVec2					oldEglSize	= checkSurfaceSize(egl,
														   m_display,
														   **m_surface,
														   *m_nativeWindow,
														   m_oldSize,
														   m_status);

	initSurface(m_gl, oldEglSize);

	this->resize(m_newSize);

	egl.swapBuffers(m_display, **m_surface);
	EGLU_CHECK_MSG(egl, "eglSwapBuffers()");
	checkSurfaceSize(egl, m_display, **m_surface, *m_nativeWindow, m_newSize, m_status);

	m_status.setTestContextResult(m_testCtx);
	return STOP;
}

class PreserveBackBufferCase : public ResizeTest
{
public:
					PreserveBackBufferCase	(EglTestContext&		eglTestCtx,
											 const ResizeParams&	params)
						: ResizeTest(eglTestCtx, params) {}

	IterateResult	iterate					(void);
	EGLenum			surfaceType				(void) const;
};

EGLenum PreserveBackBufferCase::surfaceType (void) const
{
	return EGL_WINDOW_BIT | EGL_SWAP_BEHAVIOR_PRESERVED_BIT;
}

IterateResult PreserveBackBufferCase::iterate (void)
{
	const Library&			egl			= m_eglTestCtx.getLibrary();
	ScopedCurrentContext	currentCtx	(egl, m_display, **m_surface, **m_surface, **m_context);

	EGLU_CHECK_CALL(egl, surfaceAttrib(m_display, **m_surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED));

	GLU_EXPECT_NO_ERROR(m_gl.getError(), "GL state erroneous upon initialization!");

	{
		const IVec2 oldEglSize = eglu::getSurfaceSize(egl, m_display, **m_surface);
		initSurface(m_gl, oldEglSize);

		m_gl.finish();
		GLU_EXPECT_NO_ERROR(m_gl.getError(), "glFinish() failed");

		{
			const Surface oldSurface = readSurface(m_gl, oldEglSize);

			egl.swapBuffers(m_display, **m_surface);
			this->resize(m_newSize);
			egl.swapBuffers(m_display, **m_surface);
			EGLU_CHECK_MSG(egl, "eglSwapBuffers()");

			{
				const IVec2		newEglSize	= eglu::getSurfaceSize(egl, m_display, **m_surface);
				const Surface	newSurface	= readSurface(m_gl, newEglSize);

				m_log << TestLog::ImageSet("Corner comparison",
										   "Comparing old and new surfaces at all corners")
					  << TestLog::Image("Before resizing", "Before resizing", oldSurface)
					  << TestLog::Image("After resizing", "After resizing", newSurface)
					  << TestLog::EndImageSet;

				m_status.check(compareCorners(oldSurface, newSurface),
							   "Resizing the native window changed the contents of "
							   "the EGL surface");
			}
		}
	}

	m_status.setTestContextResult(m_testCtx);
	return STOP;
}

typedef tcu::Vector<Interval, 2> IvVec2;

class UpdateResolutionCase : public ResizeTest
{
public:
					UpdateResolutionCase	(EglTestContext&		eglTestCtx,
											 const ResizeParams&	params)
						: ResizeTest(eglTestCtx, params) {}

	IterateResult	iterate					(void);

private:
	IvVec2			getNativePixelsPerInch	(void);
};

IvVec2 ivVec2 (const IVec2& vec)
{
	return IvVec2(double(vec.x()), double(vec.y()));
}

Interval approximateInt (int i)
{
	const Interval margin(-1.0, 1.0); // The resolution may be rounded

	return (Interval(i) + margin) & Interval(0.0, TCU_INFINITY);
}

IvVec2 UpdateResolutionCase::getNativePixelsPerInch	(void)
{
	const Library&	egl			= m_eglTestCtx.getLibrary();
	const int		inchPer10km	= 254 * EGL_DISPLAY_SCALING;
	const IVec2		bufSize		= eglu::getSurfaceSize(egl, m_display, **m_surface);
	const IVec2		winSize		= m_nativeWindow->getScreenSize();
	const IVec2		bufPp10km	= eglu::getSurfaceResolution(egl, m_display, **m_surface);
	const IvVec2	bufPpiI		= (IvVec2(approximateInt(bufPp10km.x()),
										  approximateInt(bufPp10km.y()))
								   / Interval(inchPer10km));
	const IvVec2	winPpiI		= ivVec2(winSize) * bufPpiI / ivVec2(bufSize);
	const IVec2		winPpi		(int(winPpiI.x().midpoint()), int(winPpiI.y().midpoint()));

	m_log << TestLog::Message
		  << "EGL surface size: "							<< bufSize		<< "\n"
		  << "EGL surface pixel density (pixels / 10 km): "	<< bufPp10km	<< "\n"
		  << "Native window size: "							<< winSize		<< "\n"
		  << "Native pixel density (ppi): "					<< winPpi		<< "\n"
		  << TestLog::EndMessage;

	m_status.checkResult(bufPp10km.x() >= 1 && bufPp10km.y() >= 1,
						 QP_TEST_RESULT_QUALITY_WARNING,
						 "Surface pixel density is less than one pixel per 10 km. "
						 "Is the surface really visible from space?");

	return winPpiI;
}

IterateResult UpdateResolutionCase::iterate (void)
{
	const Library&			egl			= m_eglTestCtx.getLibrary();
	ScopedCurrentContext	currentCtx	(egl, m_display, **m_surface, **m_surface, **m_context);

	if (!hasBits(m_nativeWindow->getCapabilities(),
				 NativeWindow::CAPABILITY_GET_SCREEN_SIZE))
		TCU_THROW(NotSupportedError, "Unable to determine surface size in screen pixels");

	{
		const IVec2 oldEglSize = eglu::getSurfaceSize(egl, m_display, **m_surface);
		initSurface(m_gl, oldEglSize);
	}
	{
		const IvVec2 oldPpi = this->getNativePixelsPerInch();
		this->resize(m_newSize);
		EGLU_CHECK_CALL(egl, swapBuffers(m_display, **m_surface));
		{
			const IvVec2 newPpi = this->getNativePixelsPerInch();
			m_status.check(oldPpi.x().intersects(newPpi.x()) &&
						   oldPpi.y().intersects(newPpi.y()),
						   "Window PPI differs after resizing");
		}
	}

	m_status.setTestContextResult(m_testCtx);
	return STOP;
}

ResizeTests::ResizeTests (EglTestContext& eglTestCtx)
	: TestCaseGroup(eglTestCtx, "resize", "Tests resizing the native surface")
{
}

template <class Case>
TestCaseGroup* createCaseGroup(EglTestContext&	eglTestCtx,
							   const string&	name,
							   const string&	desc)
{
	const ResizeParams		params[]	=
	{
		{ "shrink",			"Shrink in both dimensions",
		  IVec2(128, 128),	IVec2(32, 32) },
		{ "grow",			"Grow in both dimensions",
		  IVec2(32, 32),	IVec2(128, 128) },
		{ "stretch_width",	"Grow horizontally, shrink vertically",
		  IVec2(32, 128),	IVec2(128, 32) },
		{ "stretch_height",	"Grow vertically, shrink horizontally",
		  IVec2(128, 32),	IVec2(32, 128) },
	};
	TestCaseGroup* const	group		=
		new TestCaseGroup(eglTestCtx, name.c_str(), desc.c_str());

	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(params); ++ndx)
		group->addChild(new Case(eglTestCtx, params[ndx]));

	return group;
}

void ResizeTests::init (void)
{
	addChild(createCaseGroup<ChangeSurfaceSizeCase>(m_eglTestCtx,
													"surface_size",
													"EGL surface size update"));
	addChild(createCaseGroup<PreserveBackBufferCase>(m_eglTestCtx,
													 "back_buffer",
													 "Back buffer contents"));
	addChild(createCaseGroup<UpdateResolutionCase>(m_eglTestCtx,
												   "pixel_density",
												   "Pixel density"));
}

} // egl
} // deqp