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