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