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