• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 Dithering tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es3fDitheringTests.hpp"
25 #include "gluRenderContext.hpp"
26 #include "gluDefs.hpp"
27 #include "glsFragmentOpUtil.hpp"
28 #include "gluPixelTransfer.hpp"
29 #include "tcuRenderTarget.hpp"
30 #include "tcuRGBA.hpp"
31 #include "tcuVector.hpp"
32 #include "tcuPixelFormat.hpp"
33 #include "tcuTestLog.hpp"
34 #include "tcuSurface.hpp"
35 #include "tcuCommandLine.hpp"
36 #include "deRandom.hpp"
37 #include "deStringUtil.hpp"
38 #include "deString.h"
39 #include "deMath.h"
40 
41 #include <string>
42 #include <algorithm>
43 
44 #include "glw.h"
45 
46 namespace deqp
47 {
48 
49 using tcu::Vec4;
50 using tcu::IVec4;
51 using tcu::TestLog;
52 using gls::FragmentOpUtil::QuadRenderer;
53 using gls::FragmentOpUtil::Quad;
54 using tcu::PixelFormat;
55 using tcu::Surface;
56 using de::Random;
57 using std::vector;
58 using std::string;
59 
60 namespace gles3
61 {
62 namespace Functional
63 {
64 
65 static const char* const s_channelNames[4] = { "red", "green", "blue", "alpha" };
66 
pixelFormatToIVec4(const PixelFormat & format)67 static inline IVec4 pixelFormatToIVec4 (const PixelFormat& format)
68 {
69 	return IVec4(format.redBits, format.greenBits, format.blueBits, format.alphaBits);
70 }
71 
72 template<typename T>
choiceListStr(const vector<T> & choices)73 static inline string choiceListStr (const vector<T>& choices)
74 {
75 	string result;
76 	for (int i = 0; i < (int)choices.size(); i++)
77 	{
78 		if (i == (int)choices.size()-1)
79 			result += " or ";
80 		else if (i > 0)
81 			result += ", ";
82 		result += de::toString(choices[i]);
83 	}
84 	return result;
85 }
86 
87 class DitheringCase : public tcu::TestCase
88 {
89 public:
90 	enum PatternType
91 	{
92 		PATTERNTYPE_GRADIENT = 0,
93 		PATTERNTYPE_UNICOLORED_QUAD,
94 
95 		PATTERNTYPE_LAST
96 	};
97 
98 											DitheringCase				(tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* description, bool isEnabled, PatternType patternType, const tcu::Vec4& color);
99 											~DitheringCase				(void);
100 
101 	IterateResult							iterate						(void);
102 	void									init						(void);
103 	void									deinit						(void);
104 
105 	static const char*						getPatternTypeName			(PatternType type);
106 
107 private:
108 	bool									checkColor					(const tcu::Vec4& inputClr, const tcu::RGBA& renderedClr, bool logErrors, const bool incTol) const;
109 
110 	bool									drawAndCheckGradient		(bool isVerticallyIncreasing, const tcu::Vec4& highColor) const;
111 	bool									drawAndCheckUnicoloredQuad	(const tcu::Vec4& color) const;
112 
113 	const glu::RenderContext&				m_renderCtx;
114 
115 	const bool								m_ditheringEnabled;
116 	const PatternType						m_patternType;
117 	const tcu::Vec4							m_color;
118 
119 	const tcu::PixelFormat					m_renderFormat;
120 
121 	const QuadRenderer*						m_renderer;
122 	int										m_iteration;
123 };
124 
getPatternTypeName(const PatternType type)125 const char* DitheringCase::getPatternTypeName (const PatternType type)
126 {
127 	switch (type)
128 	{
129 		case PATTERNTYPE_GRADIENT:			return "gradient";
130 		case PATTERNTYPE_UNICOLORED_QUAD:	return "unicolored_quad";
131 		default:
132 			DE_ASSERT(false);
133 			return DE_NULL;
134 	}
135 }
136 
137 
DitheringCase(tcu::TestContext & testCtx,glu::RenderContext & renderCtx,const char * const name,const char * const description,const bool ditheringEnabled,const PatternType patternType,const Vec4 & color)138 DitheringCase::DitheringCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* const name, const char* const description, const bool ditheringEnabled, const PatternType patternType, const Vec4& color)
139 	: TestCase				(testCtx, name, description)
140 	, m_renderCtx			(renderCtx)
141 	, m_ditheringEnabled	(ditheringEnabled)
142 	, m_patternType			(patternType)
143 	, m_color				(color)
144 	, m_renderFormat		(renderCtx.getRenderTarget().getPixelFormat())
145 	, m_renderer			(DE_NULL)
146 	, m_iteration			(0)
147 {
148 }
149 
~DitheringCase(void)150 DitheringCase::~DitheringCase (void)
151 {
152 	DitheringCase::deinit();
153 }
154 
init(void)155 void DitheringCase::init (void)
156 {
157 	DE_ASSERT(!m_renderer);
158 	m_renderer = new QuadRenderer(m_renderCtx, glu::GLSL_VERSION_300_ES);
159 	m_iteration = 0;
160 }
161 
deinit(void)162 void DitheringCase::deinit (void)
163 {
164 	delete m_renderer;
165 	m_renderer = DE_NULL;
166 }
167 
checkColor(const Vec4 & inputClr,const tcu::RGBA & renderedClr,const bool logErrors,const bool incTol) const168 bool DitheringCase::checkColor (const Vec4& inputClr, const tcu::RGBA& renderedClr, const bool logErrors, const bool incTol) const
169 {
170 	const IVec4		channelBits		= pixelFormatToIVec4(m_renderFormat);
171 	bool			allChannelsOk	= true;
172 
173 	for (int chanNdx = 0; chanNdx < 4; chanNdx++)
174 	{
175 		if (channelBits[chanNdx] == 0)
176 			continue;
177 
178 		const int		channelMax			= (1 << channelBits[chanNdx]) - 1;
179 		const float		scaledInput			= inputClr[chanNdx] * (float)channelMax;
180 		const bool		useRoundingMargin	= deFloatAbs(scaledInput - deFloatRound(scaledInput)) < 0.0001f;
181 		vector<int>		channelChoices;
182 
183 		channelChoices.push_back(de::min(channelMax,	(int)deFloatCeil(scaledInput)));
184 		channelChoices.push_back(de::max(0,				(int)deFloatCeil(scaledInput) - 1));
185 		// Allow for more tolerance for small dimension render targets
186 		if (incTol)
187 		{
188 			channelChoices.push_back(de::max(0,(int)deFloatCeil(scaledInput) - 2));
189 			channelChoices.push_back(de::max(0,(int)deFloatCeil(scaledInput) + 1));
190 		}
191 
192 		// If the input color results in a scaled value that is very close to an integer, account for a little bit of possible inaccuracy.
193 		if (useRoundingMargin)
194 		{
195 			if (scaledInput > deFloatRound(scaledInput))
196 				channelChoices.push_back((int)deFloatCeil(scaledInput) - 2);
197 			else
198 				channelChoices.push_back((int)deFloatCeil(scaledInput) + 1);
199 		}
200 
201 		std::sort(channelChoices.begin(), channelChoices.end());
202 
203 		{
204 			const int		renderedClrInFormat	= (int)deFloatRound((float)(renderedClr.toIVec()[chanNdx] * channelMax) / 255.0f);
205 			bool			goodChannel			= false;
206 
207 			for (int i = 0; i < (int)channelChoices.size(); i++)
208 			{
209 				if (renderedClrInFormat == channelChoices[i])
210 				{
211 					goodChannel = true;
212 					break;
213 				}
214 			}
215 
216 			if (!goodChannel)
217 			{
218 				if (logErrors)
219 				{
220 					m_testCtx.getLog() << TestLog::Message
221 									   << "Failure: " << channelBits[chanNdx] << "-bit " << s_channelNames[chanNdx] << " channel is " << renderedClrInFormat
222 									   << ", should be " << choiceListStr(channelChoices)
223 									   << " (corresponding fragment color channel is " << inputClr[chanNdx] << ")"
224 									   << TestLog::EndMessage
225 									   << TestLog::Message
226 									   << "Note: " << inputClr[chanNdx] << " * (" << channelMax + 1 << "-1) = " << scaledInput
227 									   << TestLog::EndMessage;
228 
229 					if (useRoundingMargin)
230 					{
231 						m_testCtx.getLog() << TestLog::Message
232 										   << "Note: one extra color candidate was allowed because fragmentColorChannel * (2^bits-1) is close to an integer"
233 										   << TestLog::EndMessage;
234 					}
235 				}
236 
237 				allChannelsOk = false;
238 			}
239 		}
240 	}
241 
242 	return allChannelsOk;
243 }
244 
drawAndCheckGradient(const bool isVerticallyIncreasing,const Vec4 & highColor) const245 bool DitheringCase::drawAndCheckGradient (const bool isVerticallyIncreasing, const Vec4& highColor) const
246 {
247 	TestLog&					log					= m_testCtx.getLog();
248 	Random						rnd					(deStringHash(getName()));
249 	const int					maxViewportWid		= 256;
250 	const int					maxViewportHei		= 256;
251 	const int					viewportWid			= de::min(m_renderCtx.getRenderTarget().getWidth(), maxViewportWid);
252 	const int					viewportHei			= de::min(m_renderCtx.getRenderTarget().getHeight(), maxViewportHei);
253 	const int					viewportX			= rnd.getInt(0, m_renderCtx.getRenderTarget().getWidth() - viewportWid);
254 	const int					viewportY			= rnd.getInt(0, m_renderCtx.getRenderTarget().getHeight() - viewportHei);
255 	const Vec4					quadClr0			(0.0f, 0.0f, 0.0f, 0.0f);
256 	const Vec4&					quadClr1			= highColor;
257 	Quad						quad;
258 	Surface						renderedImg			(viewportWid, viewportHei);
259 
260 	GLU_CHECK_CALL(glViewport(viewportX, viewportY, viewportWid, viewportHei));
261 
262 	log << TestLog::Message << "Dithering is " << (m_ditheringEnabled ? "enabled" : "disabled") << TestLog::EndMessage;
263 
264 	if (m_ditheringEnabled)
265 		GLU_CHECK_CALL(glEnable(GL_DITHER));
266 	else
267 		GLU_CHECK_CALL(glDisable(GL_DITHER));
268 
269 	log << TestLog::Message << "Drawing a " << (isVerticallyIncreasing ? "vertically" : "horizontally") << " increasing gradient" << TestLog::EndMessage;
270 
271 	quad.color[0] = quadClr0;
272 	quad.color[1] = isVerticallyIncreasing ? quadClr1 : quadClr0;
273 	quad.color[2] = isVerticallyIncreasing ? quadClr0 : quadClr1;
274 	quad.color[3] = quadClr1;
275 
276 	m_renderer->render(quad);
277 
278 	glu::readPixels(m_renderCtx, viewportX, viewportY, renderedImg.getAccess());
279 	GLU_CHECK_MSG("glReadPixels()");
280 
281 	log << TestLog::Image(isVerticallyIncreasing ? "VerGradient"		: "HorGradient",
282 						  isVerticallyIncreasing ? "Vertical gradient"	: "Horizontal gradient",
283 						  renderedImg);
284 
285 	// Validate, at each pixel, that each color channel is one of its two allowed values.
286 
287 	{
288 		Surface		errorMask		(viewportWid, viewportHei);
289 		bool		colorChoicesOk	= true;
290 
291 		for (int y = 0; y < renderedImg.getHeight(); y++)
292 		{
293 			for (int x = 0; x < renderedImg.getWidth(); x++)
294 			{
295 				const float		inputF		= ((float)(isVerticallyIncreasing ? y : x) + 0.5f) / (float)(isVerticallyIncreasing ? renderedImg.getHeight() : renderedImg.getWidth());
296 				const Vec4		inputClr	= (1.0f-inputF)*quadClr0 + inputF*quadClr1;
297 				const bool              increaseTol     = ((renderedImg.getWidth() < 300) || (renderedImg.getHeight() < 300)) ? true : false;
298 
299 				if (!checkColor(inputClr, renderedImg.getPixel(x, y), colorChoicesOk, increaseTol))
300 				{
301 					errorMask.setPixel(x, y, tcu::RGBA::red());
302 
303 					if (colorChoicesOk)
304 					{
305 						log << TestLog::Message << "First failure at pixel (" << x << ", " << y << ") (not printing further errors)" << TestLog::EndMessage;
306 						colorChoicesOk = false;
307 					}
308 				}
309 				else
310 					errorMask.setPixel(x, y, tcu::RGBA::green());
311 			}
312 		}
313 
314 		if (!colorChoicesOk)
315 		{
316 			log << TestLog::Image("ColorChoiceErrorMask", "Error mask for color choices", errorMask);
317 			return false;
318 		}
319 	}
320 
321 	// When dithering is disabled, the color selection must be coordinate-independent - i.e. the colors must be constant in the gradient's constant direction.
322 
323 	if (!m_ditheringEnabled)
324 	{
325 		const int	increasingDirectionSize	= isVerticallyIncreasing ? renderedImg.getHeight() : renderedImg.getWidth();
326 		const int	constantDirectionSize	= isVerticallyIncreasing ? renderedImg.getWidth() : renderedImg.getHeight();
327 
328 		for (int incrPos = 0; incrPos < increasingDirectionSize; incrPos++)
329 		{
330 			bool		colorHasChanged			= false;
331 			tcu::RGBA	prevConstantDirectionPix;
332 
333 			for (int constPos = 0; constPos < constantDirectionSize; constPos++)
334 			{
335 				const int			x		= isVerticallyIncreasing ? constPos : incrPos;
336 				const int			y		= isVerticallyIncreasing ? incrPos : constPos;
337 				const tcu::RGBA		clr		= renderedImg.getPixel(x, y);
338 
339 				if (constPos > 0 && clr != prevConstantDirectionPix)
340 				{
341 					if (colorHasChanged)
342 					{
343 						log << TestLog::Message
344 							<< "Failure: colors should be constant per " << (isVerticallyIncreasing ? "row" : "column")
345 							<< " (since dithering is disabled), but the color at position (" << x << ", " << y << ") is " << clr
346 							<< " and does not equal the color at (" << (isVerticallyIncreasing ? x-1 : x) << ", " << (isVerticallyIncreasing ? y : y-1) << "), which is " << prevConstantDirectionPix
347 							<< TestLog::EndMessage;
348 
349 						return false;
350 					}
351 					else
352 						colorHasChanged = true;
353 				}
354 
355 				prevConstantDirectionPix = clr;
356 			}
357 		}
358 	}
359 
360 	return true;
361 }
362 
drawAndCheckUnicoloredQuad(const Vec4 & quadColor) const363 bool DitheringCase::drawAndCheckUnicoloredQuad (const Vec4& quadColor) const
364 {
365 	TestLog&					log					= m_testCtx.getLog();
366 	Random						rnd					(deStringHash(getName()));
367 	const int					maxViewportWid		= 32;
368 	const int					maxViewportHei		= 32;
369 	const int					viewportWid			= de::min(m_renderCtx.getRenderTarget().getWidth(), maxViewportWid);
370 	const int					viewportHei			= de::min(m_renderCtx.getRenderTarget().getHeight(), maxViewportHei);
371 	const int					viewportX			= rnd.getInt(0, m_renderCtx.getRenderTarget().getWidth() - viewportWid);
372 	const int					viewportY			= rnd.getInt(0, m_renderCtx.getRenderTarget().getHeight() - viewportHei);
373 	Quad						quad;
374 	Surface						renderedImg			(viewportWid, viewportHei);
375 
376 	GLU_CHECK_CALL(glViewport(viewportX, viewportY, viewportWid, viewportHei));
377 
378 	log << TestLog::Message << "Dithering is " << (m_ditheringEnabled ? "enabled" : "disabled") << TestLog::EndMessage;
379 
380 	if (m_ditheringEnabled)
381 		GLU_CHECK_CALL(glEnable(GL_DITHER));
382 	else
383 		GLU_CHECK_CALL(glDisable(GL_DITHER));
384 
385 	log << TestLog::Message << "Drawing an unicolored quad with color " << quadColor << TestLog::EndMessage;
386 
387 	quad.color[0] = quadColor;
388 	quad.color[1] = quadColor;
389 	quad.color[2] = quadColor;
390 	quad.color[3] = quadColor;
391 
392 	m_renderer->render(quad);
393 
394 	glu::readPixels(m_renderCtx, viewportX, viewportY, renderedImg.getAccess());
395 	GLU_CHECK_MSG("glReadPixels()");
396 
397 	log << TestLog::Image(("Quad" + de::toString(m_iteration)).c_str(), ("Quad " + de::toString(m_iteration)).c_str(), renderedImg);
398 
399 	// Validate, at each pixel, that each color channel is one of its two allowed values.
400 
401 	{
402 		Surface		errorMask		(viewportWid, viewportHei);
403 		bool		colorChoicesOk	= true;
404 
405 		for (int y = 0; y < renderedImg.getHeight(); y++)
406 		{
407 			for (int x = 0; x < renderedImg.getWidth(); x++)
408 			{
409 				if (!checkColor(quadColor, renderedImg.getPixel(x, y), colorChoicesOk, false))
410 				{
411 					errorMask.setPixel(x, y, tcu::RGBA::red());
412 
413 					if (colorChoicesOk)
414 					{
415 						log << TestLog::Message << "First failure at pixel (" << x << ", " << y << ") (not printing further errors)" << TestLog::EndMessage;
416 						colorChoicesOk = false;
417 					}
418 				}
419 				else
420 					errorMask.setPixel(x, y, tcu::RGBA::green());
421 			}
422 		}
423 
424 		if (!colorChoicesOk)
425 		{
426 			log << TestLog::Image("ColorChoiceErrorMask", "Error mask for color choices", errorMask);
427 			return false;
428 		}
429 	}
430 
431 	// When dithering is disabled, the color selection must be coordinate-independent - i.e. the entire rendered image must be unicolored.
432 
433 	if (!m_ditheringEnabled)
434 	{
435 		const tcu::RGBA renderedClr00 = renderedImg.getPixel(0, 0);
436 
437 		for (int y = 0; y < renderedImg.getHeight(); y++)
438 		{
439 			for (int x = 0; x < renderedImg.getWidth(); x++)
440 			{
441 				const tcu::RGBA curClr = renderedImg.getPixel(x, y);
442 
443 				if (curClr != renderedClr00)
444 				{
445 					log << TestLog::Message
446 						<< "Failure: color at (" << x << ", " << y << ") is " << curClr
447 						<< " and does not equal the color at (0, 0), which is " << renderedClr00
448 						<< TestLog::EndMessage;
449 
450 					return false;
451 				}
452 			}
453 		}
454 	}
455 
456 	return true;
457 }
458 
iterate(void)459 DitheringCase::IterateResult DitheringCase::iterate (void)
460 {
461 	if (m_patternType == PATTERNTYPE_GRADIENT)
462 	{
463 		// Draw horizontal and vertical gradients.
464 
465 		DE_ASSERT(m_iteration < 2);
466 
467 		const bool success = drawAndCheckGradient(m_iteration == 1, m_color);
468 
469 		if (!success)
470 		{
471 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
472 			return STOP;
473 		}
474 
475 		if (m_iteration == 1)
476 		{
477 			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
478 			return STOP;
479 		}
480 	}
481 	else if (m_patternType == PATTERNTYPE_UNICOLORED_QUAD)
482 	{
483 		const int numQuads = m_testCtx.getCommandLine().getTestIterationCount() > 0 ? m_testCtx.getCommandLine().getTestIterationCount() : 30;
484 
485 		DE_ASSERT(m_iteration < numQuads);
486 
487 		const Vec4 quadColor	= (float)m_iteration / (float)(numQuads-1) * m_color;
488 		const bool success		=  drawAndCheckUnicoloredQuad(quadColor);
489 
490 		if (!success)
491 		{
492 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
493 			return STOP;
494 		}
495 
496 		if (m_iteration == numQuads - 1)
497 		{
498 			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
499 			return STOP;
500 		}
501 	}
502 	else
503 		DE_ASSERT(false);
504 
505 	m_iteration++;
506 
507 	return CONTINUE;
508 }
509 
DitheringTests(Context & context)510 DitheringTests::DitheringTests (Context& context)
511 	: TestCaseGroup(context, "dither", "Dithering tests")
512 {
513 }
514 
~DitheringTests(void)515 DitheringTests::~DitheringTests (void)
516 {
517 }
518 
init(void)519 void DitheringTests::init (void)
520 {
521 	static const struct
522 	{
523 		const char*		name;
524 		Vec4			color;
525 	} caseColors[] =
526 	{
527 		{ "white",		Vec4(1.0f, 1.0f, 1.0f, 1.0f) },
528 		{ "red",		Vec4(1.0f, 0.0f, 0.0f, 1.0f) },
529 		{ "green",		Vec4(0.0f, 1.0f, 0.0f, 1.0f) },
530 		{ "blue",		Vec4(0.0f, 0.0f, 1.0f, 1.0f) },
531 		{ "alpha",		Vec4(0.0f, 0.0f, 0.0f, 1.0f) }
532 	};
533 
534 	for (int ditheringEnabledI = 0; ditheringEnabledI <= 1; ditheringEnabledI++)
535 	{
536 		const bool				ditheringEnabled	= ditheringEnabledI != 0;
537 		TestCaseGroup* const	group				= new TestCaseGroup(m_context, ditheringEnabled ? "enabled" : "disabled", "");
538 		addChild(group);
539 
540 		for (int patternTypeI = 0; patternTypeI < DitheringCase::PATTERNTYPE_LAST; patternTypeI++)
541 		{
542 			for (int caseColorNdx = 0; caseColorNdx < DE_LENGTH_OF_ARRAY(caseColors); caseColorNdx++)
543 			{
544 				const DitheringCase::PatternType	patternType		= (DitheringCase::PatternType)patternTypeI;
545 				const string						caseName		= string("") + DitheringCase::getPatternTypeName(patternType) + "_" + caseColors[caseColorNdx].name;
546 
547 				group->addChild(new DitheringCase(m_context.getTestContext(), m_context.getRenderContext(), caseName.c_str(), "", ditheringEnabled, patternType, caseColors[caseColorNdx].color));
548 			}
549 		}
550 	}
551 }
552 
553 } // Functional
554 } // gles3
555 } // deqp
556