1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 2.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 Blend tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es2fBlendTests.hpp"
25 #include "glsFragmentOpUtil.hpp"
26 #include "gluPixelTransfer.hpp"
27 #include "gluStrUtil.hpp"
28 #include "tcuSurface.hpp"
29 #include "tcuImageCompare.hpp"
30 #include "tcuRenderTarget.hpp"
31 #include "tcuTestLog.hpp"
32 #include "deRandom.hpp"
33 #include "rrFragmentOperations.hpp"
34 #include "sglrReferenceUtils.hpp"
35 
36 #include "glw.h"
37 
38 #include <string>
39 #include <vector>
40 
41 namespace deqp
42 {
43 
44 using gls::FragmentOpUtil::Quad;
45 using gls::FragmentOpUtil::IntegerQuad;
46 using gls::FragmentOpUtil::QuadRenderer;
47 using gls::FragmentOpUtil::ReferenceQuadRenderer;
48 using glu::getBlendEquationName;
49 using glu::getBlendFactorName;
50 using tcu::Vec4;
51 using tcu::UVec4;
52 using tcu::TestLog;
53 using tcu::Surface;
54 using tcu::TextureFormat;
55 using tcu::TextureLevel;
56 using std::string;
57 using std::vector;
58 
59 namespace gles2
60 {
61 namespace Functional
62 {
63 
64 static const int MAX_VIEWPORT_WIDTH		= 64;
65 static const int MAX_VIEWPORT_HEIGHT	= 64;
66 
67 struct BlendParams
68 {
69 	GLenum	equationRGB;
70 	GLenum	srcFuncRGB;
71 	GLenum	dstFuncRGB;
72 	GLenum	equationAlpha;
73 	GLenum	srcFuncAlpha;
74 	GLenum	dstFuncAlpha;
75 	Vec4	blendColor;
76 
BlendParamsdeqp::gles2::Functional::BlendParams77 	BlendParams (GLenum		equationRGB_,
78 				 GLenum		srcFuncRGB_,
79 				 GLenum		dstFuncRGB_,
80 				 GLenum		equationAlpha_,
81 				 GLenum		srcFuncAlpha_,
82 				 GLenum		dstFuncAlpha_,
83 				 Vec4		blendColor_)
84 	: equationRGB	(equationRGB_)
85 	, srcFuncRGB	(srcFuncRGB_)
86 	, dstFuncRGB	(dstFuncRGB_)
87 	, equationAlpha	(equationAlpha_)
88 	, srcFuncAlpha	(srcFuncAlpha_)
89 	, dstFuncAlpha	(dstFuncAlpha_)
90 	, blendColor	(blendColor_)
91 	{
92 	}
93 };
94 
95 class BlendCase : public TestCase
96 {
97 public:
98 							BlendCase	(Context&						context,
99 										 const char*					name,
100 										 const char*					desc,
101 										 const vector<BlendParams>&		paramSets);
102 
103 							~BlendCase	(void);
104 
105 	void					init		(void);
106 	void					deinit		(void);
107 
108 	IterateResult			iterate		(void);
109 
110 private:
111 							BlendCase	(const BlendCase& other);
112 	BlendCase&				operator=	(const BlendCase& other);
113 
114 	vector<BlendParams>		m_paramSets;
115 	int						m_curParamSetNdx;
116 
117 	QuadRenderer*			m_renderer;
118 	ReferenceQuadRenderer*	m_referenceRenderer;
119 	TextureLevel*			m_refColorBuffer;
120 	Quad					m_firstQuad;
121 	Quad					m_secondQuad;
122 	IntegerQuad				m_firstQuadInt;
123 	IntegerQuad				m_secondQuadInt;
124 
125 	int						m_viewportW;
126 	int						m_viewportH;
127 };
128 
BlendCase(Context & context,const char * name,const char * desc,const vector<BlendParams> & paramSets)129 BlendCase::BlendCase (Context&						context,
130 					  const char*					name,
131 					  const char*					desc,
132 					  const vector<BlendParams>&	paramSets)
133 	: TestCase				(context, name, desc)
134 	, m_paramSets			(paramSets)
135 	, m_curParamSetNdx		(0)
136 	, m_renderer			(DE_NULL)
137 	, m_referenceRenderer	(DE_NULL)
138 	, m_refColorBuffer		(DE_NULL)
139 	, m_viewportW			(0)
140 	, m_viewportH			(0)
141 {
142 	DE_ASSERT(!m_paramSets.empty());
143 	for (int i = 0; i < (int)m_paramSets.size(); i++)
144 		DE_ASSERT(m_paramSets[i].dstFuncRGB != GL_SRC_ALPHA_SATURATE && m_paramSets[i].dstFuncAlpha != GL_SRC_ALPHA_SATURATE);
145 }
146 
init(void)147 void BlendCase::init (void)
148 {
149 	bool useRGB = m_context.getRenderTarget().getPixelFormat().alphaBits == 0;
150 
151 	static const Vec4 baseGradientColors[4] =
152 	{
153 		Vec4(0.0f, 0.5f, 1.0f, 0.5f),
154 		Vec4(0.5f, 0.0f, 0.5f, 1.0f),
155 		Vec4(0.5f, 1.0f, 0.5f, 0.0f),
156 		Vec4(1.0f, 0.5f, 0.0f, 0.5f)
157 	};
158 
159 	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(m_firstQuad.color) == DE_LENGTH_OF_ARRAY(m_firstQuadInt.color));
160 	for (int i = 0; i < DE_LENGTH_OF_ARRAY(m_firstQuad.color); i++)
161 	{
162 		m_firstQuad.color[i]		= (baseGradientColors[i] - 0.5f) * 0.2f + 0.5f;
163 		m_firstQuadInt.color[i]		= m_firstQuad.color[i];
164 
165 		m_secondQuad.color[i]		= (Vec4(1.0f) - baseGradientColors[i] - 0.5f) * 1.0f + 0.5f;
166 		m_secondQuadInt.color[i]	= m_secondQuad.color[i];
167 	}
168 
169 	m_viewportW = de::min<int>(m_context.getRenderTarget().getWidth(),	MAX_VIEWPORT_WIDTH);
170 	m_viewportH = de::min<int>(m_context.getRenderTarget().getHeight(),	MAX_VIEWPORT_HEIGHT);
171 
172 	m_firstQuadInt.posA		= tcu::IVec2(0,					0);
173 	m_secondQuadInt.posA	= tcu::IVec2(0,					0);
174 	m_firstQuadInt.posB		= tcu::IVec2(m_viewportW-1,		m_viewportH-1);
175 	m_secondQuadInt.posB	= tcu::IVec2(m_viewportW-1,		m_viewportH-1);
176 
177 	DE_ASSERT(!m_renderer);
178 	DE_ASSERT(!m_referenceRenderer);
179 	DE_ASSERT(!m_refColorBuffer);
180 
181 	m_renderer				= new QuadRenderer(m_context.getRenderContext(), glu::GLSL_VERSION_100_ES);
182 	m_referenceRenderer		= new ReferenceQuadRenderer;
183 	m_refColorBuffer		= new TextureLevel(TextureFormat(useRGB ? TextureFormat::RGB : TextureFormat::RGBA, TextureFormat::UNORM_INT8),
184 											   m_viewportW, m_viewportH);
185 
186 	m_curParamSetNdx = 0;
187 }
188 
~BlendCase(void)189 BlendCase::~BlendCase (void)
190 {
191 	delete m_renderer;
192 	delete m_referenceRenderer;
193 	delete m_refColorBuffer;
194 }
195 
deinit(void)196 void BlendCase::deinit (void)
197 {
198 	delete m_renderer;
199 	delete m_referenceRenderer;
200 	delete m_refColorBuffer;
201 
202 	m_renderer			= DE_NULL;
203 	m_referenceRenderer	= DE_NULL;
204 	m_refColorBuffer	= DE_NULL;
205 }
206 
iterate(void)207 BlendCase::IterateResult BlendCase::iterate (void)
208 {
209 	de::Random						rnd				(deStringHash(getName()) ^ deInt32Hash(m_curParamSetNdx));
210 	int								viewportX		= rnd.getInt(0, m_context.getRenderTarget().getWidth() - m_viewportW);
211 	int								viewportY		= rnd.getInt(0, m_context.getRenderTarget().getHeight() - m_viewportH);
212 	tcu::Surface					renderedImg		(m_viewportW, m_viewportH);
213 	TestLog&						log				(m_testCtx.getLog());
214 	const BlendParams&				paramSet		= m_paramSets[m_curParamSetNdx];
215 	rr::FragmentOperationState		referenceState;
216 
217 	// Log the blend parameters.
218 
219 	log << TestLog::Message << "RGB equation = " << getBlendEquationName(paramSet.equationRGB) << TestLog::EndMessage;
220 	log << TestLog::Message << "RGB src func = " << getBlendFactorName(paramSet.srcFuncRGB) << TestLog::EndMessage;
221 	log << TestLog::Message << "RGB dst func = " << getBlendFactorName(paramSet.dstFuncRGB) << TestLog::EndMessage;
222 	log << TestLog::Message << "Alpha equation = " << getBlendEquationName(paramSet.equationAlpha) << TestLog::EndMessage;
223 	log << TestLog::Message << "Alpha src func = " << getBlendFactorName(paramSet.srcFuncAlpha) << TestLog::EndMessage;
224 	log << TestLog::Message << "Alpha dst func = " << getBlendFactorName(paramSet.dstFuncAlpha) << TestLog::EndMessage;
225 	log << TestLog::Message << "Blend color = (" << paramSet.blendColor.x() << ", " << paramSet.blendColor.y() << ", " << paramSet.blendColor.z() << ", " << paramSet.blendColor.w() << ")" << TestLog::EndMessage;
226 
227 	// Set GL state.
228 
229 	GLU_CHECK_CALL(glBlendEquationSeparate(paramSet.equationRGB, paramSet.equationAlpha));
230 	GLU_CHECK_CALL(glBlendFuncSeparate(paramSet.srcFuncRGB, paramSet.dstFuncRGB, paramSet.srcFuncAlpha, paramSet.dstFuncAlpha));
231 	GLU_CHECK_CALL(glBlendColor(paramSet.blendColor.x(), paramSet.blendColor.y(), paramSet.blendColor.z(), paramSet.blendColor.w()));
232 
233 	// Set reference state.
234 
235 	referenceState.blendRGBState.equation	= sglr::rr_util::mapGLBlendEquation(paramSet.equationRGB);
236 	referenceState.blendRGBState.srcFunc	= sglr::rr_util::mapGLBlendFunc(paramSet.srcFuncRGB);
237 	referenceState.blendRGBState.dstFunc	= sglr::rr_util::mapGLBlendFunc(paramSet.dstFuncRGB);
238 	referenceState.blendAState.equation		= sglr::rr_util::mapGLBlendEquation(paramSet.equationAlpha);
239 	referenceState.blendAState.srcFunc		= sglr::rr_util::mapGLBlendFunc(paramSet.srcFuncAlpha);
240 	referenceState.blendAState.dstFunc		= sglr::rr_util::mapGLBlendFunc(paramSet.dstFuncAlpha);
241 	referenceState.blendColor				= paramSet.blendColor;
242 
243 	// Render with GL.
244 
245 	glDisable(GL_BLEND);
246 	glViewport(viewportX, viewportY, m_viewportW, m_viewportH);
247 	m_renderer->render(m_firstQuad);
248 	glEnable(GL_BLEND);
249 	m_renderer->render(m_secondQuad);
250 	glFlush();
251 
252 	// Render reference.
253 
254 	const tcu::PixelBufferAccess nullAccess(TextureFormat(), 0, 0, 0, DE_NULL);
255 
256 	referenceState.blendMode = rr::BLENDMODE_NONE;
257 	m_referenceRenderer->render(gls::FragmentOpUtil::getMultisampleAccess(m_refColorBuffer->getAccess()), nullAccess /* no depth */, nullAccess /* no stencil */, m_firstQuadInt, referenceState);
258 	referenceState.blendMode = rr::BLENDMODE_STANDARD;
259 	m_referenceRenderer->render(gls::FragmentOpUtil::getMultisampleAccess(m_refColorBuffer->getAccess()), nullAccess /* no depth */, nullAccess /* no stencil */, m_secondQuadInt, referenceState);
260 
261 	// Read GL image.
262 
263 	glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, renderedImg.getAccess());
264 
265 	// Compare images.
266 
267 	UVec4 compareThreshold = m_context.getRenderTarget().getPixelFormat().getColorThreshold().toIVec().asUint()
268 							 * UVec4(5) / UVec4(2) + UVec4(3); // \note Non-scientific ad hoc formula. Need big threshold when few color bits; blending brings extra inaccuracy.
269 
270 	bool comparePass = tcu::intThresholdCompare(m_testCtx.getLog(), "CompareResult", "Image Comparison Result", m_refColorBuffer->getAccess(), renderedImg.getAccess(), compareThreshold, tcu::COMPARE_LOG_RESULT);
271 
272 	// Fail now if images don't match.
273 
274 	if (!comparePass)
275 	{
276 		m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Image compare failed");
277 		return STOP;
278 	}
279 
280 	// Continue if param sets still remain in m_paramSets; otherwise stop.
281 
282 	m_curParamSetNdx++;
283 
284 	if (m_curParamSetNdx < (int)m_paramSets.size())
285 		return CONTINUE;
286 	else
287 	{
288 		m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Passed");
289 		return STOP;
290 	}
291 }
292 
BlendTests(Context & context)293 BlendTests::BlendTests (Context& context)
294 	: TestCaseGroup(context, "blend", "Blend tests")
295 {
296 }
297 
~BlendTests(void)298 BlendTests::~BlendTests (void)
299 {
300 }
301 
init(void)302 void BlendTests::init (void)
303 {
304 	struct EnumGL
305 	{
306 		GLenum			glValue;
307 		const char*		nameStr;
308 	};
309 
310 	static const EnumGL blendEquations[] =
311 	{
312 		{ GL_FUNC_ADD,					"add"					},
313 		{ GL_FUNC_SUBTRACT,				"subtract"				},
314 		{ GL_FUNC_REVERSE_SUBTRACT,		"reverse_subtract"		}
315 	};
316 
317 	static const EnumGL blendFunctions[] =
318 	{
319 		{ GL_ZERO,							"zero"						},
320 		{ GL_ONE,							"one"						},
321 		{ GL_SRC_COLOR,						"src_color"					},
322 		{ GL_ONE_MINUS_SRC_COLOR,			"one_minus_src_color"		},
323 		{ GL_DST_COLOR,						"dst_color"					},
324 		{ GL_ONE_MINUS_DST_COLOR,			"one_minus_dst_color"		},
325 		{ GL_SRC_ALPHA,						"src_alpha"					},
326 		{ GL_ONE_MINUS_SRC_ALPHA,			"one_minus_src_alpha"		},
327 		{ GL_DST_ALPHA,						"dst_alpha"					},
328 		{ GL_ONE_MINUS_DST_ALPHA,			"one_minus_dst_alpha"		},
329 		{ GL_CONSTANT_COLOR,				"constant_color"			},
330 		{ GL_ONE_MINUS_CONSTANT_COLOR,		"one_minus_constant_color"	},
331 		{ GL_CONSTANT_ALPHA,				"constant_alpha"			},
332 		{ GL_ONE_MINUS_CONSTANT_ALPHA,		"one_minus_constant_alpha"	},
333 		{ GL_SRC_ALPHA_SATURATE,			"src_alpha_saturate"		}
334 	};
335 
336 	const Vec4 defaultBlendColor(0.2f, 0.4f, 0.6f, 0.8f);
337 
338 	// Test all blend equation, src blend function, dst blend function combinations. RGB and alpha modes are the same.
339 
340 	{
341 		TestCaseGroup* group = new TestCaseGroup(m_context, "equation_src_func_dst_func", "Combinations of Blend Equations and Functions");
342 		addChild(group);
343 
344 		for (int equationNdx = 0;	equationNdx < DE_LENGTH_OF_ARRAY(blendEquations);	equationNdx++)
345 		for (int srcFuncNdx = 0;	srcFuncNdx < DE_LENGTH_OF_ARRAY(blendFunctions);	srcFuncNdx++)
346 		for (int dstFuncNdx = 0;	dstFuncNdx < DE_LENGTH_OF_ARRAY(blendFunctions);	dstFuncNdx++)
347 		{
348 			const EnumGL& eq	= blendEquations[equationNdx];
349 			const EnumGL& src	= blendFunctions[srcFuncNdx];
350 			const EnumGL& dst	= blendFunctions[dstFuncNdx];
351 
352 			if (dst.glValue == GL_SRC_ALPHA_SATURATE) // SRC_ALPHA_SATURATE is only valid for src func.
353 				continue;
354 
355 			string name			= string("") + eq.nameStr + "_" + src.nameStr + "_" + dst.nameStr;
356 			string description	= string("") +
357 								  "Equations "		+ getBlendEquationName(eq.glValue) +
358 								  ", src funcs "	+ getBlendFactorName(src.glValue) +
359 								  ", dst funcs "	+ getBlendFactorName(dst.glValue);
360 
361 			vector<BlendParams> paramSets;
362 			paramSets.push_back(BlendParams(eq.glValue, src.glValue, dst.glValue, eq.glValue, src.glValue, dst.glValue, defaultBlendColor));
363 
364 			group->addChild(new BlendCase(m_context, name.c_str(), description.c_str(), paramSets));
365 		}
366 	}
367 
368 	// Test all RGB src, alpha src and RGB dst, alpha dst combinations. Equations are ADD.
369 	// \note For all RGB src, alpha src combinations, also test a couple of different RGBA dst functions, and vice versa.
370 
371 	{
372 		TestCaseGroup* mainGroup = new TestCaseGroup(m_context, "rgb_func_alpha_func", "Combinations of RGB and Alpha Functions");
373 		addChild(mainGroup);
374 		TestCaseGroup* srcGroup = new TestCaseGroup(m_context, "src", "Source functions");
375 		TestCaseGroup* dstGroup = new TestCaseGroup(m_context, "dst", "Destination functions");
376 		mainGroup->addChild(srcGroup);
377 		mainGroup->addChild(dstGroup);
378 
379 		for (int isDstI = 0;		isDstI <= 1;										isDstI++)
380 		for (int rgbFuncNdx = 0;	rgbFuncNdx < DE_LENGTH_OF_ARRAY(blendFunctions);	rgbFuncNdx++)
381 		for (int alphaFuncNdx = 0;	alphaFuncNdx < DE_LENGTH_OF_ARRAY(blendFunctions);	alphaFuncNdx++)
382 		{
383 			bool			isSrc			= isDstI == 0;
384 			TestCaseGroup*	curGroup		= isSrc ? srcGroup : dstGroup;
385 			const EnumGL&	funcRGB			= blendFunctions[rgbFuncNdx];
386 			const EnumGL&	funcAlpha		= blendFunctions[alphaFuncNdx];
387 			const char*		dstOrSrcStr		= isSrc ? "src" : "dst";
388 
389 			if (!isSrc && (funcRGB.glValue == GL_SRC_ALPHA_SATURATE || funcAlpha.glValue == GL_SRC_ALPHA_SATURATE)) // SRC_ALPHA_SATURATE is only valid for src func.
390 				continue;
391 
392 			string name			= string("") + funcRGB.nameStr + "_" + funcAlpha.nameStr;
393 			string description	= string("") +
394 								  "RGB "		+ dstOrSrcStr + " func " + getBlendFactorName(funcRGB.glValue) +
395 								  ", alpha "	+ dstOrSrcStr + " func " + getBlendFactorName(funcAlpha.glValue);
396 
397 			// First, make param sets as if this was a src case.
398 
399 			vector<BlendParams> paramSets;
400 			paramSets.push_back(BlendParams(GL_FUNC_ADD, funcRGB.glValue, GL_ONE,			GL_FUNC_ADD, funcAlpha.glValue, GL_ONE,			defaultBlendColor));
401 			paramSets.push_back(BlendParams(GL_FUNC_ADD, funcRGB.glValue, GL_ZERO,			GL_FUNC_ADD, funcAlpha.glValue, GL_ZERO,		defaultBlendColor));
402 			paramSets.push_back(BlendParams(GL_FUNC_ADD, funcRGB.glValue, GL_SRC_COLOR,		GL_FUNC_ADD, funcAlpha.glValue, GL_SRC_COLOR,	defaultBlendColor));
403 			paramSets.push_back(BlendParams(GL_FUNC_ADD, funcRGB.glValue, GL_DST_COLOR,		GL_FUNC_ADD, funcAlpha.glValue, GL_DST_COLOR,	defaultBlendColor));
404 
405 			// Swap src and dst params if this is a dst case.
406 
407 			if (!isSrc)
408 			{
409 				for (int i = 0; i < (int)paramSets.size(); i++)
410 				{
411 					std::swap(paramSets[i].srcFuncRGB,		paramSets[i].dstFuncRGB);
412 					std::swap(paramSets[i].srcFuncAlpha,	paramSets[i].dstFuncAlpha);
413 				}
414 			}
415 
416 			curGroup->addChild(new BlendCase(m_context, name.c_str(), description.c_str(), paramSets));
417 		}
418 	}
419 
420 	// Test all RGB and alpha equation combinations. Src and dst funcs are ONE for both.
421 
422 	{
423 		TestCaseGroup* group = new TestCaseGroup(m_context, "rgb_equation_alpha_equation", "Combinations of RGB and Alpha Equation Combinations");
424 		addChild(group);
425 
426 		for (int equationRGBNdx = 0;	equationRGBNdx < DE_LENGTH_OF_ARRAY(blendEquations);	equationRGBNdx++)
427 		for (int equationAlphaNdx = 0;	equationAlphaNdx < DE_LENGTH_OF_ARRAY(blendEquations);	equationAlphaNdx++)
428 		{
429 			const EnumGL& eqRGB			= blendEquations[equationRGBNdx];
430 			const EnumGL& eqAlpha		= blendEquations[equationAlphaNdx];
431 
432 			string name			= string("") + eqRGB.nameStr + "_" + eqAlpha.nameStr;
433 			string description	= string("") +
434 								  "RGB equation "		+ getBlendEquationName(eqRGB.glValue) +
435 								  ", alpha equation "	+ getBlendEquationName(eqAlpha.glValue);
436 
437 			vector<BlendParams> paramSets;
438 			paramSets.push_back(BlendParams(eqRGB.glValue, GL_ONE, GL_ONE, eqAlpha.glValue, GL_ONE, GL_ONE, defaultBlendColor));
439 
440 			group->addChild(new BlendCase(m_context, name.c_str(), description.c_str(), paramSets));
441 		}
442 	}
443 }
444 
445 } // Functional
446 } // gles2
447 } // deqp
448