1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL ES 3.1 Module 3 * ------------------------------------------------- 4 * 5 * Copyright 2015 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 Primitive bounding box tests. 22 *//*--------------------------------------------------------------------*/ 23 24 #include "es31fPrimitiveBoundingBoxTests.hpp" 25 26 #include "tcuTestLog.hpp" 27 #include "tcuRenderTarget.hpp" 28 #include "tcuStringTemplate.hpp" 29 #include "tcuSurface.hpp" 30 #include "tcuTextureUtil.hpp" 31 #include "tcuVectorUtil.hpp" 32 #include "gluCallLogWrapper.hpp" 33 #include "gluContextInfo.hpp" 34 #include "gluRenderContext.hpp" 35 #include "gluStrUtil.hpp" 36 #include "gluShaderProgram.hpp" 37 #include "gluObjectWrapper.hpp" 38 #include "gluPixelTransfer.hpp" 39 #include "glsStateQueryUtil.hpp" 40 #include "glwFunctions.hpp" 41 #include "glwEnums.hpp" 42 #include "deRandom.hpp" 43 #include "deUniquePtr.hpp" 44 #include "deStringUtil.hpp" 45 46 #include <vector> 47 #include <sstream> 48 #include <algorithm> 49 50 namespace deqp 51 { 52 namespace gles31 53 { 54 namespace Functional 55 { 56 namespace 57 { 58 59 namespace StateQueryUtil = ::deqp::gls::StateQueryUtil; 60 61 struct BoundingBox 62 { 63 tcu::Vec4 min; 64 tcu::Vec4 max; 65 66 /*--------------------------------------------------------------------*//*! 67 * Get component by index of a 8-component vector constructed by 68 * concatenating 4-component min and max vectors. 69 *//*--------------------------------------------------------------------*/ 70 float& getComponentAccess (int ndx); 71 const float& getComponentAccess (int ndx) const; 72 }; 73 74 float& BoundingBox::getComponentAccess (int ndx) 75 { 76 DE_ASSERT(ndx >= 0 && ndx < 8); 77 if (ndx < 4) 78 return min[ndx]; 79 else 80 return max[ndx-4]; 81 } 82 83 const float& BoundingBox::getComponentAccess (int ndx) const 84 { 85 return const_cast<BoundingBox*>(this)->getComponentAccess(ndx); 86 } 87 88 struct ProjectedBBox 89 { 90 tcu::Vec3 min; 91 tcu::Vec3 max; 92 }; 93 94 static ProjectedBBox projectBoundingBox (const BoundingBox& bbox) 95 { 96 const float wMin = de::max(0.0f, bbox.min.w()); // clamp to w=0 as extension requires 97 const float wMax = de::max(0.0f, bbox.max.w()); 98 ProjectedBBox retVal; 99 100 retVal.min = tcu::min(bbox.min.swizzle(0, 1, 2) / wMin, 101 bbox.min.swizzle(0, 1, 2) / wMax); 102 retVal.max = tcu::max(bbox.max.swizzle(0, 1, 2) / wMin, 103 bbox.max.swizzle(0, 1, 2) / wMax); 104 return retVal; 105 } 106 107 static tcu::IVec4 getViewportBoundingBoxArea (const ProjectedBBox& bbox, const tcu::IVec2& viewportSize, float size = 0.0f) 108 { 109 tcu::Vec4 vertexBox; 110 tcu::IVec4 pixelBox; 111 112 vertexBox.x() = (bbox.min.x() * 0.5f + 0.5f) * (float)viewportSize.x(); 113 vertexBox.y() = (bbox.min.y() * 0.5f + 0.5f) * (float)viewportSize.y(); 114 vertexBox.z() = (bbox.max.x() * 0.5f + 0.5f) * (float)viewportSize.x(); 115 vertexBox.w() = (bbox.max.y() * 0.5f + 0.5f) * (float)viewportSize.y(); 116 117 pixelBox.x() = deFloorFloatToInt32(vertexBox.x() - size/2.0f); 118 pixelBox.y() = deFloorFloatToInt32(vertexBox.y() - size/2.0f); 119 pixelBox.z() = deCeilFloatToInt32(vertexBox.z() + size/2.0f); 120 pixelBox.w() = deCeilFloatToInt32(vertexBox.w() + size/2.0f); 121 return pixelBox; 122 } 123 124 static std::string specializeShader(Context& context, const char* code) 125 { 126 const glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(context.getRenderContext().getType()); 127 std::map<std::string, std::string> specializationMap; 128 129 specializationMap["GLSL_VERSION_DECL"] = glu::getGLSLVersionDeclaration(glslVersion); 130 131 if (glu::contextSupports(context.getRenderContext().getType(), glu::ApiType::es(3, 2))) 132 { 133 specializationMap["GEOMETRY_SHADER_REQUIRE"] = ""; 134 specializationMap["GEOMETRY_POINT_SIZE"] = "#extension GL_EXT_geometry_point_size : require"; 135 specializationMap["GPU_SHADER5_REQUIRE"] = ""; 136 specializationMap["TESSELLATION_SHADER_REQUIRE"] = ""; 137 specializationMap["TESSELLATION_POINT_SIZE_REQUIRE"] = "#extension GL_EXT_tessellation_point_size : require"; 138 specializationMap["PRIMITIVE_BOUNDING_BOX_REQUIRE"] = ""; 139 specializationMap["PRIM_GL_BOUNDING_BOX"] = "gl_BoundingBox"; 140 } 141 else 142 { 143 specializationMap["GEOMETRY_SHADER_REQUIRE"] = "#extension GL_EXT_geometry_shader : require"; 144 specializationMap["GEOMETRY_POINT_SIZE"] = "#extension GL_EXT_geometry_point_size : require"; 145 specializationMap["GPU_SHADER5_REQUIRE"] = "#extension GL_EXT_gpu_shader5 : require"; 146 specializationMap["TESSELLATION_SHADER_REQUIRE"] = "#extension GL_EXT_tessellation_shader : require"; 147 specializationMap["TESSELLATION_POINT_SIZE_REQUIRE"] = "#extension GL_EXT_tessellation_point_size : require"; 148 specializationMap["PRIMITIVE_BOUNDING_BOX_REQUIRE"] = "#extension GL_EXT_primitive_bounding_box : require"; 149 specializationMap["PRIM_GL_BOUNDING_BOX"] = "gl_BoundingBoxEXT"; 150 } 151 152 return tcu::StringTemplate(code).specialize(specializationMap); 153 } 154 155 class InitialValueCase : public TestCase 156 { 157 public: 158 InitialValueCase (Context& context, const char* name, const char* desc); 159 160 void init (void); 161 IterateResult iterate (void); 162 }; 163 164 InitialValueCase::InitialValueCase (Context& context, const char* name, const char* desc) 165 : TestCase(context, name, desc) 166 { 167 } 168 169 void InitialValueCase::init (void) 170 { 171 const bool supportsES32 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)); 172 173 if (!supportsES32 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box")) 174 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension"); 175 } 176 177 InitialValueCase::IterateResult InitialValueCase::iterate (void) 178 { 179 StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLfloat[8]> state; 180 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); 181 182 gl.enableLogging(true); 183 184 m_testCtx.getLog() 185 << tcu::TestLog::Message 186 << "Querying GL_PRIMITIVE_BOUNDING_BOX_EXT, expecting (-1, -1, -1, 1) (1, 1, 1, 1)" 187 << tcu::TestLog::EndMessage; 188 189 gl.glGetFloatv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state); 190 GLU_EXPECT_NO_ERROR(gl.glGetError(), "query"); 191 192 if (!state.verifyValidity(m_testCtx)) 193 return STOP; 194 195 m_testCtx.getLog() 196 << tcu::TestLog::Message 197 << "Got " << tcu::formatArray(&state[0], &state[8]) 198 << tcu::TestLog::EndMessage; 199 200 if ((state[0] != -1.0f) || (state[1] != -1.0f) || (state[2] != -1.0f) || (state[3] != 1.0f) || 201 (state[4] != 1.0f) || (state[5] != 1.0f) || (state[6] != 1.0f) || (state[7] != 1.0f)) 202 { 203 m_testCtx.getLog() 204 << tcu::TestLog::Message 205 << "Error, unexpected value" 206 << tcu::TestLog::EndMessage; 207 208 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid initial value"); 209 } 210 else 211 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 212 213 return STOP; 214 } 215 216 class QueryCase : public TestCase 217 { 218 public: 219 enum QueryMethod 220 { 221 QUERY_FLOAT = 0, 222 QUERY_BOOLEAN, 223 QUERY_INT, 224 QUERY_INT64, 225 226 QUERY_LAST 227 }; 228 229 QueryCase (Context& context, const char* name, const char* desc, QueryMethod method); 230 231 private: 232 void init (void); 233 IterateResult iterate (void); 234 235 bool verifyState (glu::CallLogWrapper& gl, const BoundingBox& bbox) const; 236 237 const QueryMethod m_method; 238 }; 239 240 QueryCase::QueryCase (Context& context, const char* name, const char* desc, QueryMethod method) 241 : TestCase (context, name, desc) 242 , m_method (method) 243 { 244 DE_ASSERT(method < QUERY_LAST); 245 } 246 247 void QueryCase::init (void) 248 { 249 const bool supportsES32 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)); 250 251 if (!supportsES32 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box")) 252 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension"); 253 } 254 255 QueryCase::IterateResult QueryCase::iterate (void) 256 { 257 static const BoundingBox fixedCases[] = 258 { 259 { tcu::Vec4( 0.0f, 0.0f, 0.0f, 0.0f), tcu::Vec4( 0.0f, 0.0f, 0.0f, 0.0f) }, 260 { tcu::Vec4(-0.0f, -0.0f, -0.0f, -0.0f), tcu::Vec4( 0.0f, 0.0f, 0.0f, -0.0f) }, 261 { tcu::Vec4( 0.0f, 0.0f, 0.0f, 0.0f), tcu::Vec4( 1.0f, 1.0f, 1.0f, -1.0f) }, 262 { tcu::Vec4( 2.0f, 2.0f, 2.0f, 2.0f), tcu::Vec4( 1.5f, 1.5f, 1.5f, 1.0f) }, 263 { tcu::Vec4( 1.0f, 1.0f, 1.0f, 1.0f), tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f) }, 264 { tcu::Vec4( 1.0f, 1.0f, 1.0f, 0.3f), tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.2f) }, 265 }; 266 267 const int numRandomCases = 9; 268 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); 269 de::Random rnd (0xDE3210); 270 std::vector<BoundingBox> cases; 271 272 cases.insert(cases.begin(), DE_ARRAY_BEGIN(fixedCases), DE_ARRAY_END(fixedCases)); 273 for (int ndx = 0; ndx < numRandomCases; ++ndx) 274 { 275 BoundingBox boundingBox; 276 277 // parameter evaluation order is not guaranteed, cannot just do "max = (rand(), rand(), ...) 278 for (int coordNdx = 0; coordNdx < 8; ++coordNdx) 279 boundingBox.getComponentAccess(coordNdx) = rnd.getFloat(-4.0f, 4.0f); 280 281 cases.push_back(boundingBox); 282 } 283 284 gl.enableLogging(true); 285 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 286 287 for (int caseNdx = 0; caseNdx < (int)cases.size(); ++caseNdx) 288 { 289 const tcu::ScopedLogSection section (m_testCtx.getLog(), "Iteration", "Iteration " + de::toString(caseNdx+1)); 290 const BoundingBox& boundingBox = cases[caseNdx]; 291 292 gl.glPrimitiveBoundingBox(boundingBox.min.x(), boundingBox.min.y(), boundingBox.min.z(), boundingBox.min.w(), 293 boundingBox.max.x(), boundingBox.max.y(), boundingBox.max.z(), boundingBox.max.w()); 294 295 if (!verifyState(gl, boundingBox)) 296 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Unexpected query result"); 297 } 298 299 return STOP; 300 } 301 302 bool QueryCase::verifyState (glu::CallLogWrapper& gl, const BoundingBox& bbox) const 303 { 304 switch (m_method) 305 { 306 case QUERY_FLOAT: 307 { 308 StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLfloat[8]> state; 309 bool error = false; 310 311 gl.glGetFloatv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state); 312 GLU_EXPECT_NO_ERROR(gl.glGetError(), "query"); 313 314 if (!state.verifyValidity(m_testCtx)) 315 return false; 316 317 m_testCtx.getLog() 318 << tcu::TestLog::Message 319 << "glGetFloatv returned " << tcu::formatArray(&state[0], &state[8]) 320 << tcu::TestLog::EndMessage; 321 322 for (int ndx = 0; ndx < 8; ++ndx) 323 if (state[ndx] != bbox.getComponentAccess(ndx)) 324 error = true; 325 326 if (error) 327 { 328 m_testCtx.getLog() 329 << tcu::TestLog::Message 330 << "Error, unexpected value\n" 331 << "Expected [" 332 << bbox.min.x() << ", " << bbox.min.y() << ", " << bbox.min.z() << ", " << bbox.min.w() << ", " 333 << bbox.max.x() << ", " << bbox.max.y() << ", " << bbox.max.z() << ", " << bbox.max.w() << "]" 334 << tcu::TestLog::EndMessage; 335 return false; 336 } 337 return true; 338 } 339 340 case QUERY_INT: 341 { 342 StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLint[8]> state; 343 bool error = false; 344 345 gl.glGetIntegerv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state); 346 GLU_EXPECT_NO_ERROR(gl.glGetError(), "query"); 347 348 if (!state.verifyValidity(m_testCtx)) 349 return false; 350 351 m_testCtx.getLog() 352 << tcu::TestLog::Message 353 << "glGetIntegerv returned " << tcu::formatArray(&state[0], &state[8]) 354 << tcu::TestLog::EndMessage; 355 356 for (int ndx = 0; ndx < 8; ++ndx) 357 if (state[ndx] != StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint>(bbox.getComponentAccess(ndx)) && 358 state[ndx] != StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint>(bbox.getComponentAccess(ndx))) 359 error = true; 360 361 if (error) 362 { 363 tcu::MessageBuilder builder(&m_testCtx.getLog()); 364 365 builder << "Error, unexpected value\n" 366 << "Expected ["; 367 368 for (int ndx = 0; ndx < 8; ++ndx) 369 { 370 const glw::GLint roundDown = StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint>(bbox.getComponentAccess(ndx)); 371 const glw::GLint roundUp = StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint>(bbox.getComponentAccess(ndx)); 372 373 if (ndx != 0) 374 builder << ", "; 375 376 if (roundDown == roundUp) 377 builder << roundDown; 378 else 379 builder << "{" << roundDown << ", " << roundUp << "}"; 380 } 381 382 builder << "]" 383 << tcu::TestLog::EndMessage; 384 return false; 385 } 386 return true; 387 } 388 389 case QUERY_INT64: 390 { 391 StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLint64[8]> state; 392 bool error = false; 393 394 gl.glGetInteger64v(GL_PRIMITIVE_BOUNDING_BOX_EXT, state); 395 GLU_EXPECT_NO_ERROR(gl.glGetError(), "query"); 396 397 if (!state.verifyValidity(m_testCtx)) 398 return false; 399 400 m_testCtx.getLog() 401 << tcu::TestLog::Message 402 << "glGetInteger64v returned " << tcu::formatArray(&state[0], &state[8]) 403 << tcu::TestLog::EndMessage; 404 405 for (int ndx = 0; ndx < 8; ++ndx) 406 if (state[ndx] != StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint64>(bbox.getComponentAccess(ndx)) && 407 state[ndx] != StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint64>(bbox.getComponentAccess(ndx))) 408 error = true; 409 410 if (error) 411 { 412 tcu::MessageBuilder builder(&m_testCtx.getLog()); 413 414 builder << "Error, unexpected value\n" 415 << "Expected ["; 416 417 for (int ndx = 0; ndx < 8; ++ndx) 418 { 419 const glw::GLint64 roundDown = StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint64>(bbox.getComponentAccess(ndx)); 420 const glw::GLint64 roundUp = StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint64>(bbox.getComponentAccess(ndx)); 421 422 if (ndx != 0) 423 builder << ", "; 424 425 if (roundDown == roundUp) 426 builder << roundDown; 427 else 428 builder << "{" << roundDown << ", " << roundUp << "}"; 429 } 430 431 builder << "]" 432 << tcu::TestLog::EndMessage; 433 return false; 434 } 435 return true; 436 } 437 438 case QUERY_BOOLEAN: 439 { 440 StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLboolean[8]> state; 441 bool error = false; 442 443 gl.glGetBooleanv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state); 444 GLU_EXPECT_NO_ERROR(gl.glGetError(), "query"); 445 446 if (!state.verifyValidity(m_testCtx)) 447 return false; 448 449 m_testCtx.getLog() 450 << tcu::TestLog::Message 451 << "glGetBooleanv returned [" 452 << glu::getBooleanStr(state[0]) << ", " << glu::getBooleanStr(state[1]) << ", " << glu::getBooleanStr(state[2]) << ", " << glu::getBooleanStr(state[3]) << ", " 453 << glu::getBooleanStr(state[4]) << ", " << glu::getBooleanStr(state[5]) << ", " << glu::getBooleanStr(state[6]) << ", " << glu::getBooleanStr(state[7]) << "]\n" 454 << tcu::TestLog::EndMessage; 455 456 for (int ndx = 0; ndx < 8; ++ndx) 457 if (state[ndx] != ((bbox.getComponentAccess(ndx) != 0.0f) ? (GL_TRUE) : (GL_FALSE))) 458 error = true; 459 460 if (error) 461 { 462 tcu::MessageBuilder builder(&m_testCtx.getLog()); 463 464 builder << "Error, unexpected value\n" 465 << "Expected ["; 466 467 for (int ndx = 0; ndx < 8; ++ndx) 468 { 469 if (ndx != 0) 470 builder << ", "; 471 472 builder << ((bbox.getComponentAccess(ndx) != 0.0f) ? ("GL_TRUE") : ("GL_FALSE")); 473 } 474 475 builder << "]" 476 << tcu::TestLog::EndMessage; 477 return false; 478 } 479 return true; 480 } 481 482 default: 483 DE_ASSERT(false); 484 return true; 485 } 486 } 487 488 class BBoxRenderCase : public TestCase 489 { 490 public: 491 enum 492 { 493 FLAG_RENDERTARGET_DEFAULT = 1u << 0, //!< render to default renderbuffer 494 FLAG_RENDERTARGET_FBO = 1u << 1, //!< render to framebuffer object 495 496 FLAG_BBOXSIZE_EQUAL = 1u << 2, //!< set tight primitive bounding box 497 FLAG_BBOXSIZE_LARGER = 1u << 3, //!< set padded primitive bounding box 498 FLAG_BBOXSIZE_SMALLER = 1u << 4, //!< set too small primitive bounding box 499 500 FLAG_TESSELLATION = 1u << 5, //!< use tessellation shader 501 FLAG_GEOMETRY = 1u << 6, //!< use geometry shader 502 503 FLAG_SET_BBOX_STATE = 1u << 7, //!< set primitive bounding box using global state 504 FLAG_SET_BBOX_OUTPUT = 1u << 8, //!< set primitive bounding box using tessellation output 505 FLAG_PER_PRIMITIVE_BBOX = 1u << 9, //!< set primitive bounding per primitive 506 507 FLAGBIT_USER_BIT = 10u //!< bits N and and up are reserved for subclasses 508 }; 509 510 BBoxRenderCase (Context& context, const char* name, const char* description, int numIterations, deUint32 flags); 511 ~BBoxRenderCase (void); 512 513 protected: 514 enum RenderTarget 515 { 516 RENDERTARGET_DEFAULT, 517 RENDERTARGET_FBO, 518 }; 519 enum BBoxSize 520 { 521 BBOXSIZE_EQUAL, 522 BBOXSIZE_LARGER, 523 BBOXSIZE_SMALLER, 524 }; 525 526 enum 527 { 528 RENDER_TARGET_MIN_SIZE = 256, 529 FBO_SIZE = 512, 530 MIN_VIEWPORT_SIZE = 256, 531 MAX_VIEWPORT_SIZE = 512, 532 }; 533 DE_STATIC_ASSERT(MIN_VIEWPORT_SIZE <= RENDER_TARGET_MIN_SIZE); 534 535 enum 536 { 537 VA_POS_VEC_NDX = 0, 538 VA_COL_VEC_NDX = 1, 539 VA_NUM_ATTRIB_VECS = 2, 540 }; 541 542 enum AABBRoundDirection 543 { 544 ROUND_INWARDS = 0, 545 ROUND_OUTWARDS 546 }; 547 548 struct IterationConfig 549 { 550 tcu::IVec2 viewportPos; 551 tcu::IVec2 viewportSize; 552 tcu::Vec2 patternPos; //!< in NDC 553 tcu::Vec2 patternSize; //!< in NDC 554 BoundingBox bbox; 555 }; 556 557 virtual void init (void); 558 virtual void deinit (void); 559 IterateResult iterate (void); 560 561 virtual std::string genVertexSource (void) const = 0; 562 virtual std::string genFragmentSource (void) const = 0; 563 virtual std::string genTessellationControlSource (void) const = 0; 564 virtual std::string genTessellationEvaluationSource (void) const = 0; 565 virtual std::string genGeometrySource (void) const = 0; 566 567 virtual IterationConfig generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const = 0; 568 virtual void getAttributeData (std::vector<tcu::Vec4>& data) const = 0; 569 virtual void renderTestPattern (const IterationConfig& config) = 0; 570 virtual void verifyRenderResult (const IterationConfig& config) = 0; 571 572 IterationConfig generateRandomConfig (int seed, const tcu::IVec2& renderTargetSize) const; 573 tcu::IVec4 getViewportPatternArea (const tcu::Vec2& patternPos, const tcu::Vec2& patternSize, const tcu::IVec2& viewportSize, AABBRoundDirection roundDir) const; 574 575 void setupRender (const IterationConfig& config); 576 577 enum ShaderFunction 578 { 579 SHADER_FUNC_MIRROR_X, 580 SHADER_FUNC_MIRROR_Y, 581 SHADER_FUNC_INSIDE_BBOX, 582 }; 583 584 const char* genShaderFunction (ShaderFunction func) const; 585 586 const RenderTarget m_renderTarget; 587 const BBoxSize m_bboxSize; 588 const bool m_hasTessellationStage; 589 const bool m_hasGeometryStage; 590 const bool m_useGlobalState; 591 const bool m_calcPerPrimitiveBBox; 592 const int m_numIterations; 593 594 de::MovePtr<glu::ShaderProgram> m_program; 595 de::MovePtr<glu::Buffer> m_vbo; 596 de::MovePtr<glu::Framebuffer> m_fbo; 597 598 private: 599 std::vector<IterationConfig> m_iterationConfigs; 600 int m_iteration; 601 }; 602 603 BBoxRenderCase::BBoxRenderCase (Context& context, const char* name, const char* description, int numIterations, deUint32 flags) 604 : TestCase (context, name, description) 605 , m_renderTarget ((flags & FLAG_RENDERTARGET_DEFAULT) ? (RENDERTARGET_DEFAULT) : (RENDERTARGET_FBO)) 606 , m_bboxSize ((flags & FLAG_BBOXSIZE_EQUAL) ? (BBOXSIZE_EQUAL) : (flags & FLAG_BBOXSIZE_SMALLER) ? (BBOXSIZE_SMALLER) : (BBOXSIZE_LARGER)) 607 , m_hasTessellationStage ((flags & FLAG_TESSELLATION) != 0) 608 , m_hasGeometryStage ((flags & FLAG_GEOMETRY) != 0) 609 , m_useGlobalState ((flags & FLAG_SET_BBOX_STATE) != 0) 610 , m_calcPerPrimitiveBBox ((flags & FLAG_PER_PRIMITIVE_BBOX) != 0) 611 , m_numIterations (numIterations) 612 , m_iteration (0) 613 { 614 // validate flags 615 DE_ASSERT((((m_renderTarget == RENDERTARGET_DEFAULT) ? (FLAG_RENDERTARGET_DEFAULT) : (0)) | 616 ((m_renderTarget == RENDERTARGET_FBO) ? (FLAG_RENDERTARGET_FBO) : (0)) | 617 ((m_bboxSize == BBOXSIZE_EQUAL) ? (FLAG_BBOXSIZE_EQUAL) : (0)) | 618 ((m_bboxSize == BBOXSIZE_LARGER) ? (FLAG_BBOXSIZE_LARGER) : (0)) | 619 ((m_bboxSize == BBOXSIZE_SMALLER) ? (FLAG_BBOXSIZE_SMALLER) : (0)) | 620 ((m_hasTessellationStage) ? (FLAG_TESSELLATION) : (0)) | 621 ((m_hasGeometryStage) ? (FLAG_GEOMETRY) : (0)) | 622 ((m_useGlobalState) ? (FLAG_SET_BBOX_STATE) : (0)) | 623 ((!m_useGlobalState) ? (FLAG_SET_BBOX_OUTPUT) : (0)) | 624 ((m_calcPerPrimitiveBBox) ? (FLAG_PER_PRIMITIVE_BBOX) : (0))) == (flags & ((1u << FLAGBIT_USER_BIT) - 1))); 625 626 DE_ASSERT(m_useGlobalState || m_hasTessellationStage); // using non-global state requires tessellation 627 628 if (m_calcPerPrimitiveBBox) 629 { 630 DE_ASSERT(!m_useGlobalState); // per-primitive test requires per-primitive (non-global) state 631 DE_ASSERT(m_bboxSize == BBOXSIZE_EQUAL); // smaller is hard to verify, larger not interesting 632 } 633 } 634 635 BBoxRenderCase::~BBoxRenderCase (void) 636 { 637 deinit(); 638 } 639 640 void BBoxRenderCase::init (void) 641 { 642 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 643 const tcu::IVec2 renderTargetSize = (m_renderTarget == RENDERTARGET_DEFAULT) ? 644 (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : 645 (tcu::IVec2(FBO_SIZE, FBO_SIZE)); 646 const bool supportsES32 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)); 647 648 // requirements 649 if (!supportsES32 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box")) 650 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension"); 651 if (!supportsES32 && m_hasTessellationStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader")) 652 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension"); 653 if (!supportsES32 && m_hasGeometryStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")) 654 throw tcu::NotSupportedError("Test requires GL_EXT_geometry_shader extension"); 655 if (m_renderTarget == RENDERTARGET_DEFAULT && (renderTargetSize.x() < RENDER_TARGET_MIN_SIZE || renderTargetSize.y() < RENDER_TARGET_MIN_SIZE)) 656 throw tcu::NotSupportedError(std::string() + "Test requires " + de::toString<int>(RENDER_TARGET_MIN_SIZE) + "x" + de::toString<int>(RENDER_TARGET_MIN_SIZE) + " default framebuffer"); 657 658 // log case specifics 659 m_testCtx.getLog() 660 << tcu::TestLog::Message 661 << "Setting primitive bounding box " 662 << ((m_calcPerPrimitiveBBox) ? ("to exactly cover each generated primitive") 663 : (m_bboxSize == BBOXSIZE_EQUAL) ? ("to exactly cover rendered grid") 664 : (m_bboxSize == BBOXSIZE_LARGER) ? ("to cover the grid and include some padding") 665 : (m_bboxSize == BBOXSIZE_SMALLER) ? ("to cover only a subset of the grid") 666 : (DE_NULL)) 667 << ".\n" 668 << "Rendering with vertex" 669 << ((m_hasTessellationStage) ? ("-tessellation{ctrl,eval}") : ("")) 670 << ((m_hasGeometryStage) ? ("-geometry") : ("")) 671 << "-fragment program.\n" 672 << "Set bounding box using " 673 << ((m_useGlobalState) ? ("PRIMITIVE_BOUNDING_BOX_EXT state") : ("gl_BoundingBoxEXT output")) 674 << "\n" 675 << "Verifying rendering results are valid within the bounding box." 676 << tcu::TestLog::EndMessage; 677 678 // resources 679 680 { 681 glu::ProgramSources sources; 682 sources << glu::VertexSource(specializeShader(m_context, genVertexSource().c_str())); 683 sources << glu::FragmentSource(specializeShader(m_context, genFragmentSource().c_str())); 684 685 if (m_hasTessellationStage) 686 sources << glu::TessellationControlSource(specializeShader(m_context, genTessellationControlSource().c_str())) 687 << glu::TessellationEvaluationSource(specializeShader(m_context, genTessellationEvaluationSource().c_str())); 688 if (m_hasGeometryStage) 689 sources << glu::GeometrySource(specializeShader(m_context, genGeometrySource().c_str())); 690 691 m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), sources)); 692 GLU_EXPECT_NO_ERROR(gl.getError(), "build program"); 693 694 { 695 const tcu::ScopedLogSection section(m_testCtx.getLog(), "ShaderProgram", "Shader program"); 696 m_testCtx.getLog() << *m_program; 697 } 698 699 if (!m_program->isOk()) 700 throw tcu::TestError("failed to build program"); 701 } 702 703 if (m_renderTarget == RENDERTARGET_FBO) 704 { 705 glu::Texture colorAttachment(m_context.getRenderContext()); 706 707 gl.bindTexture(GL_TEXTURE_2D, *colorAttachment); 708 gl.texStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, FBO_SIZE, FBO_SIZE); 709 GLU_EXPECT_NO_ERROR(gl.getError(), "gen tex"); 710 711 m_fbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext())); 712 gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, **m_fbo); 713 gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *colorAttachment, 0); 714 GLU_EXPECT_NO_ERROR(gl.getError(), "attach"); 715 716 // unbind to prevent texture name deletion from removing it from current fbo attachments 717 gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); 718 } 719 720 { 721 std::vector<tcu::Vec4> data; 722 723 getAttributeData(data); 724 725 m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext())); 726 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo); 727 gl.bufferData(GL_ARRAY_BUFFER, (int)(data.size() * sizeof(tcu::Vec4)), &data[0], GL_STATIC_DRAW); 728 GLU_EXPECT_NO_ERROR(gl.getError(), "create vbo"); 729 } 730 731 // Iterations 732 for (int iterationNdx = 0; iterationNdx < m_numIterations; ++iterationNdx) 733 m_iterationConfigs.push_back(generateConfig(iterationNdx, renderTargetSize)); 734 } 735 736 void BBoxRenderCase::deinit (void) 737 { 738 m_program.clear(); 739 m_vbo.clear(); 740 m_fbo.clear(); 741 } 742 743 BBoxRenderCase::IterateResult BBoxRenderCase::iterate (void) 744 { 745 const tcu::ScopedLogSection section (m_testCtx.getLog(), 746 std::string() + "Iteration" + de::toString((int)m_iteration), 747 std::string() + "Iteration " + de::toString((int)m_iteration+1) + "/" + de::toString((int)m_iterationConfigs.size())); 748 const IterationConfig& config = m_iterationConfigs[m_iteration]; 749 750 // default 751 if (m_iteration == 0) 752 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 753 754 renderTestPattern(config); 755 verifyRenderResult(config); 756 757 if (++m_iteration < (int)m_iterationConfigs.size()) 758 return CONTINUE; 759 760 return STOP; 761 } 762 763 BBoxRenderCase::IterationConfig BBoxRenderCase::generateRandomConfig (int seed, const tcu::IVec2& renderTargetSize) const 764 { 765 de::Random rnd (seed); 766 IterationConfig config; 767 768 // viewport config 769 config.viewportSize.x() = rnd.getInt(MIN_VIEWPORT_SIZE, de::min<int>(renderTargetSize.x(), MAX_VIEWPORT_SIZE)); 770 config.viewportSize.y() = rnd.getInt(MIN_VIEWPORT_SIZE, de::min<int>(renderTargetSize.y(), MAX_VIEWPORT_SIZE)); 771 config.viewportPos.x() = rnd.getInt(0, renderTargetSize.x() - config.viewportSize.x()); 772 config.viewportPos.y() = rnd.getInt(0, renderTargetSize.y() - config.viewportSize.y()); 773 774 // pattern location inside viewport 775 config.patternSize.x() = rnd.getFloat(0.4f, 1.4f); 776 config.patternSize.y() = rnd.getFloat(0.4f, 1.4f); 777 config.patternPos.x() = rnd.getFloat(-1.0f, 1.0f - config.patternSize.x()); 778 config.patternPos.y() = rnd.getFloat(-1.0f, 1.0f - config.patternSize.y()); 779 780 // accurate bounding box 781 config.bbox.min = tcu::Vec4(config.patternPos.x(), config.patternPos.y(), 0.0f, 1.0f); 782 config.bbox.max = tcu::Vec4(config.patternPos.x() + config.patternSize.x(), config.patternPos.y() + config.patternSize.y(), 0.0f, 1.0f); 783 784 if (m_bboxSize == BBOXSIZE_LARGER) 785 { 786 // increase bbox size 787 config.bbox.min.x() -= rnd.getFloat() * 0.5f; 788 config.bbox.min.y() -= rnd.getFloat() * 0.5f; 789 config.bbox.min.z() -= rnd.getFloat() * 0.5f; 790 791 config.bbox.max.x() += rnd.getFloat() * 0.5f; 792 config.bbox.max.y() += rnd.getFloat() * 0.5f; 793 config.bbox.max.z() += rnd.getFloat() * 0.5f; 794 } 795 else if (m_bboxSize == BBOXSIZE_SMALLER) 796 { 797 // reduce bbox size 798 config.bbox.min.x() += rnd.getFloat() * 0.4f * config.patternSize.x(); 799 config.bbox.min.y() += rnd.getFloat() * 0.4f * config.patternSize.y(); 800 801 config.bbox.max.x() -= rnd.getFloat() * 0.4f * config.patternSize.x(); 802 config.bbox.max.y() -= rnd.getFloat() * 0.4f * config.patternSize.y(); 803 } 804 805 return config; 806 } 807 808 tcu::IVec4 BBoxRenderCase::getViewportPatternArea (const tcu::Vec2& patternPos, const tcu::Vec2& patternSize, const tcu::IVec2& viewportSize, AABBRoundDirection roundDir) const 809 { 810 const float halfPixel = 0.5f; 811 tcu::Vec4 vertexBox; 812 tcu::IVec4 pixelBox; 813 814 vertexBox.x() = (patternPos.x() * 0.5f + 0.5f) * (float)viewportSize.x(); 815 vertexBox.y() = (patternPos.y() * 0.5f + 0.5f) * (float)viewportSize.y(); 816 vertexBox.z() = ((patternPos.x() + patternSize.x()) * 0.5f + 0.5f) * (float)viewportSize.x(); 817 vertexBox.w() = ((patternPos.y() + patternSize.y()) * 0.5f + 0.5f) * (float)viewportSize.y(); 818 819 if (roundDir == ROUND_INWARDS) 820 { 821 pixelBox.x() = (int)deFloatCeil(vertexBox.x()+halfPixel); 822 pixelBox.y() = (int)deFloatCeil(vertexBox.y()+halfPixel); 823 pixelBox.z() = (int)deFloatFloor(vertexBox.z()-halfPixel); 824 pixelBox.w() = (int)deFloatFloor(vertexBox.w()-halfPixel); 825 } 826 else 827 { 828 pixelBox.x() = (int)deFloatFloor(vertexBox.x()-halfPixel); 829 pixelBox.y() = (int)deFloatFloor(vertexBox.y()-halfPixel); 830 pixelBox.z() = (int)deFloatCeil(vertexBox.z()+halfPixel); 831 pixelBox.w() = (int)deFloatCeil(vertexBox.w()+halfPixel); 832 } 833 834 return pixelBox; 835 } 836 837 void BBoxRenderCase::setupRender (const IterationConfig& config) 838 { 839 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 840 const glw::GLint posLocation = gl.getAttribLocation(m_program->getProgram(), "a_position"); 841 const glw::GLint colLocation = gl.getAttribLocation(m_program->getProgram(), "a_color"); 842 const glw::GLint posScaleLocation = gl.getUniformLocation(m_program->getProgram(), "u_posScale"); 843 844 TCU_CHECK(posLocation != -1); 845 TCU_CHECK(colLocation != -1); 846 TCU_CHECK(posScaleLocation != -1); 847 848 m_testCtx.getLog() 849 << tcu::TestLog::Message 850 << "Setting viewport to (" 851 << "x: " << config.viewportPos.x() << ", " 852 << "y: " << config.viewportPos.y() << ", " 853 << "w: " << config.viewportSize.x() << ", " 854 << "h: " << config.viewportSize.y() << ")\n" 855 << "Vertex coordinates are in range:\n" 856 << "\tx: [" << config.patternPos.x() << ", " << (config.patternPos.x() + config.patternSize.x()) << "]\n" 857 << "\ty: [" << config.patternPos.y() << ", " << (config.patternPos.y() + config.patternSize.y()) << "]\n" 858 << tcu::TestLog::EndMessage; 859 860 if (!m_calcPerPrimitiveBBox) 861 m_testCtx.getLog() 862 << tcu::TestLog::Message 863 << "Setting primitive bounding box to:\n" 864 << "\t" << config.bbox.min << "\n" 865 << "\t" << config.bbox.max << "\n" 866 << tcu::TestLog::EndMessage; 867 868 if (m_useGlobalState) 869 gl.primitiveBoundingBox(config.bbox.min.x(), config.bbox.min.y(), config.bbox.min.z(), config.bbox.min.w(), 870 config.bbox.max.x(), config.bbox.max.y(), config.bbox.max.z(), config.bbox.max.w()); 871 else 872 // state is overriden by the tessellation output, set bbox to invisible area to imitiate dirty state left by application 873 gl.primitiveBoundingBox(-2.0f, -2.0f, 0.0f, 1.0f, 874 -1.7f, -1.7f, 0.0f, 1.0f); 875 876 if (m_fbo) 877 gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, **m_fbo); 878 879 gl.viewport(config.viewportPos.x(), config.viewportPos.y(), config.viewportSize.x(), config.viewportSize.y()); 880 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 881 gl.clear(GL_COLOR_BUFFER_BIT); 882 883 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo); 884 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, (int)(VA_NUM_ATTRIB_VECS * sizeof(float[4])), glu::BufferOffsetAsPointer(4 * VA_POS_VEC_NDX * sizeof(float))); 885 gl.vertexAttribPointer(colLocation, 4, GL_FLOAT, GL_FALSE, (int)(VA_NUM_ATTRIB_VECS * sizeof(float[4])), glu::BufferOffsetAsPointer(4 * VA_COL_VEC_NDX * sizeof(float))); 886 gl.enableVertexAttribArray(posLocation); 887 gl.enableVertexAttribArray(colLocation); 888 gl.useProgram(m_program->getProgram()); 889 gl.uniform4f(posScaleLocation, config.patternPos.x(), config.patternPos.y(), config.patternSize.x(), config.patternSize.y()); 890 891 { 892 const glw::GLint bboxMinPos = gl.getUniformLocation(m_program->getProgram(), "u_primitiveBBoxMin"); 893 const glw::GLint bboxMaxPos = gl.getUniformLocation(m_program->getProgram(), "u_primitiveBBoxMax"); 894 895 gl.uniform4f(bboxMinPos, config.bbox.min.x(), config.bbox.min.y(), config.bbox.min.z(), config.bbox.min.w()); 896 gl.uniform4f(bboxMaxPos, config.bbox.max.x(), config.bbox.max.y(), config.bbox.max.z(), config.bbox.max.w()); 897 } 898 899 gl.uniform2i(gl.getUniformLocation(m_program->getProgram(), "u_viewportPos"), config.viewportPos.x(), config.viewportPos.y()); 900 gl.uniform2i(gl.getUniformLocation(m_program->getProgram(), "u_viewportSize"), config.viewportSize.x(), config.viewportSize.y()); 901 902 GLU_EXPECT_NO_ERROR(gl.getError(), "setup"); 903 } 904 905 const char* BBoxRenderCase::genShaderFunction (ShaderFunction func) const 906 { 907 switch (func) 908 { 909 case SHADER_FUNC_MIRROR_X: 910 return "vec4 mirrorX(in highp vec4 p)\n" 911 "{\n" 912 " highp vec2 patternOffset = u_posScale.xy;\n" 913 " highp vec2 patternScale = u_posScale.zw;\n" 914 " highp vec2 patternCenter = patternOffset + patternScale * 0.5;\n" 915 " return vec4(2.0 * patternCenter.x - p.x, p.y, p.z, p.w);\n" 916 "}\n"; 917 918 case SHADER_FUNC_MIRROR_Y: 919 return "vec4 mirrorY(in highp vec4 p)\n" 920 "{\n" 921 " highp vec2 patternOffset = u_posScale.xy;\n" 922 " highp vec2 patternScale = u_posScale.zw;\n" 923 " highp vec2 patternCenter = patternOffset + patternScale * 0.5;\n" 924 " return vec4(p.x, 2.0 * patternCenter.y - p.y, p.z, p.w);\n" 925 "}\n"; 926 927 case SHADER_FUNC_INSIDE_BBOX: 928 return "uniform highp ivec2 u_viewportPos;\n" 929 "uniform highp ivec2 u_viewportSize;\n" 930 "flat in highp float v_bbox_expansionSize;\n" 931 "flat in highp vec3 v_bbox_clipMin;\n" 932 "flat in highp vec3 v_bbox_clipMax;\n" 933 "\n" 934 "bool fragmentInsideTheBBox(in highp float depth)\n" 935 "{\n" 936 " highp vec4 wc = vec4(floor((v_bbox_clipMin.x * 0.5 + 0.5) * float(u_viewportSize.x) - v_bbox_expansionSize/2.0),\n" 937 " floor((v_bbox_clipMin.y * 0.5 + 0.5) * float(u_viewportSize.y) - v_bbox_expansionSize/2.0),\n" 938 " ceil((v_bbox_clipMax.x * 0.5 + 0.5) * float(u_viewportSize.x) + v_bbox_expansionSize/2.0),\n" 939 " ceil((v_bbox_clipMax.y * 0.5 + 0.5) * float(u_viewportSize.y) + v_bbox_expansionSize/2.0));\n" 940 " if (gl_FragCoord.x < float(u_viewportPos.x) + wc.x || gl_FragCoord.x > float(u_viewportPos.x) + wc.z ||\n" 941 " gl_FragCoord.y < float(u_viewportPos.y) + wc.y || gl_FragCoord.y > float(u_viewportPos.y) + wc.w)\n" 942 " return false;\n" 943 " const highp float dEpsilon = 0.001;\n" 944 " if (depth*2.0-1.0 < v_bbox_clipMin.z - dEpsilon || depth*2.0-1.0 > v_bbox_clipMax.z + dEpsilon)\n" 945 " return false;\n" 946 " return true;\n" 947 "}\n"; 948 default: 949 DE_ASSERT(false); 950 return ""; 951 } 952 } 953 954 class GridRenderCase : public BBoxRenderCase 955 { 956 public: 957 GridRenderCase (Context& context, const char* name, const char* description, deUint32 flags); 958 ~GridRenderCase (void); 959 960 private: 961 void init (void); 962 963 std::string genVertexSource (void) const; 964 std::string genFragmentSource (void) const; 965 std::string genTessellationControlSource (void) const; 966 std::string genTessellationEvaluationSource (void) const; 967 std::string genGeometrySource (void) const; 968 969 IterationConfig generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const; 970 void getAttributeData (std::vector<tcu::Vec4>& data) const; 971 void renderTestPattern (const IterationConfig& config); 972 void verifyRenderResult (const IterationConfig& config); 973 974 const int m_gridSize; 975 }; 976 977 GridRenderCase::GridRenderCase (Context& context, const char* name, const char* description, deUint32 flags) 978 : BBoxRenderCase (context, name, description, 12, flags) 979 , m_gridSize (24) 980 { 981 } 982 983 GridRenderCase::~GridRenderCase (void) 984 { 985 } 986 987 void GridRenderCase::init (void) 988 { 989 m_testCtx.getLog() 990 << tcu::TestLog::Message 991 << "Rendering yellow-green grid to " << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n" 992 << "Grid cells are in random order, varying grid size and location for each iteration.\n" 993 << "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated blue channel." 994 << tcu::TestLog::EndMessage; 995 996 BBoxRenderCase::init(); 997 } 998 999 std::string GridRenderCase::genVertexSource (void) const 1000 { 1001 std::ostringstream buf; 1002 1003 buf << "${GLSL_VERSION_DECL}\n" 1004 "in highp vec4 a_position;\n" 1005 "in highp vec4 a_color;\n" 1006 "out highp vec4 vtx_color;\n" 1007 "uniform highp vec4 u_posScale;\n" 1008 "\n"; 1009 if (!m_hasTessellationStage) 1010 { 1011 DE_ASSERT(m_useGlobalState); 1012 buf << "uniform highp vec4 u_primitiveBBoxMin;\n" 1013 "uniform highp vec4 u_primitiveBBoxMax;\n" 1014 "\n" 1015 "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n" 1016 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n" 1017 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n" 1018 "\n"; 1019 } 1020 1021 buf << "void main()\n" 1022 "{\n" 1023 " highp vec2 patternOffset = u_posScale.xy;\n" 1024 " highp vec2 patternScale = u_posScale.zw;\n" 1025 " gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n" 1026 " vtx_color = a_color;\n"; 1027 1028 if (!m_hasTessellationStage) 1029 { 1030 DE_ASSERT(m_useGlobalState); 1031 buf << "\n" 1032 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = 0.0;\n" 1033 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin =\n" 1034 " min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMin.w,\n" 1035 " vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMax.w);\n" 1036 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax =\n" 1037 " min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMin.w,\n" 1038 " vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMax.w);\n"; 1039 } 1040 1041 buf<< "}\n"; 1042 1043 return buf.str(); 1044 } 1045 1046 std::string GridRenderCase::genFragmentSource (void) const 1047 { 1048 const char* const colorInputName = (m_hasGeometryStage) ? ("geo_color") : (m_hasTessellationStage) ? ("tess_color") : ("vtx_color"); 1049 std::ostringstream buf; 1050 1051 buf << "${GLSL_VERSION_DECL}\n" 1052 "in mediump vec4 " << colorInputName << ";\n" 1053 "layout(location = 0) out mediump vec4 o_color;\n" 1054 << genShaderFunction(SHADER_FUNC_INSIDE_BBOX) 1055 << "\n" 1056 "void main()\n" 1057 "{\n" 1058 " mediump vec4 baseColor = " << colorInputName << ";\n" 1059 " mediump float blueChannel;\n" 1060 " if (fragmentInsideTheBBox(gl_FragCoord.z))\n" 1061 " blueChannel = 0.0;\n" 1062 " else\n" 1063 " blueChannel = 1.0;\n" 1064 " o_color = vec4(baseColor.r, baseColor.g, blueChannel, baseColor.a);\n" 1065 "}\n"; 1066 1067 return buf.str(); 1068 } 1069 1070 std::string GridRenderCase::genTessellationControlSource (void) const 1071 { 1072 std::ostringstream buf; 1073 1074 buf << "${GLSL_VERSION_DECL}\n" 1075 "${TESSELLATION_SHADER_REQUIRE}\n" 1076 "${PRIMITIVE_BOUNDING_BOX_REQUIRE}\n" 1077 "layout(vertices=3) out;\n" 1078 "\n" 1079 "in highp vec4 vtx_color[];\n" 1080 "out highp vec4 tess_ctrl_color[];\n" 1081 "uniform highp float u_tessellationLevel;\n" 1082 "uniform highp vec4 u_posScale;\n"; 1083 1084 if (!m_calcPerPrimitiveBBox) 1085 { 1086 buf << "uniform highp vec4 u_primitiveBBoxMin;\n" 1087 "uniform highp vec4 u_primitiveBBoxMax;\n"; 1088 } 1089 1090 buf << "patch out highp float vp_bbox_expansionSize;\n" 1091 "patch out highp vec3 vp_bbox_clipMin;\n" 1092 "patch out highp vec3 vp_bbox_clipMax;\n"; 1093 1094 if (m_calcPerPrimitiveBBox) 1095 { 1096 buf << "\n"; 1097 if (m_hasGeometryStage) 1098 buf << genShaderFunction(SHADER_FUNC_MIRROR_X); 1099 buf << genShaderFunction(SHADER_FUNC_MIRROR_Y); 1100 1101 buf << "vec4 transformVec(in highp vec4 p)\n" 1102 "{\n" 1103 " return " << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)")) << ";\n" 1104 "}\n"; 1105 } 1106 1107 buf << "\n" 1108 "void main()\n" 1109 "{\n" 1110 " // convert to nonsensical coordinates, just in case\n" 1111 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n" 1112 " tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n" 1113 "\n" 1114 " gl_TessLevelOuter[0] = u_tessellationLevel;\n" 1115 " gl_TessLevelOuter[1] = u_tessellationLevel;\n" 1116 " gl_TessLevelOuter[2] = u_tessellationLevel;\n" 1117 " gl_TessLevelInner[0] = u_tessellationLevel;\n"; 1118 1119 if (m_calcPerPrimitiveBBox) 1120 { 1121 buf << "\n" 1122 " highp vec4 bboxMin = min(min(transformVec(gl_in[0].gl_Position),\n" 1123 " transformVec(gl_in[1].gl_Position)),\n" 1124 " transformVec(gl_in[2].gl_Position));\n" 1125 " highp vec4 bboxMax = max(max(transformVec(gl_in[0].gl_Position),\n" 1126 " transformVec(gl_in[1].gl_Position)),\n" 1127 " transformVec(gl_in[2].gl_Position));\n"; 1128 } 1129 else 1130 { 1131 buf << "\n" 1132 " highp vec4 bboxMin = u_primitiveBBoxMin;\n" 1133 " highp vec4 bboxMax = u_primitiveBBoxMax;\n"; 1134 } 1135 1136 if (!m_useGlobalState) 1137 buf << "\n" 1138 " ${PRIM_GL_BOUNDING_BOX}[0] = bboxMin;\n" 1139 " ${PRIM_GL_BOUNDING_BOX}[1] = bboxMax;\n"; 1140 1141 buf << " vp_bbox_expansionSize = 0.0;\n" 1142 " vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n" 1143 " vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n" 1144 " vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n" 1145 " vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n" 1146 "}\n"; 1147 1148 return buf.str(); 1149 } 1150 1151 std::string GridRenderCase::genTessellationEvaluationSource (void) const 1152 { 1153 std::ostringstream buf; 1154 1155 buf << "${GLSL_VERSION_DECL}\n" 1156 "${TESSELLATION_SHADER_REQUIRE}\n" 1157 "${GPU_SHADER5_REQUIRE}\n" 1158 "layout(triangles) in;\n" 1159 "\n" 1160 "in highp vec4 tess_ctrl_color[];\n" 1161 "out highp vec4 tess_color;\n" 1162 "uniform highp vec4 u_posScale;\n" 1163 "patch in highp float vp_bbox_expansionSize;\n" 1164 "patch in highp vec3 vp_bbox_clipMin;\n" 1165 "patch in highp vec3 vp_bbox_clipMax;\n" 1166 "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n" 1167 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n" 1168 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n" 1169 "\n" 1170 "precise gl_Position;\n" 1171 "\n" 1172 << genShaderFunction(SHADER_FUNC_MIRROR_Y) 1173 << "void main()\n" 1174 "{\n" 1175 " // non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n" 1176 " gl_Position = mirrorY(gl_TessCoord.x * gl_in[0].gl_Position.zwyx +\n" 1177 " gl_TessCoord.y * gl_in[1].gl_Position.zwyx +\n" 1178 " gl_TessCoord.z * gl_in[2].gl_Position.zwyx);\n" 1179 " tess_color = tess_ctrl_color[0];\n" 1180 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = vp_bbox_expansionSize;\n" 1181 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin = vp_bbox_clipMin;\n" 1182 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax = vp_bbox_clipMax;\n" 1183 "}\n"; 1184 1185 return buf.str(); 1186 } 1187 1188 std::string GridRenderCase::genGeometrySource (void) const 1189 { 1190 const char* const colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color"); 1191 std::ostringstream buf; 1192 1193 buf << "${GLSL_VERSION_DECL}\n" 1194 "${GEOMETRY_SHADER_REQUIRE}\n" 1195 "layout(triangles) in;\n" 1196 "layout(max_vertices=9, triangle_strip) out;\n" 1197 "\n" 1198 "in highp vec4 " << colorInputName << "[3];\n" 1199 "out highp vec4 geo_color;\n" 1200 "uniform highp vec4 u_posScale;\n" 1201 "\n" 1202 "flat in highp float v_geo_bbox_expansionSize[3];\n" 1203 "flat in highp vec3 v_geo_bbox_clipMin[3];\n" 1204 "flat in highp vec3 v_geo_bbox_clipMax[3];\n" 1205 "flat out highp vec3 v_bbox_clipMin;\n" 1206 "flat out highp vec3 v_bbox_clipMax;\n" 1207 "flat out highp float v_bbox_expansionSize;\n" 1208 << genShaderFunction(SHADER_FUNC_MIRROR_X) 1209 << "\n" 1210 "void setVisualizationVaryings()\n" 1211 "{\n" 1212 " v_bbox_expansionSize = v_geo_bbox_expansionSize[0];\n" 1213 " v_bbox_clipMin = v_geo_bbox_clipMin[0];\n" 1214 " v_bbox_clipMax = v_geo_bbox_clipMax[0];\n" 1215 "}\n" 1216 "void main()\n" 1217 "{\n" 1218 " // Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n" 1219 " highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n" 1220 " highp vec4 p1 = mirrorX(gl_in[1].gl_Position);\n" 1221 " highp vec4 p2 = mirrorX(gl_in[2].gl_Position);\n" 1222 " highp vec4 pCentroid = vec4((p0.xyz + p1.xyz + p2.xyz) / 3.0, 1.0);\n" 1223 " highp vec4 triangleColor = " << colorInputName << "[0];\n" 1224 "\n" 1225 " gl_Position = p0; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n" 1226 " gl_Position = p1; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n" 1227 " gl_Position = pCentroid; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n" 1228 " EndPrimitive();\n" 1229 "\n" 1230 " gl_Position = p1; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n" 1231 " gl_Position = p2; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n" 1232 " gl_Position = pCentroid; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n" 1233 " EndPrimitive();\n" 1234 "\n" 1235 " gl_Position = p2; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n" 1236 " gl_Position = p0; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n" 1237 " gl_Position = pCentroid; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n" 1238 " EndPrimitive();\n" 1239 "}\n"; 1240 1241 return buf.str(); 1242 } 1243 1244 GridRenderCase::IterationConfig GridRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const 1245 { 1246 return generateRandomConfig(0xDEDEDEu * (deUint32)iteration, renderTargetSize); 1247 } 1248 1249 void GridRenderCase::getAttributeData (std::vector<tcu::Vec4>& data) const 1250 { 1251 const tcu::Vec4 green (0.0f, 1.0f, 0.0f, 1.0f); 1252 const tcu::Vec4 yellow (1.0f, 1.0f, 0.0f, 1.0f); 1253 std::vector<int> cellOrder (m_gridSize * m_gridSize); 1254 de::Random rnd (0xDE56789); 1255 1256 // generate grid with cells in random order 1257 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx) 1258 cellOrder[ndx] = ndx; 1259 rnd.shuffle(cellOrder.begin(), cellOrder.end()); 1260 1261 data.resize(m_gridSize * m_gridSize * 6 * 2); 1262 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx) 1263 { 1264 const int cellNdx = cellOrder[ndx]; 1265 const int cellX = cellNdx % m_gridSize; 1266 const int cellY = cellNdx / m_gridSize; 1267 const tcu::Vec4& cellColor = ((cellX+cellY)%2 == 0) ? (green) : (yellow); 1268 1269 data[(ndx * 6 + 0) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+0) / float(m_gridSize), float(cellY+0) / float(m_gridSize), 0.0f, 1.0f); 1270 data[(ndx * 6 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor; 1271 data[(ndx * 6 + 1) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+1) / float(m_gridSize), float(cellY+1) / float(m_gridSize), 0.0f, 1.0f); 1272 data[(ndx * 6 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor; 1273 data[(ndx * 6 + 2) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+0) / float(m_gridSize), float(cellY+1) / float(m_gridSize), 0.0f, 1.0f); 1274 data[(ndx * 6 + 2) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor; 1275 data[(ndx * 6 + 3) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+0) / float(m_gridSize), float(cellY+0) / float(m_gridSize), 0.0f, 1.0f); 1276 data[(ndx * 6 + 3) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor; 1277 data[(ndx * 6 + 4) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+1) / float(m_gridSize), float(cellY+0) / float(m_gridSize), 0.0f, 1.0f); 1278 data[(ndx * 6 + 4) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor; 1279 data[(ndx * 6 + 5) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+1) / float(m_gridSize), float(cellY+1) / float(m_gridSize), 0.0f, 1.0f); 1280 data[(ndx * 6 + 5) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor; 1281 } 1282 } 1283 1284 void GridRenderCase::renderTestPattern (const IterationConfig& config) 1285 { 1286 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 1287 1288 setupRender(config); 1289 1290 if (m_hasTessellationStage) 1291 { 1292 const glw::GLint tessLevelPos = gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel"); 1293 const glw::GLfloat tessLevel = 2.8f; // will be rounded up 1294 1295 TCU_CHECK(tessLevelPos != -1); 1296 1297 m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel << tcu::TestLog::EndMessage; 1298 1299 gl.uniform1f(tessLevelPos, tessLevel); 1300 gl.patchParameteri(GL_PATCH_VERTICES, 3); 1301 GLU_EXPECT_NO_ERROR(gl.getError(), "patch param"); 1302 } 1303 1304 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering grid." << tcu::TestLog::EndMessage; 1305 1306 gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_TRIANGLES), 0, m_gridSize * m_gridSize * 6); 1307 GLU_EXPECT_NO_ERROR(gl.getError(), "draw"); 1308 } 1309 1310 void GridRenderCase::verifyRenderResult (const IterationConfig& config) 1311 { 1312 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 1313 const ProjectedBBox projectedBBox = projectBoundingBox(config.bbox); 1314 const tcu::IVec4 viewportBBoxArea = getViewportBoundingBoxArea(projectedBBox, config.viewportSize); 1315 const tcu::IVec4 viewportGridOuterArea = getViewportPatternArea(config.patternPos, config.patternSize, config.viewportSize, ROUND_OUTWARDS); 1316 const tcu::IVec4 viewportGridInnerArea = getViewportPatternArea(config.patternPos, config.patternSize, config.viewportSize, ROUND_INWARDS); 1317 tcu::Surface viewportSurface (config.viewportSize.x(), config.viewportSize.y()); 1318 tcu::Surface errorMask (config.viewportSize.x(), config.viewportSize.y()); 1319 bool anyError = false; 1320 1321 if (!m_calcPerPrimitiveBBox) 1322 m_testCtx.getLog() 1323 << tcu::TestLog::Message 1324 << "Projected bounding box: (clip space)\n" 1325 << "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n" 1326 << "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n" 1327 << "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n" 1328 << "In viewport coordinates:\n" 1329 << "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n" 1330 << "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n" 1331 << "Verifying render results within the bounding box.\n" 1332 << tcu::TestLog::EndMessage; 1333 else 1334 m_testCtx.getLog() 1335 << tcu::TestLog::Message 1336 << "Verifying render result." 1337 << tcu::TestLog::EndMessage; 1338 1339 if (m_fbo) 1340 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo); 1341 glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(), viewportSurface.getAccess()); 1342 1343 tcu::clear(errorMask.getAccess(), tcu::IVec4(0,0,0,255)); 1344 1345 for (int y = de::max(viewportBBoxArea.y(), 0); y < de::min(viewportBBoxArea.w(), config.viewportSize.y()); ++y) 1346 for (int x = de::max(viewportBBoxArea.x(), 0); x < de::min(viewportBBoxArea.z(), config.viewportSize.x()); ++x) 1347 { 1348 const tcu::RGBA pixel = viewportSurface.getPixel(x, y); 1349 const bool outsideGrid = x < viewportGridOuterArea.x() || 1350 y < viewportGridOuterArea.y() || 1351 x > viewportGridOuterArea.z() || 1352 y > viewportGridOuterArea.w(); 1353 const bool insideGrid = x > viewportGridInnerArea.x() && 1354 y > viewportGridInnerArea.y() && 1355 x < viewportGridInnerArea.z() && 1356 y < viewportGridInnerArea.w(); 1357 1358 bool error = false; 1359 1360 if (outsideGrid) 1361 { 1362 // expect black 1363 if (pixel.getRed() != 0 || pixel.getGreen() != 0 || pixel.getBlue() != 0) 1364 error = true; 1365 } 1366 1367 else if (insideGrid) 1368 { 1369 // expect green, yellow or a combination of these 1370 if (pixel.getGreen() != 255 || pixel.getBlue() != 0) 1371 error = true; 1372 } 1373 else 1374 { 1375 // boundary, allow anything 1376 } 1377 1378 if (error) 1379 { 1380 errorMask.setPixel(x, y, tcu::RGBA::red()); 1381 anyError = true; 1382 } 1383 } 1384 1385 if (anyError) 1386 { 1387 m_testCtx.getLog() 1388 << tcu::TestLog::Message 1389 << "Image verification failed." 1390 << tcu::TestLog::EndMessage 1391 << tcu::TestLog::ImageSet("Images", "Image verification") 1392 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess()) 1393 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess()) 1394 << tcu::TestLog::EndImageSet; 1395 1396 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed"); 1397 } 1398 else 1399 { 1400 m_testCtx.getLog() 1401 << tcu::TestLog::Message 1402 << "Result image ok." 1403 << tcu::TestLog::EndMessage 1404 << tcu::TestLog::ImageSet("Images", "Image verification") 1405 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess()) 1406 << tcu::TestLog::EndImageSet; 1407 } 1408 } 1409 1410 class LineRenderCase : public BBoxRenderCase 1411 { 1412 public: 1413 enum 1414 { 1415 LINEFLAG_WIDE = 1u << FLAGBIT_USER_BIT, //!< use wide lines 1416 }; 1417 1418 LineRenderCase (Context& context, const char* name, const char* description, deUint32 flags); 1419 ~LineRenderCase (void); 1420 1421 private: 1422 enum 1423 { 1424 GREEN_COMPONENT_NDX = 1, 1425 BLUE_COMPONENT_NDX = 2, 1426 1427 SCAN_ROW_COMPONENT_NDX = GREEN_COMPONENT_NDX, // \note: scans are orthogonal to the line 1428 SCAN_COL_COMPONENT_NDX = BLUE_COMPONENT_NDX, 1429 }; 1430 1431 enum QueryDirection 1432 { 1433 DIRECTION_HORIZONTAL = 0, 1434 DIRECTION_VERTICAL, 1435 }; 1436 1437 enum ScanResult 1438 { 1439 SCANRESULT_NUM_LINES_OK_BIT = (1 << 0), 1440 SCANRESULT_LINE_WIDTH_OK_BIT = (1 << 1), 1441 SCANRESULT_LINE_WIDTH_WARN_BIT = (1 << 2), 1442 SCANRESULT_LINE_WIDTH_ERR_BIT = (1 << 3), 1443 SCANRESULT_LINE_CONT_OK_BIT = (1 << 4), 1444 SCANRESULT_LINE_CONT_ERR_BIT = (1 << 5), 1445 SCANRESULT_LINE_CONT_WARN_BIT = (1 << 6), 1446 }; 1447 1448 void init (void); 1449 1450 std::string genVertexSource (void) const; 1451 std::string genFragmentSource (void) const; 1452 std::string genTessellationControlSource (void) const; 1453 std::string genTessellationEvaluationSource (void) const; 1454 std::string genGeometrySource (void) const; 1455 1456 IterationConfig generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const; 1457 void getAttributeData (std::vector<tcu::Vec4>& data) const; 1458 void renderTestPattern (const IterationConfig& config); 1459 void verifyRenderResult (const IterationConfig& config); 1460 1461 tcu::IVec2 getNumberOfLinesRange (int queryAreaBegin, int queryAreaEnd, float patternStart, float patternSize, int viewportArea, QueryDirection queryDir) const; 1462 deUint8 scanRow (const tcu::ConstPixelBufferAccess& access, int row, int rowBegin, int rowEnd, int rowViewportBegin, int rowViewportEnd, const tcu::IVec2& numLines, int& floodCounter) const; 1463 deUint8 scanColumn (const tcu::ConstPixelBufferAccess& access, int column, int columnBegin, int columnEnd, int columnViewportBegin, int columnViewportEnd, const tcu::IVec2& numLines, int& floodCounter) const; 1464 bool checkAreaNumLines (const tcu::ConstPixelBufferAccess& access, const tcu::IVec4& area, int& floodCounter, int componentNdx, const tcu::IVec2& numLines) const; 1465 deUint8 checkLineContinuity (const tcu::ConstPixelBufferAccess& access, const tcu::IVec2& begin, const tcu::IVec2& end, int componentNdx, int& messageLimitCounter) const; 1466 tcu::IVec2 getNumMinimaMaxima (const tcu::ConstPixelBufferAccess& access, int componentNdx) const; 1467 deUint8 checkLineWidths (const tcu::ConstPixelBufferAccess& access, const tcu::IVec2& begin, const tcu::IVec2& end, int componentNdx, int& floodCounter) const; 1468 void printLineWidthError (const tcu::IVec2& pos, int detectedLineWidth, const tcu::IVec2& lineWidthRange, bool isHorizontal, int& floodCounter) const; 1469 1470 const int m_patternSide; 1471 const bool m_isWideLineCase; 1472 const int m_wideLineLineWidth; 1473 }; 1474 1475 LineRenderCase::LineRenderCase (Context& context, const char* name, const char* description, deUint32 flags) 1476 : BBoxRenderCase (context, name, description, 12, flags) 1477 , m_patternSide (12) 1478 , m_isWideLineCase ((flags & LINEFLAG_WIDE) != 0) 1479 , m_wideLineLineWidth (5) 1480 { 1481 } 1482 1483 LineRenderCase::~LineRenderCase (void) 1484 { 1485 } 1486 1487 void LineRenderCase::init (void) 1488 { 1489 m_testCtx.getLog() 1490 << tcu::TestLog::Message 1491 << "Rendering line pattern to " << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n" 1492 << "Vertical lines are green, horizontal lines blue. Using additive blending.\n" 1493 << "Line segments are in random order, varying pattern size and location for each iteration.\n" 1494 << "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated red channel." 1495 << tcu::TestLog::EndMessage; 1496 1497 if (m_isWideLineCase) 1498 { 1499 glw::GLfloat lineWidthRange[2] = {0.0f, 0.0f}; 1500 m_context.getRenderContext().getFunctions().getFloatv(GL_ALIASED_LINE_WIDTH_RANGE, lineWidthRange); 1501 1502 if (lineWidthRange[1] < (float)m_wideLineLineWidth) 1503 throw tcu::NotSupportedError("Test requires line width " + de::toString(m_wideLineLineWidth)); 1504 } 1505 1506 BBoxRenderCase::init(); 1507 } 1508 1509 std::string LineRenderCase::genVertexSource (void) const 1510 { 1511 std::ostringstream buf; 1512 1513 buf << "${GLSL_VERSION_DECL}\n" 1514 "in highp vec4 a_position;\n" 1515 "in highp vec4 a_color;\n" 1516 "out highp vec4 vtx_color;\n" 1517 "uniform highp vec4 u_posScale;\n" 1518 "uniform highp float u_lineWidth;\n" 1519 "\n"; 1520 if (!m_hasTessellationStage) 1521 { 1522 DE_ASSERT(m_useGlobalState); 1523 buf << "uniform highp vec4 u_primitiveBBoxMin;\n" 1524 "uniform highp vec4 u_primitiveBBoxMax;\n" 1525 "\n" 1526 "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n" 1527 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n" 1528 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n" 1529 "\n"; 1530 } 1531 buf << "void main()\n" 1532 "{\n" 1533 " highp vec2 patternOffset = u_posScale.xy;\n" 1534 " highp vec2 patternScale = u_posScale.zw;\n" 1535 " gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n" 1536 " vtx_color = a_color;\n"; 1537 if (!m_hasTessellationStage) 1538 { 1539 DE_ASSERT(m_useGlobalState); 1540 buf << "\n" 1541 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = u_lineWidth;\n" 1542 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin =\n" 1543 " min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMin.w,\n" 1544 " vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMax.w);\n" 1545 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax =\n" 1546 " min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMin.w,\n" 1547 " vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMax.w);\n"; 1548 } 1549 buf << "}\n"; 1550 1551 return buf.str(); 1552 } 1553 1554 std::string LineRenderCase::genFragmentSource (void) const 1555 { 1556 const char* const colorInputName = (m_hasGeometryStage) ? ("geo_color") : (m_hasTessellationStage) ? ("tess_color") : ("vtx_color"); 1557 std::ostringstream buf; 1558 1559 buf << "${GLSL_VERSION_DECL}\n" 1560 "in mediump vec4 " << colorInputName << ";\n" 1561 "layout(location = 0) out mediump vec4 o_color;\n" 1562 << genShaderFunction(SHADER_FUNC_INSIDE_BBOX) 1563 << "\n" 1564 "void main()\n" 1565 "{\n" 1566 " mediump vec4 baseColor = " << colorInputName << ";\n" 1567 " mediump float redChannel;\n" 1568 " if (fragmentInsideTheBBox(gl_FragCoord.z))\n" 1569 " redChannel = 0.0;\n" 1570 " else\n" 1571 " redChannel = 1.0;\n" 1572 " o_color = vec4(redChannel, baseColor.g, baseColor.b, baseColor.a);\n" 1573 "}\n"; 1574 1575 return buf.str(); 1576 } 1577 1578 std::string LineRenderCase::genTessellationControlSource (void) const 1579 { 1580 std::ostringstream buf; 1581 1582 buf << "${GLSL_VERSION_DECL}\n" 1583 "${TESSELLATION_SHADER_REQUIRE}\n" 1584 "${PRIMITIVE_BOUNDING_BOX_REQUIRE}\n" 1585 "layout(vertices=2) out;" 1586 "\n" 1587 "in highp vec4 vtx_color[];\n" 1588 "out highp vec4 tess_ctrl_color[];\n" 1589 "uniform highp float u_tessellationLevel;\n" 1590 "uniform highp vec4 u_posScale;\n" 1591 "uniform highp float u_lineWidth;\n"; 1592 1593 if (!m_calcPerPrimitiveBBox) 1594 { 1595 buf << "uniform highp vec4 u_primitiveBBoxMin;\n" 1596 "uniform highp vec4 u_primitiveBBoxMax;\n"; 1597 } 1598 1599 buf << "patch out highp float vp_bbox_expansionSize;\n" 1600 "patch out highp vec3 vp_bbox_clipMin;\n" 1601 "patch out highp vec3 vp_bbox_clipMax;\n"; 1602 1603 if (m_calcPerPrimitiveBBox) 1604 { 1605 buf << "\n"; 1606 if (m_hasGeometryStage) 1607 buf << genShaderFunction(SHADER_FUNC_MIRROR_X); 1608 buf << genShaderFunction(SHADER_FUNC_MIRROR_Y); 1609 1610 buf << "vec4 transformVec(in highp vec4 p)\n" 1611 "{\n" 1612 " return " << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)")) << ";\n" 1613 "}\n"; 1614 } 1615 1616 buf << "\n" 1617 "void main()\n" 1618 "{\n" 1619 " // convert to nonsensical coordinates, just in case\n" 1620 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n" 1621 " tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n" 1622 "\n" 1623 " gl_TessLevelOuter[0] = 0.8; // will be rounded up to 1\n" 1624 " gl_TessLevelOuter[1] = u_tessellationLevel;\n"; 1625 1626 if (m_calcPerPrimitiveBBox) 1627 { 1628 buf << "\n" 1629 " highp vec4 bboxMin = min(transformVec(gl_in[0].gl_Position),\n" 1630 " transformVec(gl_in[1].gl_Position));\n" 1631 " highp vec4 bboxMax = max(transformVec(gl_in[0].gl_Position),\n" 1632 " transformVec(gl_in[1].gl_Position));\n"; 1633 } 1634 else 1635 { 1636 buf << "\n" 1637 " highp vec4 bboxMin = u_primitiveBBoxMin;\n" 1638 " highp vec4 bboxMax = u_primitiveBBoxMax;\n"; 1639 } 1640 1641 if (!m_useGlobalState) 1642 buf << "\n" 1643 " ${PRIM_GL_BOUNDING_BOX}[0] = bboxMin;\n" 1644 " ${PRIM_GL_BOUNDING_BOX}[1] = bboxMax;\n"; 1645 1646 buf << " vp_bbox_expansionSize = u_lineWidth;\n" 1647 " vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n" 1648 " vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n" 1649 " vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n" 1650 " vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n" 1651 "}\n"; 1652 1653 return buf.str(); 1654 } 1655 1656 std::string LineRenderCase::genTessellationEvaluationSource (void) const 1657 { 1658 std::ostringstream buf; 1659 1660 buf << "${GLSL_VERSION_DECL}\n" 1661 "${TESSELLATION_SHADER_REQUIRE}\n" 1662 "layout(isolines) in;" 1663 "\n" 1664 "in highp vec4 tess_ctrl_color[];\n" 1665 "out highp vec4 tess_color;\n" 1666 "uniform highp vec4 u_posScale;\n" 1667 "\n" 1668 "patch in highp float vp_bbox_expansionSize;\n" 1669 "patch in highp vec3 vp_bbox_clipMin;\n" 1670 "patch in highp vec3 vp_bbox_clipMax;\n" 1671 "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n" 1672 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n" 1673 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n" 1674 << genShaderFunction(SHADER_FUNC_MIRROR_Y) 1675 << "void main()\n" 1676 "{\n" 1677 " // non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n" 1678 " gl_Position = mirrorY(mix(gl_in[0].gl_Position.zwyx, gl_in[1].gl_Position.zwyx, gl_TessCoord.x));\n" 1679 " tess_color = tess_ctrl_color[0];\n" 1680 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = vp_bbox_expansionSize;\n" 1681 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin = vp_bbox_clipMin;\n" 1682 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax = vp_bbox_clipMax;\n" 1683 "}\n"; 1684 1685 return buf.str(); 1686 } 1687 1688 std::string LineRenderCase::genGeometrySource (void) const 1689 { 1690 const char* const colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color"); 1691 std::ostringstream buf; 1692 1693 buf << "${GLSL_VERSION_DECL}\n" 1694 "${GEOMETRY_SHADER_REQUIRE}\n" 1695 "layout(lines) in;\n" 1696 "layout(max_vertices=5, line_strip) out;\n" 1697 "\n" 1698 "in highp vec4 " << colorInputName << "[2];\n" 1699 "out highp vec4 geo_color;\n" 1700 "uniform highp vec4 u_posScale;\n" 1701 "\n" 1702 "\n" 1703 "flat in highp float v_geo_bbox_expansionSize[2];\n" 1704 "flat in highp vec3 v_geo_bbox_clipMin[2];\n" 1705 "flat in highp vec3 v_geo_bbox_clipMax[2];\n" 1706 "flat out highp vec3 v_bbox_clipMin;\n" 1707 "flat out highp vec3 v_bbox_clipMax;\n" 1708 "flat out highp float v_bbox_expansionSize;\n" 1709 << genShaderFunction(SHADER_FUNC_MIRROR_X) 1710 << "\n" 1711 "void setVisualizationVaryings()\n" 1712 "{\n" 1713 " v_bbox_expansionSize = v_geo_bbox_expansionSize[0];\n" 1714 " v_bbox_clipMin = v_geo_bbox_clipMin[0];\n" 1715 " v_bbox_clipMax = v_geo_bbox_clipMax[0];\n" 1716 "}\n" 1717 "void main()\n" 1718 "{\n" 1719 " // Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n" 1720 " highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n" 1721 " highp vec4 p1 = mirrorX(gl_in[1].gl_Position);\n" 1722 " highp vec4 lineColor = " << colorInputName << "[0];\n" 1723 "\n" 1724 " // output two separate primitives, just in case\n" 1725 " gl_Position = mix(p0, p1, 0.00); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n" 1726 " gl_Position = mix(p0, p1, 0.33); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n" 1727 " EndPrimitive();\n" 1728 "\n" 1729 " gl_Position = mix(p0, p1, 0.33); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n" 1730 " gl_Position = mix(p0, p1, 0.67); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n" 1731 " gl_Position = mix(p0, p1, 1.00); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n" 1732 " EndPrimitive();\n" 1733 "}\n"; 1734 1735 return buf.str(); 1736 } 1737 1738 LineRenderCase::IterationConfig LineRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const 1739 { 1740 const int numMaxAttempts = 128; 1741 1742 // Avoid too narrow viewports, lines could merge together. Require viewport is at least 2.5x the size of the line bodies. 1743 for (int attemptNdx = 0; attemptNdx < numMaxAttempts; ++attemptNdx) 1744 { 1745 const IterationConfig& config = generateRandomConfig((0xDEDEDEu * (deUint32)iteration) ^ (0xABAB13 * attemptNdx), renderTargetSize); 1746 1747 if ((float)config.viewportSize.x() * (config.patternSize.x() * 0.5f) > 2.5f * (float)m_patternSide * (float)m_wideLineLineWidth && 1748 (float)config.viewportSize.y() * (config.patternSize.y() * 0.5f) > 2.5f * (float)m_patternSide * (float)m_wideLineLineWidth) 1749 { 1750 return config; 1751 } 1752 } 1753 1754 DE_ASSERT(false); 1755 return IterationConfig(); 1756 } 1757 1758 void LineRenderCase::getAttributeData (std::vector<tcu::Vec4>& data) const 1759 { 1760 const tcu::Vec4 green (0.0f, 1.0f, 0.0f, 1.0f); 1761 const tcu::Vec4 blue (0.0f, 0.0f, 1.0f, 1.0f); 1762 std::vector<int> cellOrder (m_patternSide * m_patternSide * 2); 1763 de::Random rnd (0xDE12345); 1764 1765 // generate crosshatch pattern with segments in random order 1766 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx) 1767 cellOrder[ndx] = ndx; 1768 rnd.shuffle(cellOrder.begin(), cellOrder.end()); 1769 1770 data.resize(cellOrder.size() * 4); 1771 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx) 1772 { 1773 const int segmentID = cellOrder[ndx]; 1774 const int direction = segmentID & 0x01; 1775 const int majorCoord = (segmentID >> 1) / m_patternSide; 1776 const int minorCoord = (segmentID >> 1) % m_patternSide; 1777 1778 if (direction) 1779 { 1780 data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(minorCoord) / float(m_patternSide), float(majorCoord) / float(m_patternSide), 0.0f, 1.0f); 1781 data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green; 1782 data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(minorCoord) / float(m_patternSide), float(majorCoord + 1) / float(m_patternSide), 0.0f, 1.0f); 1783 data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green; 1784 } 1785 else 1786 { 1787 data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(majorCoord) / float(m_patternSide), float(minorCoord) / float(m_patternSide), 0.0f, 1.0f); 1788 data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue; 1789 data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(majorCoord + 1) / float(m_patternSide), float(minorCoord) / float(m_patternSide), 0.0f, 1.0f); 1790 data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue; 1791 } 1792 } 1793 } 1794 1795 void LineRenderCase::renderTestPattern (const IterationConfig& config) 1796 { 1797 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 1798 1799 setupRender(config); 1800 1801 if (m_hasTessellationStage) 1802 { 1803 const glw::GLint tessLevelPos = gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel"); 1804 const glw::GLfloat tessLevel = 2.8f; // will be rounded up 1805 1806 TCU_CHECK(tessLevelPos != -1); 1807 1808 m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel << tcu::TestLog::EndMessage; 1809 1810 gl.uniform1f(tessLevelPos, tessLevel); 1811 gl.patchParameteri(GL_PATCH_VERTICES, 2); 1812 GLU_EXPECT_NO_ERROR(gl.getError(), "patch param"); 1813 } 1814 1815 if (m_isWideLineCase) 1816 gl.lineWidth((float)m_wideLineLineWidth); 1817 1818 gl.uniform1f(gl.getUniformLocation(m_program->getProgram(), "u_lineWidth"), (m_isWideLineCase) ? ((float)m_wideLineLineWidth) : (1.0f)); 1819 1820 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering pattern." << tcu::TestLog::EndMessage; 1821 1822 gl.enable(GL_BLEND); 1823 gl.blendFunc(GL_ONE, GL_ONE); 1824 gl.blendEquation(GL_FUNC_ADD); 1825 1826 gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_LINES), 0, m_patternSide * m_patternSide * 2 * 2); 1827 GLU_EXPECT_NO_ERROR(gl.getError(), "draw"); 1828 } 1829 1830 void LineRenderCase::verifyRenderResult (const IterationConfig& config) 1831 { 1832 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 1833 const bool isMsaa = m_context.getRenderTarget().getNumSamples() > 1; 1834 const ProjectedBBox projectedBBox = projectBoundingBox(config.bbox); 1835 const float lineWidth = (m_isWideLineCase) ? ((float)m_wideLineLineWidth) : (1.0f); 1836 const tcu::IVec4 viewportBBoxArea = getViewportBoundingBoxArea(projectedBBox, config.viewportSize, lineWidth); 1837 const tcu::IVec4 viewportPatternArea = getViewportPatternArea(config.patternPos, config.patternSize, config.viewportSize, ROUND_INWARDS); 1838 const tcu::IVec2 expectedHorizontalLines = getNumberOfLinesRange(viewportBBoxArea.y(), viewportBBoxArea.w(), config.patternPos.y(), config.patternSize.y(), config.viewportSize.y(), DIRECTION_VERTICAL); 1839 const tcu::IVec2 expectedVerticalLines = getNumberOfLinesRange(viewportBBoxArea.x(), viewportBBoxArea.z(), config.patternPos.x(), config.patternSize.x(), config.viewportSize.x(), DIRECTION_HORIZONTAL); 1840 const tcu::IVec4 verificationArea = tcu::IVec4(de::max(viewportBBoxArea.x(), 0), 1841 de::max(viewportBBoxArea.y(), 0), 1842 de::min(viewportBBoxArea.z(), config.viewportSize.x()), 1843 de::min(viewportBBoxArea.w(), config.viewportSize.y())); 1844 1845 tcu::Surface viewportSurface (config.viewportSize.x(), config.viewportSize.y()); 1846 int messageLimitCounter = 8; 1847 1848 enum ScanResultCodes 1849 { 1850 SCANRESULT_NUM_LINES_ERR = 0, 1851 SCANRESULT_LINE_WIDTH_MSAA = 1, 1852 SCANRESULT_LINE_WIDTH_WARN = 2, 1853 SCANRESULT_LINE_WIDTH_ERR = 3, 1854 SCANRESULT_LINE_CONT_ERR = 4, 1855 SCANRESULT_LINE_CONT_WARN = 5, 1856 SCANRESULT_LINE_LAST 1857 }; 1858 1859 int rowScanResult[SCANRESULT_LINE_LAST] = {0, 0, 0, 0, 0, 0}; 1860 int columnScanResult[SCANRESULT_LINE_LAST] = {0, 0, 0, 0, 0, 0}; 1861 bool anyError = false; 1862 bool msaaRelaxationRequired = false; 1863 bool hwIssueRelaxationRequired = false; 1864 1865 if (!m_calcPerPrimitiveBBox) 1866 m_testCtx.getLog() 1867 << tcu::TestLog::Message 1868 << "Projected bounding box: (clip space)\n" 1869 << "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n" 1870 << "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n" 1871 << "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n" 1872 << "In viewport coordinates:\n" 1873 << "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n" 1874 << "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n" 1875 << "Verifying render results within the bounding box:\n" 1876 << tcu::TestLog::EndMessage; 1877 else 1878 m_testCtx.getLog() 1879 << tcu::TestLog::Message 1880 << "Verifying render result:" 1881 << tcu::TestLog::EndMessage; 1882 1883 m_testCtx.getLog() 1884 << tcu::TestLog::Message 1885 << "\tCalculating number of horizontal and vertical lines within the bounding box, expecting:\n" 1886 << "\t[" << expectedHorizontalLines.x() << ", " << expectedHorizontalLines.y() << "] horizontal lines.\n" 1887 << "\t[" << expectedVerticalLines.x() << ", " << expectedVerticalLines.y() << "] vertical lines.\n" 1888 << tcu::TestLog::EndMessage; 1889 1890 if (m_fbo) 1891 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo); 1892 glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(), viewportSurface.getAccess()); 1893 1894 // scan rows 1895 for (int y = de::max(verificationArea.y(), viewportPatternArea.y()); y < de::min(verificationArea.w(), viewportPatternArea.w()); ++y) 1896 { 1897 const deUint8 result = scanRow(viewportSurface.getAccess(), 1898 y, 1899 verificationArea.x(), 1900 verificationArea.z(), 1901 de::max(verificationArea.x(), viewportPatternArea.x()), 1902 de::min(verificationArea.z(), viewportPatternArea.z()), 1903 expectedVerticalLines, 1904 messageLimitCounter); 1905 1906 if ((result & SCANRESULT_NUM_LINES_OK_BIT) == 0) 1907 rowScanResult[SCANRESULT_NUM_LINES_ERR]++; 1908 if ((result & SCANRESULT_LINE_CONT_OK_BIT) == 0) 1909 { 1910 if ((result & SCANRESULT_LINE_CONT_WARN_BIT) != 0) 1911 rowScanResult[SCANRESULT_LINE_CONT_WARN]++; 1912 else 1913 rowScanResult[SCANRESULT_LINE_CONT_ERR]++; 1914 } 1915 else if ((result & SCANRESULT_LINE_WIDTH_OK_BIT) == 0) 1916 { 1917 if (m_isWideLineCase && isMsaa) 1918 { 1919 // multisampled wide lines might not be supported 1920 rowScanResult[SCANRESULT_LINE_WIDTH_MSAA]++; 1921 } 1922 else if ((result & SCANRESULT_LINE_WIDTH_ERR_BIT) == 0 && 1923 (result & SCANRESULT_LINE_WIDTH_WARN_BIT) != 0) 1924 { 1925 rowScanResult[SCANRESULT_LINE_WIDTH_WARN]++; 1926 } 1927 else 1928 rowScanResult[SCANRESULT_LINE_WIDTH_ERR]++; 1929 } 1930 } 1931 1932 // scan columns 1933 for (int x = de::max(verificationArea.x(), viewportPatternArea.x()); x < de::min(verificationArea.z(), viewportPatternArea.z()); ++x) 1934 { 1935 const deUint8 result = scanColumn(viewportSurface.getAccess(), 1936 x, 1937 verificationArea.y(), 1938 verificationArea.w(), 1939 de::min(verificationArea.y(), viewportPatternArea.y()), 1940 de::min(verificationArea.w(), viewportPatternArea.w()), 1941 expectedHorizontalLines, 1942 messageLimitCounter); 1943 1944 if ((result & SCANRESULT_NUM_LINES_OK_BIT) == 0) 1945 columnScanResult[SCANRESULT_NUM_LINES_ERR]++; 1946 if ((result & SCANRESULT_LINE_CONT_OK_BIT) == 0) 1947 { 1948 if ((result & SCANRESULT_LINE_CONT_WARN_BIT) != 0) 1949 columnScanResult[SCANRESULT_LINE_CONT_WARN]++; 1950 else 1951 columnScanResult[SCANRESULT_LINE_CONT_ERR]++; 1952 } 1953 else if ((result & SCANRESULT_LINE_WIDTH_OK_BIT) == 0) 1954 { 1955 if (m_isWideLineCase && isMsaa) 1956 { 1957 // multisampled wide lines might not be supported 1958 columnScanResult[SCANRESULT_LINE_WIDTH_MSAA]++; 1959 } 1960 else if ((result & SCANRESULT_LINE_WIDTH_ERR_BIT) == 0 && 1961 (result & SCANRESULT_LINE_WIDTH_WARN_BIT) != 0) 1962 { 1963 columnScanResult[SCANRESULT_LINE_WIDTH_WARN]++; 1964 } 1965 else 1966 columnScanResult[SCANRESULT_LINE_WIDTH_ERR]++; 1967 } 1968 } 1969 1970 if (columnScanResult[SCANRESULT_LINE_WIDTH_ERR] != 0 || rowScanResult[SCANRESULT_LINE_WIDTH_ERR] != 0) 1971 anyError = true; 1972 else if(columnScanResult[SCANRESULT_LINE_CONT_ERR] != 0 || rowScanResult[SCANRESULT_LINE_CONT_ERR] != 0) 1973 anyError = true; 1974 else if (columnScanResult[SCANRESULT_LINE_WIDTH_MSAA] != 0 || rowScanResult[SCANRESULT_LINE_WIDTH_MSAA] != 0) 1975 msaaRelaxationRequired = true; 1976 else if (columnScanResult[SCANRESULT_LINE_WIDTH_WARN] != 0 || rowScanResult[SCANRESULT_LINE_WIDTH_WARN] != 0) 1977 hwIssueRelaxationRequired = true; 1978 else if (columnScanResult[SCANRESULT_NUM_LINES_ERR] != 0) 1979 { 1980 // found missing lines in a columnw and row line continuity check reported a warning (not an error) -> line width precision issue 1981 if (rowScanResult[SCANRESULT_LINE_CONT_ERR] == 0 && rowScanResult[SCANRESULT_LINE_CONT_WARN]) 1982 hwIssueRelaxationRequired = true; 1983 else 1984 anyError = true; 1985 } 1986 else if (rowScanResult[SCANRESULT_NUM_LINES_ERR] != 0) 1987 { 1988 // found missing lines in a row and column line continuity check reported a warning (not an error) -> line width precision issue 1989 if (columnScanResult[SCANRESULT_LINE_CONT_ERR] == 0 && columnScanResult[SCANRESULT_LINE_CONT_WARN]) 1990 hwIssueRelaxationRequired = true; 1991 else 1992 anyError = true; 1993 } 1994 1995 if (anyError || msaaRelaxationRequired || hwIssueRelaxationRequired) 1996 { 1997 if (messageLimitCounter < 0) 1998 m_testCtx.getLog() << tcu::TestLog::Message << "Omitted " << (-messageLimitCounter) << " row/column error descriptions." << tcu::TestLog::EndMessage; 1999 2000 m_testCtx.getLog() 2001 << tcu::TestLog::Message 2002 << "Image verification failed." 2003 << tcu::TestLog::EndMessage 2004 << tcu::TestLog::ImageSet("Images", "Image verification") 2005 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess()) 2006 << tcu::TestLog::EndImageSet; 2007 2008 if (anyError) 2009 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed"); 2010 else if (hwIssueRelaxationRequired) 2011 { 2012 // Line width hw issue 2013 m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Line width verification failed"); 2014 } 2015 else 2016 { 2017 // MSAA wide lines are optional 2018 m_testCtx.setTestResult(QP_TEST_RESULT_COMPATIBILITY_WARNING, "Multisampled wide line verification failed"); 2019 } 2020 } 2021 else 2022 { 2023 m_testCtx.getLog() 2024 << tcu::TestLog::Message 2025 << "Result image ok." 2026 << tcu::TestLog::EndMessage 2027 << tcu::TestLog::ImageSet("Images", "Image verification") 2028 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess()) 2029 << tcu::TestLog::EndImageSet; 2030 } 2031 } 2032 2033 tcu::IVec2 LineRenderCase::getNumberOfLinesRange (int queryAreaBegin, int queryAreaEnd, float patternStart, float patternSize, int viewportArea, QueryDirection queryDir) const 2034 { 2035 // pattern is not symmetric due to mirroring 2036 const int patternStartNdx = (queryDir == DIRECTION_HORIZONTAL) ? ((m_hasGeometryStage) ? (1) : (0)) : ((m_hasTessellationStage) ? (1) : (0)); 2037 const int patternEndNdx = patternStartNdx + m_patternSide; 2038 2039 int numLinesMin = 0; 2040 int numLinesMax = 0; 2041 2042 for (int lineNdx = patternStartNdx; lineNdx < patternEndNdx; ++lineNdx) 2043 { 2044 const float linePos = (patternStart + (float(lineNdx) / float(m_patternSide)) * patternSize) * 0.5f + 0.5f; 2045 const float lineWidth = (m_isWideLineCase) ? ((float)m_wideLineLineWidth) : (1.0f); 2046 2047 if (linePos * (float)viewportArea > (float)queryAreaBegin + 1.0f && 2048 linePos * (float)viewportArea < (float)queryAreaEnd - 1.0f) 2049 { 2050 // line center is within the area 2051 ++numLinesMin; 2052 ++numLinesMax; 2053 } 2054 else if (linePos * (float)viewportArea > (float)queryAreaBegin - lineWidth*0.5f - 1.0f && 2055 linePos * (float)viewportArea < (float)queryAreaEnd + lineWidth*0.5f + 1.0f) 2056 { 2057 // line could leak into area 2058 ++numLinesMax; 2059 } 2060 } 2061 2062 return tcu::IVec2(numLinesMin, numLinesMax); 2063 } 2064 2065 deUint8 LineRenderCase::scanRow (const tcu::ConstPixelBufferAccess& access, int row, int rowBegin, int rowEnd, int rowViewportBegin, int rowViewportEnd, const tcu::IVec2& numLines, int& messageLimitCounter) const 2066 { 2067 const bool numLinesOk = checkAreaNumLines(access, tcu::IVec4(rowBegin, row, rowEnd - rowBegin, 1), messageLimitCounter, SCAN_ROW_COMPONENT_NDX, numLines); 2068 const deUint8 lineWidthRes = checkLineWidths(access, tcu::IVec2(rowBegin, row), tcu::IVec2(rowEnd, row), SCAN_ROW_COMPONENT_NDX, messageLimitCounter); 2069 const deUint8 lineContinuityRes = checkLineContinuity(access, tcu::IVec2(rowViewportBegin, row), tcu::IVec2(rowViewportEnd, row), SCAN_COL_COMPONENT_NDX, messageLimitCounter); 2070 deUint8 result = 0; 2071 2072 if (numLinesOk) 2073 result |= (deUint8)SCANRESULT_NUM_LINES_OK_BIT; 2074 2075 if (lineContinuityRes == 0) 2076 result |= (deUint8)SCANRESULT_LINE_CONT_OK_BIT; 2077 else 2078 result |= lineContinuityRes; 2079 2080 if (lineWidthRes == 0) 2081 result |= (deUint8)SCANRESULT_LINE_WIDTH_OK_BIT; 2082 else 2083 result |= lineWidthRes; 2084 2085 return result; 2086 } 2087 2088 deUint8 LineRenderCase::scanColumn (const tcu::ConstPixelBufferAccess& access, int column, int columnBegin, int columnEnd, int columnViewportBegin, int columnViewportEnd, const tcu::IVec2& numLines, int& messageLimitCounter) const 2089 { 2090 const bool numLinesOk = checkAreaNumLines(access, tcu::IVec4(column, columnBegin, 1, columnEnd - columnBegin), messageLimitCounter, SCAN_COL_COMPONENT_NDX, numLines); 2091 const deUint8 lineWidthRes = checkLineWidths(access, tcu::IVec2(column, columnBegin), tcu::IVec2(column, columnEnd), SCAN_COL_COMPONENT_NDX, messageLimitCounter); 2092 const deUint8 lineContinuityRes = checkLineContinuity(access, tcu::IVec2(column, columnViewportBegin), tcu::IVec2(column, columnViewportEnd), SCAN_ROW_COMPONENT_NDX, messageLimitCounter); 2093 deUint8 result = 0; 2094 2095 if (numLinesOk) 2096 result |= (deUint8)SCANRESULT_NUM_LINES_OK_BIT; 2097 2098 if (lineContinuityRes == 0) 2099 result |= (deUint8)SCANRESULT_LINE_CONT_OK_BIT; 2100 else 2101 result |= lineContinuityRes; 2102 2103 if (lineWidthRes == 0) 2104 result |= (deUint8)SCANRESULT_LINE_WIDTH_OK_BIT; 2105 else 2106 result |= lineWidthRes; 2107 2108 return result; 2109 } 2110 2111 bool LineRenderCase::checkAreaNumLines (const tcu::ConstPixelBufferAccess& access, const tcu::IVec4& area, int& messageLimitCounter, int componentNdx, const tcu::IVec2& numLines) const 2112 { 2113 // Num maxima == num lines 2114 const tcu::ConstPixelBufferAccess subAccess = tcu::getSubregion(access, area.x(), area.y(), 0, area.z(), area.w(), 1); 2115 const tcu::IVec2 numMinimaMaxima = getNumMinimaMaxima(subAccess, componentNdx); 2116 const int numMaxima = numMinimaMaxima.y(); 2117 2118 // In valid range 2119 if (numMaxima >= numLines.x() && numMaxima <= numLines.y()) 2120 return true; 2121 2122 if (--messageLimitCounter < 0) 2123 return false; 2124 2125 if (area.z() == 1) 2126 m_testCtx.getLog() 2127 << tcu::TestLog::Message 2128 << "On column " << area.x() << ", y: [" << area.y() << "," << (area.y()+area.w()) << "):\n" 2129 << "\tExpected [" << numLines.x() << ", " << numLines.y() << "] lines but the number of lines in the area is " << numMaxima 2130 << tcu::TestLog::EndMessage; 2131 else 2132 m_testCtx.getLog() 2133 << tcu::TestLog::Message 2134 << "On row " << area.y() << ", x: [" << area.x() << "," << (area.x()+area.z()) << "):\n" 2135 << "\tExpected [" << numLines.x() << ", " << numLines.y() << "] lines but the number of lines in the area is " << numMaxima 2136 << tcu::TestLog::EndMessage; 2137 2138 return false; 2139 } 2140 2141 tcu::IVec2 LineRenderCase::getNumMinimaMaxima (const tcu::ConstPixelBufferAccess& access, int componentNdx) const 2142 { 2143 DE_ASSERT(access.getWidth() == 1 || access.getHeight() == 1); 2144 2145 int previousValue = -1; 2146 int previousSign = 0; 2147 int numMinima = 0; 2148 int numMaxima = 0; 2149 2150 for (int y = 0; y < access.getHeight(); ++y) 2151 for (int x = 0; x < access.getWidth(); ++x) 2152 { 2153 const int componentValue = access.getPixelInt(x, y)[componentNdx]; 2154 2155 if (previousValue != -1) 2156 { 2157 const int sign = (componentValue > previousValue) ? (+1) : (componentValue < previousValue) ? (-1) : (0); 2158 2159 // local minima/maxima in sign changes (zero signless) 2160 if (sign != 0 && sign == -previousSign) 2161 { 2162 previousSign = sign; 2163 2164 if (sign > 0) 2165 ++numMinima; 2166 else 2167 ++numMaxima; 2168 } 2169 else if (sign != 0 && previousSign == 0) 2170 { 2171 previousSign = sign; 2172 2173 // local extreme at the start boundary 2174 if (sign > 0) 2175 ++numMinima; 2176 else 2177 ++numMaxima; 2178 } 2179 } 2180 2181 previousValue = componentValue; 2182 } 2183 2184 // local extreme at the end boundary 2185 if (previousSign > 0) 2186 ++numMaxima; 2187 else if (previousSign < 0) 2188 ++numMinima; 2189 else 2190 { 2191 ++numMaxima; 2192 ++numMinima; 2193 } 2194 2195 return tcu::IVec2(numMinima, numMaxima); 2196 } 2197 2198 deUint8 LineRenderCase::checkLineContinuity (const tcu::ConstPixelBufferAccess& access, const tcu::IVec2& begin, const tcu::IVec2& end, int componentNdx, int& messageLimitCounter) const 2199 { 2200 bool line = false; 2201 const tcu::IVec2 advance = (begin.x() == end.x()) ? (tcu::IVec2(0, 1)) : (tcu::IVec2(1, 0)); 2202 int missedPixels = 0; 2203 int totalPixels = 0; 2204 deUint8 errorMask = 0; 2205 2206 for (tcu::IVec2 cursor = begin; cursor != end; cursor += advance) 2207 { 2208 const bool hit = (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0); 2209 2210 if (hit) 2211 line = true; 2212 else if (line && !hit) 2213 { 2214 // non-continuous line detected 2215 const tcu::IVec2 advanceNeighbor = tcu::IVec2(1, 1) - advance; 2216 const tcu::IVec2 cursorNeighborPos = cursor + advanceNeighbor; 2217 const tcu::IVec2 cursorNeighborNeg = cursor - advanceNeighbor; 2218 // hw precision issues may lead to a line being non-straight -> check neighboring pixels 2219 if ((access.getPixelInt(cursorNeighborPos.x(), cursorNeighborPos.y())[componentNdx] == 0) && (access.getPixelInt(cursorNeighborNeg.x(), cursorNeighborNeg.y())[componentNdx] == 0)) 2220 ++missedPixels; 2221 } 2222 ++totalPixels; 2223 } 2224 2225 if (missedPixels > 0) 2226 { 2227 if (--messageLimitCounter >= 0) 2228 { 2229 m_testCtx.getLog() 2230 << tcu::TestLog::Message 2231 << "Found non-continuous " << ((advance.x() == 1) ? ("horizontal") : ("vertical")) << " line near " << begin << ". " 2232 << "Missed pixels: " << missedPixels 2233 << tcu::TestLog::EndMessage; 2234 } 2235 // allow 10% missing pixels for warning 2236 if (missedPixels <= deRoundFloatToInt32((float)totalPixels * 0.1f)) 2237 errorMask = SCANRESULT_LINE_CONT_WARN_BIT; 2238 else 2239 errorMask = SCANRESULT_LINE_CONT_ERR_BIT; 2240 } 2241 2242 return errorMask; 2243 } 2244 2245 deUint8 LineRenderCase::checkLineWidths (const tcu::ConstPixelBufferAccess& access, const tcu::IVec2& begin, const tcu::IVec2& end, int componentNdx, int& messageLimitCounter) const 2246 { 2247 const bool multisample = m_context.getRenderTarget().getNumSamples() > 1; 2248 const int lineRenderWidth = (m_isWideLineCase) ? (m_wideLineLineWidth) : 1; 2249 const tcu::IVec2 lineWidthRange = (multisample) 2250 ? (tcu::IVec2(lineRenderWidth, lineRenderWidth+1)) // multisampled "smooth" lines may spread to neighboring pixel 2251 : (tcu::IVec2(lineRenderWidth, lineRenderWidth)); 2252 const tcu::IVec2 relaxedLineWidthRange = (tcu::IVec2(lineRenderWidth-1, lineRenderWidth+1)); 2253 2254 int lineWidth = 0; 2255 bool bboxLimitedLine = false; 2256 deUint8 errorMask = 0; 2257 2258 const tcu::IVec2 advance = (begin.x() == end.x()) ? (tcu::IVec2(0, 1)) : (tcu::IVec2(1, 0)); 2259 2260 // fragments before begin? 2261 if (access.getPixelInt(begin.x(), begin.y())[componentNdx] != 0) 2262 { 2263 bboxLimitedLine = true; 2264 2265 for (tcu::IVec2 cursor = begin - advance;; cursor -= advance) 2266 { 2267 if (cursor.x() < 0 || cursor.y() < 0) 2268 { 2269 break; 2270 } 2271 else if (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0) 2272 { 2273 ++lineWidth; 2274 } 2275 else 2276 break; 2277 } 2278 } 2279 2280 for (tcu::IVec2 cursor = begin; cursor != end; cursor += advance) 2281 { 2282 const bool hit = (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0); 2283 2284 if (hit) 2285 ++lineWidth; 2286 else if (lineWidth) 2287 { 2288 // Line is allowed to be be thinner if it borders the bbox boundary (since part of the line might have been discarded). 2289 const bool incorrectLineWidth = (lineWidth < lineWidthRange.x() && !bboxLimitedLine) || (lineWidth > lineWidthRange.y()); 2290 2291 if (incorrectLineWidth) 2292 { 2293 const bool incorrectRelaxedLineWidth = (lineWidth < relaxedLineWidthRange.x() && !bboxLimitedLine) || (lineWidth > relaxedLineWidthRange.y()); 2294 2295 if (incorrectRelaxedLineWidth) 2296 errorMask |= SCANRESULT_LINE_WIDTH_ERR_BIT; 2297 else 2298 errorMask |= SCANRESULT_LINE_WIDTH_WARN_BIT; 2299 2300 printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter); 2301 } 2302 2303 lineWidth = 0; 2304 bboxLimitedLine = false; 2305 } 2306 } 2307 2308 // fragments after end? 2309 if (lineWidth) 2310 { 2311 for (tcu::IVec2 cursor = end;; cursor += advance) 2312 { 2313 if (cursor.x() >= access.getWidth() || cursor.y() >= access.getHeight()) 2314 { 2315 if (lineWidth > lineWidthRange.y()) 2316 { 2317 if (lineWidth > relaxedLineWidthRange.y()) 2318 errorMask |= SCANRESULT_LINE_WIDTH_ERR_BIT; 2319 else 2320 errorMask |= SCANRESULT_LINE_WIDTH_WARN_BIT; 2321 2322 printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter); 2323 } 2324 2325 break; 2326 } 2327 else if (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0) 2328 { 2329 ++lineWidth; 2330 } 2331 else if (lineWidth) 2332 { 2333 // only check that line width is not larger than expected. Line width may be smaller 2334 // since the scanning 'cursor' is now outside the bounding box. 2335 const bool incorrectLineWidth = (lineWidth > lineWidthRange.y()); 2336 2337 if (incorrectLineWidth) 2338 { 2339 const bool incorrectRelaxedLineWidth = (lineWidth > relaxedLineWidthRange.y()); 2340 2341 if (incorrectRelaxedLineWidth) 2342 errorMask |= SCANRESULT_LINE_WIDTH_ERR_BIT; 2343 else 2344 errorMask |= SCANRESULT_LINE_WIDTH_WARN_BIT; 2345 2346 printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter); 2347 } 2348 2349 lineWidth = 0; 2350 } 2351 } 2352 } 2353 2354 return errorMask; 2355 } 2356 2357 void LineRenderCase::printLineWidthError (const tcu::IVec2& pos, int detectedLineWidth, const tcu::IVec2& lineWidthRange, bool isHorizontal, int& messageLimitCounter) const 2358 { 2359 if (--messageLimitCounter < 0) 2360 return; 2361 2362 m_testCtx.getLog() 2363 << tcu::TestLog::Message 2364 << "Found incorrect line width near " << pos << ": (" << ((isHorizontal) ? ("horizontal") : ("vertical")) << " line)\n" 2365 << "\tExpected line width in range [" << lineWidthRange.x() << ", " << lineWidthRange.y() << "] but found " << detectedLineWidth 2366 << tcu::TestLog::EndMessage; 2367 } 2368 2369 class PointRenderCase : public BBoxRenderCase 2370 { 2371 public: 2372 enum 2373 { 2374 POINTFLAG_WIDE = 1u << FLAGBIT_USER_BIT, //!< use wide points 2375 }; 2376 struct GeneratedPoint 2377 { 2378 tcu::Vec2 center; 2379 int size; 2380 bool even; 2381 }; 2382 2383 PointRenderCase (Context& context, const char* name, const char* description, deUint32 flags); 2384 ~PointRenderCase (void); 2385 2386 private: 2387 enum ResultPointType 2388 { 2389 POINT_FULL = 0, 2390 POINT_PARTIAL 2391 }; 2392 2393 void init (void); 2394 void deinit (void); 2395 2396 std::string genVertexSource (void) const; 2397 std::string genFragmentSource (void) const; 2398 std::string genTessellationControlSource (void) const; 2399 std::string genTessellationEvaluationSource (void) const; 2400 std::string genGeometrySource (void) const; 2401 2402 IterationConfig generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const; 2403 void generateAttributeData (void); 2404 void getAttributeData (std::vector<tcu::Vec4>& data) const; 2405 void renderTestPattern (const IterationConfig& config); 2406 void verifyRenderResult (const IterationConfig& config); 2407 2408 void genReferencePointData (const IterationConfig& config, std::vector<GeneratedPoint>& data) const; 2409 bool verifyNarrowPointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter); 2410 bool verifyWidePointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter); 2411 bool verifyWidePoint (const tcu::Surface& viewport, const GeneratedPoint& refPoint, const ProjectedBBox& bbox, ResultPointType pointType, int& logFloodCounter); 2412 bool verifyWidePointAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, const GeneratedPoint& refPoint, const tcu::IVec4& bbox, ResultPointType pointType, int componentNdx, int& logFloodCounter); 2413 tcu::IVec2 scanPointWidthAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, int expectedPointSize, int componentNdx) const; 2414 2415 const int m_numStripes; 2416 const bool m_isWidePointCase; 2417 std::vector<tcu::Vec4> m_attribData; 2418 }; 2419 2420 PointRenderCase::PointRenderCase (Context& context, const char* name, const char* description, deUint32 flags) 2421 : BBoxRenderCase (context, name, description, 12, flags) 2422 , m_numStripes (4) 2423 , m_isWidePointCase ((flags & POINTFLAG_WIDE) != 0) 2424 { 2425 } 2426 2427 PointRenderCase::~PointRenderCase (void) 2428 { 2429 } 2430 2431 void PointRenderCase::init (void) 2432 { 2433 if (m_isWidePointCase) 2434 { 2435 // extensions 2436 if (m_hasGeometryStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_point_size")) 2437 throw tcu::NotSupportedError("Test requires GL_EXT_geometry_point_size extension"); 2438 if (m_hasTessellationStage && !m_hasGeometryStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_point_size")) 2439 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_point_size extension"); 2440 2441 // point size range 2442 { 2443 glw::GLfloat pointSizeRange[2] = {0.0f, 0.0f}; 2444 m_context.getRenderContext().getFunctions().getFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange); 2445 2446 if (pointSizeRange[1] < 5.0f) 2447 throw tcu::NotSupportedError("Test requires point size 5.0"); 2448 } 2449 } 2450 2451 m_testCtx.getLog() 2452 << tcu::TestLog::Message 2453 << "Rendering point pattern to " << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n" 2454 << "Half of the points are green, half blue. Using additive blending.\n" 2455 << "Points are in random order, varying pattern size and location for each iteration.\n" 2456 << "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated red channel." 2457 << tcu::TestLog::EndMessage; 2458 2459 generateAttributeData(); 2460 2461 BBoxRenderCase::init(); 2462 } 2463 2464 void PointRenderCase::deinit (void) 2465 { 2466 // clear data 2467 m_attribData = std::vector<tcu::Vec4>(); 2468 2469 // deinit parent 2470 BBoxRenderCase::deinit(); 2471 } 2472 2473 std::string PointRenderCase::genVertexSource (void) const 2474 { 2475 std::ostringstream buf; 2476 2477 buf << "${GLSL_VERSION_DECL}\n" 2478 "in highp vec4 a_position;\n" 2479 "in highp vec4 a_color;\n" 2480 "out highp vec4 vtx_color;\n" 2481 "uniform highp vec4 u_posScale;\n" 2482 "\n"; 2483 if (!m_hasTessellationStage) 2484 { 2485 DE_ASSERT(m_useGlobalState); 2486 buf << "uniform highp vec4 u_primitiveBBoxMin;\n" 2487 "uniform highp vec4 u_primitiveBBoxMax;\n" 2488 "\n" 2489 "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n" 2490 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n" 2491 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n" 2492 "\n"; 2493 } 2494 2495 buf << "void main()\n" 2496 "{\n" 2497 " highp vec2 patternOffset = u_posScale.xy;\n" 2498 " highp vec2 patternScale = u_posScale.zw;\n" 2499 " highp float pointSize = " 2500 << ((m_isWidePointCase && !m_hasTessellationStage && !m_hasGeometryStage) ? ("(a_color.g > 0.0) ? (5.0) : (3.0)") : ("1.0")) 2501 << ";\n" 2502 << " gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n" 2503 " gl_PointSize = pointSize;\n" 2504 " vtx_color = a_color;\n"; 2505 2506 if (!m_hasTessellationStage) 2507 { 2508 DE_ASSERT(m_useGlobalState); 2509 buf << "\n" 2510 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = pointSize;\n" 2511 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin =\n" 2512 " min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMin.w,\n" 2513 " vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMax.w);\n" 2514 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax =\n" 2515 " min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMin.w,\n" 2516 " vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMax.w);\n"; 2517 } 2518 2519 buf << "}\n"; 2520 return buf.str(); 2521 } 2522 2523 std::string PointRenderCase::genFragmentSource (void) const 2524 { 2525 const char* const colorInputName = (m_hasGeometryStage) ? ("geo_color") : (m_hasTessellationStage) ? ("tess_color") : ("vtx_color"); 2526 std::ostringstream buf; 2527 2528 buf << "${GLSL_VERSION_DECL}\n" 2529 "in mediump vec4 " << colorInputName << ";\n" 2530 "layout(location = 0) out mediump vec4 o_color;\n" 2531 << genShaderFunction(SHADER_FUNC_INSIDE_BBOX) 2532 << "\n" 2533 "void main()\n" 2534 "{\n" 2535 " mediump vec4 baseColor = " << colorInputName << ";\n" 2536 " mediump float redChannel;\n" 2537 " if (fragmentInsideTheBBox(gl_FragCoord.z))\n" 2538 " redChannel = 0.0;\n" 2539 " else\n" 2540 " redChannel = 1.0;\n" 2541 " o_color = vec4(redChannel, baseColor.g, baseColor.b, baseColor.a);\n" 2542 "}\n"; 2543 2544 return buf.str(); 2545 } 2546 2547 std::string PointRenderCase::genTessellationControlSource (void) const 2548 { 2549 const bool tessellationWidePoints = (m_isWidePointCase) && (!m_hasGeometryStage); 2550 std::ostringstream buf; 2551 2552 buf << "${GLSL_VERSION_DECL}\n" 2553 "${TESSELLATION_SHADER_REQUIRE}\n" 2554 "${PRIMITIVE_BOUNDING_BOX_REQUIRE}\n" 2555 << ((tessellationWidePoints) ? ("${TESSELLATION_POINT_SIZE_REQUIRE}\n") : ("")) 2556 << "layout(vertices=1) out;" 2557 "\n" 2558 "in highp vec4 vtx_color[];\n" 2559 "out highp vec4 tess_ctrl_color[];\n" 2560 "uniform highp float u_tessellationLevel;\n" 2561 "uniform highp vec4 u_posScale;\n"; 2562 2563 if (!m_calcPerPrimitiveBBox) 2564 { 2565 buf << "uniform highp vec4 u_primitiveBBoxMin;\n" 2566 "uniform highp vec4 u_primitiveBBoxMax;\n"; 2567 } 2568 2569 buf << "patch out highp vec3 vp_bbox_clipMin;\n" 2570 "patch out highp vec3 vp_bbox_clipMax;\n"; 2571 2572 if (m_calcPerPrimitiveBBox) 2573 { 2574 buf << "\n"; 2575 if (m_hasGeometryStage) 2576 buf << genShaderFunction(SHADER_FUNC_MIRROR_X); 2577 buf << genShaderFunction(SHADER_FUNC_MIRROR_Y); 2578 2579 buf << "vec4 transformVec(in highp vec4 p)\n" 2580 "{\n" 2581 " return " << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)")) << ";\n" 2582 "}\n"; 2583 } 2584 2585 buf << "\n" 2586 "void main()\n" 2587 "{\n" 2588 " // convert to nonsensical coordinates, just in case\n" 2589 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n" 2590 " tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n" 2591 "\n" 2592 " gl_TessLevelOuter[0] = u_tessellationLevel;\n" 2593 " gl_TessLevelOuter[1] = u_tessellationLevel;\n" 2594 " gl_TessLevelOuter[2] = u_tessellationLevel;\n" 2595 " gl_TessLevelOuter[3] = u_tessellationLevel;\n" 2596 " gl_TessLevelInner[0] = 0.8; // will be rounded up to 1\n" 2597 " gl_TessLevelInner[1] = 0.8; // will be rounded up to 1\n"; 2598 2599 if (m_calcPerPrimitiveBBox) 2600 { 2601 buf << "\n"; 2602 2603 if (m_hasGeometryStage) 2604 buf << " const vec2 minExpansion = vec2(0.07 + 0.05, 0.07 + 0.02); // eval and geometry shader\n" 2605 " const vec2 maxExpansion = vec2(0.07 + 0.05, 0.07 + 0.03); // eval and geometry shader\n"; 2606 else 2607 buf << " const vec2 minExpansion = vec2(0.07, 0.07); // eval shader\n" 2608 " const vec2 maxExpansion = vec2(0.07, 0.07); // eval shader\n"; 2609 2610 buf << " highp vec2 patternScale = u_posScale.zw;\n" 2611 " highp vec4 bboxMin = transformVec(gl_in[0].gl_Position) - vec4(minExpansion * patternScale, 0.0, 0.0);\n" 2612 " highp vec4 bboxMax = transformVec(gl_in[0].gl_Position) + vec4(maxExpansion * patternScale, 0.0, 0.0);\n"; 2613 } 2614 else 2615 { 2616 buf << "\n" 2617 " highp vec4 bboxMin = u_primitiveBBoxMin;\n" 2618 " highp vec4 bboxMax = u_primitiveBBoxMax;\n"; 2619 } 2620 if (!m_useGlobalState) 2621 buf << "\n" 2622 " ${PRIM_GL_BOUNDING_BOX}[0] = bboxMin;\n" 2623 " ${PRIM_GL_BOUNDING_BOX}[1] = bboxMax;\n"; 2624 2625 buf << " vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n" 2626 " vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n" 2627 " vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n" 2628 " vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n" 2629 "}\n"; 2630 2631 return buf.str(); 2632 } 2633 2634 std::string PointRenderCase::genTessellationEvaluationSource (void) const 2635 { 2636 const bool tessellationWidePoints = (m_isWidePointCase) && (!m_hasGeometryStage); 2637 std::ostringstream buf; 2638 2639 buf << "${GLSL_VERSION_DECL}\n" 2640 "${TESSELLATION_SHADER_REQUIRE}\n" 2641 << ((tessellationWidePoints) ? ("${TESSELLATION_POINT_SIZE_REQUIRE}\n") : ("")) 2642 << "layout(quads, point_mode) in;" 2643 "\n" 2644 "in highp vec4 tess_ctrl_color[];\n" 2645 "out highp vec4 tess_color;\n" 2646 "uniform highp vec4 u_posScale;\n" 2647 "\n" 2648 "patch in highp vec3 vp_bbox_clipMin;\n" 2649 "patch in highp vec3 vp_bbox_clipMax;\n" 2650 << ((!m_hasGeometryStage) ? ("flat out highp float v_bbox_expansionSize;\n") : ("")) 2651 << "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n" 2652 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n" 2653 "\n" 2654 << genShaderFunction(SHADER_FUNC_MIRROR_Y) 2655 << "void main()\n" 2656 "{\n" 2657 " // non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n" 2658 " highp vec2 patternScale = u_posScale.zw;\n" 2659 " highp vec4 offset = vec4((gl_TessCoord.xy * 2.0 - vec2(1.0)) * 0.07 * patternScale, 0.0, 0.0);\n" 2660 " highp float pointSize = " << ((tessellationWidePoints) ? ("(tess_ctrl_color[0].g > 0.0) ? (5.0) : (3.0)") : ("1.0")) << ";\n" 2661 " gl_Position = mirrorY(gl_in[0].gl_Position.zwyx + offset);\n"; 2662 2663 if (tessellationWidePoints) 2664 buf << " gl_PointSize = pointSize;\n"; 2665 2666 buf << " tess_color = tess_ctrl_color[0];\n" 2667 << ((!m_hasGeometryStage) ? ("v_bbox_expansionSize = pointSize;\n") : ("")) 2668 << " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin = vp_bbox_clipMin;\n" 2669 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax = vp_bbox_clipMax;\n" 2670 "}\n"; 2671 2672 return buf.str(); 2673 } 2674 2675 std::string PointRenderCase::genGeometrySource (void) const 2676 { 2677 const char* const colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color"); 2678 std::ostringstream buf; 2679 2680 buf << "${GLSL_VERSION_DECL}\n" 2681 "${GEOMETRY_SHADER_REQUIRE}\n" 2682 << ((m_isWidePointCase) ? ("${GEOMETRY_POINT_SIZE}\n") : ("")) 2683 << "layout(points) in;\n" 2684 "layout(max_vertices=3, points) out;\n" 2685 "\n" 2686 "in highp vec4 " << colorInputName << "[1];\n" 2687 "out highp vec4 geo_color;\n" 2688 "uniform highp vec4 u_posScale;\n" 2689 "\n" 2690 "flat in highp vec3 v_geo_bbox_clipMin[1];\n" 2691 "flat in highp vec3 v_geo_bbox_clipMax[1];\n" 2692 "flat out highp vec3 v_bbox_clipMin;\n" 2693 "flat out highp vec3 v_bbox_clipMax;\n" 2694 "flat out highp float v_bbox_expansionSize;\n" 2695 "\n" 2696 << genShaderFunction(SHADER_FUNC_MIRROR_X) 2697 << "\n" 2698 "void main()\n" 2699 "{\n" 2700 " // Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n" 2701 " highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n" 2702 " highp vec4 pointColor = " << colorInputName << "[0];\n" 2703 " highp vec2 patternScale = u_posScale.zw;\n" 2704 " highp float pointSize = " 2705 << (m_isWidePointCase ? ("(pointColor.g > 0.0) ? (5.0) : (3.0)") : ("1.0")) 2706 << ";\n" 2707 "\n" 2708 " highp vec4 offsets[3] =\n" 2709 " vec4[3](\n" 2710 " vec4( 0.05 * patternScale.x, 0.03 * patternScale.y, 0.0, 0.0),\n" 2711 " vec4(-0.01 * patternScale.x,-0.02 * patternScale.y, 0.0, 0.0),\n" 2712 " vec4(-0.05 * patternScale.x, 0.02 * patternScale.y, 0.0, 0.0)\n" 2713 " );\n" 2714 " for (int ndx = 0; ndx < 3; ++ndx)\n" 2715 " {\n" 2716 " gl_Position = p0 + offsets[ndx];\n"; 2717 2718 if (m_isWidePointCase) 2719 buf << " gl_PointSize = pointSize;\n"; 2720 2721 buf << " v_bbox_clipMin = v_geo_bbox_clipMin[0];\n" 2722 " v_bbox_clipMax = v_geo_bbox_clipMax[0];\n" 2723 " v_bbox_expansionSize = pointSize;\n" 2724 " geo_color = pointColor;\n" 2725 " EmitVertex();\n" 2726 " }\n" 2727 "}\n"; 2728 2729 return buf.str(); 2730 } 2731 2732 PointRenderCase::IterationConfig PointRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const 2733 { 2734 IterationConfig config = generateRandomConfig(0xDEDEDEu * (deUint32)iteration, renderTargetSize); 2735 2736 // equal or larger -> expand according to shader expansion 2737 if (m_bboxSize == BBOXSIZE_EQUAL || m_bboxSize == BBOXSIZE_LARGER) 2738 { 2739 const tcu::Vec2 patternScale = config.patternSize; 2740 2741 if (m_hasTessellationStage) 2742 { 2743 config.bbox.min -= tcu::Vec4(0.07f * patternScale.x(), 0.07f * patternScale.y(), 0.0f, 0.0f); 2744 config.bbox.max += tcu::Vec4(0.07f * patternScale.x(), 0.07f * patternScale.y(), 0.0f, 0.0f); 2745 } 2746 if (m_hasGeometryStage) 2747 { 2748 config.bbox.min -= tcu::Vec4(0.05f * patternScale.x(), 0.02f * patternScale.y(), 0.0f, 0.0f); 2749 config.bbox.max += tcu::Vec4(0.05f * patternScale.x(), 0.03f * patternScale.y(), 0.0f, 0.0f); 2750 } 2751 } 2752 2753 return config; 2754 } 2755 2756 void PointRenderCase::generateAttributeData (void) 2757 { 2758 const tcu::Vec4 green (0.0f, 1.0f, 0.0f, 1.0f); 2759 const tcu::Vec4 blue (0.0f, 0.0f, 1.0f, 1.0f); 2760 std::vector<int> cellOrder (m_numStripes * m_numStripes * 2); 2761 de::Random rnd (0xDE22446); 2762 2763 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx) 2764 cellOrder[ndx] = ndx; 2765 rnd.shuffle(cellOrder.begin(), cellOrder.end()); 2766 2767 m_attribData.resize(cellOrder.size() * 2); 2768 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx) 2769 { 2770 const int pointID = cellOrder[ndx]; 2771 const int direction = pointID & 0x01; 2772 const int majorCoord = (pointID >> 1) / m_numStripes; 2773 const int minorCoord = (pointID >> 1) % m_numStripes; 2774 2775 if (direction) 2776 { 2777 m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(minorCoord) / float(m_numStripes), float(majorCoord) / float(m_numStripes), 0.0f, 1.0f); 2778 m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green; 2779 } 2780 else 2781 { 2782 m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(((float)majorCoord + 0.5f) / float(m_numStripes), ((float)minorCoord + 0.5f) / float(m_numStripes), 0.0f, 1.0f); 2783 m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue; 2784 } 2785 } 2786 } 2787 2788 void PointRenderCase::getAttributeData (std::vector<tcu::Vec4>& data) const 2789 { 2790 data = m_attribData; 2791 } 2792 2793 void PointRenderCase::renderTestPattern (const IterationConfig& config) 2794 { 2795 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 2796 2797 setupRender(config); 2798 2799 if (m_hasTessellationStage) 2800 { 2801 const glw::GLint tessLevelPos = gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel"); 2802 const glw::GLfloat tessLevel = 0.8f; // will be rounded up 2803 2804 TCU_CHECK(tessLevelPos != -1); 2805 2806 m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel << tcu::TestLog::EndMessage; 2807 2808 gl.uniform1f(tessLevelPos, tessLevel); 2809 gl.patchParameteri(GL_PATCH_VERTICES, 1); 2810 GLU_EXPECT_NO_ERROR(gl.getError(), "patch param"); 2811 } 2812 2813 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering pattern." << tcu::TestLog::EndMessage; 2814 2815 gl.enable(GL_BLEND); 2816 gl.blendFunc(GL_ONE, GL_ONE); 2817 gl.blendEquation(GL_FUNC_ADD); 2818 2819 gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_POINTS), 0, m_numStripes * m_numStripes * 2); 2820 GLU_EXPECT_NO_ERROR(gl.getError(), "draw"); 2821 } 2822 2823 void PointRenderCase::verifyRenderResult (const IterationConfig& config) 2824 { 2825 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 2826 const ProjectedBBox projectedBBox = projectBoundingBox(config.bbox); 2827 const tcu::IVec4 viewportBBoxArea = getViewportBoundingBoxArea(projectedBBox, config.viewportSize); 2828 2829 tcu::Surface viewportSurface (config.viewportSize.x(), config.viewportSize.y()); 2830 int logFloodCounter = 8; 2831 bool anyError; 2832 std::vector<GeneratedPoint> refPoints; 2833 2834 if (!m_calcPerPrimitiveBBox) 2835 m_testCtx.getLog() 2836 << tcu::TestLog::Message 2837 << "Projected bounding box: (clip space)\n" 2838 << "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n" 2839 << "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n" 2840 << "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n" 2841 << "In viewport coordinates:\n" 2842 << "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n" 2843 << "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n" 2844 << "Verifying render results within the bounding box:\n" 2845 << tcu::TestLog::EndMessage; 2846 else 2847 m_testCtx.getLog() 2848 << tcu::TestLog::Message 2849 << "Verifying render result:" 2850 << tcu::TestLog::EndMessage; 2851 2852 if (m_fbo) 2853 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo); 2854 glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(), viewportSurface.getAccess()); 2855 2856 genReferencePointData(config, refPoints); 2857 2858 if (m_isWidePointCase) 2859 anyError = verifyWidePointPattern(viewportSurface, refPoints, projectedBBox, logFloodCounter); 2860 else 2861 anyError = verifyNarrowPointPattern(viewportSurface, refPoints, projectedBBox, logFloodCounter); 2862 2863 if (anyError) 2864 { 2865 if (logFloodCounter < 0) 2866 m_testCtx.getLog() << tcu::TestLog::Message << "Omitted " << (-logFloodCounter) << " error descriptions." << tcu::TestLog::EndMessage; 2867 2868 m_testCtx.getLog() 2869 << tcu::TestLog::Message 2870 << "Image verification failed." 2871 << tcu::TestLog::EndMessage 2872 << tcu::TestLog::ImageSet("Images", "Image verification") 2873 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess()) 2874 << tcu::TestLog::EndImageSet; 2875 2876 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed"); 2877 } 2878 else 2879 { 2880 m_testCtx.getLog() 2881 << tcu::TestLog::Message 2882 << "Result image ok." 2883 << tcu::TestLog::EndMessage 2884 << tcu::TestLog::ImageSet("Images", "Image verification") 2885 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess()) 2886 << tcu::TestLog::EndImageSet; 2887 } 2888 } 2889 2890 struct PointSorter 2891 { 2892 bool operator() (const PointRenderCase::GeneratedPoint& a, const PointRenderCase::GeneratedPoint& b) const 2893 { 2894 if (a.center.y() < b.center.y()) 2895 return true; 2896 else if (a.center.y() > b.center.y()) 2897 return false; 2898 else 2899 return (a.center.x() < b.center.x()); 2900 } 2901 }; 2902 2903 void PointRenderCase::genReferencePointData (const IterationConfig& config, std::vector<GeneratedPoint>& data) const 2904 { 2905 std::vector<GeneratedPoint> currentPoints; 2906 2907 // vertex shader 2908 currentPoints.resize(m_attribData.size() / 2); 2909 for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx) 2910 { 2911 currentPoints[ndx].center = m_attribData[ndx*2].swizzle(0, 1); 2912 currentPoints[ndx].even = (m_attribData[ndx*2 + 1].y() == 1.0f); // is green 2913 currentPoints[ndx].size = ((m_isWidePointCase) ? ((currentPoints[ndx].even) ? 5 : 3) : 1); 2914 } 2915 2916 // tessellation 2917 if (m_hasTessellationStage) 2918 { 2919 std::vector<GeneratedPoint> tessellatedPoints; 2920 2921 tessellatedPoints.resize(currentPoints.size() * 4); 2922 for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx) 2923 { 2924 const tcu::Vec2 position = tcu::Vec2(currentPoints[ndx].center.x(), 1.0f - currentPoints[ndx].center.y()); // mirror Y 2925 2926 tessellatedPoints[4 * ndx + 0].center = position + tcu::Vec2(-0.07f, -0.07f); 2927 tessellatedPoints[4 * ndx + 0].size = currentPoints[ndx].size; 2928 tessellatedPoints[4 * ndx + 0].even = currentPoints[ndx].even; 2929 2930 tessellatedPoints[4 * ndx + 1].center = position + tcu::Vec2( 0.07f, -0.07f); 2931 tessellatedPoints[4 * ndx + 1].size = currentPoints[ndx].size; 2932 tessellatedPoints[4 * ndx + 1].even = currentPoints[ndx].even; 2933 2934 tessellatedPoints[4 * ndx + 2].center = position + tcu::Vec2( 0.07f, 0.07f); 2935 tessellatedPoints[4 * ndx + 2].size = currentPoints[ndx].size; 2936 tessellatedPoints[4 * ndx + 2].even = currentPoints[ndx].even; 2937 2938 tessellatedPoints[4 * ndx + 3].center = position + tcu::Vec2(-0.07f, 0.07f); 2939 tessellatedPoints[4 * ndx + 3].size = currentPoints[ndx].size; 2940 tessellatedPoints[4 * ndx + 3].even = currentPoints[ndx].even; 2941 } 2942 2943 currentPoints.swap(tessellatedPoints); 2944 } 2945 2946 // geometry 2947 if (m_hasGeometryStage) 2948 { 2949 std::vector<GeneratedPoint> geometryShadedPoints; 2950 2951 geometryShadedPoints.resize(currentPoints.size() * 3); 2952 for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx) 2953 { 2954 const tcu::Vec2 position = tcu::Vec2(1.0f - currentPoints[ndx].center.x(), currentPoints[ndx].center.y()); // mirror X 2955 2956 geometryShadedPoints[3 * ndx + 0].center = position + tcu::Vec2( 0.05f, 0.03f); 2957 geometryShadedPoints[3 * ndx + 0].size = currentPoints[ndx].size; 2958 geometryShadedPoints[3 * ndx + 0].even = currentPoints[ndx].even; 2959 2960 geometryShadedPoints[3 * ndx + 1].center = position + tcu::Vec2(-0.01f, -0.02f); 2961 geometryShadedPoints[3 * ndx + 1].size = currentPoints[ndx].size; 2962 geometryShadedPoints[3 * ndx + 1].even = currentPoints[ndx].even; 2963 2964 geometryShadedPoints[3 * ndx + 2].center = position + tcu::Vec2(-0.05f, 0.02f); 2965 geometryShadedPoints[3 * ndx + 2].size = currentPoints[ndx].size; 2966 geometryShadedPoints[3 * ndx + 2].even = currentPoints[ndx].even; 2967 } 2968 2969 currentPoints.swap(geometryShadedPoints); 2970 } 2971 2972 // sort from left to right, top to bottom 2973 std::sort(currentPoints.begin(), currentPoints.end(), PointSorter()); 2974 2975 // map to pattern space 2976 for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx) 2977 currentPoints[ndx].center = currentPoints[ndx].center * config.patternSize + config.patternPos; 2978 2979 currentPoints.swap(data); 2980 } 2981 2982 bool PointRenderCase::verifyNarrowPointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter) 2983 { 2984 bool anyError = false; 2985 2986 // check that there is something near each sample 2987 for (int pointNdx = 0; pointNdx < (int)refPoints.size(); ++pointNdx) 2988 { 2989 const float epsilon = 1.0e-6f; 2990 const GeneratedPoint& refPoint = refPoints[pointNdx]; 2991 2992 // skip points not in the the bbox, treat boundary as "in" 2993 if (refPoint.center.x() < bbox.min.x() - epsilon || 2994 refPoint.center.y() < bbox.min.y() - epsilon || 2995 refPoint.center.x() > bbox.max.x() + epsilon || 2996 refPoint.center.y() > bbox.max.y() + epsilon) 2997 continue; 2998 else 2999 { 3000 // transform to viewport coords 3001 const tcu::IVec2 pixelCenter(deRoundFloatToInt32((refPoint.center.x() * 0.5f + 0.5f) * (float)viewport.getWidth()), 3002 deRoundFloatToInt32((refPoint.center.y() * 0.5f + 0.5f) * (float)viewport.getHeight())); 3003 3004 // find rasterized point in the result 3005 if (pixelCenter.x() < 1 || pixelCenter.y() < 1 || pixelCenter.x() >= viewport.getWidth()-1 || pixelCenter.y() >= viewport.getHeight()-1) 3006 { 3007 // viewport boundary, assume point is fine 3008 } 3009 else 3010 { 3011 const int componentNdx = (refPoint.even) ? (1) : (2); // analyze either green or blue channel 3012 bool foundResult = false; 3013 3014 // check neighborhood 3015 for (int dy = -1; dy < 2 && !foundResult; ++dy) 3016 for (int dx = -1; dx < 2 && !foundResult; ++dx) 3017 { 3018 const tcu::IVec2 testPos (pixelCenter.x() + dx, pixelCenter.y() + dy); 3019 const tcu::RGBA color = viewport.getPixel(testPos.x(), testPos.y()); 3020 3021 if (color.toIVec()[componentNdx] > 0) 3022 foundResult = true; 3023 } 3024 3025 if (!foundResult) 3026 { 3027 anyError = true; 3028 3029 if (--logFloodCounter >= 0) 3030 { 3031 m_testCtx.getLog() 3032 << tcu::TestLog::Message 3033 << "Missing point near " << pixelCenter << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "." 3034 << tcu::TestLog::EndMessage; 3035 } 3036 } 3037 } 3038 } 3039 } 3040 3041 return anyError; 3042 } 3043 3044 bool PointRenderCase::verifyWidePointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter) 3045 { 3046 bool anyError = false; 3047 3048 // check that there is something near each sample 3049 for (int pointNdx = 0; pointNdx < (int)refPoints.size(); ++pointNdx) 3050 { 3051 const GeneratedPoint& refPoint = refPoints[pointNdx]; 3052 3053 if (refPoint.center.x() >= bbox.min.x() && 3054 refPoint.center.y() >= bbox.min.y() && 3055 refPoint.center.x() <= bbox.max.x() && 3056 refPoint.center.y() <= bbox.max.y()) 3057 { 3058 // point fully in the bounding box 3059 anyError |= !verifyWidePoint(viewport, refPoint, bbox, POINT_FULL, logFloodCounter); 3060 } 3061 else if (refPoint.center.x() >= bbox.min.x() + (float)refPoint.size / 2.0f && 3062 refPoint.center.y() >= bbox.min.y() - (float)refPoint.size / 2.0f && 3063 refPoint.center.x() <= bbox.max.x() + (float)refPoint.size / 2.0f && 3064 refPoint.center.y() <= bbox.max.y() - (float)refPoint.size / 2.0f) 3065 { 3066 // point leaks into bounding box 3067 anyError |= !verifyWidePoint(viewport, refPoint, bbox, POINT_PARTIAL, logFloodCounter); 3068 } 3069 } 3070 3071 return anyError; 3072 } 3073 3074 bool PointRenderCase::verifyWidePoint (const tcu::Surface& viewport, const GeneratedPoint& refPoint, const ProjectedBBox& bbox, ResultPointType pointType, int& logFloodCounter) 3075 { 3076 const int componentNdx = (refPoint.even) ? (1) : (2); 3077 const int halfPointSizeCeil = (refPoint.size + 1) / 2; 3078 const int halfPointSizeFloor = (refPoint.size + 1) / 2; 3079 const tcu::IVec4 viewportBBoxArea = getViewportBoundingBoxArea(bbox, tcu::IVec2(viewport.getWidth(), viewport.getHeight()), (float)refPoint.size); 3080 const tcu::IVec4 verificationArea = tcu::IVec4(de::max(viewportBBoxArea.x(), 0), 3081 de::max(viewportBBoxArea.y(), 0), 3082 de::min(viewportBBoxArea.z(), viewport.getWidth()), 3083 de::min(viewportBBoxArea.w(), viewport.getHeight())); 3084 const tcu::IVec2 pointPos = tcu::IVec2(deRoundFloatToInt32((refPoint.center.x()*0.5f + 0.5f) * (float)viewport.getWidth()), 3085 deRoundFloatToInt32((refPoint.center.y()*0.5f + 0.5f) * (float)viewport.getHeight())); 3086 3087 // find any fragment within the point that is inside the bbox, start search at the center 3088 3089 if (pointPos.x() >= verificationArea.x() && 3090 pointPos.y() >= verificationArea.y() && 3091 pointPos.x() < verificationArea.z() && 3092 pointPos.y() < verificationArea.w()) 3093 { 3094 if (viewport.getPixel(pointPos.x(), pointPos.y()).toIVec()[componentNdx]) 3095 return verifyWidePointAt(pointPos, viewport, refPoint, verificationArea, pointType, componentNdx, logFloodCounter); 3096 } 3097 3098 for (int dy = -halfPointSizeCeil; dy <= halfPointSizeCeil; ++dy) 3099 for (int dx = -halfPointSizeCeil; dx <= halfPointSizeCeil; ++dx) 3100 { 3101 const tcu::IVec2 testPos = pointPos + tcu::IVec2(dx, dy); 3102 3103 if (dx == 0 && dy == 0) 3104 continue; 3105 3106 if (testPos.x() >= verificationArea.x() && 3107 testPos.y() >= verificationArea.y() && 3108 testPos.x() < verificationArea.z() && 3109 testPos.y() < verificationArea.w()) 3110 { 3111 if (viewport.getPixel(testPos.x(), testPos.y()).toIVec()[componentNdx]) 3112 return verifyWidePointAt(testPos, viewport, refPoint, verificationArea, pointType, componentNdx, logFloodCounter); 3113 } 3114 } 3115 3116 // could not find point, this is only ok near boundaries 3117 if (pointPos.x() + halfPointSizeFloor < verificationArea.x() - 1 || 3118 pointPos.y() + halfPointSizeFloor < verificationArea.y() - 1 || 3119 pointPos.x() - halfPointSizeFloor >= verificationArea.z() - 1 || 3120 pointPos.y() - halfPointSizeFloor >= verificationArea.w() - 1) 3121 return true; 3122 3123 if (--logFloodCounter >= 0) 3124 { 3125 m_testCtx.getLog() 3126 << tcu::TestLog::Message 3127 << "Missing wide point near " << pointPos << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "." 3128 << tcu::TestLog::EndMessage; 3129 } 3130 3131 return false; 3132 } 3133 3134 bool PointRenderCase::verifyWidePointAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, const GeneratedPoint& refPoint, const tcu::IVec4& bbox, ResultPointType pointType, int componentNdx, int& logFloodCounter) 3135 { 3136 const int expectedPointSize = refPoint.size; 3137 bool viewportClippedTop = false; 3138 bool viewportClippedBottom = false; 3139 bool primitiveClippedTop = false; 3140 bool primitiveClippedBottom = false; 3141 std::vector<tcu::IVec2> widthsUpwards; 3142 std::vector<tcu::IVec2> widthsDownwards; 3143 std::vector<tcu::IVec2> widths; 3144 3145 // search upwards 3146 for (int y = pointPos.y();; --y) 3147 { 3148 if (y < bbox.y() || y < 0) 3149 { 3150 if (y < bbox.y()) 3151 primitiveClippedTop = true; 3152 if (y < 0) 3153 viewportClippedTop = true; 3154 break; 3155 } 3156 else if (pointPos.y() - y > expectedPointSize) 3157 { 3158 // no need to go further than point height 3159 break; 3160 } 3161 else if (viewport.getPixel(pointPos.x(), y).toIVec()[componentNdx] == 0) 3162 { 3163 break; 3164 } 3165 else 3166 { 3167 widthsUpwards.push_back(scanPointWidthAt(tcu::IVec2(pointPos.x(), y), viewport, expectedPointSize, componentNdx)); 3168 } 3169 } 3170 3171 // top is clipped 3172 if ((viewportClippedTop || (pointType == POINT_PARTIAL && primitiveClippedTop)) && !widthsUpwards.empty()) 3173 { 3174 const tcu::IVec2& range = widthsUpwards.back(); 3175 const bool squareFits = (range.y() - range.x() + 1) >= expectedPointSize; 3176 const bool widthClipped = (pointType == POINT_PARTIAL) && (range.x() <= bbox.x() || range.y() >= bbox.z()); 3177 3178 if (squareFits || widthClipped) 3179 return true; 3180 } 3181 3182 // and downwards 3183 for (int y = pointPos.y()+1;; ++y) 3184 { 3185 if (y >= bbox.w() || y >= viewport.getHeight()) 3186 { 3187 if (y >= bbox.w()) 3188 primitiveClippedBottom = true; 3189 if (y >= viewport.getHeight()) 3190 viewportClippedBottom = true; 3191 break; 3192 } 3193 else if (y - pointPos.y() > expectedPointSize) 3194 { 3195 // no need to go further than point height 3196 break; 3197 } 3198 else if (viewport.getPixel(pointPos.x(), y).toIVec()[componentNdx] == 0) 3199 { 3200 break; 3201 } 3202 else 3203 { 3204 widthsDownwards.push_back(scanPointWidthAt(tcu::IVec2(pointPos.x(), y), viewport, expectedPointSize, componentNdx)); 3205 } 3206 } 3207 3208 // bottom is clipped 3209 if ((viewportClippedBottom || (pointType == POINT_PARTIAL && primitiveClippedBottom)) && !(widthsDownwards.empty() && widthsUpwards.empty())) 3210 { 3211 const tcu::IVec2& range = (widthsDownwards.empty()) ? (widthsUpwards.front()) : (widthsDownwards.back()); 3212 const bool squareFits = (range.y() - range.x() + 1) >= expectedPointSize; 3213 const bool bboxClipped = (pointType == POINT_PARTIAL) && (range.x() <= bbox.x() || range.y() >= bbox.z()-1); 3214 const bool viewportClipped = range.x() <= 0 || range.y() >= viewport.getWidth()-1; 3215 3216 if (squareFits || bboxClipped || viewportClipped) 3217 return true; 3218 } 3219 3220 // would square point would fit into the rasterized area 3221 3222 for (int ndx = 0; ndx < (int)widthsUpwards.size(); ++ndx) 3223 widths.push_back(widthsUpwards[(int)widthsUpwards.size() - ndx - 1]); 3224 for (int ndx = 0; ndx < (int)widthsDownwards.size(); ++ndx) 3225 widths.push_back(widthsDownwards[ndx]); 3226 DE_ASSERT(!widths.empty()); 3227 3228 for (int y = 0; y < (int)widths.size() - expectedPointSize + 1; ++y) 3229 { 3230 tcu::IVec2 unionRange = widths[y]; 3231 3232 for (int dy = 1; dy < expectedPointSize; ++dy) 3233 { 3234 unionRange.x() = de::max(unionRange.x(), widths[y+dy].x()); 3235 unionRange.y() = de::min(unionRange.y(), widths[y+dy].y()); 3236 } 3237 3238 // would a N x N block fit here? 3239 { 3240 const bool squareFits = (unionRange.y() - unionRange.x() + 1) >= expectedPointSize; 3241 const bool bboxClipped = (pointType == POINT_PARTIAL) && (unionRange.x() <= bbox.x() || unionRange.y() >= bbox.z()-1); 3242 const bool viewportClipped = unionRange.x() <= 0 || unionRange.y() >= viewport.getWidth()-1; 3243 3244 if (squareFits || bboxClipped || viewportClipped) 3245 return true; 3246 } 3247 } 3248 3249 if (--logFloodCounter >= 0) 3250 { 3251 m_testCtx.getLog() 3252 << tcu::TestLog::Message 3253 << "Missing " << expectedPointSize << "x" << expectedPointSize << " point near " << pointPos << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "." 3254 << tcu::TestLog::EndMessage; 3255 } 3256 return false; 3257 } 3258 3259 tcu::IVec2 PointRenderCase::scanPointWidthAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, int expectedPointSize, int componentNdx) const 3260 { 3261 int minX = pointPos.x(); 3262 int maxX = pointPos.x(); 3263 3264 // search horizontally for a point edges 3265 for (int x = pointPos.x()-1; x >= 0; --x) 3266 { 3267 if (viewport.getPixel(x, pointPos.y()).toIVec()[componentNdx] == 0) 3268 break; 3269 3270 // no need to go further than point width 3271 if (pointPos.x() - x > expectedPointSize) 3272 break; 3273 3274 minX = x; 3275 } 3276 for (int x = pointPos.x()+1; x < viewport.getWidth(); ++x) 3277 { 3278 if (viewport.getPixel(x, pointPos.y()).toIVec()[componentNdx] == 0) 3279 break; 3280 3281 // no need to go further than point width 3282 if (x - pointPos.x() > expectedPointSize) 3283 break; 3284 3285 maxX = x; 3286 } 3287 3288 return tcu::IVec2(minX, maxX); 3289 } 3290 3291 class BlitFboCase : public TestCase 3292 { 3293 public: 3294 enum RenderTarget 3295 { 3296 TARGET_DEFAULT = 0, 3297 TARGET_FBO, 3298 3299 TARGET_LAST 3300 }; 3301 3302 BlitFboCase (Context& context, const char* name, const char* description, RenderTarget src, RenderTarget dst); 3303 ~BlitFboCase (void); 3304 3305 private: 3306 enum 3307 { 3308 FBO_SIZE = 256, 3309 }; 3310 3311 struct BlitArgs 3312 { 3313 tcu::IVec4 src; 3314 tcu::IVec4 dst; 3315 tcu::Vec4 bboxMin; 3316 tcu::Vec4 bboxMax; 3317 bool linear; 3318 }; 3319 3320 void init (void); 3321 void deinit (void); 3322 IterateResult iterate (void); 3323 3324 void fillSourceWithPattern (void); 3325 bool verifyImage (const BlitArgs& args); 3326 3327 const RenderTarget m_src; 3328 const RenderTarget m_dst; 3329 3330 std::vector<BlitArgs> m_iterations; 3331 int m_iteration; 3332 de::MovePtr<glu::Framebuffer> m_srcFbo; 3333 de::MovePtr<glu::Framebuffer> m_dstFbo; 3334 de::MovePtr<glu::Renderbuffer> m_srcRbo; 3335 de::MovePtr<glu::Renderbuffer> m_dstRbo; 3336 de::MovePtr<glu::ShaderProgram> m_program; 3337 de::MovePtr<glu::Buffer> m_vbo; 3338 }; 3339 3340 BlitFboCase::BlitFboCase (Context& context, const char* name, const char* description, RenderTarget src, RenderTarget dst) 3341 : TestCase (context, name, description) 3342 , m_src (src) 3343 , m_dst (dst) 3344 , m_iteration (0) 3345 { 3346 DE_ASSERT(src < TARGET_LAST); 3347 DE_ASSERT(dst < TARGET_LAST); 3348 } 3349 3350 BlitFboCase::~BlitFboCase (void) 3351 { 3352 deinit(); 3353 } 3354 3355 void BlitFboCase::init (void) 3356 { 3357 const int numIterations = 12; 3358 const bool defaultFBMultisampled = (m_context.getRenderTarget().getNumSamples() > 1); 3359 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 3360 de::Random rnd (0xABC123); 3361 3362 m_testCtx.getLog() 3363 << tcu::TestLog::Message 3364 << "Using BlitFramebuffer to blit area from " 3365 << ((m_src == TARGET_DEFAULT) ? ("default fb") : ("fbo")) 3366 << " to " 3367 << ((m_dst == TARGET_DEFAULT) ? ("default fb") : ("fbo")) 3368 << ".\n" 3369 << "Varying blit arguments and primitive bounding box between iterations.\n" 3370 << "Expecting bounding box to have no effect on blitting.\n" 3371 << "Source framebuffer is filled with green-yellow grid.\n" 3372 << tcu::TestLog::EndMessage; 3373 3374 const bool supportsES32 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)); 3375 3376 if (!supportsES32 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box")) 3377 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension"); 3378 if (m_dst == TARGET_DEFAULT && defaultFBMultisampled) 3379 throw tcu::NotSupportedError("Test requires non-multisampled default framebuffer"); 3380 3381 // resources 3382 3383 if (m_src == TARGET_FBO) 3384 { 3385 m_srcRbo = de::MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(m_context.getRenderContext())); 3386 gl.bindRenderbuffer(GL_RENDERBUFFER, **m_srcRbo); 3387 gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, FBO_SIZE, FBO_SIZE); 3388 GLU_EXPECT_NO_ERROR(gl.getError(), "src rbo"); 3389 3390 m_srcFbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext())); 3391 gl.bindFramebuffer(GL_FRAMEBUFFER, **m_srcFbo); 3392 gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **m_srcRbo); 3393 GLU_EXPECT_NO_ERROR(gl.getError(), "src fbo"); 3394 } 3395 3396 if (m_dst == TARGET_FBO) 3397 { 3398 m_dstRbo = de::MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(m_context.getRenderContext())); 3399 gl.bindRenderbuffer(GL_RENDERBUFFER, **m_dstRbo); 3400 gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, FBO_SIZE, FBO_SIZE); 3401 GLU_EXPECT_NO_ERROR(gl.getError(), "dst rbo"); 3402 3403 m_dstFbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext())); 3404 gl.bindFramebuffer(GL_FRAMEBUFFER, **m_dstFbo); 3405 gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **m_dstRbo); 3406 GLU_EXPECT_NO_ERROR(gl.getError(), "dst fbo"); 3407 } 3408 3409 { 3410 const char* const vertexSource = "${GLSL_VERSION_DECL}\n" 3411 "in highp vec4 a_position;\n" 3412 "out highp vec4 v_position;\n" 3413 "void main()\n" 3414 "{\n" 3415 " gl_Position = a_position;\n" 3416 " v_position = a_position;\n" 3417 "}\n"; 3418 const char* const fragmentSource = "${GLSL_VERSION_DECL}\n" 3419 "in mediump vec4 v_position;\n" 3420 "layout(location=0) out mediump vec4 dEQP_FragColor;\n" 3421 "void main()\n" 3422 "{\n" 3423 " const mediump vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n" 3424 " const mediump vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n" 3425 " dEQP_FragColor = (step(0.1, mod(v_position.x, 0.2)) == step(0.1, mod(v_position.y, 0.2))) ? (green) : (yellow);\n" 3426 "}\n"; 3427 3428 m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(specializeShader(m_context, vertexSource)) << glu::FragmentSource(specializeShader(m_context, fragmentSource)))); 3429 3430 if (!m_program->isOk()) 3431 { 3432 m_testCtx.getLog() << *m_program; 3433 throw tcu::TestError("failed to build program"); 3434 } 3435 } 3436 3437 { 3438 static const tcu::Vec4 s_quadCoords[] = 3439 { 3440 tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f), 3441 tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f), 3442 tcu::Vec4( 1.0f, -1.0f, 0.0f, 1.0f), 3443 tcu::Vec4( 1.0f, 1.0f, 0.0f, 1.0f), 3444 }; 3445 3446 m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext())); 3447 3448 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo); 3449 gl.bufferData(GL_ARRAY_BUFFER, sizeof(s_quadCoords), s_quadCoords, GL_STATIC_DRAW); 3450 GLU_EXPECT_NO_ERROR(gl.getError(), "set buf"); 3451 } 3452 3453 // gen iterations 3454 3455 { 3456 const tcu::IVec2 srcSize = (m_src == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE)); 3457 const tcu::IVec2 dstSize = (m_dst == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE)); 3458 3459 m_testCtx.getLog() 3460 << tcu::TestLog::Message 3461 << "srcSize = " << srcSize << "\n" 3462 << "dstSize = " << dstSize << "\n" 3463 << tcu::TestLog::EndMessage; 3464 3465 for (int ndx = 0; ndx < numIterations; ++ndx) 3466 { 3467 BlitArgs args; 3468 3469 if (m_src == TARGET_DEFAULT && defaultFBMultisampled) 3470 { 3471 const tcu::IVec2 unionSize = tcu::IVec2(de::min(srcSize.x(), dstSize.x()), de::min(srcSize.y(), dstSize.y())); 3472 const int srcWidth = rnd.getInt(1, unionSize.x()); 3473 const int srcHeight = rnd.getInt(1, unionSize.y()); 3474 const int srcX = rnd.getInt(0, unionSize.x() - srcWidth); 3475 const int srcY = rnd.getInt(0, unionSize.y() - srcHeight); 3476 3477 args.src.x() = srcX; 3478 args.src.y() = srcY; 3479 args.src.z() = srcX + srcWidth; 3480 args.src.w() = srcY + srcHeight; 3481 3482 args.dst = args.src; 3483 } 3484 else 3485 { 3486 const int srcWidth = rnd.getInt(1, srcSize.x()); 3487 const int srcHeight = rnd.getInt(1, srcSize.y()); 3488 const int srcX = rnd.getInt(0, srcSize.x() - srcWidth); 3489 const int srcY = rnd.getInt(0, srcSize.y() - srcHeight); 3490 const int dstWidth = rnd.getInt(1, dstSize.x()); 3491 const int dstHeight = rnd.getInt(1, dstSize.y()); 3492 const int dstX = rnd.getInt(-(dstWidth / 2), dstSize.x() - (dstWidth+1) / 2); // allow dst go out of bounds 3493 const int dstY = rnd.getInt(-(dstHeight / 2), dstSize.y() - (dstHeight+1) / 2); 3494 3495 args.src.x() = srcX; 3496 args.src.y() = srcY; 3497 args.src.z() = srcX + srcWidth; 3498 args.src.w() = srcY + srcHeight; 3499 args.dst.x() = dstX; 3500 args.dst.y() = dstY; 3501 args.dst.z() = dstX + dstWidth; 3502 args.dst.w() = dstY + dstHeight; 3503 } 3504 3505 args.bboxMin.x() = rnd.getFloat(-1.1f, 1.1f); 3506 args.bboxMin.y() = rnd.getFloat(-1.1f, 1.1f); 3507 args.bboxMin.z() = rnd.getFloat(-1.1f, 1.1f); 3508 args.bboxMin.w() = rnd.getFloat( 0.9f, 1.1f); 3509 3510 args.bboxMax.x() = rnd.getFloat(-1.1f, 1.1f); 3511 args.bboxMax.y() = rnd.getFloat(-1.1f, 1.1f); 3512 args.bboxMax.z() = rnd.getFloat(-1.1f, 1.1f); 3513 args.bboxMax.w() = rnd.getFloat( 0.9f, 1.1f); 3514 3515 if (args.bboxMin.x() / args.bboxMin.w() > args.bboxMax.x() / args.bboxMax.w()) 3516 std::swap(args.bboxMin.x(), args.bboxMax.x()); 3517 if (args.bboxMin.y() / args.bboxMin.w() > args.bboxMax.y() / args.bboxMax.w()) 3518 std::swap(args.bboxMin.y(), args.bboxMax.y()); 3519 if (args.bboxMin.z() / args.bboxMin.w() > args.bboxMax.z() / args.bboxMax.w()) 3520 std::swap(args.bboxMin.z(), args.bboxMax.z()); 3521 3522 args.linear = rnd.getBool(); 3523 3524 m_iterations.push_back(args); 3525 } 3526 } 3527 } 3528 3529 void BlitFboCase::deinit (void) 3530 { 3531 m_srcFbo.clear(); 3532 m_srcRbo.clear(); 3533 m_dstFbo.clear(); 3534 m_dstRbo.clear(); 3535 m_program.clear(); 3536 m_vbo.clear(); 3537 } 3538 3539 BlitFboCase::IterateResult BlitFboCase::iterate (void) 3540 { 3541 const tcu::ScopedLogSection section (m_testCtx.getLog(), "Iteration" + de::toString(m_iteration), "Iteration " + de::toString(m_iteration+1) + " / " + de::toString((int)m_iterations.size())); 3542 const BlitArgs& blitCfg = m_iterations[m_iteration]; 3543 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 3544 3545 if (m_iteration == 0) 3546 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 3547 3548 // fill source with test pattern. Default fb must be filled for each iteration because contents might not survive the swap 3549 if (m_src == TARGET_DEFAULT || m_iteration == 0) 3550 fillSourceWithPattern(); 3551 3552 m_testCtx.getLog() 3553 << tcu::TestLog::Message 3554 << "Set bounding box:\n" 3555 << "\tmin:" << blitCfg.bboxMin << "\n" 3556 << "\tmax:" << blitCfg.bboxMax << "\n" 3557 << "Blit:\n" 3558 << "\tsrc: " << blitCfg.src << "\n" 3559 << "\tdst: " << blitCfg.dst << "\n" 3560 << "\tfilter: " << ((blitCfg.linear) ? ("linear") : ("nearest")) 3561 << tcu::TestLog::EndMessage; 3562 3563 gl.primitiveBoundingBox(blitCfg.bboxMin.x(), blitCfg.bboxMin.y(), blitCfg.bboxMin.z(), blitCfg.bboxMin.w(), 3564 blitCfg.bboxMax.x(), blitCfg.bboxMax.y(), blitCfg.bboxMax.z(), blitCfg.bboxMax.w()); 3565 3566 gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, (m_dst == TARGET_FBO) ? (**m_dstFbo) : (m_context.getRenderContext().getDefaultFramebuffer())); 3567 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 3568 gl.clear(GL_COLOR_BUFFER_BIT); 3569 3570 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, (m_src == TARGET_FBO) ? (**m_srcFbo) : (m_context.getRenderContext().getDefaultFramebuffer())); 3571 gl.blitFramebuffer(blitCfg.src.x(), blitCfg.src.y(), blitCfg.src.z(), blitCfg.src.w(), 3572 blitCfg.dst.x(), blitCfg.dst.y(), blitCfg.dst.z(), blitCfg.dst.w(), 3573 GL_COLOR_BUFFER_BIT, 3574 ((blitCfg.linear) ? (GL_LINEAR) : (GL_NEAREST))); 3575 GLU_EXPECT_NO_ERROR(gl.getError(), "blit"); 3576 3577 if (!verifyImage(blitCfg)) 3578 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected blit result"); 3579 3580 return (++m_iteration == (int)m_iterations.size()) ? (STOP) : (CONTINUE); 3581 } 3582 3583 bool BlitFboCase::verifyImage (const BlitArgs& args) 3584 { 3585 const int colorThreshold = 4; //!< this test case is not about how color is preserved, allow almost anything 3586 const tcu::IVec2 dstSize = (m_dst == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE)); 3587 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 3588 tcu::Surface viewport (dstSize.x(), dstSize.y()); 3589 tcu::Surface errorMask (dstSize.x(), dstSize.y()); 3590 bool anyError = false; 3591 3592 m_testCtx.getLog() 3593 << tcu::TestLog::Message 3594 << "Verifying blit result" 3595 << tcu::TestLog::EndMessage; 3596 3597 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, (m_dst == TARGET_FBO) ? (**m_dstFbo) : (m_context.getRenderContext().getDefaultFramebuffer())); 3598 glu::readPixels(m_context.getRenderContext(), 0, 0, viewport.getAccess()); 3599 3600 tcu::clear(errorMask.getAccess(), tcu::IVec4(0, 0, 0, 255)); 3601 3602 for (int y = 0; y < dstSize.y(); ++y) 3603 for (int x = 0; x < dstSize.x(); ++x) 3604 { 3605 const tcu::RGBA color = viewport.getPixel(x, y); 3606 const bool inside = (x >= args.dst.x() && x < args.dst.z() && y >= args.dst.y() && y < args.dst.w()); 3607 const bool error = (inside) ? (color.getGreen() < 255 - colorThreshold || color.getBlue() > colorThreshold) 3608 : (color.getRed() > colorThreshold || color.getGreen() > colorThreshold || color.getBlue() > colorThreshold); 3609 3610 if (error) 3611 { 3612 anyError = true; 3613 errorMask.setPixel(x, y, tcu::RGBA::red()); 3614 } 3615 } 3616 3617 if (anyError) 3618 { 3619 m_testCtx.getLog() 3620 << tcu::TestLog::Message 3621 << "Image verification failed." 3622 << tcu::TestLog::EndMessage 3623 << tcu::TestLog::ImageSet("Images", "Image verification") 3624 << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess()) 3625 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess()) 3626 << tcu::TestLog::EndImageSet; 3627 return false; 3628 } 3629 else 3630 { 3631 m_testCtx.getLog() 3632 << tcu::TestLog::Message 3633 << "Result image ok." 3634 << tcu::TestLog::EndMessage 3635 << tcu::TestLog::ImageSet("Images", "Image verification") 3636 << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess()) 3637 << tcu::TestLog::EndImageSet; 3638 return true; 3639 } 3640 } 3641 3642 void BlitFboCase::fillSourceWithPattern (void) 3643 { 3644 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 3645 const tcu::IVec2 srcSize = (m_src == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE)); 3646 const int posLocation = gl.getAttribLocation(m_program->getProgram(), "a_position"); 3647 3648 gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, (m_src == TARGET_FBO) ? (**m_srcFbo) : (m_context.getRenderContext().getDefaultFramebuffer())); 3649 gl.viewport(0, 0, srcSize.x(), srcSize.y()); 3650 gl.useProgram(m_program->getProgram()); 3651 3652 gl.clearColor(0.0f, 0.0f, 1.0f, 1.0f); 3653 gl.clear(GL_COLOR_BUFFER_BIT); 3654 3655 gl.enableVertexAttribArray(posLocation); 3656 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 4 * (int)sizeof(float), NULL); 3657 gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4); 3658 GLU_EXPECT_NO_ERROR(gl.getError(), "draw"); 3659 } 3660 3661 class DepthDrawCase : public TestCase 3662 { 3663 public: 3664 enum DepthType 3665 { 3666 DEPTH_BUILTIN = 0, 3667 DEPTH_USER_DEFINED, 3668 3669 DEPTH_LAST 3670 }; 3671 enum BBoxState 3672 { 3673 STATE_GLOBAL = 0, 3674 STATE_PER_PRIMITIVE, 3675 3676 STATE_LAST 3677 }; 3678 enum BBoxSize 3679 { 3680 BBOX_EQUAL = 0, 3681 BBOX_LARGER, 3682 3683 BBOX_LAST 3684 }; 3685 3686 DepthDrawCase (Context& context, const char* name, const char* description, DepthType depthType, BBoxState state, BBoxSize bboxSize); 3687 ~DepthDrawCase (void); 3688 3689 private: 3690 void init (void); 3691 void deinit (void); 3692 IterateResult iterate (void); 3693 3694 std::string genVertexSource (void) const; 3695 std::string genFragmentSource (void) const; 3696 std::string genTessellationControlSource (void) const; 3697 std::string genTessellationEvaluationSource (void) const; 3698 void generateAttributeData (std::vector<tcu::Vec4>& data) const; 3699 bool verifyImage (const tcu::Surface& viewport) const; 3700 3701 enum 3702 { 3703 RENDER_AREA_SIZE = 256, 3704 }; 3705 3706 struct LayerInfo 3707 { 3708 float zOffset; 3709 float zScale; 3710 tcu::Vec4 color1; 3711 tcu::Vec4 color2; 3712 }; 3713 3714 const int m_numLayers; 3715 const int m_gridSize; 3716 3717 const DepthType m_depthType; 3718 const BBoxState m_state; 3719 const BBoxSize m_bboxSize; 3720 3721 de::MovePtr<glu::ShaderProgram> m_program; 3722 de::MovePtr<glu::Buffer> m_vbo; 3723 std::vector<LayerInfo> m_layers; 3724 }; 3725 3726 DepthDrawCase::DepthDrawCase (Context& context, const char* name, const char* description, DepthType depthType, BBoxState state, BBoxSize bboxSize) 3727 : TestCase (context, name, description) 3728 , m_numLayers (14) 3729 , m_gridSize (24) 3730 , m_depthType (depthType) 3731 , m_state (state) 3732 , m_bboxSize (bboxSize) 3733 { 3734 DE_ASSERT(depthType < DEPTH_LAST); 3735 DE_ASSERT(state < STATE_LAST); 3736 DE_ASSERT(bboxSize < BBOX_LAST); 3737 } 3738 3739 DepthDrawCase::~DepthDrawCase (void) 3740 { 3741 deinit(); 3742 } 3743 3744 void DepthDrawCase::init (void) 3745 { 3746 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 3747 const bool supportsES32 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)); 3748 3749 // requirements 3750 3751 if (!supportsES32 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box")) 3752 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension"); 3753 if (m_state == STATE_PER_PRIMITIVE && !supportsES32 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader")) 3754 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension"); 3755 if (m_context.getRenderTarget().getDepthBits() == 0) 3756 throw tcu::NotSupportedError("Test requires depth buffer"); 3757 if (m_context.getRenderTarget().getWidth() < RENDER_AREA_SIZE || m_context.getRenderTarget().getHeight() < RENDER_AREA_SIZE) 3758 throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_AREA_SIZE) + "x" + de::toString<int>(RENDER_AREA_SIZE) + " viewport"); 3759 3760 // log 3761 m_testCtx.getLog() 3762 << tcu::TestLog::Message 3763 << "Rendering multiple triangle grids with with different z coordinates.\n" 3764 << "Topmost grid is green-yellow, other grids are blue-red.\n" 3765 << "Expecting only the green-yellow grid to be visible.\n" 3766 << "Setting primitive bounding box " 3767 << ((m_bboxSize == BBOX_EQUAL) ? ("to exactly cover") : ("to cover")) 3768 << ((m_state == STATE_GLOBAL) ? (" each grid") : (" each triangle")) 3769 << ((m_bboxSize == BBOX_EQUAL) ? (".") : (" and include some padding.")) 3770 << "\n" 3771 << "Set bounding box using " 3772 << ((m_state == STATE_GLOBAL) ? ("PRIMITIVE_BOUNDING_BOX_EXT state") : ("gl_BoundingBoxEXT output")) 3773 << "\n" 3774 << ((m_depthType == DEPTH_USER_DEFINED) ? ("Fragment depth is set in the fragment shader") : ("")) 3775 << tcu::TestLog::EndMessage; 3776 3777 // resources 3778 3779 { 3780 glu::ProgramSources sources; 3781 sources << glu::VertexSource(specializeShader(m_context, genVertexSource().c_str())); 3782 sources << glu::FragmentSource(specializeShader(m_context, genFragmentSource().c_str())); 3783 3784 if (m_state == STATE_PER_PRIMITIVE) 3785 sources << glu::TessellationControlSource(specializeShader(m_context, genTessellationControlSource().c_str())) 3786 << glu::TessellationEvaluationSource(specializeShader(m_context, genTessellationEvaluationSource().c_str())); 3787 3788 m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), sources)); 3789 GLU_EXPECT_NO_ERROR(gl.getError(), "build program"); 3790 3791 { 3792 const tcu::ScopedLogSection section(m_testCtx.getLog(), "ShaderProgram", "Shader program"); 3793 m_testCtx.getLog() << *m_program; 3794 } 3795 3796 if (!m_program->isOk()) 3797 throw tcu::TestError("failed to build program"); 3798 } 3799 3800 { 3801 std::vector<tcu::Vec4> data; 3802 3803 generateAttributeData(data); 3804 3805 m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext())); 3806 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo); 3807 gl.bufferData(GL_ARRAY_BUFFER, (int)(sizeof(tcu::Vec4) * data.size()), &data[0], GL_STATIC_DRAW); 3808 GLU_EXPECT_NO_ERROR(gl.getError(), "buf upload"); 3809 } 3810 3811 // gen layers 3812 { 3813 de::Random rnd(0x12345); 3814 3815 m_layers.resize(m_numLayers); 3816 for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx) 3817 { 3818 m_layers[layerNdx].zOffset = ((float)layerNdx / (float)m_numLayers) * 2.0f - 1.0f; 3819 m_layers[layerNdx].zScale = (2.0f / (float)m_numLayers); 3820 m_layers[layerNdx].color1 = (layerNdx == 0) ? (tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f)) : (tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f)); 3821 m_layers[layerNdx].color2 = (layerNdx == 0) ? (tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f)) : (tcu::Vec4(1.0f, 0.0f, 1.0f, 1.0f)); 3822 } 3823 rnd.shuffle(m_layers.begin(), m_layers.end()); 3824 } 3825 } 3826 3827 void DepthDrawCase::deinit (void) 3828 { 3829 m_program.clear(); 3830 m_vbo.clear(); 3831 } 3832 3833 DepthDrawCase::IterateResult DepthDrawCase::iterate (void) 3834 { 3835 const bool hasTessellation = (m_state == STATE_PER_PRIMITIVE); 3836 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 3837 const glw::GLint posLocation = gl.getAttribLocation(m_program->getProgram(), "a_position"); 3838 const glw::GLint colLocation = gl.getAttribLocation(m_program->getProgram(), "a_colorMix"); 3839 const glw::GLint depthBiasLocation = gl.getUniformLocation(m_program->getProgram(), "u_depthBias"); 3840 const glw::GLint depthScaleLocation = gl.getUniformLocation(m_program->getProgram(), "u_depthScale"); 3841 const glw::GLint color1Location = gl.getUniformLocation(m_program->getProgram(), "u_color1"); 3842 const glw::GLint color2Location = gl.getUniformLocation(m_program->getProgram(), "u_color2"); 3843 3844 tcu::Surface viewport (RENDER_AREA_SIZE, RENDER_AREA_SIZE); 3845 de::Random rnd (0x213237); 3846 3847 TCU_CHECK(posLocation != -1); 3848 TCU_CHECK(colLocation != -1); 3849 TCU_CHECK(depthBiasLocation != -1); 3850 TCU_CHECK(depthScaleLocation != -1); 3851 TCU_CHECK(color1Location != -1); 3852 TCU_CHECK(color2Location != -1); 3853 3854 gl.viewport(0, 0, RENDER_AREA_SIZE, RENDER_AREA_SIZE); 3855 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 3856 gl.clearDepthf(1.0f); 3857 gl.depthFunc(GL_LESS); 3858 gl.enable(GL_DEPTH_TEST); 3859 gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 3860 GLU_EXPECT_NO_ERROR(gl.getError(), "setup viewport"); 3861 3862 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo); 3863 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, (int)(8 * sizeof(float)), glu::BufferOffsetAsPointer(0 * sizeof(float))); 3864 gl.vertexAttribPointer(colLocation, 4, GL_FLOAT, GL_FALSE, (int)(8 * sizeof(float)), glu::BufferOffsetAsPointer(4 * sizeof(float))); 3865 gl.enableVertexAttribArray(posLocation); 3866 gl.enableVertexAttribArray(colLocation); 3867 gl.useProgram(m_program->getProgram()); 3868 GLU_EXPECT_NO_ERROR(gl.getError(), "setup va"); 3869 3870 if (hasTessellation) 3871 gl.patchParameteri(GL_PATCH_VERTICES, 3); 3872 3873 for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx) 3874 { 3875 gl.uniform1f(depthBiasLocation, m_layers[layerNdx].zOffset); 3876 gl.uniform1f(depthScaleLocation, m_layers[layerNdx].zScale); 3877 gl.uniform4fv(color1Location, 1, m_layers[layerNdx].color1.getPtr()); 3878 gl.uniform4fv(color2Location, 1, m_layers[layerNdx].color2.getPtr()); 3879 3880 if (m_state == STATE_GLOBAL) 3881 { 3882 const float negPadding = (m_bboxSize == BBOX_EQUAL) ? (0.0f) : (rnd.getFloat() * 0.3f); 3883 const float posPadding = (m_bboxSize == BBOX_EQUAL) ? (0.0f) : (rnd.getFloat() * 0.3f); 3884 3885 gl.primitiveBoundingBox(-1.0f, -1.0f, m_layers[layerNdx].zOffset - negPadding, 1.0f, 3886 1.0f, 1.0f, (m_layers[layerNdx].zOffset + m_layers[layerNdx].zScale + posPadding), 1.0f); 3887 } 3888 3889 gl.drawArrays((hasTessellation) ? (GL_PATCHES) : (GL_TRIANGLES), 0, m_gridSize * m_gridSize * 6); 3890 } 3891 3892 glu::readPixels(m_context.getRenderContext(), 0, 0, viewport.getAccess()); 3893 GLU_EXPECT_NO_ERROR(gl.getError(), "render and read"); 3894 3895 if (verifyImage(viewport)) 3896 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 3897 else 3898 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed"); 3899 3900 return STOP; 3901 } 3902 3903 std::string DepthDrawCase::genVertexSource (void) const 3904 { 3905 const bool hasTessellation = (m_state == STATE_PER_PRIMITIVE); 3906 std::ostringstream buf; 3907 3908 buf << "${GLSL_VERSION_DECL}\n" 3909 "in highp vec4 a_position;\n" 3910 "in highp vec4 a_colorMix;\n" 3911 "out highp vec4 vtx_colorMix;\n"; 3912 3913 if (!hasTessellation && m_depthType == DEPTH_USER_DEFINED) 3914 buf << "out highp float v_fragDepth;\n"; 3915 3916 if (!hasTessellation) 3917 buf << "uniform highp float u_depthBias;\n" 3918 "uniform highp float u_depthScale;\n"; 3919 3920 buf << "\n" 3921 "void main()\n" 3922 "{\n"; 3923 3924 if (hasTessellation) 3925 buf << " gl_Position = a_position;\n"; 3926 else if (m_depthType == DEPTH_USER_DEFINED) 3927 buf << " highp float dummyZ = a_position.z;\n" 3928 " highp float writtenZ = a_position.w;\n" 3929 " gl_Position = vec4(a_position.xy, dummyZ, 1.0);\n" 3930 " v_fragDepth = writtenZ * u_depthScale + u_depthBias;\n"; 3931 else 3932 buf << " highp float writtenZ = a_position.w;\n" 3933 " gl_Position = vec4(a_position.xy, writtenZ * u_depthScale + u_depthBias, 1.0);\n"; 3934 3935 buf << " vtx_colorMix = a_colorMix;\n" 3936 "}\n"; 3937 3938 return buf.str(); 3939 } 3940 3941 std::string DepthDrawCase::genFragmentSource (void) const 3942 { 3943 const bool hasTessellation = (m_state == STATE_PER_PRIMITIVE); 3944 const char* const colorMixName = (hasTessellation) ? ("tess_eval_colorMix") : ("vtx_colorMix"); 3945 std::ostringstream buf; 3946 3947 buf << "${GLSL_VERSION_DECL}\n" 3948 "in mediump vec4 " << colorMixName << ";\n"; 3949 3950 if (m_depthType == DEPTH_USER_DEFINED) 3951 buf << "in mediump float v_fragDepth;\n"; 3952 3953 buf << "layout(location = 0) out mediump vec4 o_color;\n" 3954 "uniform highp vec4 u_color1;\n" 3955 "uniform highp vec4 u_color2;\n" 3956 "\n" 3957 "void main()\n" 3958 "{\n" 3959 " o_color = mix(u_color1, u_color2, " << colorMixName << ");\n"; 3960 3961 if (m_depthType == DEPTH_USER_DEFINED) 3962 buf << " gl_FragDepth = v_fragDepth * 0.5 + 0.5;\n"; 3963 3964 buf << "}\n"; 3965 3966 return buf.str(); 3967 } 3968 3969 std::string DepthDrawCase::genTessellationControlSource (void) const 3970 { 3971 std::ostringstream buf; 3972 3973 buf << "${GLSL_VERSION_DECL}\n" 3974 "${TESSELLATION_SHADER_REQUIRE}\n" 3975 "${PRIMITIVE_BOUNDING_BOX_REQUIRE}\n" 3976 "layout(vertices=3) out;\n" 3977 "\n" 3978 "uniform highp float u_depthBias;\n" 3979 "uniform highp float u_depthScale;\n" 3980 "\n" 3981 "in highp vec4 vtx_colorMix[];\n" 3982 "out highp vec4 tess_ctrl_colorMix[];\n" 3983 "\n" 3984 "void main()\n" 3985 "{\n" 3986 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" 3987 " tess_ctrl_colorMix[gl_InvocationID] = vtx_colorMix[0];\n" 3988 "\n" 3989 " gl_TessLevelOuter[0] = 2.8;\n" 3990 " gl_TessLevelOuter[1] = 2.8;\n" 3991 " gl_TessLevelOuter[2] = 2.8;\n" 3992 " gl_TessLevelInner[0] = 2.8;\n" 3993 "\n" 3994 " // real Z stored in w component\n" 3995 " highp vec4 minBound = vec4(min(min(vec3(gl_in[0].gl_Position.xy, gl_in[0].gl_Position.w * u_depthScale + u_depthBias),\n" 3996 " vec3(gl_in[1].gl_Position.xy, gl_in[1].gl_Position.w * u_depthScale + u_depthBias)),\n" 3997 " vec3(gl_in[2].gl_Position.xy, gl_in[2].gl_Position.w * u_depthScale + u_depthBias)), 1.0);\n" 3998 " highp vec4 maxBound = vec4(max(max(vec3(gl_in[0].gl_Position.xy, gl_in[0].gl_Position.w * u_depthScale + u_depthBias),\n" 3999 " vec3(gl_in[1].gl_Position.xy, gl_in[1].gl_Position.w * u_depthScale + u_depthBias)),\n" 4000 " vec3(gl_in[2].gl_Position.xy, gl_in[2].gl_Position.w * u_depthScale + u_depthBias)), 1.0);\n"; 4001 4002 if (m_bboxSize == BBOX_EQUAL) 4003 buf << " ${PRIM_GL_BOUNDING_BOX}[0] = minBound;\n" 4004 " ${PRIM_GL_BOUNDING_BOX}[1] = maxBound;\n"; 4005 else 4006 buf << " highp float nedPadding = mod(gl_in[0].gl_Position.z, 0.3);\n" 4007 " highp float posPadding = mod(gl_in[1].gl_Position.z, 0.3);\n" 4008 " ${PRIM_GL_BOUNDING_BOX}[0] = minBound - vec4(0.0, 0.0, nedPadding, 0.0);\n" 4009 " ${PRIM_GL_BOUNDING_BOX}[1] = maxBound + vec4(0.0, 0.0, posPadding, 0.0);\n"; 4010 4011 buf << "}\n"; 4012 4013 return buf.str(); 4014 } 4015 4016 std::string DepthDrawCase::genTessellationEvaluationSource (void) const 4017 { 4018 std::ostringstream buf; 4019 4020 buf << "${GLSL_VERSION_DECL}\n" 4021 "${TESSELLATION_SHADER_REQUIRE}\n" 4022 "${GPU_SHADER5_REQUIRE}\n" 4023 "layout(triangles) in;\n" 4024 "\n" 4025 "in highp vec4 tess_ctrl_colorMix[];\n" 4026 "out highp vec4 tess_eval_colorMix;\n"; 4027 4028 if (m_depthType == DEPTH_USER_DEFINED) 4029 buf << "out highp float v_fragDepth;\n"; 4030 4031 buf << "uniform highp float u_depthBias;\n" 4032 "uniform highp float u_depthScale;\n" 4033 "\n" 4034 "precise gl_Position;\n" 4035 "\n" 4036 "void main()\n" 4037 "{\n" 4038 " highp vec4 tessellatedPos = gl_TessCoord.x * gl_in[0].gl_Position + gl_TessCoord.y * gl_in[1].gl_Position + gl_TessCoord.z * gl_in[2].gl_Position;\n"; 4039 4040 if (m_depthType == DEPTH_USER_DEFINED) 4041 buf << " highp float dummyZ = tessellatedPos.z;\n" 4042 " highp float writtenZ = tessellatedPos.w;\n" 4043 " gl_Position = vec4(tessellatedPos.xy, dummyZ, 1.0);\n" 4044 " v_fragDepth = writtenZ * u_depthScale + u_depthBias;\n"; 4045 else 4046 buf << " highp float writtenZ = tessellatedPos.w;\n" 4047 " gl_Position = vec4(tessellatedPos.xy, writtenZ * u_depthScale + u_depthBias, 1.0);\n"; 4048 4049 buf << " tess_eval_colorMix = tess_ctrl_colorMix[0];\n" 4050 "}\n"; 4051 4052 return buf.str(); 4053 } 4054 4055 void DepthDrawCase::generateAttributeData (std::vector<tcu::Vec4>& data) const 4056 { 4057 const tcu::Vec4 color1 (0.0f, 0.0f, 0.0f, 0.0f); // mix weights 4058 const tcu::Vec4 color2 (1.0f, 1.0f, 1.0f, 1.0f); 4059 std::vector<int> cellOrder (m_gridSize * m_gridSize); 4060 de::Random rnd (0xAB54321); 4061 4062 // generate grid with cells in random order 4063 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx) 4064 cellOrder[ndx] = ndx; 4065 rnd.shuffle(cellOrder.begin(), cellOrder.end()); 4066 4067 data.resize(m_gridSize * m_gridSize * 6 * 2); 4068 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx) 4069 { 4070 const int cellNdx = cellOrder[ndx]; 4071 const int cellX = cellNdx % m_gridSize; 4072 const int cellY = cellNdx / m_gridSize; 4073 const tcu::Vec4& cellColor = ((cellX+cellY)%2 == 0) ? (color1) : (color2); 4074 4075 data[ndx * 6 * 2 + 0] = tcu::Vec4(float(cellX+0) / float(m_gridSize) * 2.0f - 1.0f, float(cellY+0) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 1] = cellColor; 4076 data[ndx * 6 * 2 + 2] = tcu::Vec4(float(cellX+1) / float(m_gridSize) * 2.0f - 1.0f, float(cellY+1) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 3] = cellColor; 4077 data[ndx * 6 * 2 + 4] = tcu::Vec4(float(cellX+0) / float(m_gridSize) * 2.0f - 1.0f, float(cellY+1) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 5] = cellColor; 4078 data[ndx * 6 * 2 + 6] = tcu::Vec4(float(cellX+0) / float(m_gridSize) * 2.0f - 1.0f, float(cellY+0) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 7] = cellColor; 4079 data[ndx * 6 * 2 + 8] = tcu::Vec4(float(cellX+1) / float(m_gridSize) * 2.0f - 1.0f, float(cellY+0) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 9] = cellColor; 4080 data[ndx * 6 * 2 + 10] = tcu::Vec4(float(cellX+1) / float(m_gridSize) * 2.0f - 1.0f, float(cellY+1) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 11] = cellColor; 4081 4082 // Fill Z with random values (fake Z) 4083 for (int vtxNdx = 0; vtxNdx < 6; ++vtxNdx) 4084 data[ndx * 6 * 2 + 2*vtxNdx].z() = rnd.getFloat(0.0f, 1.0); 4085 4086 // Fill W with other random values (written Z) 4087 for (int vtxNdx = 0; vtxNdx < 6; ++vtxNdx) 4088 data[ndx * 6 * 2 + 2*vtxNdx].w() = rnd.getFloat(0.0f, 1.0); 4089 } 4090 } 4091 4092 bool DepthDrawCase::verifyImage (const tcu::Surface& viewport) const 4093 { 4094 tcu::Surface errorMask (viewport.getWidth(), viewport.getHeight()); 4095 bool anyError = false; 4096 4097 tcu::clear(errorMask.getAccess(), tcu::IVec4(0,0,0,255)); 4098 4099 for (int y = 0; y < viewport.getHeight(); ++y) 4100 for (int x = 0; x < viewport.getWidth(); ++x) 4101 { 4102 const tcu::RGBA pixel = viewport.getPixel(x, y); 4103 bool error = false; 4104 4105 // expect green, yellow or a combination of these 4106 if (pixel.getGreen() != 255 || pixel.getBlue() != 0) 4107 error = true; 4108 4109 if (error) 4110 { 4111 errorMask.setPixel(x, y, tcu::RGBA::red()); 4112 anyError = true; 4113 } 4114 } 4115 4116 if (anyError) 4117 m_testCtx.getLog() 4118 << tcu::TestLog::Message 4119 << "Image verification failed." 4120 << tcu::TestLog::EndMessage 4121 << tcu::TestLog::ImageSet("Images", "Image verification") 4122 << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess()) 4123 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess()) 4124 << tcu::TestLog::EndImageSet; 4125 else 4126 m_testCtx.getLog() 4127 << tcu::TestLog::Message 4128 << "Result image ok." 4129 << tcu::TestLog::EndMessage 4130 << tcu::TestLog::ImageSet("Images", "Image verification") 4131 << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess()) 4132 << tcu::TestLog::EndImageSet; 4133 4134 return !anyError; 4135 } 4136 4137 class ClearCase : public TestCase 4138 { 4139 public: 4140 enum 4141 { 4142 SCISSOR_CLEAR_BIT = 1 << 0, 4143 DRAW_TRIANGLE_BIT = 1 << 1, 4144 PER_PRIMITIVE_BBOX_BIT = 1 << 2, 4145 FULLSCREEN_SCISSOR_BIT = 1 << 3, 4146 }; 4147 4148 ClearCase (Context& context, const char* name, const char* description, deUint32 flags); 4149 ~ClearCase (void); 4150 4151 private: 4152 struct DrawObject 4153 { 4154 int firstNdx; 4155 int numVertices; 4156 }; 4157 4158 void init (void); 4159 void deinit (void); 4160 IterateResult iterate (void); 4161 4162 void createVbo (void); 4163 void createProgram (void); 4164 void renderTo (tcu::Surface& dst, bool useBBox); 4165 bool verifyImagesEqual (const tcu::PixelBufferAccess& withoutBBox, const tcu::PixelBufferAccess& withBBox); 4166 bool verifyImageResultValid (const tcu::PixelBufferAccess& result); 4167 4168 std::string genVertexSource (void) const; 4169 std::string genFragmentSource (void) const; 4170 std::string genTessellationControlSource (bool setBBox) const; 4171 std::string genTessellationEvaluationSource (void) const; 4172 4173 const bool m_scissoredClear; 4174 const bool m_fullscreenScissor; 4175 const bool m_drawTriangles; 4176 const bool m_useGlobalState; 4177 4178 de::MovePtr<glu::Buffer> m_vbo; 4179 de::MovePtr<glu::ShaderProgram> m_perPrimitiveProgram; 4180 de::MovePtr<glu::ShaderProgram> m_basicProgram; 4181 std::vector<DrawObject> m_drawObjects; 4182 std::vector<tcu::Vec4> m_objectVertices; 4183 }; 4184 4185 ClearCase::ClearCase (Context& context, const char* name, const char* description, deUint32 flags) 4186 : TestCase (context, name, description) 4187 , m_scissoredClear ((flags & SCISSOR_CLEAR_BIT) != 0) 4188 , m_fullscreenScissor ((flags & FULLSCREEN_SCISSOR_BIT) != 0) 4189 , m_drawTriangles ((flags & DRAW_TRIANGLE_BIT) != 0) 4190 , m_useGlobalState ((flags & PER_PRIMITIVE_BBOX_BIT) == 0) 4191 { 4192 DE_ASSERT(m_useGlobalState || m_drawTriangles); // per-triangle bbox requires triangles 4193 DE_ASSERT(!m_fullscreenScissor || m_scissoredClear); // fullscreenScissor requires scissoredClear 4194 } 4195 4196 ClearCase::~ClearCase (void) 4197 { 4198 deinit(); 4199 } 4200 4201 void ClearCase::init (void) 4202 { 4203 const bool supportsES32 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)); 4204 4205 if (!supportsES32 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box")) 4206 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension"); 4207 if (m_drawTriangles && !supportsES32 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader")) 4208 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension"); 4209 4210 m_testCtx.getLog() 4211 << tcu::TestLog::Message 4212 << "Doing multiple" 4213 << ((m_scissoredClear) ? (" scissored") : ("")) 4214 << " color buffer clears" 4215 << ((m_drawTriangles) ? (" and drawing some geometry between them") : ("")) 4216 << ".\n" 4217 << ((m_scissoredClear && m_fullscreenScissor) ? ("Setting scissor area to cover entire viewport.\n") : ("")) 4218 << "Rendering with and without setting the bounding box.\n" 4219 << "Expecting bounding box to have no effect on clears (i.e. results are constant).\n" 4220 << "Set bounding box using " 4221 << ((m_useGlobalState) ? ("PRIMITIVE_BOUNDING_BOX_EXT state") : ("gl_BoundingBoxEXT output")) 4222 << ".\n" 4223 << "Clear color is green with yellowish shades.\n" 4224 << ((m_drawTriangles) ? ("Primitive color is yellow with greenish shades.\n") : ("")) 4225 << tcu::TestLog::EndMessage; 4226 4227 if (m_drawTriangles) 4228 { 4229 createVbo(); 4230 createProgram(); 4231 } 4232 } 4233 4234 void ClearCase::deinit (void) 4235 { 4236 m_vbo.clear(); 4237 m_perPrimitiveProgram.clear(); 4238 m_basicProgram.clear(); 4239 m_drawObjects = std::vector<DrawObject>(); 4240 m_objectVertices = std::vector<tcu::Vec4>(); 4241 } 4242 4243 ClearCase::IterateResult ClearCase::iterate (void) 4244 { 4245 const tcu::IVec2 renderTargetSize (m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight()); 4246 tcu::Surface resultWithoutBBox (renderTargetSize.x(), renderTargetSize.y()); 4247 tcu::Surface resultWithBBox (renderTargetSize.x(), renderTargetSize.y()); 4248 4249 // render with and without bbox set 4250 for (int passNdx = 0; passNdx < 2; ++passNdx) 4251 { 4252 const bool useBBox = (passNdx == 1); 4253 tcu::Surface& destination = (useBBox) ? (resultWithBBox) : (resultWithoutBBox); 4254 4255 renderTo(destination, useBBox); 4256 } 4257 4258 // Verify images are equal and that the image does not contain (trivially detectable) garbage 4259 4260 if (!verifyImagesEqual(resultWithoutBBox.getAccess(), resultWithBBox.getAccess())) 4261 { 4262 // verifyImagesEqual will print out the image and error mask 4263 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed"); 4264 } 4265 else if (!verifyImageResultValid(resultWithBBox.getAccess())) 4266 { 4267 // verifyImageResultValid will print out the image and error mask 4268 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Result verification failed"); 4269 } 4270 else 4271 { 4272 m_testCtx.getLog() 4273 << tcu::TestLog::Message 4274 << "Image comparison passed." 4275 << tcu::TestLog::EndMessage 4276 << tcu::TestLog::ImageSet("Images", "Image verification") 4277 << tcu::TestLog::Image("Result", "Result", resultWithBBox.getAccess()) 4278 << tcu::TestLog::EndImageSet; 4279 4280 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 4281 } 4282 4283 return STOP; 4284 } 4285 4286 void ClearCase::createVbo (void) 4287 { 4288 const int numObjects = 16; 4289 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 4290 de::Random rnd (deStringHash(getName())); 4291 4292 m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext())); 4293 4294 for (int objectNdx = 0; objectNdx < numObjects; ++objectNdx) 4295 { 4296 const int numTriangles = rnd.getInt(1, 4); 4297 const float minX = rnd.getFloat(-1.2f, 0.8f); 4298 const float minY = rnd.getFloat(-1.2f, 0.8f); 4299 const float maxX = minX + rnd.getFloat(0.2f, 1.0f); 4300 const float maxY = minY + rnd.getFloat(0.2f, 1.0f); 4301 4302 DrawObject drawObject; 4303 drawObject.firstNdx = (int)m_objectVertices.size(); 4304 drawObject.numVertices = numTriangles * 3; 4305 4306 m_drawObjects.push_back(drawObject); 4307 4308 for (int triangleNdx = 0; triangleNdx < numTriangles; ++triangleNdx) 4309 for (int vertexNdx = 0; vertexNdx < 3; ++vertexNdx) 4310 { 4311 const float posX = rnd.getFloat(minX, maxX); 4312 const float posY = rnd.getFloat(minY, maxY); 4313 const float posZ = rnd.getFloat(-0.7f, 0.7f); 4314 const float posW = rnd.getFloat(0.9f, 1.1f); 4315 4316 m_objectVertices.push_back(tcu::Vec4(posX, posY, posZ, posW)); 4317 } 4318 } 4319 4320 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo); 4321 gl.bufferData(GL_ARRAY_BUFFER, (int)(m_objectVertices.size() * sizeof(tcu::Vec4)), &m_objectVertices[0], GL_STATIC_DRAW); 4322 GLU_EXPECT_NO_ERROR(gl.getError(), "buffer upload"); 4323 } 4324 4325 void ClearCase::createProgram (void) 4326 { 4327 m_basicProgram = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), 4328 glu::ProgramSources() 4329 << glu::VertexSource(specializeShader(m_context, genVertexSource().c_str())) 4330 << glu::FragmentSource(specializeShader(m_context, genFragmentSource().c_str())) 4331 << glu::TessellationControlSource(specializeShader(m_context, genTessellationControlSource(false).c_str())) 4332 << glu::TessellationEvaluationSource(specializeShader(m_context, genTessellationEvaluationSource().c_str())))); 4333 4334 m_testCtx.getLog() 4335 << tcu::TestLog::Section("Program", "Shader program") 4336 << *m_basicProgram 4337 << tcu::TestLog::EndSection; 4338 4339 if (!m_basicProgram->isOk()) 4340 throw tcu::TestError("shader build failed"); 4341 4342 if (!m_useGlobalState) 4343 { 4344 m_perPrimitiveProgram = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), 4345 glu::ProgramSources() 4346 << glu::VertexSource(specializeShader(m_context, genVertexSource().c_str())) 4347 << glu::FragmentSource(specializeShader(m_context, genFragmentSource().c_str())) 4348 << glu::TessellationControlSource(specializeShader(m_context, genTessellationControlSource(true).c_str())) 4349 << glu::TessellationEvaluationSource(specializeShader(m_context, genTessellationEvaluationSource().c_str())))); 4350 4351 m_testCtx.getLog() 4352 << tcu::TestLog::Section("PerPrimitiveProgram", "Shader program that sets the bounding box") 4353 << *m_perPrimitiveProgram 4354 << tcu::TestLog::EndSection; 4355 4356 if (!m_perPrimitiveProgram->isOk()) 4357 throw tcu::TestError("shader build failed"); 4358 } 4359 } 4360 4361 void ClearCase::renderTo (tcu::Surface& dst, bool useBBox) 4362 { 4363 const int numOps = 45; 4364 const tcu::Vec4 yellow (1.0f, 1.0f, 0.0f, 1.0f); 4365 const tcu::Vec4 green (0.0f, 1.0f, 0.0f, 1.0f); 4366 const tcu::IVec2 renderTargetSize (m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight()); 4367 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 4368 de::Random rnd (deStringHash(getName())); 4369 glu::VertexArray vao (m_context.getRenderContext()); 4370 4371 // always do the initial clear 4372 gl.disable(GL_SCISSOR_TEST); 4373 gl.viewport(0, 0, renderTargetSize.x(), renderTargetSize.y()); 4374 gl.clearColor(yellow.x(), yellow.y(), yellow.z(), yellow.w()); 4375 gl.clear(GL_COLOR_BUFFER_BIT); 4376 gl.finish(); 4377 4378 // prepare draw 4379 if (m_scissoredClear) 4380 gl.enable(GL_SCISSOR_TEST); 4381 4382 if (m_drawTriangles) 4383 { 4384 const deUint32 programHandle = (m_useGlobalState || !useBBox) ? (m_basicProgram->getProgram()) : (m_perPrimitiveProgram->getProgram()); 4385 const int positionAttribLoc = gl.getAttribLocation(programHandle, "a_position"); 4386 4387 TCU_CHECK(positionAttribLoc != -1); 4388 4389 gl.useProgram(programHandle); 4390 gl.bindVertexArray(*vao); 4391 gl.enableVertexAttribArray(positionAttribLoc); 4392 gl.vertexAttribPointer(positionAttribLoc, 4, GL_FLOAT, GL_FALSE, (int)sizeof(tcu::Vec4), DE_NULL); 4393 gl.patchParameteri(GL_PATCH_VERTICES, 3); 4394 } 4395 4396 // do random scissor/clearldraw operations 4397 for (int opNdx = 0; opNdx < numOps; ++opNdx) 4398 { 4399 const int drawObjNdx = (m_drawTriangles) ? (rnd.getInt(0, (int)m_drawObjects.size() - 1)) : (0); 4400 const int objectVertexStartNdx = (m_drawTriangles) ? (m_drawObjects[drawObjNdx].firstNdx) : (0); 4401 const int objectVertexLength = (m_drawTriangles) ? (m_drawObjects[drawObjNdx].numVertices) : (0); 4402 tcu::Vec4 bboxMin; 4403 tcu::Vec4 bboxMax; 4404 4405 if (m_drawTriangles) 4406 { 4407 bboxMin = tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f); 4408 bboxMax = tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f); 4409 4410 // calc bbox 4411 for (int vertexNdx = objectVertexStartNdx; vertexNdx < objectVertexStartNdx + objectVertexLength; ++vertexNdx) 4412 for (int componentNdx = 0; componentNdx < 4; ++componentNdx) 4413 { 4414 bboxMin[componentNdx] = de::min(bboxMin[componentNdx], m_objectVertices[vertexNdx][componentNdx]); 4415 bboxMax[componentNdx] = de::max(bboxMax[componentNdx], m_objectVertices[vertexNdx][componentNdx]); 4416 } 4417 } 4418 else 4419 { 4420 // no geometry, just random something 4421 bboxMin.x() = rnd.getFloat(-1.2f, 1.0f); 4422 bboxMin.y() = rnd.getFloat(-1.2f, 1.0f); 4423 bboxMin.z() = rnd.getFloat(-1.2f, 1.0f); 4424 bboxMin.w() = 1.0f; 4425 bboxMax.x() = bboxMin.x() + rnd.getFloat(0.2f, 1.0f); 4426 bboxMax.y() = bboxMin.y() + rnd.getFloat(0.2f, 1.0f); 4427 bboxMax.z() = bboxMin.z() + rnd.getFloat(0.2f, 1.0f); 4428 bboxMax.w() = 1.0f; 4429 } 4430 4431 if (m_scissoredClear) 4432 { 4433 const int scissorX = (m_fullscreenScissor) ? (0) : rnd.getInt(0, renderTargetSize.x()-1); 4434 const int scissorY = (m_fullscreenScissor) ? (0) : rnd.getInt(0, renderTargetSize.y()-1); 4435 const int scissorW = (m_fullscreenScissor) ? (renderTargetSize.x()) : rnd.getInt(0, renderTargetSize.x()-scissorX); 4436 const int scissorH = (m_fullscreenScissor) ? (renderTargetSize.y()) : rnd.getInt(0, renderTargetSize.y()-scissorY); 4437 4438 gl.scissor(scissorX, scissorY, scissorW, scissorH); 4439 } 4440 4441 { 4442 const tcu::Vec4 color = tcu::mix(green, yellow, rnd.getFloat() * 0.4f); // greenish 4443 gl.clearColor(color.x(), color.y(), color.z(), color.w()); 4444 gl.clear(GL_COLOR_BUFFER_BIT); 4445 } 4446 4447 if (useBBox) 4448 { 4449 DE_ASSERT(m_useGlobalState || m_drawTriangles); // !m_useGlobalState -> m_drawTriangles 4450 if (m_useGlobalState) 4451 gl.primitiveBoundingBox(bboxMin.x(), bboxMin.y(), bboxMin.z(), bboxMin.w(), 4452 bboxMax.x(), bboxMax.y(), bboxMax.z(), bboxMax.w()); 4453 } 4454 4455 if (m_drawTriangles) 4456 gl.drawArrays(GL_PATCHES, objectVertexStartNdx, objectVertexLength); 4457 } 4458 4459 GLU_EXPECT_NO_ERROR(gl.getError(), "post draw"); 4460 glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess()); 4461 } 4462 4463 bool ClearCase::verifyImagesEqual (const tcu::PixelBufferAccess& withoutBBox, const tcu::PixelBufferAccess& withBBox) 4464 { 4465 DE_ASSERT(withoutBBox.getWidth() == withBBox.getWidth()); 4466 DE_ASSERT(withoutBBox.getHeight() == withBBox.getHeight()); 4467 4468 tcu::Surface errorMask (withoutBBox.getWidth(), withoutBBox.getHeight()); 4469 bool anyError = false; 4470 4471 tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec()); 4472 4473 for (int y = 0; y < withoutBBox.getHeight(); ++y) 4474 for (int x = 0; x < withoutBBox.getWidth(); ++x) 4475 { 4476 if (withoutBBox.getPixelInt(x, y) != withBBox.getPixelInt(x, y)) 4477 { 4478 errorMask.setPixel(x, y, tcu::RGBA::red()); 4479 anyError = true; 4480 } 4481 } 4482 4483 if (anyError) 4484 { 4485 m_testCtx.getLog() 4486 << tcu::TestLog::Message 4487 << "Image comparison failed." 4488 << tcu::TestLog::EndMessage 4489 << tcu::TestLog::ImageSet("Images", "Image comparison") 4490 << tcu::TestLog::Image("WithoutBBox", "Result with bounding box not set", withoutBBox) 4491 << tcu::TestLog::Image("WithBBox", "Result with bounding box set", withBBox) 4492 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess()) 4493 << tcu::TestLog::EndImageSet; 4494 } 4495 4496 return !anyError; 4497 } 4498 4499 bool ClearCase::verifyImageResultValid (const tcu::PixelBufferAccess& result) 4500 { 4501 tcu::Surface errorMask (result.getWidth(), result.getHeight()); 4502 bool anyError = false; 4503 4504 tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec()); 4505 4506 for (int y = 0; y < result.getHeight(); ++y) 4507 for (int x = 0; x < result.getWidth(); ++x) 4508 { 4509 const tcu::IVec4 pixel = result.getPixelInt(x, y); 4510 4511 // allow green, yellow and any shade between 4512 if (pixel[1] != 255 || pixel[2] != 0) 4513 { 4514 errorMask.setPixel(x, y, tcu::RGBA::red()); 4515 anyError = true; 4516 } 4517 } 4518 4519 if (anyError) 4520 { 4521 m_testCtx.getLog() 4522 << tcu::TestLog::Message 4523 << "Image verification failed." 4524 << tcu::TestLog::EndMessage 4525 << tcu::TestLog::ImageSet("Images", "Image verification") 4526 << tcu::TestLog::Image("ResultImage", "Result image", result) 4527 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask) 4528 << tcu::TestLog::EndImageSet; 4529 } 4530 4531 return !anyError; 4532 } 4533 4534 static const char* const s_yellowishPosOnlyVertexSource = "${GLSL_VERSION_DECL}\n" 4535 "in highp vec4 a_position;\n" 4536 "out highp vec4 v_vertex_color;\n" 4537 "void main()\n" 4538 "{\n" 4539 " gl_Position = a_position;\n" 4540 " // yellowish shade\n" 4541 " highp float redComponent = 0.5 + float(gl_VertexID % 5) / 8.0;\n" 4542 " v_vertex_color = vec4(redComponent, 1.0, 0.0, 1.0);\n" 4543 "}\n"; 4544 4545 static const char* const s_basicColorFragmentSource = "${GLSL_VERSION_DECL}\n" 4546 "in mediump vec4 v_color;\n" 4547 "layout(location = 0) out mediump vec4 o_color;\n" 4548 "void main()\n" 4549 "{\n" 4550 " o_color = v_color;\n" 4551 "}\n"; 4552 4553 4554 static const char* const s_basicColorTessEvalSource = "${GLSL_VERSION_DECL}\n" 4555 "${TESSELLATION_SHADER_REQUIRE}\n" 4556 "${GPU_SHADER5_REQUIRE}\n" 4557 "layout(triangles) in;\n" 4558 "in highp vec4 v_tess_eval_color[];\n" 4559 "out highp vec4 v_color;\n" 4560 "precise gl_Position;\n" 4561 "void main()\n" 4562 "{\n" 4563 " gl_Position = gl_TessCoord.x * gl_in[0].gl_Position\n" 4564 " + gl_TessCoord.y * gl_in[1].gl_Position\n" 4565 " + gl_TessCoord.z * gl_in[2].gl_Position;\n" 4566 " v_color = gl_TessCoord.x * v_tess_eval_color[0]\n" 4567 " + gl_TessCoord.y * v_tess_eval_color[1]\n" 4568 " + gl_TessCoord.z * v_tess_eval_color[2];\n" 4569 "}\n"; 4570 4571 std::string ClearCase::genVertexSource (void) const 4572 { 4573 return s_yellowishPosOnlyVertexSource; 4574 } 4575 4576 std::string ClearCase::genFragmentSource (void) const 4577 { 4578 return s_basicColorFragmentSource; 4579 } 4580 4581 std::string ClearCase::genTessellationControlSource (bool setBBox) const 4582 { 4583 std::ostringstream buf; 4584 4585 buf << "${GLSL_VERSION_DECL}\n" 4586 "${TESSELLATION_SHADER_REQUIRE}\n"; 4587 4588 if (setBBox) 4589 buf << "${PRIMITIVE_BOUNDING_BOX_REQUIRE}\n"; 4590 4591 buf << "layout(vertices=3) out;\n" 4592 "in highp vec4 v_vertex_color[];\n" 4593 "out highp vec4 v_tess_eval_color[];\n" 4594 "void main()\n" 4595 "{\n" 4596 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" 4597 " v_tess_eval_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n" 4598 " gl_TessLevelOuter[0] = 2.8;\n" 4599 " gl_TessLevelOuter[1] = 2.8;\n" 4600 " gl_TessLevelOuter[2] = 2.8;\n" 4601 " gl_TessLevelInner[0] = 2.8;\n"; 4602 4603 if (setBBox) 4604 { 4605 buf << "\n" 4606 " ${PRIM_GL_BOUNDING_BOX}[0] = min(min(gl_in[0].gl_Position,\n" 4607 " gl_in[1].gl_Position),\n" 4608 " gl_in[2].gl_Position);\n" 4609 " ${PRIM_GL_BOUNDING_BOX}[1] = max(max(gl_in[0].gl_Position,\n" 4610 " gl_in[1].gl_Position),\n" 4611 " gl_in[2].gl_Position);\n"; 4612 } 4613 4614 buf << "}\n"; 4615 return buf.str(); 4616 } 4617 4618 std::string ClearCase::genTessellationEvaluationSource (void) const 4619 { 4620 return s_basicColorTessEvalSource; 4621 } 4622 4623 class ViewportCallOrderCase : public TestCase 4624 { 4625 public: 4626 enum CallOrder 4627 { 4628 VIEWPORT_FIRST = 0, 4629 BBOX_FIRST, 4630 4631 ORDER_LAST 4632 }; 4633 4634 ViewportCallOrderCase (Context& context, const char* name, const char* description, CallOrder callOrder); 4635 ~ViewportCallOrderCase (void); 4636 4637 private: 4638 void init (void); 4639 void deinit (void); 4640 IterateResult iterate (void); 4641 4642 void genVbo (void); 4643 void genProgram (void); 4644 bool verifyImage (const tcu::PixelBufferAccess& result); 4645 4646 std::string genVertexSource (void) const; 4647 std::string genFragmentSource (void) const; 4648 std::string genTessellationControlSource (void) const; 4649 std::string genTessellationEvaluationSource (void) const; 4650 4651 const CallOrder m_callOrder; 4652 4653 de::MovePtr<glu::Buffer> m_vbo; 4654 de::MovePtr<glu::ShaderProgram> m_program; 4655 int m_numVertices; 4656 }; 4657 4658 ViewportCallOrderCase::ViewportCallOrderCase (Context& context, const char* name, const char* description, CallOrder callOrder) 4659 : TestCase (context, name, description) 4660 , m_callOrder (callOrder) 4661 , m_numVertices (-1) 4662 { 4663 DE_ASSERT(m_callOrder < ORDER_LAST); 4664 } 4665 4666 ViewportCallOrderCase::~ViewportCallOrderCase (void) 4667 { 4668 deinit(); 4669 } 4670 4671 void ViewportCallOrderCase::init (void) 4672 { 4673 const bool supportsES32 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)); 4674 4675 if (!supportsES32 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box")) 4676 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension"); 4677 4678 if (!supportsES32 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader")) 4679 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension"); 4680 4681 m_testCtx.getLog() 4682 << tcu::TestLog::Message 4683 << "Testing call order of state setting functions have no effect on the rendering.\n" 4684 << "Setting viewport and bounding box in the following order:\n" 4685 << ((m_callOrder == VIEWPORT_FIRST) 4686 ? ("\tFirst viewport with glViewport function.\n") 4687 : ("\tFirst bounding box with glPrimitiveBoundingBoxEXT function.\n")) 4688 << ((m_callOrder == VIEWPORT_FIRST) 4689 ? ("\tThen bounding box with glPrimitiveBoundingBoxEXT function.\n") 4690 : ("\tThen viewport with glViewport function.\n")) 4691 << "Verifying rendering result." 4692 << tcu::TestLog::EndMessage; 4693 4694 // resources 4695 genVbo(); 4696 genProgram(); 4697 } 4698 4699 void ViewportCallOrderCase::deinit (void) 4700 { 4701 m_vbo.clear(); 4702 m_program.clear(); 4703 } 4704 4705 ViewportCallOrderCase::IterateResult ViewportCallOrderCase::iterate (void) 4706 { 4707 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 4708 const tcu::IVec2 viewportSize = tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight()); 4709 const glw::GLint posLocation = gl.getAttribLocation(m_program->getProgram(), "a_position"); 4710 tcu::Surface resultSurface (viewportSize.x(), viewportSize.y()); 4711 4712 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 4713 gl.clear(GL_COLOR_BUFFER_BIT); 4714 4715 // set state 4716 for (int orderNdx = 0; orderNdx < 2; ++orderNdx) 4717 { 4718 if ((orderNdx == 0 && m_callOrder == VIEWPORT_FIRST) || 4719 (orderNdx == 1 && m_callOrder == BBOX_FIRST)) 4720 { 4721 m_testCtx.getLog() 4722 << tcu::TestLog::Message 4723 << "Setting viewport to cover the left half of the render target.\n" 4724 << "\t(0, 0, " << (viewportSize.x()/2) << ", " << viewportSize.y() << ")" 4725 << tcu::TestLog::EndMessage; 4726 4727 gl.viewport(0, 0, viewportSize.x()/2, viewportSize.y()); 4728 } 4729 else 4730 { 4731 m_testCtx.getLog() 4732 << tcu::TestLog::Message 4733 << "Setting bounding box to cover the right half of the clip space.\n" 4734 << "\t(0.0, -1.0, -1.0, 1.0) .. (1.0, 1.0, 1.0f, 1.0)" 4735 << tcu::TestLog::EndMessage; 4736 4737 gl.primitiveBoundingBox(0.0f, -1.0f, -1.0f, 1.0f, 4738 1.0f, 1.0f, 1.0f, 1.0f); 4739 } 4740 } 4741 4742 m_testCtx.getLog() 4743 << tcu::TestLog::Message 4744 << "Rendering mesh covering the right half of the clip space." 4745 << tcu::TestLog::EndMessage; 4746 4747 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo); 4748 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, sizeof(float[4]), (const float*)DE_NULL); 4749 gl.enableVertexAttribArray(posLocation); 4750 gl.useProgram(m_program->getProgram()); 4751 gl.patchParameteri(GL_PATCH_VERTICES, 3); 4752 gl.drawArrays(GL_PATCHES, 0, m_numVertices); 4753 GLU_EXPECT_NO_ERROR(gl.getError(), "post-draw"); 4754 4755 m_testCtx.getLog() 4756 << tcu::TestLog::Message 4757 << "Verifying image" 4758 << tcu::TestLog::EndMessage; 4759 glu::readPixels(m_context.getRenderContext(), 0, 0, resultSurface.getAccess()); 4760 4761 if (!verifyImage(resultSurface.getAccess())) 4762 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed"); 4763 else 4764 { 4765 m_testCtx.getLog() 4766 << tcu::TestLog::Message 4767 << "Result ok." 4768 << tcu::TestLog::EndMessage 4769 << tcu::TestLog::ImageSet("Images", "Image verification") 4770 << tcu::TestLog::Image("Result", "Result", resultSurface.getAccess()) 4771 << tcu::TestLog::EndImageSet; 4772 4773 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 4774 } 4775 return STOP; 4776 } 4777 4778 void ViewportCallOrderCase::genVbo (void) 4779 { 4780 const int gridSize = 6; 4781 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 4782 std::vector<tcu::Vec4> data (gridSize * gridSize * 2 * 3); 4783 std::vector<int> cellOrder (gridSize * gridSize * 2); 4784 de::Random rnd (0x55443322); 4785 4786 // generate grid with triangles in random order 4787 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx) 4788 cellOrder[ndx] = ndx; 4789 rnd.shuffle(cellOrder.begin(), cellOrder.end()); 4790 4791 // generate grid filling the right half of the clip space: (x: 0.0, y: -1.0) .. (x: 1.0, y: 1.0) 4792 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx) 4793 { 4794 const int cellNdx = cellOrder[ndx]; 4795 const bool cellSide = ((cellNdx % 2) == 0); 4796 const int cellX = (cellNdx / 2) % gridSize; 4797 const int cellY = (cellNdx / 2) / gridSize; 4798 4799 if (cellSide) 4800 { 4801 data[ndx * 3 + 0] = tcu::Vec4(float(cellX+0) / float(gridSize), (float(cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f); 4802 data[ndx * 3 + 1] = tcu::Vec4(float(cellX+1) / float(gridSize), (float(cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f); 4803 data[ndx * 3 + 2] = tcu::Vec4(float(cellX+0) / float(gridSize), (float(cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f); 4804 } 4805 else 4806 { 4807 data[ndx * 3 + 0] = tcu::Vec4(float(cellX+0) / float(gridSize), (float(cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f); 4808 data[ndx * 3 + 1] = tcu::Vec4(float(cellX+1) / float(gridSize), (float(cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f); 4809 data[ndx * 3 + 2] = tcu::Vec4(float(cellX+1) / float(gridSize), (float(cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f); 4810 } 4811 } 4812 4813 m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext())); 4814 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo); 4815 gl.bufferData(GL_ARRAY_BUFFER, (int)(data.size() * sizeof(tcu::Vec4)), &data[0], GL_STATIC_DRAW); 4816 GLU_EXPECT_NO_ERROR(gl.getError(), "create vbo"); 4817 4818 m_numVertices = (int)data.size(); 4819 } 4820 4821 void ViewportCallOrderCase::genProgram (void) 4822 { 4823 m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), 4824 glu::ProgramSources() 4825 << glu::VertexSource(specializeShader(m_context, genVertexSource().c_str())) 4826 << glu::FragmentSource(specializeShader(m_context, genFragmentSource().c_str())) 4827 << glu::TessellationControlSource(specializeShader(m_context, genTessellationControlSource().c_str())) 4828 << glu::TessellationEvaluationSource(specializeShader(m_context, genTessellationEvaluationSource().c_str())))); 4829 4830 m_testCtx.getLog() 4831 << tcu::TestLog::Section("Program", "Shader program") 4832 << *m_program 4833 << tcu::TestLog::EndSection; 4834 4835 if (!m_program->isOk()) 4836 throw tcu::TestError("shader build failed"); 4837 } 4838 4839 bool ViewportCallOrderCase::verifyImage (const tcu::PixelBufferAccess& result) 4840 { 4841 const tcu::IVec2 insideBorder (deCeilFloatToInt32(0.25f * (float)result.getWidth()) + 1, deFloorFloatToInt32(0.5f * (float)result.getWidth()) - 1); 4842 const tcu::IVec2 outsideBorder (deFloorFloatToInt32(0.25f * (float)result.getWidth()) - 1, deCeilFloatToInt32(0.5f * (float)result.getWidth()) + 1); 4843 tcu::Surface errorMask (result.getWidth(), result.getHeight()); 4844 bool anyError = false; 4845 4846 tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec()); 4847 4848 for (int y = 0; y < result.getHeight(); ++y) 4849 for (int x = 0; x < result.getWidth(); ++x) 4850 { 4851 const tcu::IVec4 pixel = result.getPixelInt(x, y); 4852 const bool insideMeshArea = x >= insideBorder.x() && x <= insideBorder.x(); 4853 const bool outsideMeshArea = x <= outsideBorder.x() && x >= outsideBorder.x(); 4854 4855 // inside mesh, allow green, yellow and any shade between 4856 // outside mesh, allow background (black) only 4857 // in the border area, allow anything 4858 if ((insideMeshArea && (pixel[1] != 255 || pixel[2] != 0)) || 4859 (outsideMeshArea && (pixel[0] != 0 || pixel[1] != 0 || pixel[2] != 0))) 4860 { 4861 errorMask.setPixel(x, y, tcu::RGBA::red()); 4862 anyError = true; 4863 } 4864 } 4865 4866 if (anyError) 4867 { 4868 m_testCtx.getLog() 4869 << tcu::TestLog::Message 4870 << "Image verification failed." 4871 << tcu::TestLog::EndMessage 4872 << tcu::TestLog::ImageSet("Images", "Image verification") 4873 << tcu::TestLog::Image("ResultImage", "Result image", result) 4874 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask) 4875 << tcu::TestLog::EndImageSet; 4876 } 4877 4878 return !anyError; 4879 } 4880 4881 std::string ViewportCallOrderCase::genVertexSource (void) const 4882 { 4883 return s_yellowishPosOnlyVertexSource; 4884 } 4885 4886 std::string ViewportCallOrderCase::genFragmentSource (void) const 4887 { 4888 return s_basicColorFragmentSource; 4889 } 4890 4891 std::string ViewportCallOrderCase::genTessellationControlSource (void) const 4892 { 4893 return "${GLSL_VERSION_DECL}\n" 4894 "${TESSELLATION_SHADER_REQUIRE}\n" 4895 "layout(vertices=3) out;\n" 4896 "in highp vec4 v_vertex_color[];\n" 4897 "out highp vec4 v_tess_eval_color[];\n" 4898 "void main()\n" 4899 "{\n" 4900 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" 4901 " v_tess_eval_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n" 4902 " gl_TessLevelOuter[0] = 2.8;\n" 4903 " gl_TessLevelOuter[1] = 2.8;\n" 4904 " gl_TessLevelOuter[2] = 2.8;\n" 4905 " gl_TessLevelInner[0] = 2.8;\n" 4906 "}\n"; 4907 } 4908 4909 std::string ViewportCallOrderCase::genTessellationEvaluationSource (void) const 4910 { 4911 return s_basicColorTessEvalSource; 4912 } 4913 4914 } // anonymous 4915 4916 PrimitiveBoundingBoxTests::PrimitiveBoundingBoxTests (Context& context) 4917 : TestCaseGroup(context, "primitive_bounding_box", "Tests for EXT_primitive_bounding_box") 4918 { 4919 } 4920 4921 PrimitiveBoundingBoxTests::~PrimitiveBoundingBoxTests (void) 4922 { 4923 } 4924 4925 void PrimitiveBoundingBoxTests::init (void) 4926 { 4927 static const struct 4928 { 4929 const char* name; 4930 const char* description; 4931 deUint32 methodFlags; 4932 } stateSetMethods[] = 4933 { 4934 { 4935 "global_state", 4936 "Set bounding box using PRIMITIVE_BOUNDING_BOX_EXT state", 4937 BBoxRenderCase::FLAG_SET_BBOX_STATE, 4938 }, 4939 { 4940 "tessellation_set_per_draw", 4941 "Set bounding box using gl_BoundingBoxEXT, use same value for all primitives", 4942 BBoxRenderCase::FLAG_SET_BBOX_OUTPUT, 4943 }, 4944 { 4945 "tessellation_set_per_primitive", 4946 "Set bounding box using gl_BoundingBoxEXT, use per-primitive bounding box", 4947 BBoxRenderCase::FLAG_SET_BBOX_OUTPUT | BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX, 4948 }, 4949 }; 4950 static const struct 4951 { 4952 const char* name; 4953 const char* description; 4954 deUint32 stageFlags; 4955 } pipelineConfigs[] = 4956 { 4957 { 4958 "vertex_fragment", 4959 "Render with vertex-fragment program", 4960 0u 4961 }, 4962 { 4963 "vertex_tessellation_fragment", 4964 "Render with vertex-tessellation{ctrl,eval}-fragment program", 4965 BBoxRenderCase::FLAG_TESSELLATION 4966 }, 4967 { 4968 "vertex_geometry_fragment", 4969 "Render with vertex-tessellation{ctrl,eval}-geometry-fragment program", 4970 BBoxRenderCase::FLAG_GEOMETRY 4971 }, 4972 { 4973 "vertex_tessellation_geometry_fragment", 4974 "Render with vertex-geometry-fragment program", 4975 BBoxRenderCase::FLAG_TESSELLATION | BBoxRenderCase::FLAG_GEOMETRY 4976 }, 4977 }; 4978 static const struct 4979 { 4980 const char* name; 4981 const char* description; 4982 deUint32 flags; 4983 deUint32 invalidFlags; 4984 deUint32 requiredFlags; 4985 } usageConfigs[] = 4986 { 4987 { 4988 "default_framebuffer_bbox_equal", 4989 "Render to default framebuffer, set tight bounding box", 4990 BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL, 4991 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX, 4992 0 4993 }, 4994 { 4995 "default_framebuffer_bbox_larger", 4996 "Render to default framebuffer, set padded bounding box", 4997 BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_LARGER, 4998 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX, 4999 0 5000 }, 5001 { 5002 "default_framebuffer_bbox_smaller", 5003 "Render to default framebuffer, set too small bounding box", 5004 BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_SMALLER, 5005 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX, 5006 0 5007 }, 5008 { 5009 "fbo_bbox_equal", 5010 "Render to texture, set tight bounding box", 5011 BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL, 5012 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX, 5013 0 5014 }, 5015 { 5016 "fbo_bbox_larger", 5017 "Render to texture, set padded bounding box", 5018 BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_LARGER, 5019 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX, 5020 0 5021 }, 5022 { 5023 "fbo_bbox_smaller", 5024 "Render to texture, set too small bounding box", 5025 BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_SMALLER, 5026 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX, 5027 0 5028 }, 5029 { 5030 "default_framebuffer", 5031 "Render to default framebuffer, set tight bounding box", 5032 BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL, 5033 0, 5034 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX 5035 }, 5036 { 5037 "fbo", 5038 "Render to texture, set tight bounding box", 5039 BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL, 5040 0, 5041 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX 5042 }, 5043 }; 5044 enum PrimitiveRenderType 5045 { 5046 TYPE_TRIANGLE, 5047 TYPE_LINE, 5048 TYPE_POINT, 5049 }; 5050 const struct 5051 { 5052 const char* name; 5053 const char* description; 5054 PrimitiveRenderType type; 5055 deUint32 flags; 5056 } primitiveTypes[] = 5057 { 5058 { 5059 "triangles", 5060 "Triangle render tests", 5061 TYPE_TRIANGLE, 5062 0 5063 }, 5064 { 5065 "lines", 5066 "Line render tests", 5067 TYPE_LINE, 5068 0 5069 }, 5070 { 5071 "points", 5072 "Point render tests", 5073 TYPE_POINT, 5074 0 5075 }, 5076 { 5077 "wide_lines", 5078 "Wide line render tests", 5079 TYPE_LINE, 5080 LineRenderCase::LINEFLAG_WIDE 5081 }, 5082 { 5083 "wide_points", 5084 "Wide point render tests", 5085 TYPE_POINT, 5086 PointRenderCase::POINTFLAG_WIDE 5087 }, 5088 }; 5089 5090 // .state_query 5091 { 5092 tcu::TestCaseGroup* const stateQueryGroup = new tcu::TestCaseGroup(m_testCtx, "state_query", "State queries"); 5093 addChild(stateQueryGroup); 5094 5095 stateQueryGroup->addChild(new InitialValueCase (m_context, "initial_value", "Initial value case")); 5096 stateQueryGroup->addChild(new QueryCase (m_context, "getfloat", "getFloatv", QueryCase::QUERY_FLOAT)); 5097 stateQueryGroup->addChild(new QueryCase (m_context, "getboolean", "getBooleanv", QueryCase::QUERY_BOOLEAN)); 5098 stateQueryGroup->addChild(new QueryCase (m_context, "getinteger", "getIntegerv", QueryCase::QUERY_INT)); 5099 stateQueryGroup->addChild(new QueryCase (m_context, "getinteger64", "getInteger64v", QueryCase::QUERY_INT64)); 5100 } 5101 5102 // .triangles 5103 // .(wide_)lines 5104 // .(wide_)points 5105 for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); ++primitiveTypeNdx) 5106 { 5107 tcu::TestCaseGroup* const primitiveGroup = new tcu::TestCaseGroup(m_testCtx, primitiveTypes[primitiveTypeNdx].name, primitiveTypes[primitiveTypeNdx].description); 5108 addChild(primitiveGroup); 5109 5110 for (int stateSetMethodNdx = 0; stateSetMethodNdx < DE_LENGTH_OF_ARRAY(stateSetMethods); ++stateSetMethodNdx) 5111 { 5112 tcu::TestCaseGroup* const methodGroup = new tcu::TestCaseGroup(m_testCtx, stateSetMethods[stateSetMethodNdx].name, stateSetMethods[stateSetMethodNdx].description); 5113 primitiveGroup->addChild(methodGroup); 5114 5115 for (int pipelineConfigNdx = 0; pipelineConfigNdx < DE_LENGTH_OF_ARRAY(pipelineConfigs); ++pipelineConfigNdx) 5116 { 5117 if ((stateSetMethods[stateSetMethodNdx].methodFlags & BBoxRenderCase::FLAG_SET_BBOX_OUTPUT) != 0 && 5118 (pipelineConfigs[pipelineConfigNdx].stageFlags & BBoxRenderCase::FLAG_TESSELLATION) == 0) 5119 { 5120 // invalid config combination 5121 } 5122 else 5123 { 5124 tcu::TestCaseGroup* const pipelineGroup = new tcu::TestCaseGroup(m_testCtx, pipelineConfigs[pipelineConfigNdx].name, pipelineConfigs[pipelineConfigNdx].description); 5125 methodGroup->addChild(pipelineGroup); 5126 5127 for (int usageNdx = 0; usageNdx < DE_LENGTH_OF_ARRAY(usageConfigs); ++usageNdx) 5128 { 5129 const deUint32 flags = primitiveTypes[primitiveTypeNdx].flags | 5130 stateSetMethods[stateSetMethodNdx].methodFlags | 5131 pipelineConfigs[pipelineConfigNdx].stageFlags | 5132 usageConfigs[usageNdx].flags; 5133 5134 if (usageConfigs[usageNdx].invalidFlags && (flags & usageConfigs[usageNdx].invalidFlags) != 0) 5135 continue; 5136 if (usageConfigs[usageNdx].requiredFlags && (flags & usageConfigs[usageNdx].requiredFlags) == 0) 5137 continue; 5138 5139 switch (primitiveTypes[primitiveTypeNdx].type) 5140 { 5141 case TYPE_TRIANGLE: 5142 pipelineGroup->addChild(new GridRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags)); 5143 break; 5144 case TYPE_LINE: 5145 pipelineGroup->addChild(new LineRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags)); 5146 break; 5147 case TYPE_POINT: 5148 pipelineGroup->addChild(new PointRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags)); 5149 break; 5150 default: 5151 DE_ASSERT(false); 5152 } 5153 } 5154 } 5155 } 5156 } 5157 } 5158 5159 // .depth 5160 { 5161 static const struct 5162 { 5163 const char* name; 5164 const char* description; 5165 DepthDrawCase::DepthType depthMethod; 5166 } depthMethods[] = 5167 { 5168 { 5169 "builtin_depth", 5170 "Fragment depth not modified in fragment shader", 5171 DepthDrawCase::DEPTH_BUILTIN 5172 }, 5173 { 5174 "user_defined_depth", 5175 "Fragment depth is defined in the fragment shader", 5176 DepthDrawCase::DEPTH_USER_DEFINED 5177 }, 5178 }; 5179 static const struct 5180 { 5181 const char* name; 5182 const char* description; 5183 DepthDrawCase::BBoxState bboxState; 5184 DepthDrawCase::BBoxSize bboxSize; 5185 } depthCases[] = 5186 { 5187 { 5188 "global_state_bbox_equal", 5189 "Test tight bounding box with global bbox state", 5190 DepthDrawCase::STATE_GLOBAL, 5191 DepthDrawCase::BBOX_EQUAL, 5192 }, 5193 { 5194 "global_state_bbox_larger", 5195 "Test padded bounding box with global bbox state", 5196 DepthDrawCase::STATE_GLOBAL, 5197 DepthDrawCase::BBOX_LARGER, 5198 }, 5199 { 5200 "per_primitive_bbox_equal", 5201 "Test tight bounding box with tessellation output bbox", 5202 DepthDrawCase::STATE_PER_PRIMITIVE, 5203 DepthDrawCase::BBOX_EQUAL, 5204 }, 5205 { 5206 "per_primitive_bbox_larger", 5207 "Test padded bounding box with tessellation output bbox", 5208 DepthDrawCase::STATE_PER_PRIMITIVE, 5209 DepthDrawCase::BBOX_LARGER, 5210 }, 5211 }; 5212 5213 tcu::TestCaseGroup* const depthGroup = new tcu::TestCaseGroup(m_testCtx, "depth", "Test bounding box depth component"); 5214 addChild(depthGroup); 5215 5216 // .builtin_depth 5217 // .user_defined_depth 5218 for (int depthNdx = 0; depthNdx < DE_LENGTH_OF_ARRAY(depthMethods); ++depthNdx) 5219 { 5220 tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, depthMethods[depthNdx].name, depthMethods[depthNdx].description); 5221 depthGroup->addChild(group); 5222 5223 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(depthCases); ++caseNdx) 5224 group->addChild(new DepthDrawCase(m_context, depthCases[caseNdx].name, depthCases[caseNdx].description, depthMethods[depthNdx].depthMethod, depthCases[caseNdx].bboxState, depthCases[caseNdx].bboxSize)); 5225 } 5226 } 5227 5228 // .blit_fbo 5229 { 5230 tcu::TestCaseGroup* const blitFboGroup = new tcu::TestCaseGroup(m_testCtx, "blit_fbo", "Test bounding box does not affect blitting"); 5231 addChild(blitFboGroup); 5232 5233 blitFboGroup->addChild(new BlitFboCase(m_context, "blit_default_to_fbo", "Blit from default fb to fbo", BlitFboCase::TARGET_DEFAULT, BlitFboCase::TARGET_FBO)); 5234 blitFboGroup->addChild(new BlitFboCase(m_context, "blit_fbo_to_default", "Blit from fbo to default fb", BlitFboCase::TARGET_FBO, BlitFboCase::TARGET_DEFAULT)); 5235 blitFboGroup->addChild(new BlitFboCase(m_context, "blit_fbo_to_fbo", "Blit from fbo to fbo", BlitFboCase::TARGET_FBO, BlitFboCase::TARGET_FBO)); 5236 } 5237 5238 // .clear 5239 { 5240 tcu::TestCaseGroup* const clearGroup = new tcu::TestCaseGroup(m_testCtx, "clear", "Test bounding box does not clears"); 5241 addChild(clearGroup); 5242 5243 clearGroup->addChild(new ClearCase(m_context, "full_clear", "Do full clears", 0)); 5244 clearGroup->addChild(new ClearCase(m_context, "full_clear_with_triangles", "Do full clears and render some geometry", ClearCase::DRAW_TRIANGLE_BIT)); 5245 clearGroup->addChild(new ClearCase(m_context, "full_clear_with_triangles_per_primitive_bbox", "Do full clears and render some geometry", ClearCase::DRAW_TRIANGLE_BIT | ClearCase::PER_PRIMITIVE_BBOX_BIT)); 5246 clearGroup->addChild(new ClearCase(m_context, "scissored_clear", "Do scissored clears", ClearCase::SCISSOR_CLEAR_BIT)); 5247 clearGroup->addChild(new ClearCase(m_context, "scissored_clear_with_triangles", "Do scissored clears and render some geometry", ClearCase::SCISSOR_CLEAR_BIT | ClearCase::DRAW_TRIANGLE_BIT)); 5248 clearGroup->addChild(new ClearCase(m_context, "scissored_clear_with_triangles_per_primitive_bbox", "Do scissored clears and render some geometry", ClearCase::SCISSOR_CLEAR_BIT | ClearCase::DRAW_TRIANGLE_BIT | ClearCase::PER_PRIMITIVE_BBOX_BIT)); 5249 clearGroup->addChild(new ClearCase(m_context, "scissored_full_clear", "Do full clears with enabled scissor", ClearCase::FULLSCREEN_SCISSOR_BIT | ClearCase::SCISSOR_CLEAR_BIT)); 5250 clearGroup->addChild(new ClearCase(m_context, "scissored_full_clear_with_triangles", "Do full clears with enabled scissor and render some geometry", ClearCase::FULLSCREEN_SCISSOR_BIT | ClearCase::SCISSOR_CLEAR_BIT | ClearCase::DRAW_TRIANGLE_BIT)); 5251 clearGroup->addChild(new ClearCase(m_context, "scissored_full_clear_with_triangles_per_primitive_bbox", "Do full clears with enabled scissor and render some geometry", ClearCase::FULLSCREEN_SCISSOR_BIT | ClearCase::SCISSOR_CLEAR_BIT | ClearCase::DRAW_TRIANGLE_BIT | ClearCase::PER_PRIMITIVE_BBOX_BIT)); 5252 } 5253 5254 // .call_order (Khronos bug #13262) 5255 { 5256 tcu::TestCaseGroup* const callOrderGroup = new tcu::TestCaseGroup(m_testCtx, "call_order", "Test viewport and bounding box calls have no effect"); 5257 addChild(callOrderGroup); 5258 5259 callOrderGroup->addChild(new ViewportCallOrderCase(m_context, "viewport_first_bbox_second", "Set up viewport first and bbox after", ViewportCallOrderCase::VIEWPORT_FIRST)); 5260 callOrderGroup->addChild(new ViewportCallOrderCase(m_context, "bbox_first_viewport_second", "Set up bbox first and viewport after", ViewportCallOrderCase::BBOX_FIRST)); 5261 } 5262 } 5263 5264 } // Functional 5265 } // gles31 5266 } // deqp 5267