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			// tcu does not support degenerate subregions. Since they correspond to no-op rendering, just skip them.
338			if (coloredRect.bottomLeft.x() == coloredRect.topRight.x() || coloredRect.bottomLeft.y() == coloredRect.topRight.y())
339				continue;
340
341			const tcu::UVec4 color(coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255);
342			tcu::clear(tcu::getSubregion(target->getAccess(), coloredRect.bottomLeft.x(), coloredRect.bottomLeft.y(),
343										 coloredRect.topRight.x()-coloredRect.bottomLeft.x(), coloredRect.topRight.y()-coloredRect.bottomLeft.y()), color);
344		}
345		else
346			DE_FATAL("Invalid drawtype");
347	}
348}
349
350PartialUpdateTest::PartialUpdateTest (EglTestContext& eglTestCtx,
351									  const vector<DrawType>& oddFrameDrawType,
352									  const vector<DrawType>& evenFrameDrawType,
353									  const char* name, const char* description)
354	: TestCase				(eglTestCtx, name, description)
355	, m_window				(DE_NULL)
356	, m_eglContext			(EGL_NO_CONTEXT)
357	, m_seed				(deStringHash(name))
358	, m_oddFrameDrawType	(oddFrameDrawType)
359	, m_evenFrameDrawType	(evenFrameDrawType)
360	, m_supportBufferAge	(false)
361	, m_eglDisplay			(EGL_NO_DISPLAY)
362	, m_eglSurface			(EGL_NO_SURFACE)
363	, m_gles2Renderer		(DE_NULL)
364	, m_refRenderer			(DE_NULL)
365{
366}
367
368PartialUpdateTest::~PartialUpdateTest (void)
369{
370	deinit();
371}
372
373void PartialUpdateTest::init (void)
374{
375	const Library&	egl	= m_eglTestCtx.getLibrary();
376
377	m_eglDisplay		= eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay());
378
379	if (!eglu::hasExtension(egl, m_eglDisplay, "EGL_KHR_partial_update"))
380	{
381		egl.terminate(m_eglDisplay);
382		m_eglDisplay = EGL_NO_DISPLAY;
383		TCU_THROW(NotSupportedError, "EGL_KHR_partial_update is not supported");
384	}
385
386	m_eglConfig			= getEGLConfig(m_eglTestCtx.getLibrary(), m_eglDisplay);
387
388	//create surface and context and make them current
389	initEGLSurface(m_eglConfig);
390	initEGLContext(m_eglConfig);
391
392	m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0));
393
394	m_supportBufferAge = eglu::hasExtension(egl, m_eglDisplay, "EGL_EXT_buffer_age");
395
396	m_gles2Renderer = new GLES2Renderer(m_gl);
397	m_refRenderer   = new ReferenceRenderer();
398}
399
400void PartialUpdateTest::deinit (void)
401{
402	const Library& egl = m_eglTestCtx.getLibrary();
403
404	delete m_refRenderer;
405	m_refRenderer = DE_NULL;
406
407	delete m_gles2Renderer;
408	m_gles2Renderer = DE_NULL;
409
410	if (m_eglContext != EGL_NO_CONTEXT)
411	{
412		egl.makeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
413		egl.destroyContext(m_eglDisplay, m_eglContext);
414		m_eglContext = EGL_NO_CONTEXT;
415	}
416
417	if (m_eglSurface != EGL_NO_SURFACE)
418	{
419		egl.destroySurface(m_eglDisplay, m_eglSurface);
420		m_eglSurface = EGL_NO_SURFACE;
421	}
422
423	if (m_eglDisplay != EGL_NO_DISPLAY)
424	{
425		egl.terminate(m_eglDisplay);
426		m_eglDisplay = EGL_NO_DISPLAY;
427	}
428
429	delete m_window;
430	m_window = DE_NULL;
431}
432
433void PartialUpdateTest::initEGLSurface (EGLConfig config)
434{
435	const eglu::NativeWindowFactory& factory = eglu::selectNativeWindowFactory(m_eglTestCtx.getNativeDisplayFactory(), m_testCtx.getCommandLine());
436	m_window = factory.createWindow(&m_eglTestCtx.getNativeDisplay(), m_eglDisplay, config, DE_NULL,
437									eglu::WindowParams(480, 480, eglu::parseWindowVisibility(m_testCtx.getCommandLine())));
438	m_eglSurface = eglu::createWindowSurface(m_eglTestCtx.getNativeDisplay(), *m_window, m_eglDisplay, config, DE_NULL);
439}
440
441void PartialUpdateTest::initEGLContext (EGLConfig config)
442{
443	const Library&	egl			 = m_eglTestCtx.getLibrary();
444	const EGLint	attribList[] =
445	{
446		EGL_CONTEXT_CLIENT_VERSION, 2,
447		EGL_NONE
448	};
449
450	egl.bindAPI(EGL_OPENGL_ES_API);
451	m_eglContext = egl.createContext(m_eglDisplay, config, EGL_NO_CONTEXT, attribList);
452	EGLU_CHECK_MSG(egl, "eglCreateContext");
453	TCU_CHECK(m_eglSurface != EGL_NO_SURFACE);
454	egl.makeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
455	EGLU_CHECK_MSG(egl, "eglMakeCurrent");
456}
457
458// return indices of frames that have been written to the given buffer
459vector<int> getFramesOnBuffer (const vector<int>& bufferAges, int frameNdx)
460{
461	DE_ASSERT(frameNdx < (int)bufferAges.size());
462	vector<int> frameOnBuffer;
463	int			age = bufferAges[frameNdx];
464	while (age != 0)
465	{
466		frameNdx = frameNdx - age;
467		DE_ASSERT(frameNdx >= 0);
468		frameOnBuffer.push_back(frameNdx);
469		age = bufferAges[frameNdx];
470	}
471
472	reverse(frameOnBuffer.begin(), frameOnBuffer.end());
473	return frameOnBuffer;
474}
475
476vector<EGLint> getDamageRegion (const Frame& frame, int marginLeft, int marginBottom, int marginRight, int marginTop)
477{
478	vector<EGLint> damageRegion;
479	for (size_t drawNdx = 0; drawNdx < frame.draws.size(); drawNdx++)
480	{
481		const ColoredRect& rect = frame.draws[drawNdx].rect;
482		damageRegion.push_back(rect.bottomLeft.x() - marginLeft);
483		damageRegion.push_back(rect.bottomLeft.y() - marginBottom);
484		damageRegion.push_back(rect.topRight.x() - rect.bottomLeft.x() + marginLeft + marginRight);
485		damageRegion.push_back(rect.topRight.y() - rect.bottomLeft.y() + marginBottom + marginTop);
486	}
487
488	DE_ASSERT(damageRegion.size() % 4 == 0);
489	return damageRegion;
490}
491
492TestCase::IterateResult PartialUpdateTest::iterate (void)
493{
494	de::Random		rnd				(m_seed);
495	const Library&	egl				= m_eglTestCtx.getLibrary();
496	tcu::TestLog&	log				= m_testCtx.getLog();
497	const int		width			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
498	const int		height			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
499	const float		clearRed		= rnd.getFloat();
500	const float		clearGreen		= rnd.getFloat();
501	const float		clearBlue		= rnd.getFloat();
502	const tcu::Vec4	clearColor		(clearRed, clearGreen, clearBlue, 1.0f);
503	const int		numFrames		= 20;
504	FrameSequence	frameSequence;
505	vector<int>		bufferAges;
506	bool			hasPositiveAge  = false;
507
508	EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED));
509
510	for (int frameNdx = 0; frameNdx < numFrames; frameNdx++)
511	{
512		tcu::Surface	currentBuffer	 (width, height);
513		tcu::Surface	refBuffer		 (width, height);
514		Frame			newFrame		 (width, height);
515		EGLint			currentBufferAge = -1;
516
517		if (frameNdx % 2 == 0)
518			generateRandomFrame(newFrame, m_evenFrameDrawType, rnd);
519		else
520			generateRandomFrame(newFrame, m_oddFrameDrawType, rnd);
521
522		frameSequence.push_back(newFrame);
523
524		EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_BUFFER_AGE_KHR, &currentBufferAge));
525
526		if (currentBufferAge > frameNdx || currentBufferAge < 0) // invalid buffer age
527		{
528			std::ostringstream stream;
529			stream << "Fail, the age is invalid. Age: " << currentBufferAge << ", frameNdx: " << frameNdx;
530			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, stream.str().c_str());
531			return STOP;
532		}
533
534		bufferAges.push_back(currentBufferAge);
535		DE_ASSERT((int)bufferAges.size() == frameNdx+1);
536
537		if (currentBufferAge > 0)
538		{
539			vector<EGLint>	damageRegion;
540
541			hasPositiveAge = true;
542
543			if (m_supportBufferAge)
544			{
545				damageRegion = getDamageRegion(newFrame, 10, 10, 10, 10);
546			}
547			else
548			{
549				damageRegion = getDamageRegion(newFrame, 0, 0, 0, 0);
550			}
551
552			// Set empty damage region to avoid invalidating the framebuffer. The damage area is invalidated
553			// if the buffer age extension is not supported.
554			if (damageRegion.size() == 0)
555				damageRegion = vector<EGLint>(4, 0);
556
557			EGLU_CHECK_CALL(egl, setDamageRegionKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size()/4));
558		}
559		else
560		{
561			EGLU_CHECK_CALL(egl, setDamageRegionKHR(m_eglDisplay, m_eglSurface, NULL, 0));
562			clearColorScreen(m_gl, clearColor);
563		}
564
565		// during first half, just keep rendering without reading pixel back to mimic ordinary use case
566		if (frameNdx < numFrames/2)
567			m_gles2Renderer->render(width, height, newFrame);
568		else // do verification in the second half
569		{
570			const vector<int> framesOnBuffer = getFramesOnBuffer(bufferAges, frameNdx);
571
572			clearColorReference(&refBuffer, clearColor);
573
574			for (vector<int>::const_iterator it = framesOnBuffer.begin(); it != framesOnBuffer.end(); it++)
575				m_refRenderer->render(&refBuffer, frameSequence[*it]);
576
577			m_gles2Renderer->render(width, height, newFrame);
578			m_refRenderer->render(&refBuffer, newFrame);
579
580			readPixels(m_gl, &currentBuffer);
581
582			if (!compareToReference(log, refBuffer, currentBuffer, frameNdx, frameNdx))
583			{
584				string errorMessage("Fail, render result is wrong. Buffer age is ");
585				errorMessage += (m_supportBufferAge ? "supported" : "not supported");
586				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, errorMessage.c_str());
587				return STOP;
588			}
589		}
590		EGLU_CHECK_CALL(egl, swapBuffers(m_eglDisplay, m_eglSurface));
591	}
592
593	if (!hasPositiveAge) // fraud behavior, pretend to support partial_update
594	{
595		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail, claim to support partial_update but buffer age is always 0");
596		return STOP;
597	}
598
599	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
600	return STOP;
601}
602
603string generateDrawTypeName (const vector<PartialUpdateTest::DrawType>& drawTypes)
604{
605	std::ostringstream stream;
606	if (drawTypes.size() == 0)
607		return string("_none");
608
609	for (size_t ndx = 0; ndx < drawTypes.size(); ndx++)
610	{
611		if (drawTypes[ndx] == PartialUpdateTest::DRAWTYPE_GLES2_RENDER)
612			stream << "_render";
613		else if (drawTypes[ndx] == PartialUpdateTest::DRAWTYPE_GLES2_CLEAR)
614			stream << "_clear";
615		else
616			DE_ASSERT(false);
617	}
618	return stream.str();
619}
620
621string generateTestName (const vector<PartialUpdateTest::DrawType>& oddFrameDrawType, const vector<PartialUpdateTest::DrawType>& evenFrameDrawType)
622{
623	return "odd" + generateDrawTypeName(oddFrameDrawType) + "_even" + generateDrawTypeName(evenFrameDrawType);
624}
625
626bool isWindow (const eglu::CandidateConfig& c)
627{
628	return (c.surfaceType() & EGL_WINDOW_BIT) == EGL_WINDOW_BIT;
629}
630
631bool isES2Renderable (const eglu::CandidateConfig& c)
632{
633	return (c.get(EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES2_BIT) == EGL_OPENGL_ES2_BIT;
634}
635
636EGLConfig getEGLConfig (const Library& egl, EGLDisplay eglDisplay)
637{
638	eglu::FilterList filters;
639	filters << isWindow << isES2Renderable;
640	return eglu::chooseSingleConfig(egl, eglDisplay, filters);
641}
642
643void clearColorScreen (const glw::Functions& gl, const tcu::Vec4& clearColor)
644{
645	gl.clearColor(clearColor.x(), clearColor.y(), clearColor.z(), clearColor.w());
646	gl.clear(GL_COLOR_BUFFER_BIT);
647}
648
649void clearColorReference (tcu::Surface* ref, const tcu::Vec4& clearColor)
650{
651	tcu::clear(ref->getAccess(), clearColor);
652}
653
654void readPixels (const glw::Functions& gl, tcu::Surface* screen)
655{
656	gl.readPixels(0, 0, screen->getWidth(), screen->getHeight(),  GL_RGBA, GL_UNSIGNED_BYTE, screen->getAccess().getDataPtr());
657}
658
659float windowToDeviceCoordinates (int x, int length)
660{
661	return (2.0f * float(x) / float(length)) - 1.0f;
662}
663
664bool compareToReference (tcu::TestLog& log,	 const tcu::Surface& reference, const tcu::Surface& buffer, int frameNdx, int bufferNum)
665{
666	std::ostringstream stream;
667	stream << "FrameNdx = " << frameNdx << ", compare current buffer (numbered: " << bufferNum << ") to reference";
668	return tcu::intThresholdPositionDeviationCompare(log, "partial update test", stream.str().c_str(), reference.getAccess(), buffer.getAccess(),
669													 tcu::UVec4(8, 8, 8, 0), tcu::IVec3(2,2,0), true, tcu::COMPARE_LOG_RESULT);
670}
671
672class RenderOutsideDamageRegion : public PartialUpdateTest
673{
674public:
675								RenderOutsideDamageRegion		(EglTestContext& eglTestCtx);
676	TestCase::IterateResult		iterate							(void);
677};
678
679RenderOutsideDamageRegion::RenderOutsideDamageRegion (EglTestContext& eglTestCtx)
680	: PartialUpdateTest (eglTestCtx, vector<DrawType>(1, DRAWTYPE_GLES2_RENDER), vector<DrawType>(1, DRAWTYPE_GLES2_RENDER), "render_outside_damage_region", "")
681{
682}
683
684TestCase::IterateResult RenderOutsideDamageRegion::iterate (void)
685{
686	de::Random			rnd				(m_seed);
687	const Library&		egl				= m_eglTestCtx.getLibrary();
688	tcu::TestLog&		log				= m_testCtx.getLog();
689	const int			width			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
690	const int			height			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
691	const float			clearRed		= rnd.getFloat();
692	const float			clearGreen		= rnd.getFloat();
693	const float			clearBlue		= rnd.getFloat();
694	const tcu::Vec4		clearColor		(clearRed, clearGreen, clearBlue, 1.0f);
695	tcu::Surface		currentBuffer	(width, height);
696	tcu::Surface		refBuffer		(width, height);
697	Frame				frame			(width, height);
698
699	EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED));
700
701	generateRandomFrame(frame, m_evenFrameDrawType, rnd);
702
703	{
704		// render outside the region
705		EGLint		   bufferAge	= -1;
706		vector<EGLint> damageRegion = getDamageRegion(frame, 0, 0, 0, 0);
707
708		EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_BUFFER_AGE_KHR, &bufferAge));
709		EGLU_CHECK_CALL(egl, setDamageRegionKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size()/4));
710		clearColorScreen(m_gl, clearColor);
711		m_gles2Renderer->render(width, height, frame);
712
713		// next line will make the bug on Nexus 6 disappear
714		// readPixels(m_gl, &currentBuffer);
715	}
716
717	EGLU_CHECK_CALL(egl, swapBuffers(m_eglDisplay, m_eglSurface));
718
719	// render a new frame
720	clearColorScreen(m_gl, clearColor);
721	m_gles2Renderer->render(width, height, frame);
722	clearColorReference(&refBuffer, clearColor);
723	m_refRenderer->render(&refBuffer, frame);
724	readPixels(m_gl, &currentBuffer);
725
726	if (!compareToReference(log, refBuffer, currentBuffer, 0, 0))
727		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail, fail to recover after rendering outside damageRegion");
728	else
729		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
730
731	return STOP;
732}
733
734class RenderBeforeSetDamageRegion : public PartialUpdateTest
735{
736public:
737								RenderBeforeSetDamageRegion		(EglTestContext& eglTestCtx);
738	TestCase::IterateResult		iterate							(void);
739};
740
741RenderBeforeSetDamageRegion::RenderBeforeSetDamageRegion (EglTestContext& eglTestCtx)
742	: PartialUpdateTest (eglTestCtx, vector<DrawType>(1, DRAWTYPE_GLES2_RENDER), vector<DrawType>(1, DRAWTYPE_GLES2_RENDER), "render_before_set_damage_region", "")
743{
744}
745
746TestCase::IterateResult RenderBeforeSetDamageRegion::iterate (void)
747{
748	de::Random			rnd				(m_seed);
749	const Library&		egl				= m_eglTestCtx.getLibrary();
750	tcu::TestLog&		log				= m_testCtx.getLog();
751	const int			width			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
752	const int			height			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
753	const float			clearRed		= rnd.getFloat();
754	const float			clearGreen		= rnd.getFloat();
755	const float			clearBlue		= rnd.getFloat();
756	const tcu::Vec4		clearColor		(clearRed, clearGreen, clearBlue, 1.0f);
757	tcu::Surface		currentBuffer	(width, height);
758	tcu::Surface		refBuffer		(width, height);
759	Frame				frame			(width, height);
760
761	EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED));
762
763	generateRandomFrame(frame, m_evenFrameDrawType, rnd);
764
765	{
766		// render before setDamageRegion
767		EGLint		   bufferAge	= -1;
768		vector<EGLint> damageRegion = getDamageRegion(frame, 0, 0, 0, 0);
769
770		m_gles2Renderer->render(width, height, frame);
771		EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_BUFFER_AGE_KHR, &bufferAge));
772		EGLU_CHECK_CALL(egl, setDamageRegionKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size()/4));
773
774		// next line will make the bug on Nexus 6 disappear
775		// readPixels(m_gl, &currentBuffer);
776	}
777
778	EGLU_CHECK_CALL(egl, swapBuffers(m_eglDisplay, m_eglSurface));
779
780	// render a new frame
781	clearColorScreen(m_gl, clearColor);
782	m_gles2Renderer->render(width, height, frame);
783	clearColorReference(&refBuffer, clearColor);
784	m_refRenderer->render(&refBuffer, frame);
785	readPixels(m_gl, &currentBuffer);
786
787	if (!compareToReference(log, refBuffer, currentBuffer, 0, 0))
788		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
789	else
790		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
791
792	return STOP;
793}
794
795} // anonymous
796
797PartialUpdateTests::PartialUpdateTests (EglTestContext& eglTestCtx)
798	: TestCaseGroup(eglTestCtx, "partial_update", "Partial update tests")
799{
800}
801
802void PartialUpdateTests::init (void)
803{
804	const PartialUpdateTest::DrawType clearRender[2] =
805	{
806		PartialUpdateTest::DRAWTYPE_GLES2_CLEAR,
807		PartialUpdateTest::DRAWTYPE_GLES2_RENDER
808	};
809
810	const PartialUpdateTest::DrawType renderClear[2] =
811	{
812		PartialUpdateTest::DRAWTYPE_GLES2_RENDER,
813		PartialUpdateTest::DRAWTYPE_GLES2_CLEAR
814	};
815
816	vector< vector<PartialUpdateTest::DrawType> > frameDrawTypes;
817	frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> ());
818	frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (1, PartialUpdateTest::DRAWTYPE_GLES2_CLEAR));
819	frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (1, PartialUpdateTest::DRAWTYPE_GLES2_RENDER));
820	frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (2, PartialUpdateTest::DRAWTYPE_GLES2_CLEAR));
821	frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (2, PartialUpdateTest::DRAWTYPE_GLES2_RENDER));
822	frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (DE_ARRAY_BEGIN(clearRender), DE_ARRAY_END(clearRender)));
823	frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (DE_ARRAY_BEGIN(renderClear), DE_ARRAY_END(renderClear)));
824
825	for (size_t evenNdx = 0; evenNdx < frameDrawTypes.size(); evenNdx++)
826	{
827		const vector<PartialUpdateTest::DrawType>& evenFrameDrawType = frameDrawTypes[evenNdx];
828
829		for (size_t oddNdx = evenNdx; oddNdx < frameDrawTypes.size(); oddNdx++)
830		{
831			const vector<PartialUpdateTest::DrawType>&	oddFrameDrawType = frameDrawTypes[oddNdx];
832			const std::string							name			 = generateTestName(oddFrameDrawType, evenFrameDrawType);
833			if (oddFrameDrawType.size() == 0 && evenFrameDrawType.size() == 0)
834				continue;
835
836			addChild(new PartialUpdateTest(m_eglTestCtx, oddFrameDrawType, evenFrameDrawType, name.c_str(), ""));
837		}
838	}
839	addChild(new RenderOutsideDamageRegion(m_eglTestCtx));
840	addChild(new RenderBeforeSetDamageRegion(m_eglTestCtx));
841}
842
843} // egl
844} // deqp
845