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