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 78 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 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 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 190 BlendCase::~BlendCase (void) 191 { 192 delete m_renderer; 193 delete m_referenceRenderer; 194 delete m_refColorBuffer; 195 } 196 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 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 298 BlendTests::BlendTests (Context& context) 299 : TestCaseGroup(context, "blend", "Blend tests") 300 { 301 } 302 303 BlendTests::~BlendTests (void) 304 { 305 } 306 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