1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.0 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 Screen clearing test.
22 *//*--------------------------------------------------------------------*/
23
24#include "es3fColorClearTest.hpp"
25#include "tcuRGBA.hpp"
26#include "tcuSurface.hpp"
27#include "tcuTestLog.hpp"
28#include "gluRenderContext.hpp"
29#include "gluPixelTransfer.hpp"
30#include "tcuVector.hpp"
31#include "tcuRenderTarget.hpp"
32#include "deRandom.hpp"
33#include "deInt32.h"
34
35#include "glwFunctions.hpp"
36#include "glwEnums.hpp"
37
38#include <vector>
39
40using tcu::RGBA;
41using tcu::Surface;
42using tcu::TestLog;
43
44using namespace std;
45
46namespace deqp
47{
48namespace gles3
49{
50namespace Functional
51{
52
53class ColorClearCase : public TestCase
54{
55public:
56	ColorClearCase(Context& context, const char* name, int numIters, int numClearsMin, int numClearsMax, bool testAlpha, bool testScissoring, bool testColorMasks, bool firstClearFull)
57		: TestCase			(context, name, name)
58		, m_numIters		(numIters)
59		, m_numClearsMin	(numClearsMin)
60		, m_numClearsMax	(numClearsMax)
61		, m_testAlpha		(testAlpha)
62		, m_testScissoring	(testScissoring)
63		, m_testColorMasks	(testColorMasks)
64		, m_firstClearFull	(firstClearFull)
65	{
66		m_curIter = 0;
67	}
68
69	virtual ~ColorClearCase (void)
70	{
71	}
72
73	virtual IterateResult iterate (void);
74
75private:
76	const int		m_numIters;
77	const int		m_numClearsMin;
78	const int		m_numClearsMax;
79	const bool		m_testAlpha;
80	const bool		m_testScissoring;
81	const bool		m_testColorMasks;
82	const bool		m_firstClearFull;
83
84	int				m_curIter;
85};
86
87class ClearInfo
88{
89public:
90	ClearInfo (const tcu::IVec4& rect, deUint8 colorMask, tcu::RGBA color)
91		: m_rect(rect), m_colorMask(colorMask), m_color(color)
92	{
93	}
94
95	tcu::IVec4		m_rect;
96	deUint8			m_colorMask;
97	tcu::RGBA		m_color;
98};
99
100TestCase::IterateResult ColorClearCase::iterate (void)
101{
102	TestLog&					log						= m_testCtx.getLog();
103	const glw::Functions&		gl						= m_context.getRenderContext().getFunctions();
104	const tcu::RenderTarget&	renderTarget			= m_context.getRenderTarget();
105	const tcu::PixelFormat&		pixelFormat				= renderTarget.getPixelFormat();
106	const int					targetWidth				= renderTarget.getWidth();
107	const int					targetHeight			= renderTarget.getHeight();
108	const int					numPixels				= targetWidth * targetHeight;
109
110	de::Random					rnd						(deInt32Hash(m_curIter));
111	vector<deUint8>				pixelKnownChannelMask	(numPixels, 0);
112	Surface						refImage				(targetWidth, targetHeight);
113	Surface						resImage				(targetWidth, targetHeight);
114	Surface						diffImage				(targetWidth, targetHeight);
115	int							numClears				= rnd.getUint32() % (m_numClearsMax + 1 - m_numClearsMin) + m_numClearsMin;
116	std::vector<ClearInfo>		clearOps;
117
118	if (m_testScissoring)
119		gl.enable(GL_SCISSOR_TEST);
120
121	for (int clearNdx = 0; clearNdx < numClears; clearNdx++)
122	{
123		// Rectangle.
124		int clearX;
125		int clearY;
126		int clearWidth;
127		int clearHeight;
128		if (!m_testScissoring || (clearNdx == 0 && m_firstClearFull))
129		{
130			clearX		= 0;
131			clearY		= 0;
132			clearWidth	= targetWidth;
133			clearHeight	= targetHeight;
134		}
135		else
136		{
137			clearX		= (rnd.getUint32() % (2*targetWidth)) - targetWidth;
138			clearY		= (rnd.getUint32() % (2*targetHeight)) - targetHeight;
139			clearWidth	= (rnd.getUint32() % targetWidth);
140			clearHeight	= (rnd.getUint32() % targetHeight);
141		}
142		gl.scissor(clearX, clearY, clearWidth, clearHeight);
143
144		// Color.
145		int		r = (int)(rnd.getUint32() & 0xFF);
146		int		g = (int)(rnd.getUint32() & 0xFF);
147		int		b = (int)(rnd.getUint32() & 0xFF);
148		int		a = m_testAlpha ? (int)(rnd.getUint32() & 0xFF) : 0xFF;
149		RGBA	clearCol(r, g, b, a);
150		gl.clearColor(r/255.0f, g/255.0f, b/255.0f, a/255.0f);
151
152		// Mask.
153		deUint8	clearMask;
154		if (!m_testColorMasks || (clearNdx == 0 && m_firstClearFull))
155			clearMask = 0xF;
156		else
157			clearMask = (rnd.getUint32() & 0xF);
158		gl.colorMask((clearMask&0x1) != 0, (clearMask&0x2) != 0, (clearMask&0x4) != 0, (clearMask&0x8) != 0);
159
160		// Clear & store op.
161		gl.clear(GL_COLOR_BUFFER_BIT);
162		clearOps.push_back(ClearInfo(tcu::IVec4(clearX, clearY, clearWidth, clearHeight), clearMask, clearCol));
163
164		// Let watchdog know we're alive.
165		m_testCtx.touchWatchdog();
166	}
167
168	// Compute reference image.
169	{
170		for (int y = 0; y < targetHeight; y++)
171		{
172			std::vector<ClearInfo>	scanlineClearOps;
173
174			// Find all rectangles affecting this scanline.
175			for (int opNdx = 0; opNdx < (int)clearOps.size(); opNdx++)
176			{
177				ClearInfo& op = clearOps[opNdx];
178				if (de::inBounds(y, op.m_rect.y(), op.m_rect.y()+op.m_rect.w()))
179					scanlineClearOps.push_back(op);
180			}
181
182			// Compute reference for scanline.
183			int x = 0;
184			while (x < targetWidth)
185			{
186				tcu::RGBA	spanColor;
187				deUint8		spanKnownMask	= 0;
188				int			spanLength		= (targetWidth - x);
189
190				for (int opNdx = (int)scanlineClearOps.size() - 1; opNdx >= 0; opNdx--)
191				{
192					const ClearInfo& op = scanlineClearOps[opNdx];
193
194					if (de::inBounds(x, op.m_rect.x(), op.m_rect.x()+op.m_rect.z()) &&
195						de::inBounds(y, op.m_rect.y(), op.m_rect.y()+op.m_rect.w()))
196					{
197						// Compute span length until end of given rectangle.
198						spanLength = deMin32(spanLength, op.m_rect.x() + op.m_rect.z() - x);
199
200						tcu::RGBA	clearCol	= op.m_color;
201						deUint8		clearMask	= op.m_colorMask;
202						if ((clearMask & 0x1) && !(spanKnownMask & 0x1)) spanColor.setRed(clearCol.getRed());
203						if ((clearMask & 0x2) && !(spanKnownMask & 0x2)) spanColor.setGreen(clearCol.getGreen());
204						if ((clearMask & 0x4) && !(spanKnownMask & 0x4)) spanColor.setBlue(clearCol.getBlue());
205						if ((clearMask & 0x8) && !(spanKnownMask & 0x8)) spanColor.setAlpha(clearCol.getAlpha());
206						spanKnownMask |= clearMask;
207
208						// Break if have all colors.
209						if (spanKnownMask == 0xF)
210							break;
211					}
212					else if (op.m_rect.x() > x)
213						spanLength = deMin32(spanLength, op.m_rect.x() - x);
214				}
215
216				// Set reference alpha channel to 0xFF, if no alpha in target.
217				if (pixelFormat.alphaBits == 0)
218					spanColor.setAlpha(0xFF);
219
220				// Fill the span.
221				for (int ndx = 0; ndx < spanLength; ndx++)
222				{
223					refImage.setPixel(x + ndx, y, spanColor);
224					pixelKnownChannelMask[y*targetWidth + x + ndx] |= spanKnownMask;
225				}
226
227				x += spanLength;
228			}
229		}
230	}
231
232	glu::readPixels(m_context.getRenderContext(), 0, 0, resImage.getAccess());
233	GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels");
234
235	// Compute difference image.
236	RGBA colorThreshold = pixelFormat.getColorThreshold();
237	RGBA matchColor(0, 255, 0, 255);
238	RGBA diffColor(255, 0, 0, 255);
239	RGBA maxDiff(0, 0, 0, 0);
240	bool isImageOk = true;
241
242	if (gl.isEnabled(GL_DITHER))
243	{
244		colorThreshold.setRed(colorThreshold.getRed() + 1);
245		colorThreshold.setGreen(colorThreshold.getGreen() + 1);
246		colorThreshold.setBlue(colorThreshold.getBlue() + 1);
247		colorThreshold.setAlpha(colorThreshold.getAlpha() + 1);
248	}
249
250	for (int y = 0; y < targetHeight; y++)
251	for (int x = 0; x < targetWidth; x++)
252	{
253		int			offset		= (y*targetWidth + x);
254		RGBA		refRGBA		= refImage.getPixel(x, y);
255		RGBA		resRGBA		= resImage.getPixel(x, y);
256		deUint8		colMask		= pixelKnownChannelMask[offset];
257		RGBA		diff		= computeAbsDiffMasked(refRGBA, resRGBA, colMask);
258		bool		isPixelOk	= diff.isBelowThreshold(colorThreshold);
259
260		diffImage.setPixel(x, y, isPixelOk ? matchColor : diffColor);
261
262		isImageOk	= isImageOk && isPixelOk;
263		maxDiff		= max(maxDiff, diff);
264	}
265
266	if (isImageOk)
267	{
268		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
269	}
270	else
271	{
272		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
273
274		m_testCtx.getLog() << tcu::TestLog::Message << "Image comparison failed, max diff = " << maxDiff << ", threshold = " << colorThreshold << tcu::TestLog::EndMessage;
275
276		log << TestLog::ImageSet("Result", "Resulting framebuffer")
277			<< TestLog::Image("Result",		"Resulting framebuffer",	resImage)
278			<< TestLog::Image("Reference",	"Reference image",			refImage)
279			<< TestLog::Image("DiffMask",	"Failing pixels",			diffImage)
280			<< TestLog::EndImageSet;
281		return TestCase::STOP;
282	}
283
284	bool isFinal = (++m_curIter == m_numIters);
285
286	// On final frame, dump images.
287	if (isFinal)
288	{
289		log << TestLog::ImageSet("Result", "Resulting framebuffer")
290			<< TestLog::Image("Result",		"Resulting framebuffer",	resImage)
291			<< TestLog::EndImageSet;
292	}
293
294	return isFinal ? TestCase::STOP : TestCase::CONTINUE;
295}
296
297ColorClearTest::ColorClearTest (Context& context) : TestCaseGroup(context, "color_clear", "Color Clear Tests")
298{
299}
300
301ColorClearTest::~ColorClearTest (void)
302{
303}
304
305void ColorClearTest::init (void)
306{
307	//										name					iters,	#..#,		alpha?,	scissor,masks,	1stfull?
308	addChild(new ColorClearCase(m_context, "single_rgb",			30,		1,3,		false,	false,	false,	true	));
309	addChild(new ColorClearCase(m_context, "single_rgba",			30,		1,3,		true,	false,	false,	true	));
310	addChild(new ColorClearCase(m_context, "multiple_rgb",			15,		4,20,		false,	false,	false,	true	));
311	addChild(new ColorClearCase(m_context, "multiple_rgba",			15,		4,20,		true,	false,	false,	true	));
312	addChild(new ColorClearCase(m_context, "long_rgb",				2,		100,500,	false,	false,	false,	true	));
313	addChild(new ColorClearCase(m_context, "long_rgba",				2,		100,500,	true,	false,	false,	true	));
314	addChild(new ColorClearCase(m_context, "subclears_rgb",			15,		4,30,		false,	false,	false,	false	));
315	addChild(new ColorClearCase(m_context, "subclears_rgba",		15,		4,30,		true,	false,	false,	false	));
316	addChild(new ColorClearCase(m_context, "short_scissored_rgb",	30,		2,4,		false,	true,	false,	true	));
317	addChild(new ColorClearCase(m_context, "scissored_rgb",			15,		4,30,		false,	true,	false,	true	));
318	addChild(new ColorClearCase(m_context, "scissored_rgba",		15,		4,30,		true,	true,	false,	true	));
319	addChild(new ColorClearCase(m_context, "masked_rgb",			15,		4,30,		false,	true,	false,	true	));
320	addChild(new ColorClearCase(m_context, "masked_rgba",			15,		4,30,		true,	true,	false,	true	));
321	addChild(new ColorClearCase(m_context, "masked_scissored_rgb",	15,		4,30,		false,	true,	true,	true	));
322	addChild(new ColorClearCase(m_context, "masked_scissored_rgba",	15,		4,30,		true,	true,	true,	true	));
323	addChild(new ColorClearCase(m_context, "complex_rgb",			15,		5,50,		false,	true,	true,	false	));
324	addChild(new ColorClearCase(m_context, "complex_rgba",			15,		5,50,		true,	true,	true,	false	));
325	addChild(new ColorClearCase(m_context, "long_masked_rgb",		2,		100,500,	false,	true,	true,	true	));
326	addChild(new ColorClearCase(m_context, "long_masked_rgba",		2,		100,500,	true,	true,	true,	true	));
327}
328
329} // Functional
330} // gles3
331} // deqp
332