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 77 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 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 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 189 BlendCase::~BlendCase (void) 190 { 191 delete m_renderer; 192 delete m_referenceRenderer; 193 delete m_refColorBuffer; 194 } 195 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 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(2); // \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 293 BlendTests::BlendTests (Context& context) 294 : TestCaseGroup(context, "blend", "Blend tests") 295 { 296 } 297 298 BlendTests::~BlendTests (void) 299 { 300 } 301 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