teglPartialUpdateTests.cpp revision 9dc3540368ee7ef7318ea690e4f58f654474db28
1/*-------------------------------------------------------------------------
2 * drawElements Quality Program EGL Module
3 * ---------------------------------------
4 *
5 * Copyright 2015 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 Test KHR_partial_update
22 *//*--------------------------------------------------------------------*/
23
24#include "teglPartialUpdateTests.hpp"
25
26#include "tcuImageCompare.hpp"
27#include "tcuTestLog.hpp"
28#include "tcuSurface.hpp"
29#include "tcuTextureUtil.hpp"
30
31#include "egluNativeWindow.hpp"
32#include "egluUtil.hpp"
33#include "egluConfigFilter.hpp"
34
35#include "eglwLibrary.hpp"
36#include "eglwEnums.hpp"
37
38#include "gluDefs.hpp"
39#include "gluRenderContext.hpp"
40#include "gluShaderProgram.hpp"
41
42#include "glwDefs.hpp"
43#include "glwEnums.hpp"
44#include "glwFunctions.hpp"
45
46#include "deRandom.hpp"
47#include "deString.h"
48
49#include <string>
50#include <vector>
51#include <sstream>
52
53using std::string;
54using std::vector;
55using glw::GLubyte;
56using tcu::IVec2;
57
58using namespace eglw;
59
60namespace deqp
61{
62namespace egl
63{
64namespace
65{
66
67typedef	tcu::Vector<GLubyte, 3> Color;
68
69class GLES2Renderer;
70
71class ReferenceRenderer;
72
73class PartialUpdateTest : public TestCase
74{
75public:
76	enum DrawType
77	{
78		DRAWTYPE_GLES2_CLEAR,
79		DRAWTYPE_GLES2_RENDER
80	};
81
82								PartialUpdateTest		(EglTestContext& eglTestCtx,
83														 const vector<DrawType>& oddFrameDrawType,
84														 const vector<DrawType>& evenFrameDrawType,
85														 const char* name,
86														 const char* description);
87								~PartialUpdateTest		(void);
88
89	void						init					(void);
90	void						deinit					(void);
91	IterateResult				iterate					(void);
92
93private:
94	eglu::NativeWindow*			m_window;
95	EGLConfig					m_eglConfig;
96	EGLContext					m_eglContext;
97
98protected:
99	void						initEGLSurface			(EGLConfig config);
100	void						initEGLContext			(EGLConfig config);
101
102	const int					m_seed;
103	const vector<DrawType>	    m_oddFrameDrawType;
104	const vector<DrawType>		m_evenFrameDrawType;
105
106	bool 						m_supportBufferAge;
107	EGLDisplay					m_eglDisplay;
108	EGLSurface					m_eglSurface;
109	glw::Functions				m_gl;
110
111	GLES2Renderer*				m_gles2Renderer;
112	ReferenceRenderer*			m_refRenderer;
113};
114
115struct ColoredRect
116{
117public:
118								ColoredRect (const IVec2& bottomLeft_, const IVec2& topRight_, const Color& color_);
119	IVec2	 					bottomLeft;
120	IVec2	 					topRight;
121	Color 						color;
122};
123
124ColoredRect::ColoredRect (const IVec2& bottomLeft_, const IVec2& topRight_, const Color& color_)
125	: bottomLeft (bottomLeft_)
126	, topRight	 (topRight_)
127	, color		 (color_)
128{
129}
130
131struct DrawCommand
132{
133								DrawCommand (const PartialUpdateTest::DrawType drawType_, const ColoredRect& rect_);
134	PartialUpdateTest::DrawType	drawType;
135	ColoredRect					rect;
136};
137
138DrawCommand::DrawCommand (const PartialUpdateTest::DrawType drawType_, const ColoredRect& rect_)
139	: drawType	(drawType_)
140	, rect    	(rect_)
141{
142}
143
144struct Frame
145{
146								Frame (int width_, int height_);
147	int 						width;
148	int							height;
149	vector<DrawCommand> 		draws;
150};
151
152Frame::Frame (int width_, int height_)
153	: width (width_)
154	, height(height_)
155{
156}
157
158// (x1,y1) lie in the lower-left quadrant while (x2,y2) lie in the upper-right.
159// the coords are multiplied by 4 to amplify the minimial difference between coords to 4 (if not zero)
160// to avoid the situation where two edges are too close to each other which makes the rounding error
161// intoleratable by compareToReference()
162void generateRandomFrame (Frame& dst, const vector<PartialUpdateTest::DrawType>& drawTypes, de::Random& rnd)
163{
164	for (size_t ndx = 0; ndx < drawTypes.size(); ndx++)
165	{
166		const int			x1			= rnd.getInt(0, (dst.width-1)/8) * 4;
167		const int			y1			= rnd.getInt(0, (dst.height-1)/8) * 4;
168		const int			x2			= rnd.getInt((dst.width-1)/8, (dst.width-1)/4) * 4;
169		const int			y2			= rnd.getInt((dst.height-1)/8, (dst.height-1)/4) * 4;
170		const GLubyte		r			= rnd.getUint8();
171		const GLubyte		g			= rnd.getUint8();
172		const GLubyte		b			= rnd.getUint8();
173		const ColoredRect	coloredRect	(IVec2(x1, y1), IVec2(x2, y2), Color(r, g, b));
174		const DrawCommand	drawCommand	(drawTypes[ndx], coloredRect);
175
176		dst.draws.push_back(drawCommand);
177	}
178}
179
180typedef vector<Frame> FrameSequence;
181
182//helper function declaration
183EGLConfig		getEGLConfig					(const Library& egl, EGLDisplay eglDisplay);
184void			clearColorScreen				(const glw::Functions& gl, const tcu::Vec4& clearColor);
185void			clearColorReference				(tcu::Surface* ref, const tcu::Vec4& clearColor);
186void			readPixels						(const glw::Functions& gl, tcu::Surface* screen);
187float			windowToDeviceCoordinates		(int x, int length);
188bool			compareToReference				(tcu::TestLog& log, const tcu::Surface& reference, const tcu::Surface& buffer, int frameNdx, int bufferNum);
189vector<int> 	getFramesOnBuffer 				(const vector<int>& bufferAges, int frameNdx);
190
191class GLES2Renderer
192{
193public:
194							GLES2Renderer		(const glw::Functions& gl);
195							~GLES2Renderer		(void);
196	void					render				(int width, int height, const Frame& frame) const;
197
198private:
199							GLES2Renderer		(const GLES2Renderer&);
200	GLES2Renderer&			operator=			(const GLES2Renderer&);
201
202	const glw::Functions&	m_gl;
203	glu::ShaderProgram		m_glProgram;
204	glw::GLuint				m_coordLoc;
205	glw::GLuint				m_colorLoc;
206};
207
208// generate sources for vertex and fragment buffer
209glu::ProgramSources getSources (void)
210{
211	const char* const vertexShaderSource =
212		"attribute mediump vec2 a_pos;\n"
213		"attribute mediump vec4 a_color;\n"
214		"varying mediump vec4 v_color;\n"
215		"void main(void)\n"
216		"{\n"
217		"\tv_color = a_color;\n"
218		"\tgl_Position = vec4(a_pos, 0.0, 1.0);\n"
219		"}";
220
221	const char* const fragmentShaderSource =
222		"varying mediump vec4 v_color;\n"
223		"void main(void)\n"
224		"{\n"
225		"\tgl_FragColor = v_color;\n"
226		"}";
227
228	return glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource);
229}
230
231GLES2Renderer::GLES2Renderer (const glw::Functions& gl)
232	: m_gl				(gl)
233	, m_glProgram		(gl, getSources())
234	, m_coordLoc		((glw::GLuint)-1)
235	, m_colorLoc		((glw::GLuint)-1)
236{
237	m_colorLoc = m_gl.getAttribLocation(m_glProgram.getProgram(), "a_color");
238	m_coordLoc = m_gl.getAttribLocation(m_glProgram.getProgram(), "a_pos");
239	GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to get attribute locations");
240}
241
242GLES2Renderer::~GLES2Renderer (void)
243{
244}
245
246void GLES2Renderer::render (int width, int height, const Frame& frame) const
247{
248	for (size_t drawNdx = 0; drawNdx < frame.draws.size(); drawNdx++)
249	{
250		const ColoredRect& coloredRect = frame.draws[drawNdx].rect;
251
252		if (frame.draws[drawNdx].drawType == PartialUpdateTest::DRAWTYPE_GLES2_RENDER)
253		{
254			const float x1 = windowToDeviceCoordinates(coloredRect.bottomLeft.x(), width);
255			const float y1 = windowToDeviceCoordinates(coloredRect.bottomLeft.y(), height);
256			const float x2 = windowToDeviceCoordinates(coloredRect.topRight.x(), width);
257			const float y2 = windowToDeviceCoordinates(coloredRect.topRight.y(), height);
258
259			const glw::GLfloat coords[] =
260			{
261				x1, y1,
262				x1, y2,
263				x2, y2,
264
265				x2, y2,
266				x2, y1,
267				x1, y1,
268			};
269
270			const glw::GLubyte colors[] =
271			{
272				coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
273				coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
274				coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
275
276				coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
277				coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
278				coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
279			};
280
281			m_gl.useProgram(m_glProgram.getProgram());
282			GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram() failed");
283
284			m_gl.enableVertexAttribArray(m_coordLoc);
285			m_gl.enableVertexAttribArray(m_colorLoc);
286			GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to enable attributes");
287
288			m_gl.vertexAttribPointer(m_coordLoc, 2, GL_FLOAT, GL_FALSE, 0, coords);
289			m_gl.vertexAttribPointer(m_colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, colors);
290			GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to set attribute pointers");
291
292			m_gl.drawArrays(GL_TRIANGLES, 0, DE_LENGTH_OF_ARRAY(coords)/2);
293			GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDrawArrays(), failed");
294
295			m_gl.disableVertexAttribArray(m_coordLoc);
296			m_gl.disableVertexAttribArray(m_colorLoc);
297			GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to disable attributes");
298
299			m_gl.useProgram(0);
300			GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram() failed");
301		}
302		else if (frame.draws[drawNdx].drawType == PartialUpdateTest::DRAWTYPE_GLES2_CLEAR)
303		{
304			m_gl.enable(GL_SCISSOR_TEST);
305			m_gl.scissor(coloredRect.bottomLeft.x(), coloredRect.bottomLeft.y(),
306						 coloredRect.topRight.x()-coloredRect.bottomLeft.x(), coloredRect.topRight.y()-coloredRect.bottomLeft.y());
307			m_gl.clearColor(coloredRect.color.x()/255.0f, coloredRect.color.y()/255.0f, coloredRect.color.z()/255.0f, 1.0f);
308			m_gl.clear(GL_COLOR_BUFFER_BIT);
309			m_gl.disable(GL_SCISSOR_TEST);
310		}
311		else
312			DE_FATAL("Invalid drawtype");
313	}
314}
315
316class ReferenceRenderer
317{
318public:
319						ReferenceRenderer		(void);
320	void				render					(tcu::Surface* target, const Frame& frame) const;
321private:
322						ReferenceRenderer		(const ReferenceRenderer&);
323	ReferenceRenderer&	operator=				(const ReferenceRenderer&);
324};
325
326ReferenceRenderer::ReferenceRenderer(void)
327{
328}
329
330void ReferenceRenderer::render (tcu::Surface* target, const Frame& frame) const
331{
332	for (size_t drawNdx = 0; drawNdx < frame.draws.size(); drawNdx++)
333	{
334		const ColoredRect& coloredRect = frame.draws[drawNdx].rect;
335		if (frame.draws[drawNdx].drawType == PartialUpdateTest::DRAWTYPE_GLES2_RENDER || frame.draws[drawNdx].drawType == PartialUpdateTest::DRAWTYPE_GLES2_CLEAR)
336		{
337			const tcu::UVec4 color(coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255);
338			tcu::clear(tcu::getSubregion(target->getAccess(), coloredRect.bottomLeft.x(), coloredRect.bottomLeft.y(),
339										 coloredRect.topRight.x()-coloredRect.bottomLeft.x(), coloredRect.topRight.y()-coloredRect.bottomLeft.y()), color);
340		}
341		else
342			DE_FATAL("Invalid drawtype");
343	}
344}
345
346PartialUpdateTest::PartialUpdateTest (EglTestContext& eglTestCtx,
347									  const vector<DrawType>& oddFrameDrawType,
348									  const vector<DrawType>& evenFrameDrawType,
349									  const char* name, const char* description)
350	: TestCase				(eglTestCtx, name, description)
351	, m_window				(DE_NULL)
352	, m_eglContext			(EGL_NO_CONTEXT)
353	, m_seed				(deStringHash(name))
354	, m_oddFrameDrawType	(oddFrameDrawType)
355	, m_evenFrameDrawType	(evenFrameDrawType)
356	, m_supportBufferAge	(false)
357	, m_eglDisplay			(EGL_NO_DISPLAY)
358	, m_eglSurface			(EGL_NO_SURFACE)
359	, m_gles2Renderer		(DE_NULL)
360	, m_refRenderer			(DE_NULL)
361{
362}
363
364PartialUpdateTest::~PartialUpdateTest (void)
365{
366	deinit();
367}
368
369void PartialUpdateTest::init (void)
370{
371	const Library&	egl	= m_eglTestCtx.getLibrary();
372
373	m_eglDisplay		= eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay());
374	m_eglConfig			= getEGLConfig(m_eglTestCtx.getLibrary(), m_eglDisplay);
375
376	//create surface and context and make them current
377	initEGLSurface(m_eglConfig);
378	initEGLContext(m_eglConfig);
379
380	m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0));
381
382	m_supportBufferAge = eglu::hasExtension(egl, m_eglDisplay, "EGL_EXT_buffer_age");
383
384	if (!eglu::hasExtension(egl, m_eglDisplay, "EGL_KHR_partial_update"))
385		TCU_THROW(NotSupportedError, "EGL_KHR_partial_update is not supported");
386
387	m_gles2Renderer = new GLES2Renderer(m_gl);
388	m_refRenderer   = new ReferenceRenderer();
389}
390
391void PartialUpdateTest::deinit (void)
392{
393	const Library& egl = m_eglTestCtx.getLibrary();
394
395	delete m_refRenderer;
396	m_refRenderer = DE_NULL;
397
398	delete m_gles2Renderer;
399	m_gles2Renderer = DE_NULL;
400
401	if (m_eglContext != EGL_NO_CONTEXT)
402	{
403		egl.makeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
404		egl.destroyContext(m_eglDisplay, m_eglContext);
405		m_eglContext = EGL_NO_CONTEXT;
406	}
407
408	if (m_eglSurface != EGL_NO_SURFACE)
409	{
410		egl.destroySurface(m_eglDisplay, m_eglSurface);
411		m_eglSurface = EGL_NO_SURFACE;
412	}
413
414	if (m_eglDisplay != EGL_NO_DISPLAY)
415	{
416		egl.terminate(m_eglDisplay);
417		m_eglDisplay = EGL_NO_DISPLAY;
418	}
419
420	delete m_window;
421	m_window = DE_NULL;
422}
423
424void PartialUpdateTest::initEGLSurface (EGLConfig config)
425{
426	const eglu::NativeWindowFactory& factory = eglu::selectNativeWindowFactory(m_eglTestCtx.getNativeDisplayFactory(), m_testCtx.getCommandLine());
427	m_window = factory.createWindow(&m_eglTestCtx.getNativeDisplay(), m_eglDisplay, config, DE_NULL,
428									eglu::WindowParams(480, 480, eglu::parseWindowVisibility(m_testCtx.getCommandLine())));
429	m_eglSurface = eglu::createWindowSurface(m_eglTestCtx.getNativeDisplay(), *m_window, m_eglDisplay, config, DE_NULL);
430}
431
432void PartialUpdateTest::initEGLContext (EGLConfig config)
433{
434	const Library& 	egl 		 = m_eglTestCtx.getLibrary();
435	const EGLint 	attribList[] =
436	{
437		EGL_CONTEXT_CLIENT_VERSION, 2,
438		EGL_NONE
439	};
440
441	egl.bindAPI(EGL_OPENGL_ES_API);
442	m_eglContext = egl.createContext(m_eglDisplay, config, EGL_NO_CONTEXT, attribList);
443	EGLU_CHECK_MSG(egl, "eglCreateContext");
444	TCU_CHECK(m_eglSurface != EGL_NO_SURFACE);
445	egl.makeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
446	EGLU_CHECK_MSG(egl, "eglMakeCurrent");
447}
448
449// return indices of frames that have been written to the given buffer
450vector<int> getFramesOnBuffer (const vector<int>& bufferAges, int frameNdx)
451{
452	DE_ASSERT(frameNdx < (int)bufferAges.size());
453	vector<int> frameOnBuffer;
454	int 		age = bufferAges[frameNdx];
455	while (age != 0)
456	{
457		frameNdx = frameNdx - age;
458		DE_ASSERT(frameNdx >= 0);
459		frameOnBuffer.push_back(frameNdx);
460		age = bufferAges[frameNdx];
461	}
462
463	reverse(frameOnBuffer.begin(), frameOnBuffer.end());
464	return frameOnBuffer;
465}
466
467vector<EGLint> getDamageRegion (const Frame& frame, int marginLeft, int marginBottom, int marginRight, int marginTop)
468{
469	vector<EGLint> damageRegion;
470	for (size_t drawNdx = 0; drawNdx < frame.draws.size(); drawNdx++)
471	{
472		const ColoredRect& rect = frame.draws[drawNdx].rect;
473		damageRegion.push_back(rect.bottomLeft.x() - marginLeft);
474		damageRegion.push_back(rect.bottomLeft.y() - marginBottom);
475		damageRegion.push_back(rect.topRight.x() - rect.bottomLeft.x() + marginLeft + marginRight);
476		damageRegion.push_back(rect.topRight.y() - rect.bottomLeft.y() + marginBottom + marginTop);
477	}
478
479	DE_ASSERT(damageRegion.size() % 4 == 0);
480	return damageRegion;
481}
482
483TestCase::IterateResult PartialUpdateTest::iterate (void)
484{
485	de::Random 		rnd				(m_seed);
486	const Library&	egl				= m_eglTestCtx.getLibrary();
487	tcu::TestLog& 	log				= m_testCtx.getLog();
488	const int 		width			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
489	const int 		height			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
490	const float 	clearRed		= rnd.getFloat();
491	const float 	clearGreen		= rnd.getFloat();
492	const float 	clearBlue		= rnd.getFloat();
493	const tcu::Vec4	clearColor		(clearRed, clearGreen, clearBlue, 1.0f);
494	const int 		numFrames		= 20;
495	FrameSequence 	frameSequence;
496	vector<int> 	bufferAges;
497	bool			hasPositiveAge  = false;
498
499	for (int frameNdx = 0; frameNdx < numFrames; frameNdx++)
500	{
501		tcu::Surface	currentBuffer	 (width, height);
502		tcu::Surface	refBuffer		 (width, height);
503		Frame			newFrame		 (width, height);
504		EGLint			currentBufferAge = -1;
505		vector<EGLint>	damageRegion;
506
507		if (frameNdx % 2 == 0)
508			generateRandomFrame(newFrame, m_evenFrameDrawType, rnd);
509		else
510			generateRandomFrame(newFrame, m_oddFrameDrawType, rnd);
511
512		frameSequence.push_back(newFrame);
513
514		EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_BUFFER_AGE_KHR, &currentBufferAge));
515
516		if (currentBufferAge > frameNdx || currentBufferAge < 0) // invalid buffer age
517		{
518			std::ostringstream stream;
519			stream << "Fail, the age is invalid. Age: " << currentBufferAge << ", frameNdx: " << frameNdx;
520			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, stream.str().c_str());
521			return STOP;
522		}
523
524		bufferAges.push_back(currentBufferAge);
525		DE_ASSERT((int)bufferAges.size() == frameNdx+1);
526
527		if (currentBufferAge > 0)
528		{
529			hasPositiveAge = true;
530
531			if (m_supportBufferAge)
532				damageRegion = getDamageRegion(newFrame, 10, 10, 10, 10);
533			else
534				damageRegion = getDamageRegion(newFrame, 0, 0, 0, 0);
535
536			EGLU_CHECK_CALL(egl, setDamageRegionKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size()/4));
537		}
538		else
539		{
540			EGLU_CHECK_CALL(egl, setDamageRegionKHR(m_eglDisplay, m_eglSurface, NULL, 0));
541			clearColorScreen(m_gl, clearColor);
542		}
543
544		// during first half, just keep rendering without reading pixel back to mimic ordinary use case
545		if (frameNdx < numFrames/2)
546			m_gles2Renderer->render(width, height, newFrame);
547		else // do verification in the second half
548		{
549			const vector<int> framesOnBuffer = getFramesOnBuffer(bufferAges, frameNdx);
550
551			clearColorReference(&refBuffer, clearColor);
552
553			for (vector<int>::const_iterator it = framesOnBuffer.begin(); it != framesOnBuffer.end(); it++)
554				m_refRenderer->render(&refBuffer, frameSequence[*it]);
555
556			m_gles2Renderer->render(width, height, newFrame);
557			m_refRenderer->render(&refBuffer, newFrame);
558
559			readPixels(m_gl, &currentBuffer);
560
561			if (!compareToReference(log, refBuffer, currentBuffer, frameNdx, frameNdx))
562			{
563				string errorMessage("Fail, render result is wrong. Buffer age is ");
564				errorMessage += (m_supportBufferAge ? "supported" : "not supported");
565				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, errorMessage.c_str());
566				return STOP;
567			}
568		}
569		EGLU_CHECK_CALL(egl, swapBuffers(m_eglDisplay, m_eglSurface));
570	}
571
572	if (!hasPositiveAge) // fraud behavior, pretend to support partial_update
573	{
574		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail, claim to support partial_update but buffer age is always 0");
575		return STOP;
576	}
577
578	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
579	return STOP;
580}
581
582string generateDrawTypeName (const vector<PartialUpdateTest::DrawType>& drawTypes)
583{
584	std::ostringstream stream;
585	if (drawTypes.size() == 0)
586		return string("_none");
587
588	for (size_t ndx = 0; ndx < drawTypes.size(); ndx++)
589	{
590		if (drawTypes[ndx] == PartialUpdateTest::DRAWTYPE_GLES2_RENDER)
591			stream << "_render";
592		else if (drawTypes[ndx] == PartialUpdateTest::DRAWTYPE_GLES2_CLEAR)
593			stream << "_clear";
594		else
595			DE_ASSERT(false);
596	}
597	return stream.str();
598}
599
600string generateTestName (const vector<PartialUpdateTest::DrawType>& oddFrameDrawType, const vector<PartialUpdateTest::DrawType>& evenFrameDrawType)
601{
602	return "odd" + generateDrawTypeName(oddFrameDrawType) + "_even" + generateDrawTypeName(evenFrameDrawType);
603}
604
605bool isWindow (const eglu::CandidateConfig& c)
606{
607	return (c.surfaceType() & EGL_WINDOW_BIT) == EGL_WINDOW_BIT;
608}
609
610bool isES2Renderable (const eglu::CandidateConfig& c)
611{
612	return (c.get(EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES2_BIT) == EGL_OPENGL_ES2_BIT;
613}
614
615EGLConfig getEGLConfig (const Library& egl, EGLDisplay eglDisplay)
616{
617	eglu::FilterList filters;
618	filters << isWindow << isES2Renderable;
619	return eglu::chooseSingleConfig(egl, eglDisplay, filters);
620}
621
622void clearColorScreen (const glw::Functions& gl, const tcu::Vec4& clearColor)
623{
624	gl.clearColor(clearColor.x(), clearColor.y(), clearColor.z(), clearColor.w());
625	gl.clear(GL_COLOR_BUFFER_BIT);
626}
627
628void clearColorReference (tcu::Surface* ref, const tcu::Vec4& clearColor)
629{
630	tcu::clear(ref->getAccess(), clearColor);
631}
632
633void readPixels (const glw::Functions& gl, tcu::Surface* screen)
634{
635	gl.readPixels(0, 0, screen->getWidth(), screen->getHeight(),  GL_RGBA, GL_UNSIGNED_BYTE, screen->getAccess().getDataPtr());
636}
637
638float windowToDeviceCoordinates (int x, int length)
639{
640	return (2.0f * float(x) / float(length)) - 1.0f;
641}
642
643bool compareToReference (tcu::TestLog& log,	 const tcu::Surface& reference, const tcu::Surface& buffer, int frameNdx, int bufferNum)
644{
645	std::ostringstream stream;
646	stream << "FrameNdx = " << frameNdx << ", compare current buffer (numbered: " << bufferNum << ") to reference";
647	return tcu::intThresholdPositionDeviationCompare(log, "partial update test", stream.str().c_str(), reference.getAccess(), buffer.getAccess(),
648													 tcu::UVec4(8, 8, 8, 0), tcu::IVec3(2,2,0), true, tcu::COMPARE_LOG_RESULT);
649}
650
651class RenderOutsideDamageRegion : public PartialUpdateTest
652{
653public:
654								RenderOutsideDamageRegion		(EglTestContext& eglTestCtx);
655	TestCase::IterateResult		iterate							(void);
656};
657
658RenderOutsideDamageRegion::RenderOutsideDamageRegion (EglTestContext& eglTestCtx)
659	: PartialUpdateTest (eglTestCtx, vector<DrawType>(1, DRAWTYPE_GLES2_RENDER), vector<DrawType>(1, DRAWTYPE_GLES2_RENDER), "render_outside_damage_region", "")
660{
661}
662
663TestCase::IterateResult RenderOutsideDamageRegion::iterate (void)
664{
665	de::Random			rnd				(m_seed);
666	const Library&		egl				= m_eglTestCtx.getLibrary();
667	tcu::TestLog&		log				= m_testCtx.getLog();
668	const int			width			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
669	const int			height			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
670	const float			clearRed		= rnd.getFloat();
671	const float			clearGreen		= rnd.getFloat();
672	const float			clearBlue		= rnd.getFloat();
673	const tcu::Vec4		clearColor		(clearRed, clearGreen, clearBlue, 1.0f);
674	tcu::Surface		currentBuffer	(width, height);
675	tcu::Surface		refBuffer		(width, height);
676	Frame				frame			(width, height);
677
678	generateRandomFrame(frame, m_evenFrameDrawType, rnd);
679
680	{
681		// render outside the region
682		EGLint		   bufferAge	= -1;
683		vector<EGLint> damageRegion = getDamageRegion(frame, 0, 0, 0, 0);
684
685		EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_BUFFER_AGE_KHR, &bufferAge));
686		EGLU_CHECK_CALL(egl, setDamageRegionKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size()/4));
687		clearColorScreen(m_gl, clearColor);
688		m_gles2Renderer->render(width, height, frame);
689
690		// next line will make the bug on Nexus 6 disappear
691		// readPixels(m_gl, &currentBuffer);
692	}
693
694	EGLU_CHECK_CALL(egl, swapBuffers(m_eglDisplay, m_eglSurface));
695
696	// render a new frame
697	clearColorScreen(m_gl, clearColor);
698	m_gles2Renderer->render(width, height, frame);
699	clearColorReference(&refBuffer, clearColor);
700	m_refRenderer->render(&refBuffer, frame);
701	readPixels(m_gl, &currentBuffer);
702
703	if (!compareToReference(log, refBuffer, currentBuffer, 0, 0))
704		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail, fail to recover after rendering outside damageRegion");
705	else
706		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
707
708	return STOP;
709}
710
711class RenderBeforeSetDamageRegion : public PartialUpdateTest
712{
713public:
714								RenderBeforeSetDamageRegion		(EglTestContext& eglTestCtx);
715	TestCase::IterateResult		iterate							(void);
716};
717
718RenderBeforeSetDamageRegion::RenderBeforeSetDamageRegion (EglTestContext& eglTestCtx)
719	: PartialUpdateTest (eglTestCtx, vector<DrawType>(1, DRAWTYPE_GLES2_RENDER), vector<DrawType>(1, DRAWTYPE_GLES2_RENDER), "render_before_set_damage_region", "")
720{
721}
722
723TestCase::IterateResult RenderBeforeSetDamageRegion::iterate (void)
724{
725	de::Random			rnd				(m_seed);
726	const Library&		egl				= m_eglTestCtx.getLibrary();
727	tcu::TestLog&		log				= m_testCtx.getLog();
728	const int			width			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
729	const int			height			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
730	const float			clearRed		= rnd.getFloat();
731	const float			clearGreen		= rnd.getFloat();
732	const float			clearBlue		= rnd.getFloat();
733	const tcu::Vec4		clearColor		(clearRed, clearGreen, clearBlue, 1.0f);
734	tcu::Surface		currentBuffer	(width, height);
735	tcu::Surface		refBuffer		(width, height);
736	Frame				frame			(width, height);
737
738	generateRandomFrame(frame, m_evenFrameDrawType, rnd);
739
740	{
741		// render before setDamageRegion
742		EGLint		   bufferAge	= -1;
743		vector<EGLint> damageRegion = getDamageRegion(frame, 0, 0, 0, 0);
744
745		m_gles2Renderer->render(width, height, frame);
746		EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_BUFFER_AGE_KHR, &bufferAge));
747		EGLU_CHECK_CALL(egl, setDamageRegionKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size()/4));
748
749		// next line will make the bug on Nexus 6 disappear
750		// readPixels(m_gl, &currentBuffer);
751	}
752
753	EGLU_CHECK_CALL(egl, swapBuffers(m_eglDisplay, m_eglSurface));
754
755	// render a new frame
756	clearColorScreen(m_gl, clearColor);
757	m_gles2Renderer->render(width, height, frame);
758	clearColorReference(&refBuffer, clearColor);
759	m_refRenderer->render(&refBuffer, frame);
760	readPixels(m_gl, &currentBuffer);
761
762	if (!compareToReference(log, refBuffer, currentBuffer, 0, 0))
763		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
764	else
765		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
766
767	return STOP;
768}
769
770} // anonymous
771
772PartialUpdateTests::PartialUpdateTests (EglTestContext& eglTestCtx)
773	: TestCaseGroup(eglTestCtx, "partial_update", "Partial update tests")
774{
775}
776
777void PartialUpdateTests::init (void)
778{
779	const PartialUpdateTest::DrawType clearRender[2] =
780	{
781		PartialUpdateTest::DRAWTYPE_GLES2_CLEAR,
782		PartialUpdateTest::DRAWTYPE_GLES2_RENDER
783	};
784
785	const PartialUpdateTest::DrawType renderClear[2] =
786	{
787		PartialUpdateTest::DRAWTYPE_GLES2_RENDER,
788		PartialUpdateTest::DRAWTYPE_GLES2_CLEAR
789	};
790
791	vector< vector<PartialUpdateTest::DrawType> > frameDrawTypes;
792	frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> ());
793	frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (1, PartialUpdateTest::DRAWTYPE_GLES2_CLEAR));
794	frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (1, PartialUpdateTest::DRAWTYPE_GLES2_RENDER));
795	frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (2, PartialUpdateTest::DRAWTYPE_GLES2_CLEAR));
796	frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (2, PartialUpdateTest::DRAWTYPE_GLES2_RENDER));
797	frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (DE_ARRAY_BEGIN(clearRender), DE_ARRAY_END(clearRender)));
798	frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (DE_ARRAY_BEGIN(renderClear), DE_ARRAY_END(renderClear)));
799
800	for (size_t evenNdx = 0; evenNdx < frameDrawTypes.size(); evenNdx++)
801	{
802		const vector<PartialUpdateTest::DrawType>& evenFrameDrawType = frameDrawTypes[evenNdx];
803
804		for (size_t oddNdx = evenNdx; oddNdx < frameDrawTypes.size(); oddNdx++)
805		{
806			const vector<PartialUpdateTest::DrawType>& 	oddFrameDrawType = frameDrawTypes[oddNdx];
807			const std::string 							name 			 = generateTestName(oddFrameDrawType, evenFrameDrawType);
808			if (oddFrameDrawType.size() == 0 && evenFrameDrawType.size() == 0)
809				continue;
810
811			addChild(new PartialUpdateTest(m_eglTestCtx, oddFrameDrawType, evenFrameDrawType, name.c_str(), ""));
812		}
813	}
814	addChild(new RenderOutsideDamageRegion(m_eglTestCtx));
815	addChild(new RenderBeforeSetDamageRegion(m_eglTestCtx));
816}
817
818} // egl
819} // deqp
820