es31fPrimitiveBoundingBoxTests.cpp revision 30fde0c3faee7af43a958bd572d8b6c584333a15
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 49namespace deqp 50{ 51namespace gles31 52{ 53namespace Functional 54{ 55namespace 56{ 57 58namespace StateQueryUtil = ::deqp::gls::StateQueryUtil; 59 60struct 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 73float& 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 82const float& BoundingBox::getComponentAccess (int ndx) const 83{ 84 return const_cast<BoundingBox*>(this)->getComponentAccess(ndx); 85} 86 87struct ProjectedBBox 88{ 89 tcu::Vec3 min; 90 tcu::Vec3 max; 91}; 92 93static 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 106static 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) * viewportSize.x(); 112 vertexBox.y() = (bbox.min.y() * 0.5f + 0.5f) * viewportSize.y(); 113 vertexBox.z() = (bbox.max.x() * 0.5f + 0.5f) * viewportSize.x(); 114 vertexBox.w() = (bbox.max.y() * 0.5f + 0.5f) * 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 124class InitialValueCase : public TestCase 125{ 126public: 127 InitialValueCase (Context& context, const char* name, const char* desc); 128 129 void init (void); 130 IterateResult iterate (void); 131}; 132 133InitialValueCase::InitialValueCase (Context& context, const char* name, const char* desc) 134 : TestCase(context, name, desc) 135{ 136} 137 138void 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 144InitialValueCase::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 183class QueryCase : public TestCase 184{ 185public: 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 198private: 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 207QueryCase::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 214void 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 220QueryCase::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.glPrimitiveBoundingBoxEXT(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 267bool 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 453class BBoxRenderCase : public TestCase 454{ 455public: 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 478protected: 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 563private: 564 std::vector<IterationConfig> m_iterationConfigs; 565 int m_iteration; 566}; 567 568BBoxRenderCase::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 600BBoxRenderCase::~BBoxRenderCase (void) 601{ 602 deinit(); 603} 604 605void 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 700void BBoxRenderCase::deinit (void) 701{ 702 m_program.clear(); 703 m_vbo.clear(); 704 m_fbo.clear(); 705} 706 707BBoxRenderCase::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 727BBoxRenderCase::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 772tcu::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) * viewportSize.x(); 779 vertexBox.y() = (patternPos.y() * 0.5f + 0.5f) * viewportSize.y(); 780 vertexBox.z() = ((patternPos.x() + patternSize.x()) * 0.5f + 0.5f) * viewportSize.x(); 781 vertexBox.w() = ((patternPos.y() + patternSize.y()) * 0.5f + 0.5f) * 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 801void 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.primitiveBoundingBoxEXT(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.primitiveBoundingBoxEXT(-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 869const 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 918class GridRenderCase : public BBoxRenderCase 919{ 920public: 921 GridRenderCase (Context& context, const char* name, const char* description, deUint32 flags); 922 ~GridRenderCase (void); 923 924private: 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 941GridRenderCase::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 947GridRenderCase::~GridRenderCase (void) 948{ 949} 950 951void 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 963std::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 1010std::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 1034std::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 1115std::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 1152std::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 1208GridRenderCase::IterationConfig GridRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const 1209{ 1210 return generateRandomConfig(0xDEDEDEu * (deUint32)iteration, renderTargetSize); 1211} 1212 1213void 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((cellX+0) / float(m_gridSize), (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((cellX+1) / float(m_gridSize), (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((cellX+0) / float(m_gridSize), (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((cellX+0) / float(m_gridSize), (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((cellX+1) / float(m_gridSize), (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((cellX+1) / float(m_gridSize), (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 1248void 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 1274void 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 1374class LineRenderCase : public BBoxRenderCase 1375{ 1376public: 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 1385private: 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 void init (void); 1402 1403 std::string genVertexSource (void) const; 1404 std::string genFragmentSource (void) const; 1405 std::string genTessellationControlSource (void) const; 1406 std::string genTessellationEvaluationSource (void) const; 1407 std::string genGeometrySource (void) const; 1408 1409 IterationConfig generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const; 1410 void getAttributeData (std::vector<tcu::Vec4>& data) const; 1411 void renderTestPattern (const IterationConfig& config); 1412 void verifyRenderResult (const IterationConfig& config); 1413 1414 tcu::IVec2 getNumberOfLinesRange (int queryAreaBegin, int queryAreaEnd, float patternStart, float patternSize, int viewportArea, QueryDirection queryDir) const; 1415 bool scanRow (const tcu::ConstPixelBufferAccess& access, int row, int rowBegin, int rowEnd, const tcu::IVec2& numLines, int& floodCounter) const; 1416 bool scanColumn (const tcu::ConstPixelBufferAccess& access, int column, int columnBegin, int columnEnd, const tcu::IVec2& numLines, int& floodCounter) const; 1417 bool checkAreaNumLines (const tcu::ConstPixelBufferAccess& access, const tcu::IVec4& area, int& floodCounter, int componentNdx, const tcu::IVec2& numLines) const; 1418 tcu::IVec2 getNumMinimaMaxima (const tcu::ConstPixelBufferAccess& access, int componentNdx) const; 1419 bool checkLineWidths (const tcu::ConstPixelBufferAccess& access, const tcu::IVec2& begin, const tcu::IVec2& end, int componentNdx, int& floodCounter) const; 1420 void printLineWidthError (const tcu::IVec2& pos, int detectedLineWidth, const tcu::IVec2& lineWidthRange, bool isHorizontal, int& floodCounter) const; 1421 1422 const int m_patternSide; 1423 const bool m_isWideLineCase; 1424 const int m_wideLineLineWidth; 1425}; 1426 1427LineRenderCase::LineRenderCase (Context& context, const char* name, const char* description, deUint32 flags) 1428 : BBoxRenderCase (context, name, description, 12, flags) 1429 , m_patternSide (12) 1430 , m_isWideLineCase ((flags & LINEFLAG_WIDE) != 0) 1431 , m_wideLineLineWidth (5) 1432{ 1433} 1434 1435LineRenderCase::~LineRenderCase (void) 1436{ 1437} 1438 1439void LineRenderCase::init (void) 1440{ 1441 m_testCtx.getLog() 1442 << tcu::TestLog::Message 1443 << "Rendering line pattern to " << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n" 1444 << "Vertical lines are green, horizontal lines blue. Using additive blending.\n" 1445 << "Line segments are in random order, varying pattern size and location for each iteration.\n" 1446 << "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated red channel." 1447 << tcu::TestLog::EndMessage; 1448 1449 if (m_isWideLineCase) 1450 { 1451 glw::GLfloat lineWidthRange[2] = {0.0f, 0.0f}; 1452 m_context.getRenderContext().getFunctions().getFloatv(GL_ALIASED_LINE_WIDTH_RANGE, lineWidthRange); 1453 1454 if (lineWidthRange[1] < (float)m_wideLineLineWidth) 1455 throw tcu::NotSupportedError("Test requires line width " + de::toString(m_wideLineLineWidth)); 1456 } 1457 1458 BBoxRenderCase::init(); 1459} 1460 1461std::string LineRenderCase::genVertexSource (void) const 1462{ 1463 std::ostringstream buf; 1464 1465 buf << "#version 310 es\n" 1466 "in highp vec4 a_position;\n" 1467 "in highp vec4 a_color;\n" 1468 "out highp vec4 vtx_color;\n" 1469 "uniform highp vec4 u_posScale;\n" 1470 "uniform highp float u_lineWidth;\n" 1471 "\n"; 1472 if (!m_hasTessellationStage) 1473 { 1474 DE_ASSERT(m_useGlobalState); 1475 buf << "uniform highp vec4 u_primitiveBBoxMin;\n" 1476 "uniform highp vec4 u_primitiveBBoxMax;\n" 1477 "\n" 1478 "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n" 1479 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n" 1480 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n" 1481 "\n"; 1482 } 1483 buf << "void main()\n" 1484 "{\n" 1485 " highp vec2 patternOffset = u_posScale.xy;\n" 1486 " highp vec2 patternScale = u_posScale.zw;\n" 1487 " gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n" 1488 " vtx_color = a_color;\n"; 1489 if (!m_hasTessellationStage) 1490 { 1491 DE_ASSERT(m_useGlobalState); 1492 buf << "\n" 1493 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = u_lineWidth;\n" 1494 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin =\n" 1495 " min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMin.w,\n" 1496 " vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMax.w);\n" 1497 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax =\n" 1498 " min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMin.w,\n" 1499 " vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMax.w);\n"; 1500 } 1501 buf << "}\n"; 1502 1503 return buf.str(); 1504} 1505 1506std::string LineRenderCase::genFragmentSource (void) const 1507{ 1508 const char* const colorInputName = (m_hasGeometryStage) ? ("geo_color") : (m_hasTessellationStage) ? ("tess_color") : ("vtx_color"); 1509 std::ostringstream buf; 1510 1511 buf << "#version 310 es\n" 1512 "in mediump vec4 " << colorInputName << ";\n" 1513 "layout(location = 0) out mediump vec4 o_color;\n" 1514 << genShaderFunction(SHADER_FUNC_INSIDE_BBOX) 1515 << "\n" 1516 "void main()\n" 1517 "{\n" 1518 " mediump vec4 baseColor = " << colorInputName << ";\n" 1519 " mediump float redChannel;\n" 1520 " if (fragmentInsideTheBBox(gl_FragCoord.z))\n" 1521 " redChannel = 0.0;\n" 1522 " else\n" 1523 " redChannel = 1.0;\n" 1524 " o_color = vec4(redChannel, baseColor.g, baseColor.b, baseColor.a);\n" 1525 "}\n"; 1526 1527 return buf.str(); 1528} 1529 1530std::string LineRenderCase::genTessellationControlSource (void) const 1531{ 1532 std::ostringstream buf; 1533 1534 buf << "#version 310 es\n" 1535 "#extension GL_EXT_tessellation_shader : require\n" 1536 "#extension GL_EXT_primitive_bounding_box : require\n" 1537 "layout(vertices=2) out;" 1538 "\n" 1539 "in highp vec4 vtx_color[];\n" 1540 "out highp vec4 tess_ctrl_color[];\n" 1541 "uniform highp float u_tessellationLevel;\n" 1542 "uniform highp vec4 u_posScale;\n" 1543 "uniform highp float u_lineWidth;\n"; 1544 1545 if (!m_calcPerPrimitiveBBox) 1546 { 1547 buf << "uniform highp vec4 u_primitiveBBoxMin;\n" 1548 "uniform highp vec4 u_primitiveBBoxMax;\n"; 1549 } 1550 1551 buf << "patch out highp float vp_bbox_expansionSize;\n" 1552 "patch out highp vec3 vp_bbox_clipMin;\n" 1553 "patch out highp vec3 vp_bbox_clipMax;\n"; 1554 1555 if (m_calcPerPrimitiveBBox) 1556 { 1557 buf << "\n"; 1558 if (m_hasGeometryStage) 1559 buf << genShaderFunction(SHADER_FUNC_MIRROR_X); 1560 buf << genShaderFunction(SHADER_FUNC_MIRROR_Y); 1561 1562 buf << "vec4 transformVec(in highp vec4 p)\n" 1563 "{\n" 1564 " return " << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)")) << ";\n" 1565 "}\n"; 1566 } 1567 1568 buf << "\n" 1569 "void main()\n" 1570 "{\n" 1571 " // convert to nonsensical coordinates, just in case\n" 1572 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n" 1573 " tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n" 1574 "\n" 1575 " gl_TessLevelOuter[0] = 0.8; // will be rounded up to 1\n" 1576 " gl_TessLevelOuter[1] = u_tessellationLevel;\n"; 1577 1578 if (m_calcPerPrimitiveBBox) 1579 { 1580 buf << "\n" 1581 " highp vec4 bboxMin = min(transformVec(gl_in[0].gl_Position),\n" 1582 " transformVec(gl_in[1].gl_Position));\n" 1583 " highp vec4 bboxMax = max(transformVec(gl_in[0].gl_Position),\n" 1584 " transformVec(gl_in[1].gl_Position));\n"; 1585 } 1586 else 1587 { 1588 buf << "\n" 1589 " highp vec4 bboxMin = u_primitiveBBoxMin;\n" 1590 " highp vec4 bboxMax = u_primitiveBBoxMax;\n"; 1591 } 1592 1593 if (!m_useGlobalState) 1594 buf << "\n" 1595 " gl_BoundingBoxEXT[0] = bboxMin;\n" 1596 " gl_BoundingBoxEXT[1] = bboxMax;\n"; 1597 1598 buf << " vp_bbox_expansionSize = u_lineWidth;\n" 1599 " vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n" 1600 " vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n" 1601 " vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n" 1602 " vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n" 1603 "}\n"; 1604 1605 return buf.str(); 1606} 1607 1608std::string LineRenderCase::genTessellationEvaluationSource (void) const 1609{ 1610 std::ostringstream buf; 1611 1612 buf << "#version 310 es\n" 1613 "#extension GL_EXT_tessellation_shader : require\n" 1614 "layout(isolines) in;" 1615 "\n" 1616 "in highp vec4 tess_ctrl_color[];\n" 1617 "out highp vec4 tess_color;\n" 1618 "uniform highp vec4 u_posScale;\n" 1619 "\n" 1620 "patch in highp float vp_bbox_expansionSize;\n" 1621 "patch in highp vec3 vp_bbox_clipMin;\n" 1622 "patch in highp vec3 vp_bbox_clipMax;\n" 1623 "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n" 1624 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n" 1625 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n" 1626 << genShaderFunction(SHADER_FUNC_MIRROR_Y) 1627 << "void main()\n" 1628 "{\n" 1629 " // non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n" 1630 " gl_Position = mirrorY(mix(gl_in[0].gl_Position.zwyx, gl_in[1].gl_Position.zwyx, gl_TessCoord.x));\n" 1631 " tess_color = tess_ctrl_color[0];\n" 1632 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = vp_bbox_expansionSize;\n" 1633 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin = vp_bbox_clipMin;\n" 1634 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax = vp_bbox_clipMax;\n" 1635 "}\n"; 1636 1637 return buf.str(); 1638} 1639 1640std::string LineRenderCase::genGeometrySource (void) const 1641{ 1642 const char* const colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color"); 1643 std::ostringstream buf; 1644 1645 buf << "#version 310 es\n" 1646 "#extension GL_EXT_geometry_shader : require\n" 1647 "layout(lines) in;\n" 1648 "layout(max_vertices=5, line_strip) out;\n" 1649 "\n" 1650 "in highp vec4 " << colorInputName << "[2];\n" 1651 "out highp vec4 geo_color;\n" 1652 "uniform highp vec4 u_posScale;\n" 1653 "\n" 1654 "\n" 1655 "flat in highp float v_geo_bbox_expansionSize[2];\n" 1656 "flat in highp vec3 v_geo_bbox_clipMin[2];\n" 1657 "flat in highp vec3 v_geo_bbox_clipMax[2];\n" 1658 "flat out highp vec3 v_bbox_clipMin;\n" 1659 "flat out highp vec3 v_bbox_clipMax;\n" 1660 "flat out highp float v_bbox_expansionSize;\n" 1661 << genShaderFunction(SHADER_FUNC_MIRROR_X) 1662 << "\n" 1663 "void setVisualizationVaryings()\n" 1664 "{\n" 1665 " v_bbox_expansionSize = v_geo_bbox_expansionSize[0];\n" 1666 " v_bbox_clipMin = v_geo_bbox_clipMin[0];\n" 1667 " v_bbox_clipMax = v_geo_bbox_clipMax[0];\n" 1668 "}\n" 1669 "void main()\n" 1670 "{\n" 1671 " // Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n" 1672 " highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n" 1673 " highp vec4 p1 = mirrorX(gl_in[1].gl_Position);\n" 1674 " highp vec4 lineColor = " << colorInputName << "[0];\n" 1675 "\n" 1676 " // output two separate primitives, just in case\n" 1677 " gl_Position = mix(p0, p1, 0.00); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n" 1678 " gl_Position = mix(p0, p1, 0.33); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n" 1679 " EndPrimitive();\n" 1680 "\n" 1681 " gl_Position = mix(p0, p1, 0.33); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n" 1682 " gl_Position = mix(p0, p1, 0.67); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n" 1683 " gl_Position = mix(p0, p1, 1.00); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n" 1684 " EndPrimitive();\n" 1685 "}\n"; 1686 1687 return buf.str(); 1688} 1689 1690LineRenderCase::IterationConfig LineRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const 1691{ 1692 const int numMaxAttempts = 128; 1693 1694 // Avoid too narrow viewports, lines could merge together. Require viewport is at least 2.5x the size of the line bodies. 1695 for (int attemptNdx = 0; attemptNdx < numMaxAttempts; ++attemptNdx) 1696 { 1697 const IterationConfig& config = generateRandomConfig((0xDEDEDEu * (deUint32)iteration) ^ (0xABAB13 * attemptNdx), renderTargetSize); 1698 1699 if (config.viewportSize.x() * (config.patternSize.x() * 0.5f) > 2.5f * m_patternSide * m_wideLineLineWidth && 1700 config.viewportSize.y() * (config.patternSize.y() * 0.5f) > 2.5f * m_patternSide * m_wideLineLineWidth) 1701 { 1702 return config; 1703 } 1704 } 1705 1706 DE_ASSERT(false); 1707 return IterationConfig(); 1708} 1709 1710void LineRenderCase::getAttributeData (std::vector<tcu::Vec4>& data) const 1711{ 1712 const tcu::Vec4 green (0.0f, 1.0f, 0.0f, 1.0f); 1713 const tcu::Vec4 blue (0.0f, 0.0f, 1.0f, 1.0f); 1714 std::vector<int> cellOrder (m_patternSide * m_patternSide * 2); 1715 de::Random rnd (0xDE12345); 1716 1717 // generate crosshatch pattern with segments in random order 1718 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx) 1719 cellOrder[ndx] = ndx; 1720 rnd.shuffle(cellOrder.begin(), cellOrder.end()); 1721 1722 data.resize(cellOrder.size() * 4); 1723 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx) 1724 { 1725 const int segmentID = cellOrder[ndx]; 1726 const int direction = segmentID & 0x01; 1727 const int majorCoord = (segmentID >> 1) / m_patternSide; 1728 const int minorCoord = (segmentID >> 1) % m_patternSide; 1729 1730 if (direction) 1731 { 1732 data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(minorCoord / float(m_patternSide), majorCoord / float(m_patternSide), 0.0f, 1.0f); 1733 data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green; 1734 data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(minorCoord / float(m_patternSide), (majorCoord + 1) / float(m_patternSide), 0.0f, 1.0f); 1735 data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green; 1736 } 1737 else 1738 { 1739 data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(majorCoord / float(m_patternSide), minorCoord / float(m_patternSide), 0.0f, 1.0f); 1740 data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue; 1741 data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4((majorCoord + 1) / float(m_patternSide), minorCoord / float(m_patternSide), 0.0f, 1.0f); 1742 data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue; 1743 } 1744 } 1745} 1746 1747void LineRenderCase::renderTestPattern (const IterationConfig& config) 1748{ 1749 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 1750 1751 setupRender(config); 1752 1753 if (m_hasTessellationStage) 1754 { 1755 const glw::GLint tessLevelPos = gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel"); 1756 const glw::GLfloat tessLevel = 2.8f; // will be rounded up 1757 1758 TCU_CHECK(tessLevelPos != -1); 1759 1760 m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel << tcu::TestLog::EndMessage; 1761 1762 gl.uniform1f(tessLevelPos, tessLevel); 1763 gl.patchParameteri(GL_PATCH_VERTICES, 2); 1764 GLU_EXPECT_NO_ERROR(gl.getError(), "patch param"); 1765 } 1766 1767 if (m_isWideLineCase) 1768 gl.lineWidth((float)m_wideLineLineWidth); 1769 1770 gl.uniform1f(gl.getUniformLocation(m_program->getProgram(), "u_lineWidth"), (m_isWideLineCase) ? (m_wideLineLineWidth) : (1.0f)); 1771 1772 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering pattern." << tcu::TestLog::EndMessage; 1773 1774 gl.enable(GL_BLEND); 1775 gl.blendFunc(GL_ONE, GL_ONE); 1776 gl.blendEquation(GL_FUNC_ADD); 1777 1778 gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_LINES), 0, m_patternSide * m_patternSide * 2 * 2); 1779 GLU_EXPECT_NO_ERROR(gl.getError(), "draw"); 1780} 1781 1782void LineRenderCase::verifyRenderResult (const IterationConfig& config) 1783{ 1784 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 1785 const ProjectedBBox projectedBBox = projectBoundingBox(config.bbox); 1786 const float lineWidth = (m_isWideLineCase) ? (m_wideLineLineWidth) : (1.0f); 1787 const tcu::IVec4 viewportBBoxArea = getViewportBoundingBoxArea(projectedBBox, config.viewportSize, lineWidth); 1788 const tcu::IVec4 viewportPatternArea = getViewportPatternArea(config.patternPos, config.patternSize, config.viewportSize, ROUND_INWARDS); 1789 const tcu::IVec2 expectedHorizontalLines = getNumberOfLinesRange(viewportBBoxArea.y(), viewportBBoxArea.w(), config.patternPos.y(), config.patternSize.y(), config.viewportSize.y(), DIRECTION_VERTICAL); 1790 const tcu::IVec2 expectedVerticalLines = getNumberOfLinesRange(viewportBBoxArea.x(), viewportBBoxArea.z(), config.patternPos.x(), config.patternSize.x(), config.viewportSize.x(), DIRECTION_HORIZONTAL); 1791 const tcu::IVec4 verificationArea = tcu::IVec4(de::max(viewportBBoxArea.x(), 0), 1792 de::max(viewportBBoxArea.y(), 0), 1793 de::min(viewportBBoxArea.z(), config.viewportSize.x()), 1794 de::min(viewportBBoxArea.w(), config.viewportSize.y())); 1795 1796 tcu::Surface viewportSurface (config.viewportSize.x(), config.viewportSize.y()); 1797 bool anyError = false; 1798 int messageLimitCounter = 8; 1799 1800 if (!m_calcPerPrimitiveBBox) 1801 m_testCtx.getLog() 1802 << tcu::TestLog::Message 1803 << "Projected bounding box: (clip space)\n" 1804 << "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n" 1805 << "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n" 1806 << "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n" 1807 << "In viewport coordinates:\n" 1808 << "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n" 1809 << "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n" 1810 << "Verifying render results within the bounding box:\n" 1811 << tcu::TestLog::EndMessage; 1812 else 1813 m_testCtx.getLog() 1814 << tcu::TestLog::Message 1815 << "Verifying render result:" 1816 << tcu::TestLog::EndMessage; 1817 1818 m_testCtx.getLog() 1819 << tcu::TestLog::Message 1820 << "\tCalculating number of horizontal and vertical lines within the bounding box, expecting:\n" 1821 << "\t[" << expectedHorizontalLines.x() << ", " << expectedHorizontalLines.y() << "] horizontal lines.\n" 1822 << "\t[" << expectedVerticalLines.x() << ", " << expectedVerticalLines.y() << "] vertical lines.\n" 1823 << tcu::TestLog::EndMessage; 1824 1825 if (m_fbo) 1826 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo); 1827 glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(), viewportSurface.getAccess()); 1828 1829 // scan rows 1830 for (int y = de::max(verificationArea.y(), viewportPatternArea.y()); y < de::min(verificationArea.w(), viewportPatternArea.w()); ++y) 1831 anyError |= !scanRow(viewportSurface.getAccess(), 1832 y, 1833 verificationArea.x(), 1834 verificationArea.z(), 1835 expectedVerticalLines, 1836 messageLimitCounter); 1837 1838 // scan columns 1839 for (int x = de::max(verificationArea.x(), viewportPatternArea.x()); x < de::min(verificationArea.z(), viewportPatternArea.z()); ++x) 1840 anyError |= !scanColumn(viewportSurface.getAccess(), 1841 x, 1842 verificationArea.y(), 1843 verificationArea.w(), 1844 expectedHorizontalLines, 1845 messageLimitCounter); 1846 1847 if (anyError) 1848 { 1849 if (messageLimitCounter < 0) 1850 m_testCtx.getLog() << tcu::TestLog::Message << "Omitted " << (-messageLimitCounter) << " row/column error descriptions." << tcu::TestLog::EndMessage; 1851 1852 m_testCtx.getLog() 1853 << tcu::TestLog::Message 1854 << "Image verification failed." 1855 << tcu::TestLog::EndMessage 1856 << tcu::TestLog::ImageSet("Images", "Image verification") 1857 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess()) 1858 << tcu::TestLog::EndImageSet; 1859 1860 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed"); 1861 } 1862 else 1863 { 1864 m_testCtx.getLog() 1865 << tcu::TestLog::Message 1866 << "Result image ok." 1867 << tcu::TestLog::EndMessage 1868 << tcu::TestLog::ImageSet("Images", "Image verification") 1869 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess()) 1870 << tcu::TestLog::EndImageSet; 1871 } 1872} 1873 1874tcu::IVec2 LineRenderCase::getNumberOfLinesRange (int queryAreaBegin, int queryAreaEnd, float patternStart, float patternSize, int viewportArea, QueryDirection queryDir) const 1875{ 1876 // pattern is not symmetric due to mirroring 1877 const int patternStartNdx = (queryDir == DIRECTION_HORIZONTAL) ? ((m_hasGeometryStage) ? (1) : (0)) : ((m_hasTessellationStage) ? (1) : (0)); 1878 const int patternEndNdx = patternStartNdx + m_patternSide; 1879 1880 int numLinesMin = 0; 1881 int numLinesMax = 0; 1882 1883 for (int lineNdx = patternStartNdx; lineNdx < patternEndNdx; ++lineNdx) 1884 { 1885 const float linePos = (patternStart + (lineNdx / float(m_patternSide)) * patternSize) * 0.5f + 0.5f; 1886 const float lineWidth = (m_isWideLineCase) ? (m_wideLineLineWidth) : (1.0f); 1887 1888 if (linePos * viewportArea > queryAreaBegin + 1.0f && 1889 linePos * viewportArea < queryAreaEnd - 1.0f) 1890 { 1891 // line center is within the area 1892 ++numLinesMin; 1893 ++numLinesMax; 1894 } 1895 else if (linePos * viewportArea > queryAreaBegin - lineWidth*0.5f - 1.0f && 1896 linePos * viewportArea < queryAreaEnd + lineWidth*0.5f + 1.0f) 1897 { 1898 // line could leak into area 1899 ++numLinesMax; 1900 } 1901 } 1902 1903 return tcu::IVec2(numLinesMin, numLinesMax); 1904} 1905 1906bool LineRenderCase::scanRow (const tcu::ConstPixelBufferAccess& access, int row, int rowBegin, int rowEnd, const tcu::IVec2& numLines, int& messageLimitCounter) const 1907{ 1908 const bool numLinesOk = checkAreaNumLines(access, tcu::IVec4(rowBegin, row, rowEnd - rowBegin, 1), messageLimitCounter, SCAN_ROW_COMPONENT_NDX, numLines); 1909 const bool lineWidthOk = checkLineWidths(access, tcu::IVec2(rowBegin, row), tcu::IVec2(rowEnd, row), SCAN_ROW_COMPONENT_NDX, messageLimitCounter); 1910 return numLinesOk && lineWidthOk; 1911} 1912 1913bool LineRenderCase::scanColumn (const tcu::ConstPixelBufferAccess& access, int column, int columnBegin, int columnEnd, const tcu::IVec2& numLines, int& messageLimitCounter) const 1914{ 1915 const bool numLinesOk = checkAreaNumLines(access, tcu::IVec4(column, columnBegin, 1, columnEnd - columnBegin), messageLimitCounter, SCAN_COL_COMPONENT_NDX, numLines); 1916 const bool lineWidthOk = checkLineWidths(access, tcu::IVec2(column, columnBegin), tcu::IVec2(column, columnEnd), SCAN_COL_COMPONENT_NDX, messageLimitCounter); 1917 return numLinesOk && lineWidthOk; 1918} 1919 1920bool LineRenderCase::checkAreaNumLines (const tcu::ConstPixelBufferAccess& access, const tcu::IVec4& area, int& messageLimitCounter, int componentNdx, const tcu::IVec2& numLines) const 1921{ 1922 // Num maxima == num lines 1923 const tcu::ConstPixelBufferAccess subAccess = tcu::getSubregion(access, area.x(), area.y(), 0, area.z(), area.w(), 1); 1924 const tcu::IVec2 numMinimaMaxima = getNumMinimaMaxima(subAccess, componentNdx); 1925 const int numMaxima = numMinimaMaxima.y(); 1926 1927 // In valid range 1928 if (numMaxima >= numLines.x() && numMaxima <= numLines.y()) 1929 return true; 1930 1931 if (--messageLimitCounter < 0) 1932 return false; 1933 1934 if (area.z() == 1) 1935 m_testCtx.getLog() 1936 << tcu::TestLog::Message 1937 << "On column " << area.x() << ", y: [" << area.y() << "," << (area.y()+area.w()) << "):\n" 1938 << "\tExpected [" << numLines.x() << ", " << numLines.y() << "] lines but the number of lines in the area is " << numMaxima 1939 << tcu::TestLog::EndMessage; 1940 else 1941 m_testCtx.getLog() 1942 << tcu::TestLog::Message 1943 << "On row " << area.y() << ", x: [" << area.x() << "," << (area.x()+area.z()) << "):\n" 1944 << "\tExpected [" << numLines.x() << ", " << numLines.y() << "] lines but the number of lines in the area is " << numMaxima 1945 << tcu::TestLog::EndMessage; 1946 1947 return false; 1948} 1949 1950tcu::IVec2 LineRenderCase::getNumMinimaMaxima (const tcu::ConstPixelBufferAccess& access, int componentNdx) const 1951{ 1952 DE_ASSERT(access.getWidth() == 1 || access.getHeight() == 1); 1953 1954 int previousValue = -1; 1955 int previousSign = 0; 1956 int numMinima = 0; 1957 int numMaxima = 0; 1958 1959 for (int y = 0; y < access.getHeight(); ++y) 1960 for (int x = 0; x < access.getWidth(); ++x) 1961 { 1962 const int componentValue = access.getPixelInt(x, y)[componentNdx]; 1963 1964 if (previousValue != -1) 1965 { 1966 const int sign = (componentValue > previousValue) ? (+1) : (componentValue < previousValue) ? (-1) : (0); 1967 1968 // local minima/maxima in sign changes (zero signless) 1969 if (sign != 0 && sign == -previousSign) 1970 { 1971 previousSign = sign; 1972 1973 if (sign > 0) 1974 ++numMinima; 1975 else 1976 ++numMaxima; 1977 } 1978 else if (sign != 0 && previousSign == 0) 1979 { 1980 previousSign = sign; 1981 1982 // local extreme at the start boundary 1983 if (sign > 0) 1984 ++numMinima; 1985 else 1986 ++numMaxima; 1987 } 1988 } 1989 1990 previousValue = componentValue; 1991 } 1992 1993 // local extreme at the end boundary 1994 if (previousSign > 0) 1995 ++numMaxima; 1996 else if (previousSign < 0) 1997 ++numMinima; 1998 else 1999 { 2000 ++numMaxima; 2001 ++numMinima; 2002 } 2003 2004 return tcu::IVec2(numMinima, numMaxima); 2005} 2006 2007bool LineRenderCase::checkLineWidths (const tcu::ConstPixelBufferAccess& access, const tcu::IVec2& begin, const tcu::IVec2& end, int componentNdx, int& messageLimitCounter) const 2008{ 2009 const bool multisample = m_context.getRenderTarget().getNumSamples() > 1; 2010 const int lineRenderWidth = (m_isWideLineCase) ? (m_wideLineLineWidth) : 1; 2011 const tcu::IVec2 lineWidthRange = (multisample) 2012 ? (tcu::IVec2(lineRenderWidth, lineRenderWidth+1)) // multisampled "smooth" lines may spread to neighboring pixel 2013 : (tcu::IVec2(lineRenderWidth, lineRenderWidth)); 2014 2015 int lineWidth = 0; 2016 bool bboxLimitedLine = false; 2017 bool anyError = false; 2018 2019 const tcu::IVec2 advance = (begin.x() == end.x()) ? (tcu::IVec2(0, 1)) : (tcu::IVec2(1, 0)); 2020 2021 // fragments before begin? 2022 if (access.getPixelInt(begin.x(), begin.y())[componentNdx] != 0) 2023 { 2024 bboxLimitedLine = true; 2025 2026 for (tcu::IVec2 cursor = begin - advance;; cursor -= advance) 2027 { 2028 if (cursor.x() < 0 || cursor.y() < 0) 2029 { 2030 break; 2031 } 2032 else if (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0) 2033 { 2034 ++lineWidth; 2035 } 2036 else 2037 break; 2038 } 2039 } 2040 2041 for (tcu::IVec2 cursor = begin; cursor != end; cursor += advance) 2042 { 2043 const bool hit = (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0); 2044 2045 if (hit) 2046 ++lineWidth; 2047 else if (lineWidth) 2048 { 2049 // Line is allowed to be be thinner if it borders the bbox boundary (since part of the line might have been discarded). 2050 const bool incorrectLineWidth = (lineWidth < lineWidthRange.x() && !bboxLimitedLine) || (lineWidth > lineWidthRange.y()); 2051 2052 if (incorrectLineWidth) 2053 { 2054 anyError = true; 2055 printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter); 2056 } 2057 2058 lineWidth = 0; 2059 bboxLimitedLine = false; 2060 } 2061 } 2062 2063 // fragments after end? 2064 if (lineWidth) 2065 { 2066 for (tcu::IVec2 cursor = end;; cursor += advance) 2067 { 2068 if (cursor.x() >= access.getWidth() || cursor.y() >= access.getHeight()) 2069 { 2070 if (lineWidth > lineWidthRange.y()) 2071 { 2072 anyError = true; 2073 printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter); 2074 } 2075 2076 break; 2077 } 2078 else if (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0) 2079 { 2080 ++lineWidth; 2081 } 2082 else if (lineWidth) 2083 { 2084 // only check that line width is not larger than expected. Line width may be smaller 2085 // since the scanning 'cursor' is now outside the bounding box. 2086 const bool incorrectLineWidth = (lineWidth > lineWidthRange.y()); 2087 2088 if (incorrectLineWidth) 2089 { 2090 anyError = true; 2091 printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter); 2092 } 2093 2094 lineWidth = 0; 2095 } 2096 } 2097 } 2098 2099 return !anyError; 2100} 2101 2102void LineRenderCase::printLineWidthError (const tcu::IVec2& pos, int detectedLineWidth, const tcu::IVec2& lineWidthRange, bool isHorizontal, int& messageLimitCounter) const 2103{ 2104 if (--messageLimitCounter < 0) 2105 return; 2106 2107 m_testCtx.getLog() 2108 << tcu::TestLog::Message 2109 << "Found incorrect line width near " << pos << ": (" << ((isHorizontal) ? ("horizontal") : ("vertical")) << " line)\n" 2110 << "\tExpected line width in range [" << lineWidthRange.x() << ", " << lineWidthRange.y() << "] but found " << detectedLineWidth 2111 << tcu::TestLog::EndMessage; 2112} 2113 2114class PointRenderCase : public BBoxRenderCase 2115{ 2116public: 2117 enum 2118 { 2119 POINTFLAG_WIDE = 1u << FLAGBIT_USER_BIT, //!< use wide points 2120 }; 2121 struct GeneratedPoint 2122 { 2123 tcu::Vec2 center; 2124 int size; 2125 bool even; 2126 }; 2127 2128 PointRenderCase (Context& context, const char* name, const char* description, deUint32 flags); 2129 ~PointRenderCase (void); 2130 2131private: 2132 enum ResultPointType 2133 { 2134 POINT_FULL = 0, 2135 POINT_PARTIAL 2136 }; 2137 2138 void init (void); 2139 void deinit (void); 2140 2141 std::string genVertexSource (void) const; 2142 std::string genFragmentSource (void) const; 2143 std::string genTessellationControlSource (void) const; 2144 std::string genTessellationEvaluationSource (void) const; 2145 std::string genGeometrySource (void) const; 2146 2147 IterationConfig generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const; 2148 void generateAttributeData (void); 2149 void getAttributeData (std::vector<tcu::Vec4>& data) const; 2150 void renderTestPattern (const IterationConfig& config); 2151 void verifyRenderResult (const IterationConfig& config); 2152 2153 void genReferencePointData (const IterationConfig& config, std::vector<GeneratedPoint>& data) const; 2154 bool verifyNarrowPointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter); 2155 bool verifyWidePointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter); 2156 bool verifyWidePoint (const tcu::Surface& viewport, const GeneratedPoint& refPoint, const ProjectedBBox& bbox, ResultPointType pointType, int& logFloodCounter); 2157 bool verifyWidePointAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, const GeneratedPoint& refPoint, const tcu::IVec4& bbox, ResultPointType pointType, int componentNdx, int& logFloodCounter); 2158 tcu::IVec2 scanPointWidthAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, int expectedPointSize, int componentNdx) const; 2159 2160 const int m_numStripes; 2161 const bool m_isWidePointCase; 2162 std::vector<tcu::Vec4> m_attribData; 2163}; 2164 2165PointRenderCase::PointRenderCase (Context& context, const char* name, const char* description, deUint32 flags) 2166 : BBoxRenderCase (context, name, description, 12, flags) 2167 , m_numStripes (4) 2168 , m_isWidePointCase ((flags & POINTFLAG_WIDE) != 0) 2169{ 2170} 2171 2172PointRenderCase::~PointRenderCase (void) 2173{ 2174} 2175 2176void PointRenderCase::init (void) 2177{ 2178 if (m_isWidePointCase) 2179 { 2180 // extensions 2181 if (m_hasGeometryStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_point_size")) 2182 throw tcu::NotSupportedError("Test requires GL_EXT_geometry_point_size extension"); 2183 if (m_hasTessellationStage && !m_hasGeometryStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_point_size")) 2184 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_point_size extension"); 2185 2186 // point size range 2187 { 2188 glw::GLfloat pointSizeRange[2] = {0.0f, 0.0f}; 2189 m_context.getRenderContext().getFunctions().getFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange); 2190 2191 if (pointSizeRange[1] < 5.0f) 2192 throw tcu::NotSupportedError("Test requires point size 5.0"); 2193 } 2194 } 2195 2196 m_testCtx.getLog() 2197 << tcu::TestLog::Message 2198 << "Rendering point pattern to " << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n" 2199 << "Half of the points are green, half blue. Using additive blending.\n" 2200 << "Points are in random order, varying pattern size and location for each iteration.\n" 2201 << "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated red channel." 2202 << tcu::TestLog::EndMessage; 2203 2204 generateAttributeData(); 2205 2206 BBoxRenderCase::init(); 2207} 2208 2209void PointRenderCase::deinit (void) 2210{ 2211 // clear data 2212 m_attribData = std::vector<tcu::Vec4>(); 2213 2214 // deinit parent 2215 BBoxRenderCase::deinit(); 2216} 2217 2218std::string PointRenderCase::genVertexSource (void) const 2219{ 2220 std::ostringstream buf; 2221 2222 buf << "#version 310 es\n" 2223 "in highp vec4 a_position;\n" 2224 "in highp vec4 a_color;\n" 2225 "out highp vec4 vtx_color;\n" 2226 "uniform highp vec4 u_posScale;\n" 2227 "\n"; 2228 if (!m_hasTessellationStage) 2229 { 2230 DE_ASSERT(m_useGlobalState); 2231 buf << "uniform highp vec4 u_primitiveBBoxMin;\n" 2232 "uniform highp vec4 u_primitiveBBoxMax;\n" 2233 "\n" 2234 "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n" 2235 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n" 2236 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n" 2237 "\n"; 2238 } 2239 2240 buf << "void main()\n" 2241 "{\n" 2242 " highp vec2 patternOffset = u_posScale.xy;\n" 2243 " highp vec2 patternScale = u_posScale.zw;\n" 2244 " highp float pointSize = " 2245 << ((m_isWidePointCase && !m_hasTessellationStage && !m_hasGeometryStage) ? ("(a_color.g > 0.0) ? (5.0) : (3.0)") : ("1.0")) 2246 << ";\n" 2247 << " gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n" 2248 " gl_PointSize = pointSize;\n" 2249 " vtx_color = a_color;\n"; 2250 2251 if (!m_hasTessellationStage) 2252 { 2253 DE_ASSERT(m_useGlobalState); 2254 buf << "\n" 2255 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = pointSize;\n" 2256 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin =\n" 2257 " min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMin.w,\n" 2258 " vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMax.w);\n" 2259 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax =\n" 2260 " min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMin.w,\n" 2261 " vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMax.w);\n"; 2262 } 2263 2264 buf << "}\n"; 2265 return buf.str(); 2266} 2267 2268std::string PointRenderCase::genFragmentSource (void) const 2269{ 2270 const char* const colorInputName = (m_hasGeometryStage) ? ("geo_color") : (m_hasTessellationStage) ? ("tess_color") : ("vtx_color"); 2271 std::ostringstream buf; 2272 2273 buf << "#version 310 es\n" 2274 "in mediump vec4 " << colorInputName << ";\n" 2275 "layout(location = 0) out mediump vec4 o_color;\n" 2276 << genShaderFunction(SHADER_FUNC_INSIDE_BBOX) 2277 << "\n" 2278 "void main()\n" 2279 "{\n" 2280 " mediump vec4 baseColor = " << colorInputName << ";\n" 2281 " mediump float redChannel;\n" 2282 " if (fragmentInsideTheBBox(gl_FragCoord.z))\n" 2283 " redChannel = 0.0;\n" 2284 " else\n" 2285 " redChannel = 1.0;\n" 2286 " o_color = vec4(redChannel, baseColor.g, baseColor.b, baseColor.a);\n" 2287 "}\n"; 2288 2289 return buf.str(); 2290} 2291 2292std::string PointRenderCase::genTessellationControlSource (void) const 2293{ 2294 const bool tessellationWidePoints = (m_isWidePointCase) && (!m_hasGeometryStage); 2295 std::ostringstream buf; 2296 2297 buf << "#version 310 es\n" 2298 "#extension GL_EXT_tessellation_shader : require\n" 2299 "#extension GL_EXT_primitive_bounding_box : require\n" 2300 << ((tessellationWidePoints) ? ("#extension GL_EXT_tessellation_point_size : require\n") : ("")) 2301 << "layout(vertices=1) out;" 2302 "\n" 2303 "in highp vec4 vtx_color[];\n" 2304 "out highp vec4 tess_ctrl_color[];\n" 2305 "uniform highp float u_tessellationLevel;\n" 2306 "uniform highp vec4 u_posScale;\n"; 2307 2308 if (!m_calcPerPrimitiveBBox) 2309 { 2310 buf << "uniform highp vec4 u_primitiveBBoxMin;\n" 2311 "uniform highp vec4 u_primitiveBBoxMax;\n"; 2312 } 2313 2314 buf << "patch out highp vec3 vp_bbox_clipMin;\n" 2315 "patch out highp vec3 vp_bbox_clipMax;\n"; 2316 2317 if (m_calcPerPrimitiveBBox) 2318 { 2319 buf << "\n"; 2320 if (m_hasGeometryStage) 2321 buf << genShaderFunction(SHADER_FUNC_MIRROR_X); 2322 buf << genShaderFunction(SHADER_FUNC_MIRROR_Y); 2323 2324 buf << "vec4 transformVec(in highp vec4 p)\n" 2325 "{\n" 2326 " return " << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)")) << ";\n" 2327 "}\n"; 2328 } 2329 2330 buf << "\n" 2331 "void main()\n" 2332 "{\n" 2333 " // convert to nonsensical coordinates, just in case\n" 2334 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n" 2335 " tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n" 2336 "\n" 2337 " gl_TessLevelOuter[0] = u_tessellationLevel;\n" 2338 " gl_TessLevelOuter[1] = u_tessellationLevel;\n" 2339 " gl_TessLevelOuter[2] = u_tessellationLevel;\n" 2340 " gl_TessLevelOuter[3] = u_tessellationLevel;\n" 2341 " gl_TessLevelInner[0] = 0.8; // will be rounded up to 1\n" 2342 " gl_TessLevelInner[1] = 0.8; // will be rounded up to 1\n"; 2343 2344 if (m_calcPerPrimitiveBBox) 2345 { 2346 buf << "\n"; 2347 2348 if (m_hasGeometryStage) 2349 buf << " const vec2 minExpansion = vec2(0.07 + 0.05, 0.07 + 0.02); // eval and geometry shader\n" 2350 " const vec2 maxExpansion = vec2(0.07 + 0.05, 0.07 + 0.03); // eval and geometry shader\n"; 2351 else 2352 buf << " const vec2 minExpansion = vec2(0.07, 0.07); // eval shader\n" 2353 " const vec2 maxExpansion = vec2(0.07, 0.07); // eval shader\n"; 2354 2355 buf << " highp vec2 patternScale = u_posScale.zw;\n" 2356 " highp vec4 bboxMin = transformVec(gl_in[0].gl_Position) - vec4(minExpansion * patternScale, 0.0, 0.0);\n" 2357 " highp vec4 bboxMax = transformVec(gl_in[0].gl_Position) + vec4(maxExpansion * patternScale, 0.0, 0.0);\n"; 2358 } 2359 else 2360 { 2361 buf << "\n" 2362 " highp vec4 bboxMin = u_primitiveBBoxMin;\n" 2363 " highp vec4 bboxMax = u_primitiveBBoxMax;\n"; 2364 } 2365 if (!m_useGlobalState) 2366 buf << "\n" 2367 " gl_BoundingBoxEXT[0] = bboxMin;\n" 2368 " gl_BoundingBoxEXT[1] = bboxMax;\n"; 2369 2370 buf << " vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n" 2371 " vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n" 2372 " vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n" 2373 " vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n" 2374 "}\n"; 2375 2376 return buf.str(); 2377} 2378 2379std::string PointRenderCase::genTessellationEvaluationSource (void) const 2380{ 2381 const bool tessellationWidePoints = (m_isWidePointCase) && (!m_hasGeometryStage); 2382 std::ostringstream buf; 2383 2384 buf << "#version 310 es\n" 2385 "#extension GL_EXT_tessellation_shader : require\n" 2386 << ((tessellationWidePoints) ? ("#extension GL_EXT_tessellation_point_size : require\n") : ("")) 2387 << "layout(quads, point_mode) in;" 2388 "\n" 2389 "in highp vec4 tess_ctrl_color[];\n" 2390 "out highp vec4 tess_color;\n" 2391 "uniform highp vec4 u_posScale;\n" 2392 "\n" 2393 "patch in highp vec3 vp_bbox_clipMin;\n" 2394 "patch in highp vec3 vp_bbox_clipMax;\n" 2395 << ((!m_hasGeometryStage) ? ("flat out highp float v_bbox_expansionSize;\n") : ("")) 2396 << "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n" 2397 "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n" 2398 "\n" 2399 << genShaderFunction(SHADER_FUNC_MIRROR_Y) 2400 << "void main()\n" 2401 "{\n" 2402 " // non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n" 2403 " highp vec2 patternScale = u_posScale.zw;\n" 2404 " highp vec4 offset = vec4((gl_TessCoord.xy * 2.0 - vec2(1.0)) * 0.07 * patternScale, 0.0, 0.0);\n" 2405 " highp float pointSize = " << ((tessellationWidePoints) ? ("(tess_ctrl_color[0].g > 0.0) ? (5.0) : (3.0)") : ("1.0")) << ";\n" 2406 " gl_Position = mirrorY(gl_in[0].gl_Position.zwyx + offset);\n"; 2407 2408 if (tessellationWidePoints) 2409 buf << " gl_PointSize = pointSize;\n"; 2410 2411 buf << " tess_color = tess_ctrl_color[0];\n" 2412 << ((!m_hasGeometryStage) ? ("v_bbox_expansionSize = pointSize;\n") : ("")) 2413 << " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin = vp_bbox_clipMin;\n" 2414 " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax = vp_bbox_clipMax;\n" 2415 "}\n"; 2416 2417 return buf.str(); 2418} 2419 2420std::string PointRenderCase::genGeometrySource (void) const 2421{ 2422 const char* const colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color"); 2423 std::ostringstream buf; 2424 2425 buf << "#version 310 es\n" 2426 "#extension GL_EXT_geometry_shader : require\n" 2427 << ((m_isWidePointCase) ? ("#extension GL_EXT_geometry_point_size : require\n") : ("")) 2428 << "layout(points) in;\n" 2429 "layout(max_vertices=3, points) out;\n" 2430 "\n" 2431 "in highp vec4 " << colorInputName << "[1];\n" 2432 "out highp vec4 geo_color;\n" 2433 "uniform highp vec4 u_posScale;\n" 2434 "\n" 2435 "flat in highp vec3 v_geo_bbox_clipMin[1];\n" 2436 "flat in highp vec3 v_geo_bbox_clipMax[1];\n" 2437 "flat out highp vec3 v_bbox_clipMin;\n" 2438 "flat out highp vec3 v_bbox_clipMax;\n" 2439 "flat out highp float v_bbox_expansionSize;\n" 2440 "\n" 2441 << genShaderFunction(SHADER_FUNC_MIRROR_X) 2442 << "\n" 2443 "void main()\n" 2444 "{\n" 2445 " // Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n" 2446 " highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n" 2447 " highp vec4 pointColor = " << colorInputName << "[0];\n" 2448 " highp vec2 patternScale = u_posScale.zw;\n" 2449 " highp float pointSize = " 2450 << (m_isWidePointCase ? ("(pointColor.g > 0.0) ? (5.0) : (3.0)") : ("1.0")) 2451 << ";\n" 2452 "\n" 2453 " highp vec4 offsets[3] =\n" 2454 " {\n" 2455 " vec4( 0.05 * patternScale.x, 0.03 * patternScale.y, 0.0, 0.0),\n" 2456 " vec4(-0.01 * patternScale.x,-0.02 * patternScale.y, 0.0, 0.0),\n" 2457 " vec4(-0.05 * patternScale.x, 0.02 * patternScale.y, 0.0, 0.0),\n" 2458 " };\n" 2459 " for (int ndx = 0; ndx < 3; ++ndx)\n" 2460 " {\n" 2461 " gl_Position = p0 + offsets[ndx];\n"; 2462 2463 if (m_isWidePointCase) 2464 buf << " gl_PointSize = pointSize;\n"; 2465 2466 buf << " v_bbox_clipMin = v_geo_bbox_clipMin[0];\n" 2467 " v_bbox_clipMax = v_geo_bbox_clipMax[0];\n" 2468 " v_bbox_expansionSize = pointSize;\n" 2469 " geo_color = pointColor;\n" 2470 " EmitVertex();\n" 2471 " }\n" 2472 "}\n"; 2473 2474 return buf.str(); 2475} 2476 2477PointRenderCase::IterationConfig PointRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const 2478{ 2479 IterationConfig config = generateRandomConfig(0xDEDEDEu * (deUint32)iteration, renderTargetSize); 2480 2481 // equal or larger -> expand according to shader expansion 2482 if (m_bboxSize == BBOXSIZE_EQUAL || m_bboxSize == BBOXSIZE_LARGER) 2483 { 2484 const tcu::Vec2 patternScale = config.patternSize; 2485 2486 if (m_hasTessellationStage) 2487 { 2488 config.bbox.min -= tcu::Vec4(0.07f * patternScale.x(), 0.07f * patternScale.y(), 0.0f, 0.0f); 2489 config.bbox.max += tcu::Vec4(0.07f * patternScale.x(), 0.07f * patternScale.y(), 0.0f, 0.0f); 2490 } 2491 if (m_hasGeometryStage) 2492 { 2493 config.bbox.min -= tcu::Vec4(0.05f * patternScale.x(), 0.02f * patternScale.y(), 0.0f, 0.0f); 2494 config.bbox.max += tcu::Vec4(0.05f * patternScale.x(), 0.03f * patternScale.y(), 0.0f, 0.0f); 2495 } 2496 } 2497 2498 return config; 2499} 2500 2501void PointRenderCase::generateAttributeData (void) 2502{ 2503 const tcu::Vec4 green (0.0f, 1.0f, 0.0f, 1.0f); 2504 const tcu::Vec4 blue (0.0f, 0.0f, 1.0f, 1.0f); 2505 std::vector<int> cellOrder (m_numStripes * m_numStripes * 2); 2506 de::Random rnd (0xDE22446); 2507 2508 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx) 2509 cellOrder[ndx] = ndx; 2510 rnd.shuffle(cellOrder.begin(), cellOrder.end()); 2511 2512 m_attribData.resize(cellOrder.size() * 2); 2513 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx) 2514 { 2515 const int pointID = cellOrder[ndx]; 2516 const int direction = pointID & 0x01; 2517 const int majorCoord = (pointID >> 1) / m_numStripes; 2518 const int minorCoord = (pointID >> 1) % m_numStripes; 2519 2520 if (direction) 2521 { 2522 m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(minorCoord / float(m_numStripes), majorCoord / float(m_numStripes), 0.0f, 1.0f); 2523 m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green; 2524 } 2525 else 2526 { 2527 m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4((majorCoord + 0.5f) / float(m_numStripes), (minorCoord + 0.5f) / float(m_numStripes), 0.0f, 1.0f); 2528 m_attribData[ndx * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue; 2529 } 2530 } 2531} 2532 2533void PointRenderCase::getAttributeData (std::vector<tcu::Vec4>& data) const 2534{ 2535 data = m_attribData; 2536} 2537 2538void PointRenderCase::renderTestPattern (const IterationConfig& config) 2539{ 2540 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 2541 2542 setupRender(config); 2543 2544 if (m_hasTessellationStage) 2545 { 2546 const glw::GLint tessLevelPos = gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel"); 2547 const glw::GLfloat tessLevel = 0.8f; // will be rounded up 2548 2549 TCU_CHECK(tessLevelPos != -1); 2550 2551 m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel << tcu::TestLog::EndMessage; 2552 2553 gl.uniform1f(tessLevelPos, tessLevel); 2554 gl.patchParameteri(GL_PATCH_VERTICES, 1); 2555 GLU_EXPECT_NO_ERROR(gl.getError(), "patch param"); 2556 } 2557 2558 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering pattern." << tcu::TestLog::EndMessage; 2559 2560 gl.enable(GL_BLEND); 2561 gl.blendFunc(GL_ONE, GL_ONE); 2562 gl.blendEquation(GL_FUNC_ADD); 2563 2564 gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_POINTS), 0, m_numStripes * m_numStripes * 2); 2565 GLU_EXPECT_NO_ERROR(gl.getError(), "draw"); 2566} 2567 2568void PointRenderCase::verifyRenderResult (const IterationConfig& config) 2569{ 2570 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 2571 const ProjectedBBox projectedBBox = projectBoundingBox(config.bbox); 2572 const tcu::IVec4 viewportBBoxArea = getViewportBoundingBoxArea(projectedBBox, config.viewportSize); 2573 2574 tcu::Surface viewportSurface (config.viewportSize.x(), config.viewportSize.y()); 2575 int logFloodCounter = 8; 2576 bool anyError; 2577 std::vector<GeneratedPoint> refPoints; 2578 2579 if (!m_calcPerPrimitiveBBox) 2580 m_testCtx.getLog() 2581 << tcu::TestLog::Message 2582 << "Projected bounding box: (clip space)\n" 2583 << "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n" 2584 << "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n" 2585 << "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n" 2586 << "In viewport coordinates:\n" 2587 << "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n" 2588 << "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n" 2589 << "Verifying render results within the bounding box:\n" 2590 << tcu::TestLog::EndMessage; 2591 else 2592 m_testCtx.getLog() 2593 << tcu::TestLog::Message 2594 << "Verifying render result:" 2595 << tcu::TestLog::EndMessage; 2596 2597 if (m_fbo) 2598 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo); 2599 glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(), viewportSurface.getAccess()); 2600 2601 genReferencePointData(config, refPoints); 2602 2603 if (m_isWidePointCase) 2604 anyError = verifyWidePointPattern(viewportSurface, refPoints, projectedBBox, logFloodCounter); 2605 else 2606 anyError = verifyNarrowPointPattern(viewportSurface, refPoints, projectedBBox, logFloodCounter); 2607 2608 if (anyError) 2609 { 2610 if (logFloodCounter < 0) 2611 m_testCtx.getLog() << tcu::TestLog::Message << "Omitted " << (-logFloodCounter) << " error descriptions." << tcu::TestLog::EndMessage; 2612 2613 m_testCtx.getLog() 2614 << tcu::TestLog::Message 2615 << "Image verification failed." 2616 << tcu::TestLog::EndMessage 2617 << tcu::TestLog::ImageSet("Images", "Image verification") 2618 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess()) 2619 << tcu::TestLog::EndImageSet; 2620 2621 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed"); 2622 } 2623 else 2624 { 2625 m_testCtx.getLog() 2626 << tcu::TestLog::Message 2627 << "Result image ok." 2628 << tcu::TestLog::EndMessage 2629 << tcu::TestLog::ImageSet("Images", "Image verification") 2630 << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess()) 2631 << tcu::TestLog::EndImageSet; 2632 } 2633} 2634 2635struct PointSorter 2636{ 2637 bool operator() (const PointRenderCase::GeneratedPoint& a, const PointRenderCase::GeneratedPoint& b) const 2638 { 2639 if (a.center.y() < b.center.y()) 2640 return true; 2641 else if (a.center.y() > b.center.y()) 2642 return false; 2643 else 2644 return (a.center.x() < b.center.x()); 2645 } 2646}; 2647 2648void PointRenderCase::genReferencePointData (const IterationConfig& config, std::vector<GeneratedPoint>& data) const 2649{ 2650 std::vector<GeneratedPoint> currentPoints; 2651 2652 // vertex shader 2653 currentPoints.resize(m_attribData.size() / 2); 2654 for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx) 2655 { 2656 currentPoints[ndx].center = m_attribData[ndx*2].swizzle(0, 1); 2657 currentPoints[ndx].even = (m_attribData[ndx*2 + 1].y() == 1.0f); // is green 2658 currentPoints[ndx].size = ((m_isWidePointCase) ? ((currentPoints[ndx].even) ? 5 : 3) : 1); 2659 } 2660 2661 // tessellation 2662 if (m_hasTessellationStage) 2663 { 2664 std::vector<GeneratedPoint> tessellatedPoints; 2665 2666 tessellatedPoints.resize(currentPoints.size() * 4); 2667 for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx) 2668 { 2669 const tcu::Vec2 position = tcu::Vec2(currentPoints[ndx].center.x(), 1.0f - currentPoints[ndx].center.y()); // mirror Y 2670 2671 tessellatedPoints[4 * ndx + 0].center = position + tcu::Vec2(-0.07f, -0.07f); 2672 tessellatedPoints[4 * ndx + 0].size = currentPoints[ndx].size; 2673 tessellatedPoints[4 * ndx + 0].even = currentPoints[ndx].even; 2674 2675 tessellatedPoints[4 * ndx + 1].center = position + tcu::Vec2( 0.07f, -0.07f); 2676 tessellatedPoints[4 * ndx + 1].size = currentPoints[ndx].size; 2677 tessellatedPoints[4 * ndx + 1].even = currentPoints[ndx].even; 2678 2679 tessellatedPoints[4 * ndx + 2].center = position + tcu::Vec2( 0.07f, 0.07f); 2680 tessellatedPoints[4 * ndx + 2].size = currentPoints[ndx].size; 2681 tessellatedPoints[4 * ndx + 2].even = currentPoints[ndx].even; 2682 2683 tessellatedPoints[4 * ndx + 3].center = position + tcu::Vec2(-0.07f, 0.07f); 2684 tessellatedPoints[4 * ndx + 3].size = currentPoints[ndx].size; 2685 tessellatedPoints[4 * ndx + 3].even = currentPoints[ndx].even; 2686 } 2687 2688 currentPoints.swap(tessellatedPoints); 2689 } 2690 2691 // geometry 2692 if (m_hasGeometryStage) 2693 { 2694 std::vector<GeneratedPoint> geometryShadedPoints; 2695 2696 geometryShadedPoints.resize(currentPoints.size() * 3); 2697 for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx) 2698 { 2699 const tcu::Vec2 position = tcu::Vec2(1.0f - currentPoints[ndx].center.x(), currentPoints[ndx].center.y()); // mirror X 2700 2701 geometryShadedPoints[3 * ndx + 0].center = position + tcu::Vec2( 0.05f, 0.03f); 2702 geometryShadedPoints[3 * ndx + 0].size = currentPoints[ndx].size; 2703 geometryShadedPoints[3 * ndx + 0].even = currentPoints[ndx].even; 2704 2705 geometryShadedPoints[3 * ndx + 1].center = position + tcu::Vec2(-0.01f, -0.02f); 2706 geometryShadedPoints[3 * ndx + 1].size = currentPoints[ndx].size; 2707 geometryShadedPoints[3 * ndx + 1].even = currentPoints[ndx].even; 2708 2709 geometryShadedPoints[3 * ndx + 2].center = position + tcu::Vec2(-0.05f, 0.02f); 2710 geometryShadedPoints[3 * ndx + 2].size = currentPoints[ndx].size; 2711 geometryShadedPoints[3 * ndx + 2].even = currentPoints[ndx].even; 2712 } 2713 2714 currentPoints.swap(geometryShadedPoints); 2715 } 2716 2717 // sort from left to right, top to bottom 2718 std::sort(currentPoints.begin(), currentPoints.end(), PointSorter()); 2719 2720 // map to pattern space 2721 for (int ndx = 0; ndx < (int)currentPoints.size(); ++ndx) 2722 currentPoints[ndx].center = currentPoints[ndx].center * config.patternSize + config.patternPos; 2723 2724 currentPoints.swap(data); 2725} 2726 2727bool PointRenderCase::verifyNarrowPointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter) 2728{ 2729 bool anyError = false; 2730 2731 // check that there is something near each sample 2732 for (int pointNdx = 0; pointNdx < (int)refPoints.size(); ++pointNdx) 2733 { 2734 const float epsilon = 1.0e-6f; 2735 const GeneratedPoint& refPoint = refPoints[pointNdx]; 2736 2737 // skip points not in the the bbox, treat boundary as "in" 2738 if (refPoint.center.x() < bbox.min.x() - epsilon || 2739 refPoint.center.y() < bbox.min.y() - epsilon || 2740 refPoint.center.x() > bbox.max.x() + epsilon || 2741 refPoint.center.y() > bbox.max.y() + epsilon) 2742 continue; 2743 else 2744 { 2745 // transform to viewport coords 2746 const tcu::IVec2 pixelCenter(deRoundFloatToInt32((refPoint.center.x() * 0.5f + 0.5f) * viewport.getWidth()), deRoundFloatToInt32((refPoint.center.y() * 0.5f + 0.5f) * viewport.getHeight())); 2747 2748 // find rasterized point in the result 2749 if (pixelCenter.x() < 1 || pixelCenter.y() < 1 || pixelCenter.x() >= viewport.getWidth()-1 || pixelCenter.y() >= viewport.getHeight()-1) 2750 { 2751 // viewport boundary, assume point is fine 2752 } 2753 else 2754 { 2755 const int componentNdx = (refPoint.even) ? (1) : (2); // analyze either green or blue channel 2756 bool foundResult = false; 2757 2758 // check neighborhood 2759 for (int dy = -1; dy < 2 && !foundResult; ++dy) 2760 for (int dx = -1; dx < 2 && !foundResult; ++dx) 2761 { 2762 const tcu::IVec2 testPos (pixelCenter.x() + dx, pixelCenter.y() + dy); 2763 const tcu::RGBA color = viewport.getPixel(testPos.x(), testPos.y()); 2764 2765 if (color.toIVec()[componentNdx] > 0) 2766 foundResult = true; 2767 } 2768 2769 if (!foundResult) 2770 { 2771 anyError = true; 2772 2773 if (--logFloodCounter >= 0) 2774 { 2775 m_testCtx.getLog() 2776 << tcu::TestLog::Message 2777 << "Missing point near " << pixelCenter << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "." 2778 << tcu::TestLog::EndMessage; 2779 } 2780 } 2781 } 2782 } 2783 } 2784 2785 return anyError; 2786} 2787 2788bool PointRenderCase::verifyWidePointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter) 2789{ 2790 bool anyError = false; 2791 2792 // check that there is something near each sample 2793 for (int pointNdx = 0; pointNdx < (int)refPoints.size(); ++pointNdx) 2794 { 2795 const GeneratedPoint& refPoint = refPoints[pointNdx]; 2796 2797 if (refPoint.center.x() >= bbox.min.x() && 2798 refPoint.center.y() >= bbox.min.y() && 2799 refPoint.center.x() <= bbox.max.x() && 2800 refPoint.center.y() <= bbox.max.y()) 2801 { 2802 // point fully in the bounding box 2803 anyError |= !verifyWidePoint(viewport, refPoint, bbox, POINT_FULL, logFloodCounter); 2804 } 2805 else if (refPoint.center.x() >= bbox.min.x() + refPoint.size / 2.0f && 2806 refPoint.center.y() >= bbox.min.y() - refPoint.size / 2.0f && 2807 refPoint.center.x() <= bbox.max.x() + refPoint.size / 2.0f && 2808 refPoint.center.y() <= bbox.max.y() - refPoint.size / 2.0f) 2809 { 2810 // point leaks into bounding box 2811 anyError |= !verifyWidePoint(viewport, refPoint, bbox, POINT_PARTIAL, logFloodCounter); 2812 } 2813 } 2814 2815 return anyError; 2816} 2817 2818bool PointRenderCase::verifyWidePoint (const tcu::Surface& viewport, const GeneratedPoint& refPoint, const ProjectedBBox& bbox, ResultPointType pointType, int& logFloodCounter) 2819{ 2820 const int componentNdx = (refPoint.even) ? (1) : (2); 2821 const int halfPointSizeCeil = (refPoint.size + 1) / 2; 2822 const int halfPointSizeFloor = (refPoint.size + 1) / 2; 2823 const tcu::IVec4 viewportBBoxArea = getViewportBoundingBoxArea(bbox, tcu::IVec2(viewport.getWidth(), viewport.getHeight()), (float)refPoint.size); 2824 const tcu::IVec4 verificationArea = tcu::IVec4(de::max(viewportBBoxArea.x(), 0), 2825 de::max(viewportBBoxArea.y(), 0), 2826 de::min(viewportBBoxArea.z(), viewport.getWidth()), 2827 de::min(viewportBBoxArea.w(), viewport.getHeight())); 2828 const tcu::IVec2 pointPos = tcu::IVec2(deRoundFloatToInt32((refPoint.center.x()*0.5f + 0.5f) * viewport.getWidth()), 2829 deRoundFloatToInt32((refPoint.center.y()*0.5f + 0.5f) * viewport.getHeight())); 2830 2831 // find any fragment within the point that is inside the bbox, start search at the center 2832 2833 if (pointPos.x() >= verificationArea.x() && 2834 pointPos.y() >= verificationArea.y() && 2835 pointPos.x() < verificationArea.z() && 2836 pointPos.y() < verificationArea.w()) 2837 { 2838 if (viewport.getPixel(pointPos.x(), pointPos.y()).toIVec()[componentNdx]) 2839 return verifyWidePointAt(pointPos, viewport, refPoint, verificationArea, pointType, componentNdx, logFloodCounter); 2840 } 2841 2842 for (int dy = -halfPointSizeCeil; dy <= halfPointSizeCeil; ++dy) 2843 for (int dx = -halfPointSizeCeil; dx <= halfPointSizeCeil; ++dx) 2844 { 2845 const tcu::IVec2 testPos = pointPos + tcu::IVec2(dx, dy); 2846 2847 if (dx == 0 && dy == 0) 2848 continue; 2849 2850 if (testPos.x() >= verificationArea.x() && 2851 testPos.y() >= verificationArea.y() && 2852 testPos.x() < verificationArea.z() && 2853 testPos.y() < verificationArea.w()) 2854 { 2855 if (viewport.getPixel(testPos.x(), testPos.y()).toIVec()[componentNdx]) 2856 return verifyWidePointAt(testPos, viewport, refPoint, verificationArea, pointType, componentNdx, logFloodCounter); 2857 } 2858 } 2859 2860 // could not find point, this is only ok near boundaries 2861 if (pointPos.x() + halfPointSizeFloor < verificationArea.x() - 1 || 2862 pointPos.y() + halfPointSizeFloor < verificationArea.y() - 1 || 2863 pointPos.x() - halfPointSizeFloor >= verificationArea.z() - 1 || 2864 pointPos.y() - halfPointSizeFloor >= verificationArea.w() - 1) 2865 return true; 2866 2867 if (--logFloodCounter >= 0) 2868 { 2869 m_testCtx.getLog() 2870 << tcu::TestLog::Message 2871 << "Missing wide point near " << pointPos << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "." 2872 << tcu::TestLog::EndMessage; 2873 } 2874 2875 return false; 2876} 2877 2878bool PointRenderCase::verifyWidePointAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, const GeneratedPoint& refPoint, const tcu::IVec4& bbox, ResultPointType pointType, int componentNdx, int& logFloodCounter) 2879{ 2880 const int expectedPointSize = refPoint.size; 2881 bool viewportClippedTop = false; 2882 bool viewportClippedBottom = false; 2883 bool primitiveClippedTop = false; 2884 bool primitiveClippedBottom = false; 2885 std::vector<tcu::IVec2> widthsUpwards; 2886 std::vector<tcu::IVec2> widthsDownwards; 2887 std::vector<tcu::IVec2> widths; 2888 2889 // search upwards 2890 for (int y = pointPos.y();; --y) 2891 { 2892 if (y < bbox.y() || y < 0) 2893 { 2894 if (y < bbox.y()) 2895 primitiveClippedTop = true; 2896 if (y < 0) 2897 viewportClippedTop = true; 2898 break; 2899 } 2900 else if (pointPos.y() - y > expectedPointSize) 2901 { 2902 // no need to go further than point height 2903 break; 2904 } 2905 else if (viewport.getPixel(pointPos.x(), y).toIVec()[componentNdx] == 0) 2906 { 2907 break; 2908 } 2909 else 2910 { 2911 widthsUpwards.push_back(scanPointWidthAt(tcu::IVec2(pointPos.x(), y), viewport, expectedPointSize, componentNdx)); 2912 } 2913 } 2914 2915 // top is clipped 2916 if ((viewportClippedTop || (pointType == POINT_PARTIAL && primitiveClippedTop)) && !widthsUpwards.empty()) 2917 { 2918 const tcu::IVec2& range = widthsUpwards.back(); 2919 const bool squareFits = (range.y() - range.x() + 1) >= expectedPointSize; 2920 const bool widthClipped = (pointType == POINT_PARTIAL) && (range.x() <= bbox.x() || range.y() >= bbox.z()); 2921 2922 if (squareFits || widthClipped) 2923 return true; 2924 } 2925 2926 // and downwards 2927 for (int y = pointPos.y()+1;; ++y) 2928 { 2929 if (y >= bbox.w() || y >= viewport.getHeight()) 2930 { 2931 if (y >= bbox.w()) 2932 primitiveClippedBottom = true; 2933 if (y >= viewport.getHeight()) 2934 viewportClippedBottom = true; 2935 break; 2936 } 2937 else if (y - pointPos.y() > expectedPointSize) 2938 { 2939 // no need to go further than point height 2940 break; 2941 } 2942 else if (viewport.getPixel(pointPos.x(), y).toIVec()[componentNdx] == 0) 2943 { 2944 break; 2945 } 2946 else 2947 { 2948 widthsDownwards.push_back(scanPointWidthAt(tcu::IVec2(pointPos.x(), y), viewport, expectedPointSize, componentNdx)); 2949 } 2950 } 2951 2952 // bottom is clipped 2953 if ((viewportClippedBottom || (pointType == POINT_PARTIAL && primitiveClippedBottom)) && !(widthsDownwards.empty() && widthsUpwards.empty())) 2954 { 2955 const tcu::IVec2& range = (widthsDownwards.empty()) ? (widthsUpwards.front()) : (widthsDownwards.back()); 2956 const bool squareFits = (range.y() - range.x() + 1) >= expectedPointSize; 2957 const bool bboxClipped = (pointType == POINT_PARTIAL) && (range.x() <= bbox.x() || range.y() >= bbox.z()-1); 2958 const bool viewportClipped = range.x() <= 0 || range.y() >= viewport.getWidth()-1; 2959 2960 if (squareFits || bboxClipped || viewportClipped) 2961 return true; 2962 } 2963 2964 // would square point would fit into the rasterized area 2965 2966 for (int ndx = 0; ndx < (int)widthsUpwards.size(); ++ndx) 2967 widths.push_back(widthsUpwards[(int)widthsUpwards.size() - ndx - 1]); 2968 for (int ndx = 0; ndx < (int)widthsDownwards.size(); ++ndx) 2969 widths.push_back(widthsDownwards[ndx]); 2970 DE_ASSERT(!widths.empty()); 2971 2972 for (int y = 0; y < (int)widths.size() - expectedPointSize + 1; ++y) 2973 { 2974 tcu::IVec2 unionRange = widths[y]; 2975 2976 for (int dy = 1; dy < expectedPointSize; ++dy) 2977 { 2978 unionRange.x() = de::max(unionRange.x(), widths[y+dy].x()); 2979 unionRange.y() = de::min(unionRange.y(), widths[y+dy].y()); 2980 } 2981 2982 // would a N x N block fit here? 2983 { 2984 const bool squareFits = (unionRange.y() - unionRange.x() + 1) >= expectedPointSize; 2985 const bool bboxClipped = (pointType == POINT_PARTIAL) && (unionRange.x() <= bbox.x() || unionRange.y() >= bbox.z()-1); 2986 const bool viewportClipped = unionRange.x() <= 0 || unionRange.y() >= viewport.getWidth()-1; 2987 2988 if (squareFits || bboxClipped || viewportClipped) 2989 return true; 2990 } 2991 } 2992 2993 if (--logFloodCounter >= 0) 2994 { 2995 m_testCtx.getLog() 2996 << tcu::TestLog::Message 2997 << "Missing " << expectedPointSize << "x" << expectedPointSize << " point near " << pointPos << ", vertex coordinates=" << refPoint.center.swizzle(0, 1) << "." 2998 << tcu::TestLog::EndMessage; 2999 } 3000 return false; 3001} 3002 3003tcu::IVec2 PointRenderCase::scanPointWidthAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, int expectedPointSize, int componentNdx) const 3004{ 3005 int minX = pointPos.x(); 3006 int maxX = pointPos.x(); 3007 3008 // search horizontally for a point edges 3009 for (int x = pointPos.x()-1; x >= 0; --x) 3010 { 3011 if (viewport.getPixel(x, pointPos.y()).toIVec()[componentNdx] == 0) 3012 break; 3013 3014 // no need to go further than point width 3015 if (pointPos.x() - x > expectedPointSize) 3016 break; 3017 3018 minX = x; 3019 } 3020 for (int x = pointPos.x()+1; x < viewport.getWidth(); ++x) 3021 { 3022 if (viewport.getPixel(x, pointPos.y()).toIVec()[componentNdx] == 0) 3023 break; 3024 3025 // no need to go further than point width 3026 if (x - pointPos.x() > expectedPointSize) 3027 break; 3028 3029 maxX = x; 3030 } 3031 3032 return tcu::IVec2(minX, maxX); 3033} 3034 3035class BlitFboCase : public TestCase 3036{ 3037public: 3038 enum RenderTarget 3039 { 3040 TARGET_DEFAULT = 0, 3041 TARGET_FBO, 3042 3043 TARGET_LAST 3044 }; 3045 3046 BlitFboCase (Context& context, const char* name, const char* description, RenderTarget src, RenderTarget dst); 3047 ~BlitFboCase (void); 3048 3049private: 3050 enum 3051 { 3052 FBO_SIZE = 256, 3053 }; 3054 3055 struct BlitArgs 3056 { 3057 tcu::IVec4 src; 3058 tcu::IVec4 dst; 3059 tcu::Vec4 bboxMin; 3060 tcu::Vec4 bboxMax; 3061 bool linear; 3062 }; 3063 3064 void init (void); 3065 void deinit (void); 3066 IterateResult iterate (void); 3067 3068 void fillSourceWithPattern (void); 3069 bool verifyImage (const BlitArgs& args); 3070 3071 const RenderTarget m_src; 3072 const RenderTarget m_dst; 3073 3074 std::vector<BlitArgs> m_iterations; 3075 int m_iteration; 3076 de::MovePtr<glu::Framebuffer> m_srcFbo; 3077 de::MovePtr<glu::Framebuffer> m_dstFbo; 3078 de::MovePtr<glu::Renderbuffer> m_srcRbo; 3079 de::MovePtr<glu::Renderbuffer> m_dstRbo; 3080 de::MovePtr<glu::ShaderProgram> m_program; 3081 de::MovePtr<glu::Buffer> m_vbo; 3082}; 3083 3084BlitFboCase::BlitFboCase (Context& context, const char* name, const char* description, RenderTarget src, RenderTarget dst) 3085 : TestCase (context, name, description) 3086 , m_src (src) 3087 , m_dst (dst) 3088 , m_iteration (0) 3089{ 3090 DE_ASSERT(src < TARGET_LAST); 3091 DE_ASSERT(dst < TARGET_LAST); 3092} 3093 3094BlitFboCase::~BlitFboCase (void) 3095{ 3096 deinit(); 3097} 3098 3099void BlitFboCase::init (void) 3100{ 3101 const int numIterations = 12; 3102 const bool defaultFBMultisampled = (m_context.getRenderTarget().getNumSamples() > 1); 3103 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 3104 de::Random rnd (0xABC123); 3105 3106 m_testCtx.getLog() 3107 << tcu::TestLog::Message 3108 << "Using BlitFramebuffer to blit area from " 3109 << ((m_src == TARGET_DEFAULT) ? ("default fb") : ("fbo")) 3110 << " to " 3111 << ((m_dst == TARGET_DEFAULT) ? ("default fb") : ("fbo")) 3112 << ".\n" 3113 << "Varying blit arguments and primitive bounding box between iterations.\n" 3114 << "Expecting bounding box to have no effect on blitting.\n" 3115 << "Source framebuffer is filled with green-yellow grid.\n" 3116 << tcu::TestLog::EndMessage; 3117 3118 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box")) 3119 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension"); 3120 if (m_dst == TARGET_DEFAULT && defaultFBMultisampled) 3121 throw tcu::NotSupportedError("Test requires non-multisampled default framebuffer"); 3122 3123 // resources 3124 3125 if (m_src == TARGET_FBO) 3126 { 3127 m_srcRbo = de::MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(m_context.getRenderContext())); 3128 gl.bindRenderbuffer(GL_RENDERBUFFER, **m_srcRbo); 3129 gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, FBO_SIZE, FBO_SIZE); 3130 GLU_EXPECT_NO_ERROR(gl.getError(), "src rbo"); 3131 3132 m_srcFbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext())); 3133 gl.bindFramebuffer(GL_FRAMEBUFFER, **m_srcFbo); 3134 gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **m_srcRbo); 3135 GLU_EXPECT_NO_ERROR(gl.getError(), "src fbo"); 3136 } 3137 3138 if (m_dst == TARGET_FBO) 3139 { 3140 m_dstRbo = de::MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(m_context.getRenderContext())); 3141 gl.bindRenderbuffer(GL_RENDERBUFFER, **m_dstRbo); 3142 gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, FBO_SIZE, FBO_SIZE); 3143 GLU_EXPECT_NO_ERROR(gl.getError(), "dst rbo"); 3144 3145 m_dstFbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext())); 3146 gl.bindFramebuffer(GL_FRAMEBUFFER, **m_dstFbo); 3147 gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **m_dstRbo); 3148 GLU_EXPECT_NO_ERROR(gl.getError(), "dst fbo"); 3149 } 3150 3151 { 3152 static const char* const s_vertexSource = "#version 310 es\n" 3153 "in highp vec4 a_position;\n" 3154 "out highp vec4 v_position;\n" 3155 "void main()\n" 3156 "{\n" 3157 " gl_Position = a_position;\n" 3158 " v_position = a_position;\n" 3159 "}\n"; 3160 static const char* const s_fragmentSource = "#version 310 es\n" 3161 "in mediump vec4 v_position;\n" 3162 "layout(location=0) out mediump vec4 dEQP_FragColor;\n" 3163 "void main()\n" 3164 "{\n" 3165 " const mediump vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n" 3166 " const mediump vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n" 3167 " dEQP_FragColor = (step(0.1, mod(v_position.x, 0.2)) == step(0.1, mod(v_position.y, 0.2))) ? (green) : (yellow);\n" 3168 "}\n"; 3169 3170 m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(s_vertexSource) << glu::FragmentSource(s_fragmentSource))); 3171 3172 if (!m_program->isOk()) 3173 { 3174 m_testCtx.getLog() << *m_program; 3175 throw tcu::TestError("failed to build program"); 3176 } 3177 } 3178 3179 { 3180 static const tcu::Vec4 s_quadCoords[] = 3181 { 3182 tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f), 3183 tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f), 3184 tcu::Vec4( 1.0f, -1.0f, 0.0f, 1.0f), 3185 tcu::Vec4( 1.0f, 1.0f, 0.0f, 1.0f), 3186 }; 3187 3188 m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext())); 3189 3190 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo); 3191 gl.bufferData(GL_ARRAY_BUFFER, sizeof(s_quadCoords), s_quadCoords, GL_STATIC_DRAW); 3192 GLU_EXPECT_NO_ERROR(gl.getError(), "set buf"); 3193 } 3194 3195 // gen iterations 3196 3197 { 3198 const tcu::IVec2 srcSize = (m_src == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE)); 3199 const tcu::IVec2 dstSize = (m_dst == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE)); 3200 3201 m_testCtx.getLog() 3202 << tcu::TestLog::Message 3203 << "srcSize = " << srcSize << "\n" 3204 << "dstSize = " << dstSize << "\n" 3205 << tcu::TestLog::EndMessage; 3206 3207 for (int ndx = 0; ndx < numIterations; ++ndx) 3208 { 3209 BlitArgs args; 3210 3211 if (m_src == TARGET_DEFAULT && defaultFBMultisampled) 3212 { 3213 const tcu::IVec2 unionSize = tcu::IVec2(de::min(srcSize.x(), dstSize.x()), de::min(srcSize.y(), dstSize.y())); 3214 const int srcWidth = rnd.getInt(1, unionSize.x()); 3215 const int srcHeight = rnd.getInt(1, unionSize.y()); 3216 const int srcX = rnd.getInt(0, unionSize.x() - srcWidth); 3217 const int srcY = rnd.getInt(0, unionSize.y() - srcHeight); 3218 3219 args.src.x() = srcX; 3220 args.src.y() = srcY; 3221 args.src.z() = srcX + srcWidth; 3222 args.src.w() = srcY + srcHeight; 3223 3224 args.dst = args.src; 3225 } 3226 else 3227 { 3228 const int srcWidth = rnd.getInt(1, srcSize.x()); 3229 const int srcHeight = rnd.getInt(1, srcSize.y()); 3230 const int srcX = rnd.getInt(0, srcSize.x() - srcWidth); 3231 const int srcY = rnd.getInt(0, srcSize.y() - srcHeight); 3232 const int dstWidth = rnd.getInt(1, dstSize.x()); 3233 const int dstHeight = rnd.getInt(1, dstSize.y()); 3234 const int dstX = rnd.getInt(-(dstWidth / 2), dstSize.x() - (dstWidth+1) / 2); // allow dst go out of bounds 3235 const int dstY = rnd.getInt(-(dstHeight / 2), dstSize.y() - (dstHeight+1) / 2); 3236 3237 args.src.x() = srcX; 3238 args.src.y() = srcY; 3239 args.src.z() = srcX + srcWidth; 3240 args.src.w() = srcY + srcHeight; 3241 args.dst.x() = dstX; 3242 args.dst.y() = dstY; 3243 args.dst.z() = dstX + dstWidth; 3244 args.dst.w() = dstY + dstHeight; 3245 } 3246 3247 args.bboxMin.x() = rnd.getFloat(-1.1f, 1.1f); 3248 args.bboxMin.y() = rnd.getFloat(-1.1f, 1.1f); 3249 args.bboxMin.z() = rnd.getFloat(-1.1f, 1.1f); 3250 args.bboxMin.w() = rnd.getFloat( 0.9f, 1.1f); 3251 3252 args.bboxMax.x() = rnd.getFloat(-1.1f, 1.1f); 3253 args.bboxMax.y() = rnd.getFloat(-1.1f, 1.1f); 3254 args.bboxMax.z() = rnd.getFloat(-1.1f, 1.1f); 3255 args.bboxMax.w() = rnd.getFloat( 0.9f, 1.1f); 3256 3257 if (args.bboxMin.x() / args.bboxMin.w() > args.bboxMax.x() / args.bboxMax.w()) 3258 std::swap(args.bboxMin.x(), args.bboxMax.x()); 3259 if (args.bboxMin.y() / args.bboxMin.w() > args.bboxMax.y() / args.bboxMax.w()) 3260 std::swap(args.bboxMin.y(), args.bboxMax.y()); 3261 if (args.bboxMin.z() / args.bboxMin.w() > args.bboxMax.z() / args.bboxMax.w()) 3262 std::swap(args.bboxMin.z(), args.bboxMax.z()); 3263 3264 args.linear = rnd.getBool(); 3265 3266 m_iterations.push_back(args); 3267 } 3268 } 3269} 3270 3271void BlitFboCase::deinit (void) 3272{ 3273 m_srcFbo.clear(); 3274 m_srcRbo.clear(); 3275 m_dstFbo.clear(); 3276 m_dstRbo.clear(); 3277 m_program.clear(); 3278 m_vbo.clear(); 3279} 3280 3281BlitFboCase::IterateResult BlitFboCase::iterate (void) 3282{ 3283 const tcu::ScopedLogSection section (m_testCtx.getLog(), "Iteration" + de::toString(m_iteration), "Iteration " + de::toString(m_iteration+1) + " / " + de::toString((int)m_iterations.size())); 3284 const BlitArgs& blitCfg = m_iterations[m_iteration]; 3285 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 3286 3287 if (m_iteration == 0) 3288 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 3289 3290 // fill source with test pattern. Default fb must be filled for each iteration because contents might not survive the swap 3291 if (m_src == TARGET_DEFAULT || m_iteration == 0) 3292 fillSourceWithPattern(); 3293 3294 m_testCtx.getLog() 3295 << tcu::TestLog::Message 3296 << "Set bounding box:\n" 3297 << "\tmin:" << blitCfg.bboxMin << "\n" 3298 << "\tmax:" << blitCfg.bboxMax << "\n" 3299 << "Blit:\n" 3300 << "\tsrc: " << blitCfg.src << "\n" 3301 << "\tdst: " << blitCfg.dst << "\n" 3302 << "\tfilter: " << ((blitCfg.linear) ? ("linear") : ("nearest")) 3303 << tcu::TestLog::EndMessage; 3304 3305 gl.primitiveBoundingBoxEXT(blitCfg.bboxMin.x(), blitCfg.bboxMin.y(), blitCfg.bboxMin.z(), blitCfg.bboxMin.w(), 3306 blitCfg.bboxMax.x(), blitCfg.bboxMax.y(), blitCfg.bboxMax.z(), blitCfg.bboxMax.w()); 3307 3308 gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, (m_dst == TARGET_FBO) ? (**m_dstFbo) : (m_context.getRenderContext().getDefaultFramebuffer())); 3309 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 3310 gl.clear(GL_COLOR_BUFFER_BIT); 3311 3312 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, (m_src == TARGET_FBO) ? (**m_srcFbo) : (m_context.getRenderContext().getDefaultFramebuffer())); 3313 gl.blitFramebuffer(blitCfg.src.x(), blitCfg.src.y(), blitCfg.src.z(), blitCfg.src.w(), 3314 blitCfg.dst.x(), blitCfg.dst.y(), blitCfg.dst.z(), blitCfg.dst.w(), 3315 GL_COLOR_BUFFER_BIT, 3316 ((blitCfg.linear) ? (GL_LINEAR) : (GL_NEAREST))); 3317 GLU_EXPECT_NO_ERROR(gl.getError(), "blit"); 3318 3319 if (!verifyImage(blitCfg)) 3320 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected blit result"); 3321 3322 return (++m_iteration == (int)m_iterations.size()) ? (STOP) : (CONTINUE); 3323} 3324 3325bool BlitFboCase::verifyImage (const BlitArgs& args) 3326{ 3327 const int colorThreshold = 4; //!< this test case is not about how color is preserved, allow almost anything 3328 const tcu::IVec2 dstSize = (m_dst == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE)); 3329 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 3330 tcu::Surface viewport (dstSize.x(), dstSize.y()); 3331 tcu::Surface errorMask (dstSize.x(), dstSize.y()); 3332 bool anyError = false; 3333 3334 m_testCtx.getLog() 3335 << tcu::TestLog::Message 3336 << "Verifying blit result" 3337 << tcu::TestLog::EndMessage; 3338 3339 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, (m_dst == TARGET_FBO) ? (**m_dstFbo) : (m_context.getRenderContext().getDefaultFramebuffer())); 3340 glu::readPixels(m_context.getRenderContext(), 0, 0, viewport.getAccess()); 3341 3342 tcu::clear(errorMask.getAccess(), tcu::IVec4(0, 0, 0, 255)); 3343 3344 for (int y = 0; y < dstSize.y(); ++y) 3345 for (int x = 0; x < dstSize.x(); ++x) 3346 { 3347 const tcu::RGBA color = viewport.getPixel(x, y); 3348 const bool inside = (x >= args.dst.x() && x < args.dst.z() && y >= args.dst.y() && y < args.dst.w()); 3349 const bool error = (inside) ? (color.getGreen() < 255 - colorThreshold || color.getBlue() > colorThreshold) 3350 : (color.getRed() > colorThreshold || color.getGreen() > colorThreshold || color.getBlue() > colorThreshold); 3351 3352 if (error) 3353 { 3354 anyError = true; 3355 errorMask.setPixel(x, y, tcu::RGBA::red); 3356 } 3357 } 3358 3359 if (anyError) 3360 { 3361 m_testCtx.getLog() 3362 << tcu::TestLog::Message 3363 << "Image verification failed." 3364 << tcu::TestLog::EndMessage 3365 << tcu::TestLog::ImageSet("Images", "Image verification") 3366 << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess()) 3367 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess()) 3368 << tcu::TestLog::EndImageSet; 3369 return false; 3370 } 3371 else 3372 { 3373 m_testCtx.getLog() 3374 << tcu::TestLog::Message 3375 << "Result image ok." 3376 << tcu::TestLog::EndMessage 3377 << tcu::TestLog::ImageSet("Images", "Image verification") 3378 << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess()) 3379 << tcu::TestLog::EndImageSet; 3380 return true; 3381 } 3382} 3383 3384void BlitFboCase::fillSourceWithPattern (void) 3385{ 3386 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 3387 const tcu::IVec2 srcSize = (m_src == TARGET_DEFAULT) ? (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : (tcu::IVec2(FBO_SIZE, FBO_SIZE)); 3388 const int posLocation = gl.getAttribLocation(m_program->getProgram(), "a_position"); 3389 3390 gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, (m_src == TARGET_FBO) ? (**m_srcFbo) : (m_context.getRenderContext().getDefaultFramebuffer())); 3391 gl.viewport(0, 0, srcSize.x(), srcSize.y()); 3392 gl.useProgram(m_program->getProgram()); 3393 3394 gl.clearColor(0.0f, 0.0f, 1.0f, 1.0f); 3395 gl.clear(GL_COLOR_BUFFER_BIT); 3396 3397 gl.enableVertexAttribArray(posLocation); 3398 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 4 * (int)sizeof(float), NULL); 3399 gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4); 3400 GLU_EXPECT_NO_ERROR(gl.getError(), "draw"); 3401} 3402 3403class DepthDrawCase : public TestCase 3404{ 3405public: 3406 enum DepthType 3407 { 3408 DEPTH_BUILTIN = 0, 3409 DEPTH_USER_DEFINED, 3410 3411 DEPTH_LAST 3412 }; 3413 enum BBoxState 3414 { 3415 STATE_GLOBAL = 0, 3416 STATE_PER_PRIMITIVE, 3417 3418 STATE_LAST 3419 }; 3420 enum BBoxSize 3421 { 3422 BBOX_EQUAL = 0, 3423 BBOX_LARGER, 3424 3425 BBOX_LAST 3426 }; 3427 3428 DepthDrawCase (Context& context, const char* name, const char* description, DepthType depthType, BBoxState state, BBoxSize bboxSize); 3429 ~DepthDrawCase (void); 3430 3431private: 3432 void init (void); 3433 void deinit (void); 3434 IterateResult iterate (void); 3435 3436 std::string genVertexSource (void) const; 3437 std::string genFragmentSource (void) const; 3438 std::string genTessellationControlSource (void) const; 3439 std::string genTessellationEvaluationSource (void) const; 3440 void generateAttributeData (std::vector<tcu::Vec4>& data) const; 3441 bool verifyImage (const tcu::Surface& viewport) const; 3442 3443 enum 3444 { 3445 RENDER_AREA_SIZE = 256, 3446 }; 3447 3448 struct LayerInfo 3449 { 3450 float zOffset; 3451 float zScale; 3452 tcu::Vec4 color1; 3453 tcu::Vec4 color2; 3454 }; 3455 3456 const int m_numLayers; 3457 const int m_gridSize; 3458 3459 const DepthType m_depthType; 3460 const BBoxState m_state; 3461 const BBoxSize m_bboxSize; 3462 3463 de::MovePtr<glu::ShaderProgram> m_program; 3464 de::MovePtr<glu::Buffer> m_vbo; 3465 std::vector<LayerInfo> m_layers; 3466}; 3467 3468DepthDrawCase::DepthDrawCase (Context& context, const char* name, const char* description, DepthType depthType, BBoxState state, BBoxSize bboxSize) 3469 : TestCase (context, name, description) 3470 , m_numLayers (14) 3471 , m_gridSize (24) 3472 , m_depthType (depthType) 3473 , m_state (state) 3474 , m_bboxSize (bboxSize) 3475{ 3476 DE_ASSERT(depthType < DEPTH_LAST); 3477 DE_ASSERT(state < STATE_LAST); 3478 DE_ASSERT(bboxSize < BBOX_LAST); 3479} 3480 3481DepthDrawCase::~DepthDrawCase (void) 3482{ 3483 deinit(); 3484} 3485 3486void DepthDrawCase::init (void) 3487{ 3488 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 3489 3490 // requirements 3491 3492 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box")) 3493 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension"); 3494 if (m_state == STATE_PER_PRIMITIVE && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader")) 3495 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension"); 3496 if (m_context.getRenderTarget().getDepthBits() == 0) 3497 throw tcu::NotSupportedError("Test requires depth buffer"); 3498 if (m_context.getRenderTarget().getWidth() < RENDER_AREA_SIZE || m_context.getRenderTarget().getHeight() < RENDER_AREA_SIZE) 3499 throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_AREA_SIZE) + "x" + de::toString<int>(RENDER_AREA_SIZE) + " viewport"); 3500 3501 // log 3502 m_testCtx.getLog() 3503 << tcu::TestLog::Message 3504 << "Rendering multiple triangle grids with with different z coordinates.\n" 3505 << "Topmost grid is green-yellow, other grids are blue-red.\n" 3506 << "Expecting only the green-yellow grid to be visible.\n" 3507 << "Setting primitive bounding box " 3508 << ((m_bboxSize == BBOX_EQUAL) ? ("to exactly cover") : ("to cover")) 3509 << ((m_state == STATE_GLOBAL) ? (" each grid") : (" each triangle")) 3510 << ((m_bboxSize == BBOX_EQUAL) ? (".") : (" and include some padding.")) 3511 << "\n" 3512 << "Set bounding box using " 3513 << ((m_state == STATE_GLOBAL) ? ("PRIMITIVE_BOUNDING_BOX_EXT state") : ("gl_BoundingBoxEXT output")) 3514 << "\n" 3515 << ((m_depthType == DEPTH_USER_DEFINED) ? ("Fragment depth is set in the fragment shader") : ("")) 3516 << tcu::TestLog::EndMessage; 3517 3518 // resources 3519 3520 { 3521 glu::ProgramSources sources; 3522 sources << glu::VertexSource(genVertexSource()); 3523 sources << glu::FragmentSource(genFragmentSource()); 3524 3525 if (m_state == STATE_PER_PRIMITIVE) 3526 sources << glu::TessellationControlSource(genTessellationControlSource()) 3527 << glu::TessellationEvaluationSource(genTessellationEvaluationSource()); 3528 3529 m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), sources)); 3530 GLU_EXPECT_NO_ERROR(gl.getError(), "build program"); 3531 3532 { 3533 const tcu::ScopedLogSection section(m_testCtx.getLog(), "ShaderProgram", "Shader program"); 3534 m_testCtx.getLog() << *m_program; 3535 } 3536 3537 if (!m_program->isOk()) 3538 throw tcu::TestError("failed to build program"); 3539 } 3540 3541 { 3542 std::vector<tcu::Vec4> data; 3543 3544 generateAttributeData(data); 3545 3546 m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext())); 3547 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo); 3548 gl.bufferData(GL_ARRAY_BUFFER, (int)(sizeof(tcu::Vec4) * data.size()), &data[0], GL_STATIC_DRAW); 3549 GLU_EXPECT_NO_ERROR(gl.getError(), "buf upload"); 3550 } 3551 3552 // gen layers 3553 { 3554 de::Random rnd(0x12345); 3555 3556 m_layers.resize(m_numLayers); 3557 for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx) 3558 { 3559 m_layers[layerNdx].zOffset = ((float)layerNdx / m_numLayers) * 2.0f - 1.0f; 3560 m_layers[layerNdx].zScale = (2.0f / m_numLayers); 3561 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)); 3562 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)); 3563 } 3564 rnd.shuffle(m_layers.begin(), m_layers.end()); 3565 } 3566} 3567 3568void DepthDrawCase::deinit (void) 3569{ 3570 m_program.clear(); 3571 m_vbo.clear(); 3572} 3573 3574DepthDrawCase::IterateResult DepthDrawCase::iterate (void) 3575{ 3576 const bool hasTessellation = (m_state == STATE_PER_PRIMITIVE); 3577 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 3578 const glw::GLint posLocation = gl.getAttribLocation(m_program->getProgram(), "a_position"); 3579 const glw::GLint colLocation = gl.getAttribLocation(m_program->getProgram(), "a_colorMix"); 3580 const glw::GLint depthBiasLocation = gl.getUniformLocation(m_program->getProgram(), "u_depthBias"); 3581 const glw::GLint depthScaleLocation = gl.getUniformLocation(m_program->getProgram(), "u_depthScale"); 3582 const glw::GLint color1Location = gl.getUniformLocation(m_program->getProgram(), "u_color1"); 3583 const glw::GLint color2Location = gl.getUniformLocation(m_program->getProgram(), "u_color2"); 3584 3585 tcu::Surface viewport (RENDER_AREA_SIZE, RENDER_AREA_SIZE); 3586 de::Random rnd (0x213237); 3587 3588 TCU_CHECK(posLocation != -1); 3589 TCU_CHECK(colLocation != -1); 3590 TCU_CHECK(depthBiasLocation != -1); 3591 TCU_CHECK(depthScaleLocation != -1); 3592 TCU_CHECK(color1Location != -1); 3593 TCU_CHECK(color2Location != -1); 3594 3595 gl.viewport(0, 0, RENDER_AREA_SIZE, RENDER_AREA_SIZE); 3596 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 3597 gl.clearDepthf(1.0f); 3598 gl.depthFunc(GL_LESS); 3599 gl.enable(GL_DEPTH_TEST); 3600 gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 3601 GLU_EXPECT_NO_ERROR(gl.getError(), "setup viewport"); 3602 3603 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo); 3604 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, (int)(8 * sizeof(float)), (const float*)DE_NULL); 3605 gl.vertexAttribPointer(colLocation, 4, GL_FLOAT, GL_FALSE, (int)(8 * sizeof(float)), (const float*)DE_NULL + 4); 3606 gl.enableVertexAttribArray(posLocation); 3607 gl.enableVertexAttribArray(colLocation); 3608 gl.useProgram(m_program->getProgram()); 3609 GLU_EXPECT_NO_ERROR(gl.getError(), "setup va"); 3610 3611 if (hasTessellation) 3612 gl.patchParameteri(GL_PATCH_VERTICES, 3); 3613 3614 for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx) 3615 { 3616 gl.uniform1f(depthBiasLocation, m_layers[layerNdx].zOffset); 3617 gl.uniform1f(depthScaleLocation, m_layers[layerNdx].zScale); 3618 gl.uniform4fv(color1Location, 1, m_layers[layerNdx].color1.getPtr()); 3619 gl.uniform4fv(color2Location, 1, m_layers[layerNdx].color2.getPtr()); 3620 3621 if (m_state == STATE_GLOBAL) 3622 { 3623 const float negPadding = (m_bboxSize == BBOX_EQUAL) ? (0.0f) : (rnd.getFloat() * 0.3f); 3624 const float posPadding = (m_bboxSize == BBOX_EQUAL) ? (0.0f) : (rnd.getFloat() * 0.3f); 3625 3626 gl.primitiveBoundingBoxEXT(-1.0f, -1.0f, m_layers[layerNdx].zOffset - negPadding, 1.0f, 3627 1.0f, 1.0f, (m_layers[layerNdx].zOffset + m_layers[layerNdx].zScale + posPadding), 1.0f); 3628 } 3629 3630 gl.drawArrays((hasTessellation) ? (GL_PATCHES) : (GL_TRIANGLES), 0, m_gridSize * m_gridSize * 6); 3631 } 3632 3633 glu::readPixels(m_context.getRenderContext(), 0, 0, viewport.getAccess()); 3634 GLU_EXPECT_NO_ERROR(gl.getError(), "render and read"); 3635 3636 if (verifyImage(viewport)) 3637 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 3638 else 3639 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed"); 3640 3641 return STOP; 3642} 3643 3644std::string DepthDrawCase::genVertexSource (void) const 3645{ 3646 const bool hasTessellation = (m_state == STATE_PER_PRIMITIVE); 3647 std::ostringstream buf; 3648 3649 buf << "#version 310 es\n" 3650 "in highp vec4 a_position;\n" 3651 "in highp vec4 a_colorMix;\n" 3652 "out highp vec4 vtx_colorMix;\n"; 3653 3654 if (!hasTessellation && m_depthType == DEPTH_USER_DEFINED) 3655 buf << "out highp float v_fragDepth;\n"; 3656 3657 if (!hasTessellation) 3658 buf << "uniform highp float u_depthBias;\n" 3659 "uniform highp float u_depthScale;\n"; 3660 3661 buf << "\n" 3662 "void main()\n" 3663 "{\n"; 3664 3665 if (hasTessellation) 3666 buf << " gl_Position = a_position;\n"; 3667 else if (m_depthType == DEPTH_USER_DEFINED) 3668 buf << " highp float dummyZ = a_position.z;\n" 3669 " highp float writtenZ = a_position.w;\n" 3670 " gl_Position = vec4(a_position.xy, dummyZ, 1.0);\n" 3671 " v_fragDepth = writtenZ * u_depthScale + u_depthBias;\n"; 3672 else 3673 buf << " highp float writtenZ = a_position.w;\n" 3674 " gl_Position = vec4(a_position.xy, writtenZ * u_depthScale + u_depthBias, 1.0);\n"; 3675 3676 buf << " vtx_colorMix = a_colorMix;\n" 3677 "}\n"; 3678 3679 return buf.str(); 3680} 3681 3682std::string DepthDrawCase::genFragmentSource (void) const 3683{ 3684 const bool hasTessellation = (m_state == STATE_PER_PRIMITIVE); 3685 const char* const colorMixName = (hasTessellation) ? ("tess_eval_colorMix") : ("vtx_colorMix"); 3686 std::ostringstream buf; 3687 3688 buf << "#version 310 es\n" 3689 "in mediump vec4 " << colorMixName << ";\n"; 3690 3691 if (m_depthType == DEPTH_USER_DEFINED) 3692 buf << "in mediump float v_fragDepth;\n"; 3693 3694 buf << "layout(location = 0) out mediump vec4 o_color;\n" 3695 "uniform highp vec4 u_color1;\n" 3696 "uniform highp vec4 u_color2;\n" 3697 "\n" 3698 "void main()\n" 3699 "{\n" 3700 " o_color = mix(u_color1, u_color2, " << colorMixName << ");\n"; 3701 3702 if (m_depthType == DEPTH_USER_DEFINED) 3703 buf << " gl_FragDepth = v_fragDepth * 0.5 + 0.5;\n"; 3704 3705 buf << "}\n"; 3706 3707 return buf.str(); 3708} 3709 3710std::string DepthDrawCase::genTessellationControlSource (void) const 3711{ 3712 std::ostringstream buf; 3713 3714 buf << "#version 310 es\n" 3715 "#extension GL_EXT_tessellation_shader : require\n" 3716 "#extension GL_EXT_primitive_bounding_box : require\n" 3717 "layout(vertices=3) out;\n" 3718 "\n" 3719 "uniform highp float u_depthBias;\n" 3720 "uniform highp float u_depthScale;\n" 3721 "\n" 3722 "in highp vec4 vtx_colorMix[];\n" 3723 "out highp vec4 tess_ctrl_colorMix[];\n" 3724 "\n" 3725 "void main()\n" 3726 "{\n" 3727 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" 3728 " tess_ctrl_colorMix[gl_InvocationID] = vtx_colorMix[0];\n" 3729 "\n" 3730 " gl_TessLevelOuter[0] = 2.8;\n" 3731 " gl_TessLevelOuter[1] = 2.8;\n" 3732 " gl_TessLevelOuter[2] = 2.8;\n" 3733 " gl_TessLevelInner[0] = 2.8;\n" 3734 "\n" 3735 " // real Z stored in w component\n" 3736 " highp vec4 minBound = vec4(min(min(vec3(gl_in[0].gl_Position.xy, gl_in[0].gl_Position.w * u_depthScale + u_depthBias),\n" 3737 " vec3(gl_in[1].gl_Position.xy, gl_in[1].gl_Position.w * u_depthScale + u_depthBias)),\n" 3738 " vec3(gl_in[2].gl_Position.xy, gl_in[2].gl_Position.w * u_depthScale + u_depthBias)), 1.0);\n" 3739 " highp vec4 maxBound = vec4(max(max(vec3(gl_in[0].gl_Position.xy, gl_in[0].gl_Position.w * u_depthScale + u_depthBias),\n" 3740 " vec3(gl_in[1].gl_Position.xy, gl_in[1].gl_Position.w * u_depthScale + u_depthBias)),\n" 3741 " vec3(gl_in[2].gl_Position.xy, gl_in[2].gl_Position.w * u_depthScale + u_depthBias)), 1.0);\n"; 3742 3743 if (m_bboxSize == BBOX_EQUAL) 3744 buf << " gl_BoundingBoxEXT[0] = minBound;\n" 3745 " gl_BoundingBoxEXT[1] = maxBound;\n"; 3746 else 3747 buf << " highp float nedPadding = mod(gl_in[0].gl_Position.z, 0.3);\n" 3748 " highp float posPadding = mod(gl_in[1].gl_Position.z, 0.3);\n" 3749 " gl_BoundingBoxEXT[0] = minBound - vec4(0.0, 0.0, nedPadding, 0.0);\n" 3750 " gl_BoundingBoxEXT[1] = maxBound + vec4(0.0, 0.0, posPadding, 0.0);\n"; 3751 3752 buf << "}\n"; 3753 3754 return buf.str(); 3755} 3756 3757std::string DepthDrawCase::genTessellationEvaluationSource (void) const 3758{ 3759 std::ostringstream buf; 3760 3761 buf << "#version 310 es\n" 3762 "#extension GL_EXT_tessellation_shader : require\n" 3763 "#extension GL_EXT_gpu_shader5 : require\n" 3764 "layout(triangles) in;\n" 3765 "\n" 3766 "in highp vec4 tess_ctrl_colorMix[];\n" 3767 "out highp vec4 tess_eval_colorMix;\n"; 3768 3769 if (m_depthType == DEPTH_USER_DEFINED) 3770 buf << "out highp float v_fragDepth;\n"; 3771 3772 buf << "uniform highp float u_depthBias;\n" 3773 "uniform highp float u_depthScale;\n" 3774 "\n" 3775 "precise gl_Position;\n" 3776 "\n" 3777 "void main()\n" 3778 "{\n" 3779 " 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"; 3780 3781 if (m_depthType == DEPTH_USER_DEFINED) 3782 buf << " highp float dummyZ = tessellatedPos.z;\n" 3783 " highp float writtenZ = tessellatedPos.w;\n" 3784 " gl_Position = vec4(tessellatedPos.xy, dummyZ, 1.0);\n" 3785 " v_fragDepth = writtenZ * u_depthScale + u_depthBias;\n"; 3786 else 3787 buf << " highp float writtenZ = tessellatedPos.w;\n" 3788 " gl_Position = vec4(tessellatedPos.xy, writtenZ * u_depthScale + u_depthBias, 1.0);\n"; 3789 3790 buf << " tess_eval_colorMix = tess_ctrl_colorMix[0];\n" 3791 "}\n"; 3792 3793 return buf.str(); 3794} 3795 3796void DepthDrawCase::generateAttributeData (std::vector<tcu::Vec4>& data) const 3797{ 3798 const tcu::Vec4 color1 (0.0f, 0.0f, 0.0f, 0.0f); // mix weights 3799 const tcu::Vec4 color2 (1.0f, 1.0f, 1.0f, 1.0f); 3800 std::vector<int> cellOrder (m_gridSize * m_gridSize); 3801 de::Random rnd (0xAB54321); 3802 3803 // generate grid with cells in random order 3804 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx) 3805 cellOrder[ndx] = ndx; 3806 rnd.shuffle(cellOrder.begin(), cellOrder.end()); 3807 3808 data.resize(m_gridSize * m_gridSize * 6 * 2); 3809 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx) 3810 { 3811 const int cellNdx = cellOrder[ndx]; 3812 const int cellX = cellNdx % m_gridSize; 3813 const int cellY = cellNdx / m_gridSize; 3814 const tcu::Vec4& cellColor = ((cellX+cellY)%2 == 0) ? (color1) : (color2); 3815 3816 data[ndx * 6 * 2 + 0] = tcu::Vec4((cellX+0) / float(m_gridSize) * 2.0f - 1.0f, (cellY+0) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 1] = cellColor; 3817 data[ndx * 6 * 2 + 2] = tcu::Vec4((cellX+1) / float(m_gridSize) * 2.0f - 1.0f, (cellY+1) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 3] = cellColor; 3818 data[ndx * 6 * 2 + 4] = tcu::Vec4((cellX+0) / float(m_gridSize) * 2.0f - 1.0f, (cellY+1) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 5] = cellColor; 3819 data[ndx * 6 * 2 + 6] = tcu::Vec4((cellX+0) / float(m_gridSize) * 2.0f - 1.0f, (cellY+0) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 7] = cellColor; 3820 data[ndx * 6 * 2 + 8] = tcu::Vec4((cellX+1) / float(m_gridSize) * 2.0f - 1.0f, (cellY+0) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 9] = cellColor; 3821 data[ndx * 6 * 2 + 10] = tcu::Vec4((cellX+1) / float(m_gridSize) * 2.0f - 1.0f, (cellY+1) / float(m_gridSize) * 2.0f - 1.0f, 0.0f, 0.0f); data[ndx * 6 * 2 + 11] = cellColor; 3822 3823 // Fill Z with random values (fake Z) 3824 for (int vtxNdx = 0; vtxNdx < 6; ++vtxNdx) 3825 data[ndx * 6 * 2 + 2*vtxNdx].z() = rnd.getFloat(0.0f, 1.0); 3826 3827 // Fill W with other random values (written Z) 3828 for (int vtxNdx = 0; vtxNdx < 6; ++vtxNdx) 3829 data[ndx * 6 * 2 + 2*vtxNdx].w() = rnd.getFloat(0.0f, 1.0); 3830 } 3831} 3832 3833bool DepthDrawCase::verifyImage (const tcu::Surface& viewport) const 3834{ 3835 tcu::Surface errorMask (viewport.getWidth(), viewport.getHeight()); 3836 bool anyError = false; 3837 3838 tcu::clear(errorMask.getAccess(), tcu::IVec4(0,0,0,255)); 3839 3840 for (int y = 0; y < viewport.getHeight(); ++y) 3841 for (int x = 0; x < viewport.getWidth(); ++x) 3842 { 3843 const tcu::RGBA pixel = viewport.getPixel(x, y); 3844 bool error = false; 3845 3846 // expect green, yellow or a combination of these 3847 if (pixel.getGreen() != 255 || pixel.getBlue() != 0) 3848 error = true; 3849 3850 if (error) 3851 { 3852 errorMask.setPixel(x, y, tcu::RGBA::red); 3853 anyError = true; 3854 } 3855 } 3856 3857 if (anyError) 3858 m_testCtx.getLog() 3859 << tcu::TestLog::Message 3860 << "Image verification failed." 3861 << tcu::TestLog::EndMessage 3862 << tcu::TestLog::ImageSet("Images", "Image verification") 3863 << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess()) 3864 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess()) 3865 << tcu::TestLog::EndImageSet; 3866 else 3867 m_testCtx.getLog() 3868 << tcu::TestLog::Message 3869 << "Result image ok." 3870 << tcu::TestLog::EndMessage 3871 << tcu::TestLog::ImageSet("Images", "Image verification") 3872 << tcu::TestLog::Image("Viewport", "Viewport contents", viewport.getAccess()) 3873 << tcu::TestLog::EndImageSet; 3874 3875 return !anyError; 3876} 3877 3878class ClearCase : public TestCase 3879{ 3880public: 3881 enum 3882 { 3883 SCISSOR_CLEAR_BIT = 1 << 0, 3884 DRAW_TRIANGLE_BIT = 1 << 1, 3885 PER_PRIMITIVE_BBOX_BIT = 1 << 2, 3886 FULLSCREEN_SCISSOR_BIT = 1 << 3, 3887 }; 3888 3889 ClearCase (Context& context, const char* name, const char* description, deUint32 flags); 3890 ~ClearCase (void); 3891 3892private: 3893 struct DrawObject 3894 { 3895 int firstNdx; 3896 int numVertices; 3897 }; 3898 3899 void init (void); 3900 void deinit (void); 3901 IterateResult iterate (void); 3902 3903 void createVbo (void); 3904 void createProgram (void); 3905 void renderTo (tcu::Surface& dst, bool useBBox); 3906 bool verifyImagesEqual (const tcu::PixelBufferAccess& withoutBBox, const tcu::PixelBufferAccess& withBBox); 3907 bool verifyImageResultValid (const tcu::PixelBufferAccess& result); 3908 3909 std::string genVertexSource (void) const; 3910 std::string genFragmentSource (void) const; 3911 std::string genTessellationControlSource (bool setBBox) const; 3912 std::string genTessellationEvaluationSource (void) const; 3913 3914 const bool m_scissoredClear; 3915 const bool m_fullscreenScissor; 3916 const bool m_drawTriangles; 3917 const bool m_useGlobalState; 3918 3919 de::MovePtr<glu::Buffer> m_vbo; 3920 de::MovePtr<glu::ShaderProgram> m_perPrimitiveProgram; 3921 de::MovePtr<glu::ShaderProgram> m_basicProgram; 3922 std::vector<DrawObject> m_drawObjects; 3923 std::vector<tcu::Vec4> m_objectVertices; 3924}; 3925 3926ClearCase::ClearCase (Context& context, const char* name, const char* description, deUint32 flags) 3927 : TestCase (context, name, description) 3928 , m_scissoredClear ((flags & SCISSOR_CLEAR_BIT) != 0) 3929 , m_fullscreenScissor ((flags & FULLSCREEN_SCISSOR_BIT) != 0) 3930 , m_drawTriangles ((flags & DRAW_TRIANGLE_BIT) != 0) 3931 , m_useGlobalState ((flags & PER_PRIMITIVE_BBOX_BIT) == 0) 3932{ 3933 DE_ASSERT(m_useGlobalState || m_drawTriangles); // per-triangle bbox requires triangles 3934 DE_ASSERT(!m_fullscreenScissor || m_scissoredClear); // fullscreenScissor requires scissoredClear 3935} 3936 3937ClearCase::~ClearCase (void) 3938{ 3939 deinit(); 3940} 3941 3942void ClearCase::init (void) 3943{ 3944 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box")) 3945 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension"); 3946 if (m_drawTriangles && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader")) 3947 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension"); 3948 3949 m_testCtx.getLog() 3950 << tcu::TestLog::Message 3951 << "Doing multiple" 3952 << ((m_scissoredClear) ? (" scissored") : ("")) 3953 << " color buffer clears" 3954 << ((m_drawTriangles) ? (" and drawing some geometry between them") : ("")) 3955 << ".\n" 3956 << ((m_scissoredClear && m_fullscreenScissor) ? ("Setting scissor area to cover entire viewport.\n") : ("")) 3957 << "Rendering with and without setting the bounding box.\n" 3958 << "Expecting bounding box to have no effect on clears (i.e. results are constant).\n" 3959 << "Set bounding box using " 3960 << ((m_useGlobalState) ? ("PRIMITIVE_BOUNDING_BOX_EXT state") : ("gl_BoundingBoxEXT output")) 3961 << ".\n" 3962 << "Clear color is green with yellowish shades.\n" 3963 << ((m_drawTriangles) ? ("Primitive color is yellow with greenish shades.\n") : ("")) 3964 << tcu::TestLog::EndMessage; 3965 3966 if (m_drawTriangles) 3967 { 3968 createVbo(); 3969 createProgram(); 3970 } 3971} 3972 3973void ClearCase::deinit (void) 3974{ 3975 m_vbo.clear(); 3976 m_perPrimitiveProgram.clear(); 3977 m_basicProgram.clear(); 3978 m_drawObjects = std::vector<DrawObject>(); 3979 m_objectVertices = std::vector<tcu::Vec4>(); 3980} 3981 3982ClearCase::IterateResult ClearCase::iterate (void) 3983{ 3984 const tcu::IVec2 renderTargetSize (m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight()); 3985 tcu::Surface resultWithoutBBox (renderTargetSize.x(), renderTargetSize.y()); 3986 tcu::Surface resultWithBBox (renderTargetSize.x(), renderTargetSize.y()); 3987 3988 // render with and without bbox set 3989 for (int passNdx = 0; passNdx < 2; ++passNdx) 3990 { 3991 const bool useBBox = (passNdx == 1); 3992 tcu::Surface& destination = (useBBox) ? (resultWithBBox) : (resultWithoutBBox); 3993 3994 renderTo(destination, useBBox); 3995 } 3996 3997 // Verify images are equal and that the image does not contain (trivially detectable) garbage 3998 3999 if (!verifyImagesEqual(resultWithoutBBox.getAccess(), resultWithBBox.getAccess())) 4000 { 4001 // verifyImagesEqual will print out the image and error mask 4002 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed"); 4003 } 4004 else if (!verifyImageResultValid(resultWithBBox.getAccess())) 4005 { 4006 // verifyImageResultValid will print out the image and error mask 4007 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Result verification failed"); 4008 } 4009 else 4010 { 4011 m_testCtx.getLog() 4012 << tcu::TestLog::Message 4013 << "Image comparison passed." 4014 << tcu::TestLog::EndMessage 4015 << tcu::TestLog::ImageSet("Images", "Image verification") 4016 << tcu::TestLog::Image("Result", "Result", resultWithBBox.getAccess()) 4017 << tcu::TestLog::EndImageSet; 4018 4019 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 4020 } 4021 4022 return STOP; 4023} 4024 4025void ClearCase::createVbo (void) 4026{ 4027 const int numObjects = 16; 4028 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 4029 de::Random rnd (deStringHash(getName())); 4030 4031 m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext())); 4032 4033 for (int objectNdx = 0; objectNdx < numObjects; ++objectNdx) 4034 { 4035 const int numTriangles = rnd.getInt(1, 4); 4036 const float minX = rnd.getFloat(-1.2f, 0.8f); 4037 const float minY = rnd.getFloat(-1.2f, 0.8f); 4038 const float maxX = minX + rnd.getFloat(0.2f, 1.0f); 4039 const float maxY = minY + rnd.getFloat(0.2f, 1.0f); 4040 4041 DrawObject drawObject; 4042 drawObject.firstNdx = (int)m_objectVertices.size(); 4043 drawObject.numVertices = numTriangles * 3; 4044 4045 m_drawObjects.push_back(drawObject); 4046 4047 for (int triangleNdx = 0; triangleNdx < numTriangles; ++triangleNdx) 4048 for (int vertexNdx = 0; vertexNdx < 3; ++vertexNdx) 4049 { 4050 const float posX = rnd.getFloat(minX, maxX); 4051 const float posY = rnd.getFloat(minY, maxY); 4052 const float posZ = rnd.getFloat(-0.7f, 0.7f); 4053 const float posW = rnd.getFloat(0.9f, 1.1f); 4054 4055 m_objectVertices.push_back(tcu::Vec4(posX, posY, posZ, posW)); 4056 } 4057 } 4058 4059 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo); 4060 gl.bufferData(GL_ARRAY_BUFFER, (int)(m_objectVertices.size() * sizeof(tcu::Vec4)), &m_objectVertices[0], GL_STATIC_DRAW); 4061 GLU_EXPECT_NO_ERROR(gl.getError(), "buffer upload"); 4062} 4063 4064void ClearCase::createProgram (void) 4065{ 4066 m_basicProgram = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), 4067 glu::ProgramSources() 4068 << glu::VertexSource(genVertexSource()) 4069 << glu::FragmentSource(genFragmentSource()) 4070 << glu::TessellationControlSource(genTessellationControlSource(false)) 4071 << glu::TessellationEvaluationSource(genTessellationEvaluationSource()))); 4072 4073 m_testCtx.getLog() 4074 << tcu::TestLog::Section("Program", "Shader program") 4075 << *m_basicProgram 4076 << tcu::TestLog::EndSection; 4077 4078 if (!m_basicProgram->isOk()) 4079 throw tcu::TestError("shader build failed"); 4080 4081 if (!m_useGlobalState) 4082 { 4083 m_perPrimitiveProgram = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), 4084 glu::ProgramSources() 4085 << glu::VertexSource(genVertexSource()) 4086 << glu::FragmentSource(genFragmentSource()) 4087 << glu::TessellationControlSource(genTessellationControlSource(true)) 4088 << glu::TessellationEvaluationSource(genTessellationEvaluationSource()))); 4089 4090 m_testCtx.getLog() 4091 << tcu::TestLog::Section("PerPrimitiveProgram", "Shader program that sets the bounding box") 4092 << *m_perPrimitiveProgram 4093 << tcu::TestLog::EndSection; 4094 4095 if (!m_perPrimitiveProgram->isOk()) 4096 throw tcu::TestError("shader build failed"); 4097 } 4098} 4099 4100void ClearCase::renderTo (tcu::Surface& dst, bool useBBox) 4101{ 4102 const int numOps = 45; 4103 const tcu::Vec4 yellow (1.0f, 1.0f, 0.0f, 1.0f); 4104 const tcu::Vec4 green (0.0f, 1.0f, 0.0f, 1.0f); 4105 const tcu::IVec2 renderTargetSize (m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight()); 4106 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 4107 de::Random rnd (deStringHash(getName())); 4108 glu::VertexArray vao (m_context.getRenderContext()); 4109 4110 // always do the initial clear 4111 gl.disable(GL_SCISSOR_TEST); 4112 gl.viewport(0, 0, renderTargetSize.x(), renderTargetSize.y()); 4113 gl.clearColor(yellow.x(), yellow.y(), yellow.z(), yellow.w()); 4114 gl.clear(GL_COLOR_BUFFER_BIT); 4115 gl.finish(); 4116 4117 // prepare draw 4118 if (m_scissoredClear) 4119 gl.enable(GL_SCISSOR_TEST); 4120 4121 if (m_drawTriangles) 4122 { 4123 const deUint32 programHandle = (m_useGlobalState || !useBBox) ? (m_basicProgram->getProgram()) : (m_perPrimitiveProgram->getProgram()); 4124 const int positionAttribLoc = gl.getAttribLocation(programHandle, "a_position"); 4125 4126 TCU_CHECK(positionAttribLoc != -1); 4127 4128 gl.useProgram(programHandle); 4129 gl.bindVertexArray(*vao); 4130 gl.enableVertexAttribArray(positionAttribLoc); 4131 gl.vertexAttribPointer(positionAttribLoc, 4, GL_FLOAT, GL_FALSE, (int)sizeof(tcu::Vec4), DE_NULL); 4132 gl.patchParameteri(GL_PATCH_VERTICES, 3); 4133 } 4134 4135 // do random scissor/clearldraw operations 4136 for (int opNdx = 0; opNdx < numOps; ++opNdx) 4137 { 4138 const int drawObjNdx = (m_drawTriangles) ? (rnd.getInt(0, (int)m_drawObjects.size() - 1)) : (0); 4139 const int objectVertexStartNdx = (m_drawTriangles) ? (m_drawObjects[drawObjNdx].firstNdx) : (0); 4140 const int objectVertexLength = (m_drawTriangles) ? (m_drawObjects[drawObjNdx].numVertices) : (0); 4141 tcu::Vec4 bboxMin; 4142 tcu::Vec4 bboxMax; 4143 4144 if (m_drawTriangles) 4145 { 4146 bboxMin = tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f); 4147 bboxMax = tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f); 4148 4149 // calc bbox 4150 for (int vertexNdx = objectVertexStartNdx; vertexNdx < objectVertexStartNdx + objectVertexLength; ++vertexNdx) 4151 for (int componentNdx = 0; componentNdx < 4; ++componentNdx) 4152 { 4153 bboxMin[componentNdx] = de::min(bboxMin[componentNdx], m_objectVertices[vertexNdx][componentNdx]); 4154 bboxMax[componentNdx] = de::max(bboxMax[componentNdx], m_objectVertices[vertexNdx][componentNdx]); 4155 } 4156 } 4157 else 4158 { 4159 // no geometry, just random something 4160 bboxMin.x() = rnd.getFloat(-1.2f, 1.0f); 4161 bboxMin.y() = rnd.getFloat(-1.2f, 1.0f); 4162 bboxMin.z() = rnd.getFloat(-1.2f, 1.0f); 4163 bboxMin.w() = 1.0f; 4164 bboxMax.x() = bboxMin.x() + rnd.getFloat(0.2f, 1.0f); 4165 bboxMax.y() = bboxMin.y() + rnd.getFloat(0.2f, 1.0f); 4166 bboxMax.z() = bboxMin.z() + rnd.getFloat(0.2f, 1.0f); 4167 bboxMax.w() = 1.0f; 4168 } 4169 4170 if (m_scissoredClear) 4171 { 4172 const int scissorX = (m_fullscreenScissor) ? (0) : rnd.getInt(0, renderTargetSize.x()-1); 4173 const int scissorY = (m_fullscreenScissor) ? (0) : rnd.getInt(0, renderTargetSize.y()-1); 4174 const int scissorW = (m_fullscreenScissor) ? (renderTargetSize.x()) : rnd.getInt(0, renderTargetSize.x()-scissorX); 4175 const int scissorH = (m_fullscreenScissor) ? (renderTargetSize.y()) : rnd.getInt(0, renderTargetSize.y()-scissorY); 4176 4177 gl.scissor(scissorX, scissorY, scissorW, scissorH); 4178 } 4179 4180 { 4181 const tcu::Vec4 color = tcu::mix(green, yellow, rnd.getFloat() * 0.4f); // greenish 4182 gl.clearColor(color.x(), color.y(), color.z(), color.w()); 4183 gl.clear(GL_COLOR_BUFFER_BIT); 4184 } 4185 4186 if (useBBox) 4187 { 4188 DE_ASSERT(m_useGlobalState || m_drawTriangles); // !m_useGlobalState -> m_drawTriangles 4189 if (m_useGlobalState) 4190 gl.primitiveBoundingBoxEXT(bboxMin.x(), bboxMin.y(), bboxMin.z(), bboxMin.w(), 4191 bboxMax.x(), bboxMax.y(), bboxMax.z(), bboxMax.w()); 4192 } 4193 4194 if (m_drawTriangles) 4195 gl.drawArrays(GL_PATCHES, objectVertexStartNdx, objectVertexLength); 4196 } 4197 4198 GLU_EXPECT_NO_ERROR(gl.getError(), "post draw"); 4199 glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess()); 4200} 4201 4202bool ClearCase::verifyImagesEqual (const tcu::PixelBufferAccess& withoutBBox, const tcu::PixelBufferAccess& withBBox) 4203{ 4204 DE_ASSERT(withoutBBox.getWidth() == withBBox.getWidth()); 4205 DE_ASSERT(withoutBBox.getHeight() == withBBox.getHeight()); 4206 4207 tcu::Surface errorMask (withoutBBox.getWidth(), withoutBBox.getHeight()); 4208 bool anyError = false; 4209 4210 tcu::clear(errorMask.getAccess(), tcu::RGBA::green.toIVec()); 4211 4212 for (int y = 0; y < withoutBBox.getHeight(); ++y) 4213 for (int x = 0; x < withoutBBox.getWidth(); ++x) 4214 { 4215 if (withoutBBox.getPixelInt(x, y) != withBBox.getPixelInt(x, y)) 4216 { 4217 errorMask.setPixel(x, y, tcu::RGBA::red); 4218 anyError = true; 4219 } 4220 } 4221 4222 if (anyError) 4223 { 4224 m_testCtx.getLog() 4225 << tcu::TestLog::Message 4226 << "Image comparison failed." 4227 << tcu::TestLog::EndMessage 4228 << tcu::TestLog::ImageSet("Images", "Image comparison") 4229 << tcu::TestLog::Image("WithoutBBox", "Result with bounding box not set", withoutBBox) 4230 << tcu::TestLog::Image("WithBBox", "Result with bounding box set", withBBox) 4231 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess()) 4232 << tcu::TestLog::EndImageSet; 4233 } 4234 4235 return !anyError; 4236} 4237 4238bool ClearCase::verifyImageResultValid (const tcu::PixelBufferAccess& result) 4239{ 4240 tcu::Surface errorMask (result.getWidth(), result.getHeight()); 4241 bool anyError = false; 4242 4243 tcu::clear(errorMask.getAccess(), tcu::RGBA::green.toIVec()); 4244 4245 for (int y = 0; y < result.getHeight(); ++y) 4246 for (int x = 0; x < result.getWidth(); ++x) 4247 { 4248 const tcu::IVec4 pixel = result.getPixelInt(x, y); 4249 4250 // allow green, yellow and any shade between 4251 if (pixel[1] != 255 || pixel[2] != 0) 4252 { 4253 errorMask.setPixel(x, y, tcu::RGBA::red); 4254 anyError = true; 4255 } 4256 } 4257 4258 if (anyError) 4259 { 4260 m_testCtx.getLog() 4261 << tcu::TestLog::Message 4262 << "Image verification failed." 4263 << tcu::TestLog::EndMessage 4264 << tcu::TestLog::ImageSet("Images", "Image verification") 4265 << tcu::TestLog::Image("ResultImage", "Result image", result) 4266 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask) 4267 << tcu::TestLog::EndImageSet; 4268 } 4269 4270 return !anyError; 4271} 4272 4273static const char* const s_yellowishPosOnlyVertexSource = "#version 310 es\n" 4274 "in highp vec4 a_position;\n" 4275 "out highp vec4 v_vertex_color;\n" 4276 "void main()\n" 4277 "{\n" 4278 " gl_Position = a_position;\n" 4279 " // yellowish shade\n" 4280 " highp float redComponent = 0.5 + float(gl_VertexID % 5) / 8.0;\n" 4281 " v_vertex_color = vec4(redComponent, 1.0, 0.0, 1.0);\n" 4282 "}\n"; 4283 4284static const char* const s_basicColorFragmentSource = "#version 310 es\n" 4285 "in mediump vec4 v_color;\n" 4286 "layout(location = 0) out mediump vec4 o_color;\n" 4287 "void main()\n" 4288 "{\n" 4289 " o_color = v_color;\n" 4290 "}\n"; 4291 4292 4293static const char* const s_basicColorTessEvalSource = "#version 310 es\n" 4294 "#extension GL_EXT_tessellation_shader : require\n" 4295 "#extension GL_EXT_gpu_shader5 : require\n" 4296 "layout(triangles) in;\n" 4297 "in highp vec4 v_tess_eval_color[];\n" 4298 "out highp vec4 v_color;\n" 4299 "precise gl_Position;\n" 4300 "void main()\n" 4301 "{\n" 4302 " gl_Position = gl_TessCoord.x * gl_in[0].gl_Position\n" 4303 " + gl_TessCoord.y * gl_in[1].gl_Position\n" 4304 " + gl_TessCoord.z * gl_in[2].gl_Position;\n" 4305 " v_color = gl_TessCoord.x * v_tess_eval_color[0]\n" 4306 " + gl_TessCoord.y * v_tess_eval_color[1]\n" 4307 " + gl_TessCoord.z * v_tess_eval_color[2];\n" 4308 "}\n"; 4309 4310std::string ClearCase::genVertexSource (void) const 4311{ 4312 return s_yellowishPosOnlyVertexSource; 4313} 4314 4315std::string ClearCase::genFragmentSource (void) const 4316{ 4317 return s_basicColorFragmentSource; 4318} 4319 4320std::string ClearCase::genTessellationControlSource (bool setBBox) const 4321{ 4322 std::ostringstream buf; 4323 4324 buf << "#version 310 es\n" 4325 "#extension GL_EXT_tessellation_shader : require\n"; 4326 4327 if (setBBox) 4328 buf << "#extension GL_EXT_primitive_bounding_box : require\n"; 4329 4330 buf << "layout(vertices=3) out;\n" 4331 "in highp vec4 v_vertex_color[];\n" 4332 "out highp vec4 v_tess_eval_color[];\n" 4333 "void main()\n" 4334 "{\n" 4335 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" 4336 " v_tess_eval_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n" 4337 " gl_TessLevelOuter[0] = 2.8;\n" 4338 " gl_TessLevelOuter[1] = 2.8;\n" 4339 " gl_TessLevelOuter[2] = 2.8;\n" 4340 " gl_TessLevelInner[0] = 2.8;\n"; 4341 4342 if (setBBox) 4343 { 4344 buf << "\n" 4345 " gl_BoundingBoxEXT[0] = min(min(gl_in[0].gl_Position,\n" 4346 " gl_in[1].gl_Position),\n" 4347 " gl_in[2].gl_Position);\n" 4348 " gl_BoundingBoxEXT[1] = max(max(gl_in[0].gl_Position,\n" 4349 " gl_in[1].gl_Position),\n" 4350 " gl_in[2].gl_Position);\n"; 4351 } 4352 4353 buf << "}\n"; 4354 return buf.str(); 4355} 4356 4357std::string ClearCase::genTessellationEvaluationSource (void) const 4358{ 4359 return s_basicColorTessEvalSource; 4360} 4361 4362class ViewportCallOrderCase : public TestCase 4363{ 4364public: 4365 enum CallOrder 4366 { 4367 VIEWPORT_FIRST = 0, 4368 BBOX_FIRST, 4369 4370 ORDER_LAST 4371 }; 4372 4373 ViewportCallOrderCase (Context& context, const char* name, const char* description, CallOrder callOrder); 4374 ~ViewportCallOrderCase (void); 4375 4376private: 4377 void init (void); 4378 void deinit (void); 4379 IterateResult iterate (void); 4380 4381 void genVbo (void); 4382 void genProgram (void); 4383 bool verifyImage (const tcu::PixelBufferAccess& result); 4384 4385 std::string genVertexSource (void) const; 4386 std::string genFragmentSource (void) const; 4387 std::string genTessellationControlSource (void) const; 4388 std::string genTessellationEvaluationSource (void) const; 4389 4390 const CallOrder m_callOrder; 4391 4392 de::MovePtr<glu::Buffer> m_vbo; 4393 de::MovePtr<glu::ShaderProgram> m_program; 4394 int m_numVertices; 4395}; 4396 4397ViewportCallOrderCase::ViewportCallOrderCase (Context& context, const char* name, const char* description, CallOrder callOrder) 4398 : TestCase (context, name, description) 4399 , m_callOrder (callOrder) 4400 , m_numVertices (-1) 4401{ 4402 DE_ASSERT(m_callOrder < ORDER_LAST); 4403} 4404 4405ViewportCallOrderCase::~ViewportCallOrderCase (void) 4406{ 4407 deinit(); 4408} 4409 4410void ViewportCallOrderCase::init (void) 4411{ 4412 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box")) 4413 throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension"); 4414 4415 m_testCtx.getLog() 4416 << tcu::TestLog::Message 4417 << "Testing call order of state setting functions have no effect on the rendering.\n" 4418 << "Setting viewport and bounding box in the following order:\n" 4419 << ((m_callOrder == VIEWPORT_FIRST) 4420 ? ("\tFirst viewport with glViewport function.\n") 4421 : ("\tFirst bounding box with glPrimitiveBoundingBoxEXT function.\n")) 4422 << ((m_callOrder == VIEWPORT_FIRST) 4423 ? ("\tThen bounding box with glPrimitiveBoundingBoxEXT function.\n") 4424 : ("\tThen viewport with glViewport function.\n")) 4425 << "Verifying rendering result." 4426 << tcu::TestLog::EndMessage; 4427 4428 // resources 4429 genVbo(); 4430 genProgram(); 4431} 4432 4433void ViewportCallOrderCase::deinit (void) 4434{ 4435 m_vbo.clear(); 4436 m_program.clear(); 4437} 4438 4439ViewportCallOrderCase::IterateResult ViewportCallOrderCase::iterate (void) 4440{ 4441 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 4442 const tcu::IVec2 viewportSize = tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight()); 4443 const glw::GLint posLocation = gl.getAttribLocation(m_program->getProgram(), "a_position"); 4444 tcu::Surface resultSurface (viewportSize.x(), viewportSize.y()); 4445 4446 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 4447 gl.clear(GL_COLOR_BUFFER_BIT); 4448 4449 // set state 4450 for (int orderNdx = 0; orderNdx < 2; ++orderNdx) 4451 { 4452 if ((orderNdx == 0 && m_callOrder == VIEWPORT_FIRST) || 4453 (orderNdx == 1 && m_callOrder == BBOX_FIRST)) 4454 { 4455 m_testCtx.getLog() 4456 << tcu::TestLog::Message 4457 << "Setting viewport to cover the left half of the render target.\n" 4458 << "\t(0, 0, " << (viewportSize.x()/2) << ", " << viewportSize.y() << ")" 4459 << tcu::TestLog::EndMessage; 4460 4461 gl.viewport(0, 0, viewportSize.x()/2, viewportSize.y()); 4462 } 4463 else 4464 { 4465 m_testCtx.getLog() 4466 << tcu::TestLog::Message 4467 << "Setting bounding box to cover the right half of the clip space.\n" 4468 << "\t(0.0, -1.0, -1.0, 1.0) .. (1.0, 1.0, 1.0f, 1.0)" 4469 << tcu::TestLog::EndMessage; 4470 4471 gl.primitiveBoundingBoxEXT(0.0f, -1.0f, -1.0f, 1.0f, 4472 1.0f, 1.0f, 1.0f, 1.0f); 4473 } 4474 } 4475 4476 m_testCtx.getLog() 4477 << tcu::TestLog::Message 4478 << "Rendering mesh covering the right half of the clip space." 4479 << tcu::TestLog::EndMessage; 4480 4481 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo); 4482 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, sizeof(float[4]), (const float*)DE_NULL); 4483 gl.enableVertexAttribArray(posLocation); 4484 gl.useProgram(m_program->getProgram()); 4485 gl.patchParameteri(GL_PATCH_VERTICES, 3); 4486 gl.drawArrays(GL_PATCHES, 0, m_numVertices); 4487 GLU_EXPECT_NO_ERROR(gl.getError(), "post-draw"); 4488 4489 m_testCtx.getLog() 4490 << tcu::TestLog::Message 4491 << "Verifying image" 4492 << tcu::TestLog::EndMessage; 4493 glu::readPixels(m_context.getRenderContext(), 0, 0, resultSurface.getAccess()); 4494 4495 if (!verifyImage(resultSurface.getAccess())) 4496 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed"); 4497 else 4498 { 4499 m_testCtx.getLog() 4500 << tcu::TestLog::Message 4501 << "Result ok." 4502 << tcu::TestLog::EndMessage 4503 << tcu::TestLog::ImageSet("Images", "Image verification") 4504 << tcu::TestLog::Image("Result", "Result", resultSurface.getAccess()) 4505 << tcu::TestLog::EndImageSet; 4506 4507 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 4508 } 4509 return STOP; 4510} 4511 4512void ViewportCallOrderCase::genVbo (void) 4513{ 4514 const int gridSize = 6; 4515 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 4516 std::vector<tcu::Vec4> data (gridSize * gridSize * 2 * 3); 4517 std::vector<int> cellOrder (gridSize * gridSize * 2); 4518 de::Random rnd (0x55443322); 4519 4520 // generate grid with triangles in random order 4521 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx) 4522 cellOrder[ndx] = ndx; 4523 rnd.shuffle(cellOrder.begin(), cellOrder.end()); 4524 4525 // generate grid filling the right half of the clip space: (x: 0.0, y: -1.0) .. (x: 1.0, y: 1.0) 4526 for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx) 4527 { 4528 const int cellNdx = cellOrder[ndx]; 4529 const bool cellSide = ((cellNdx % 2) == 0); 4530 const int cellX = (cellNdx / 2) % gridSize; 4531 const int cellY = (cellNdx / 2) / gridSize; 4532 4533 if (cellSide) 4534 { 4535 data[ndx * 3 + 0] = tcu::Vec4((cellX+0) / float(gridSize), ((cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f); 4536 data[ndx * 3 + 1] = tcu::Vec4((cellX+1) / float(gridSize), ((cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f); 4537 data[ndx * 3 + 2] = tcu::Vec4((cellX+0) / float(gridSize), ((cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f); 4538 } 4539 else 4540 { 4541 data[ndx * 3 + 0] = tcu::Vec4((cellX+0) / float(gridSize), ((cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f); 4542 data[ndx * 3 + 1] = tcu::Vec4((cellX+1) / float(gridSize), ((cellY+0) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f); 4543 data[ndx * 3 + 2] = tcu::Vec4((cellX+1) / float(gridSize), ((cellY+1) / float(gridSize)) * 2.0f - 1.0f, 0.0f, 1.0f); 4544 } 4545 } 4546 4547 m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext())); 4548 gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo); 4549 gl.bufferData(GL_ARRAY_BUFFER, (int)(data.size() * sizeof(tcu::Vec4)), &data[0], GL_STATIC_DRAW); 4550 GLU_EXPECT_NO_ERROR(gl.getError(), "create vbo"); 4551 4552 m_numVertices = (int)data.size(); 4553} 4554 4555void ViewportCallOrderCase::genProgram (void) 4556{ 4557 m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), 4558 glu::ProgramSources() 4559 << glu::VertexSource(genVertexSource()) 4560 << glu::FragmentSource(genFragmentSource()) 4561 << glu::TessellationControlSource(genTessellationControlSource()) 4562 << glu::TessellationEvaluationSource(genTessellationEvaluationSource()))); 4563 4564 m_testCtx.getLog() 4565 << tcu::TestLog::Section("Program", "Shader program") 4566 << *m_program 4567 << tcu::TestLog::EndSection; 4568 4569 if (!m_program->isOk()) 4570 throw tcu::TestError("shader build failed"); 4571} 4572 4573bool ViewportCallOrderCase::verifyImage (const tcu::PixelBufferAccess& result) 4574{ 4575 const tcu::IVec2 insideBorder (deCeilFloatToInt32(0.25f * result.getWidth()) + 1, deFloorFloatToInt32(0.5f * result.getWidth()) - 1); 4576 const tcu::IVec2 outsideBorder (deFloorFloatToInt32(0.25f * result.getWidth()) - 1, deCeilFloatToInt32(0.5f * result.getWidth()) + 1); 4577 tcu::Surface errorMask (result.getWidth(), result.getHeight()); 4578 bool anyError = false; 4579 4580 tcu::clear(errorMask.getAccess(), tcu::RGBA::green.toIVec()); 4581 4582 for (int y = 0; y < result.getHeight(); ++y) 4583 for (int x = 0; x < result.getWidth(); ++x) 4584 { 4585 const tcu::IVec4 pixel = result.getPixelInt(x, y); 4586 const bool insideMeshArea = x >= insideBorder.x() && x <= insideBorder.x(); 4587 const bool outsideMeshArea = x <= outsideBorder.x() && x >= outsideBorder.x(); 4588 4589 // inside mesh, allow green, yellow and any shade between 4590 // outside mesh, allow background (black) only 4591 // in the border area, allow anything 4592 if ((insideMeshArea && (pixel[1] != 255 || pixel[2] != 0)) || 4593 (outsideMeshArea && (pixel[0] != 0 || pixel[1] != 0 || pixel[2] != 0))) 4594 { 4595 errorMask.setPixel(x, y, tcu::RGBA::red); 4596 anyError = true; 4597 } 4598 } 4599 4600 if (anyError) 4601 { 4602 m_testCtx.getLog() 4603 << tcu::TestLog::Message 4604 << "Image verification failed." 4605 << tcu::TestLog::EndMessage 4606 << tcu::TestLog::ImageSet("Images", "Image verification") 4607 << tcu::TestLog::Image("ResultImage", "Result image", result) 4608 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask) 4609 << tcu::TestLog::EndImageSet; 4610 } 4611 4612 return !anyError; 4613} 4614 4615std::string ViewportCallOrderCase::genVertexSource (void) const 4616{ 4617 return s_yellowishPosOnlyVertexSource; 4618} 4619 4620std::string ViewportCallOrderCase::genFragmentSource (void) const 4621{ 4622 return s_basicColorFragmentSource; 4623} 4624 4625std::string ViewportCallOrderCase::genTessellationControlSource (void) const 4626{ 4627 return "#version 310 es\n" 4628 "#extension GL_EXT_tessellation_shader : require\n" 4629 "layout(vertices=3) out;\n" 4630 "in highp vec4 v_vertex_color[];\n" 4631 "out highp vec4 v_tess_eval_color[];\n" 4632 "void main()\n" 4633 "{\n" 4634 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" 4635 " v_tess_eval_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n" 4636 " gl_TessLevelOuter[0] = 2.8;\n" 4637 " gl_TessLevelOuter[1] = 2.8;\n" 4638 " gl_TessLevelOuter[2] = 2.8;\n" 4639 " gl_TessLevelInner[0] = 2.8;\n" 4640 "}\n"; 4641} 4642 4643std::string ViewportCallOrderCase::genTessellationEvaluationSource (void) const 4644{ 4645 return s_basicColorTessEvalSource; 4646} 4647 4648} // anonymous 4649 4650PrimitiveBoundingBoxTests::PrimitiveBoundingBoxTests (Context& context) 4651 : TestCaseGroup(context, "primitive_bounding_box", "Tests for EXT_primitive_bounding_box") 4652{ 4653} 4654 4655PrimitiveBoundingBoxTests::~PrimitiveBoundingBoxTests (void) 4656{ 4657} 4658 4659void PrimitiveBoundingBoxTests::init (void) 4660{ 4661 static const struct 4662 { 4663 const char* name; 4664 const char* description; 4665 deUint32 methodFlags; 4666 } stateSetMethods[] = 4667 { 4668 { 4669 "global_state", 4670 "Set bounding box using PRIMITIVE_BOUNDING_BOX_EXT state", 4671 BBoxRenderCase::FLAG_SET_BBOX_STATE, 4672 }, 4673 { 4674 "tessellation_set_per_draw", 4675 "Set bounding box using gl_BoundingBoxEXT, use same value for all primitives", 4676 BBoxRenderCase::FLAG_SET_BBOX_OUTPUT, 4677 }, 4678 { 4679 "tessellation_set_per_primitive", 4680 "Set bounding box using gl_BoundingBoxEXT, use per-primitive bounding box", 4681 BBoxRenderCase::FLAG_SET_BBOX_OUTPUT | BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX, 4682 }, 4683 }; 4684 static const struct 4685 { 4686 const char* name; 4687 const char* description; 4688 deUint32 stageFlags; 4689 } pipelineConfigs[] = 4690 { 4691 { 4692 "vertex_fragment", 4693 "Render with vertex-fragment program", 4694 0u 4695 }, 4696 { 4697 "vertex_tessellation_fragment", 4698 "Render with vertex-tessellation{ctrl,eval}-fragment program", 4699 BBoxRenderCase::FLAG_TESSELLATION 4700 }, 4701 { 4702 "vertex_geometry_fragment", 4703 "Render with vertex-tessellation{ctrl,eval}-geometry-fragment program", 4704 BBoxRenderCase::FLAG_GEOMETRY 4705 }, 4706 { 4707 "vertex_tessellation_geometry_fragment", 4708 "Render with vertex-geometry-fragment program", 4709 BBoxRenderCase::FLAG_TESSELLATION | BBoxRenderCase::FLAG_GEOMETRY 4710 }, 4711 }; 4712 static const struct 4713 { 4714 const char* name; 4715 const char* description; 4716 deUint32 flags; 4717 deUint32 invalidFlags; 4718 deUint32 requiredFlags; 4719 } usageConfigs[] = 4720 { 4721 { 4722 "default_framebuffer_bbox_equal", 4723 "Render to default framebuffer, set tight bounding box", 4724 BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL, 4725 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX, 4726 0 4727 }, 4728 { 4729 "default_framebuffer_bbox_larger", 4730 "Render to default framebuffer, set padded bounding box", 4731 BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_LARGER, 4732 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX, 4733 0 4734 }, 4735 { 4736 "default_framebuffer_bbox_smaller", 4737 "Render to default framebuffer, set too small bounding box", 4738 BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_SMALLER, 4739 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX, 4740 0 4741 }, 4742 { 4743 "fbo_bbox_equal", 4744 "Render to texture, set tight bounding box", 4745 BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL, 4746 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX, 4747 0 4748 }, 4749 { 4750 "fbo_bbox_larger", 4751 "Render to texture, set padded bounding box", 4752 BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_LARGER, 4753 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX, 4754 0 4755 }, 4756 { 4757 "fbo_bbox_smaller", 4758 "Render to texture, set too small bounding box", 4759 BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_SMALLER, 4760 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX, 4761 0 4762 }, 4763 { 4764 "default_framebuffer", 4765 "Render to default framebuffer, set tight bounding box", 4766 BBoxRenderCase::FLAG_RENDERTARGET_DEFAULT | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL, 4767 0, 4768 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX 4769 }, 4770 { 4771 "fbo", 4772 "Render to texture, set tight bounding box", 4773 BBoxRenderCase::FLAG_RENDERTARGET_FBO | BBoxRenderCase::FLAG_BBOXSIZE_EQUAL, 4774 0, 4775 BBoxRenderCase::FLAG_PER_PRIMITIVE_BBOX 4776 }, 4777 }; 4778 enum PrimitiveRenderType 4779 { 4780 TYPE_TRIANGLE, 4781 TYPE_LINE, 4782 TYPE_POINT, 4783 }; 4784 const struct 4785 { 4786 const char* name; 4787 const char* description; 4788 PrimitiveRenderType type; 4789 deUint32 flags; 4790 } primitiveTypes[] = 4791 { 4792 { 4793 "triangles", 4794 "Triangle render tests", 4795 TYPE_TRIANGLE, 4796 0 4797 }, 4798 { 4799 "lines", 4800 "Line render tests", 4801 TYPE_LINE, 4802 0 4803 }, 4804 { 4805 "points", 4806 "Point render tests", 4807 TYPE_POINT, 4808 0 4809 }, 4810 { 4811 "wide_lines", 4812 "Wide line render tests", 4813 TYPE_LINE, 4814 LineRenderCase::LINEFLAG_WIDE 4815 }, 4816 { 4817 "wide_points", 4818 "Wide point render tests", 4819 TYPE_POINT, 4820 PointRenderCase::POINTFLAG_WIDE 4821 }, 4822 }; 4823 4824 // .state_query 4825 { 4826 tcu::TestCaseGroup* const stateQueryGroup = new tcu::TestCaseGroup(m_testCtx, "state_query", "State queries"); 4827 addChild(stateQueryGroup); 4828 4829 stateQueryGroup->addChild(new InitialValueCase (m_context, "initial_value", "Initial value case")); 4830 stateQueryGroup->addChild(new QueryCase (m_context, "getfloat", "getFloatv", QueryCase::QUERY_FLOAT)); 4831 stateQueryGroup->addChild(new QueryCase (m_context, "getboolean", "getBooleanv", QueryCase::QUERY_BOOLEAN)); 4832 stateQueryGroup->addChild(new QueryCase (m_context, "getinteger", "getIntegerv", QueryCase::QUERY_INT)); 4833 stateQueryGroup->addChild(new QueryCase (m_context, "getinteger64", "getInteger64v", QueryCase::QUERY_INT64)); 4834 } 4835 4836 // .triangles 4837 // .(wide_)lines 4838 // .(wide_)points 4839 for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); ++primitiveTypeNdx) 4840 { 4841 tcu::TestCaseGroup* const primitiveGroup = new tcu::TestCaseGroup(m_testCtx, primitiveTypes[primitiveTypeNdx].name, primitiveTypes[primitiveTypeNdx].description); 4842 addChild(primitiveGroup); 4843 4844 for (int stateSetMethodNdx = 0; stateSetMethodNdx < DE_LENGTH_OF_ARRAY(stateSetMethods); ++stateSetMethodNdx) 4845 { 4846 tcu::TestCaseGroup* const methodGroup = new tcu::TestCaseGroup(m_testCtx, stateSetMethods[stateSetMethodNdx].name, stateSetMethods[stateSetMethodNdx].description); 4847 primitiveGroup->addChild(methodGroup); 4848 4849 for (int pipelineConfigNdx = 0; pipelineConfigNdx < DE_LENGTH_OF_ARRAY(pipelineConfigs); ++pipelineConfigNdx) 4850 { 4851 if ((stateSetMethods[stateSetMethodNdx].methodFlags & BBoxRenderCase::FLAG_SET_BBOX_OUTPUT) != 0 && 4852 (pipelineConfigs[pipelineConfigNdx].stageFlags & BBoxRenderCase::FLAG_TESSELLATION) == 0) 4853 { 4854 // invalid config combination 4855 } 4856 else 4857 { 4858 tcu::TestCaseGroup* const pipelineGroup = new tcu::TestCaseGroup(m_testCtx, pipelineConfigs[pipelineConfigNdx].name, pipelineConfigs[pipelineConfigNdx].description); 4859 methodGroup->addChild(pipelineGroup); 4860 4861 for (int usageNdx = 0; usageNdx < DE_LENGTH_OF_ARRAY(usageConfigs); ++usageNdx) 4862 { 4863 const deUint32 flags = primitiveTypes[primitiveTypeNdx].flags | 4864 stateSetMethods[stateSetMethodNdx].methodFlags | 4865 pipelineConfigs[pipelineConfigNdx].stageFlags | 4866 usageConfigs[usageNdx].flags; 4867 4868 if (usageConfigs[usageNdx].invalidFlags && (flags & usageConfigs[usageNdx].invalidFlags) != 0) 4869 continue; 4870 if (usageConfigs[usageNdx].requiredFlags && (flags & usageConfigs[usageNdx].requiredFlags) == 0) 4871 continue; 4872 4873 switch (primitiveTypes[primitiveTypeNdx].type) 4874 { 4875 case TYPE_TRIANGLE: 4876 pipelineGroup->addChild(new GridRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags)); 4877 break; 4878 case TYPE_LINE: 4879 pipelineGroup->addChild(new LineRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags)); 4880 break; 4881 case TYPE_POINT: 4882 pipelineGroup->addChild(new PointRenderCase(m_context, usageConfigs[usageNdx].name, usageConfigs[usageNdx].description, flags)); 4883 break; 4884 default: 4885 DE_ASSERT(false); 4886 } 4887 } 4888 } 4889 } 4890 } 4891 } 4892 4893 // .depth 4894 { 4895 static const struct 4896 { 4897 const char* name; 4898 const char* description; 4899 DepthDrawCase::DepthType depthMethod; 4900 } depthMethods[] = 4901 { 4902 { 4903 "builtin_depth", 4904 "Fragment depth not modified in fragment shader", 4905 DepthDrawCase::DEPTH_BUILTIN 4906 }, 4907 { 4908 "user_defined_depth", 4909 "Fragment depth is defined in the fragment shader", 4910 DepthDrawCase::DEPTH_USER_DEFINED 4911 }, 4912 }; 4913 static const struct 4914 { 4915 const char* name; 4916 const char* description; 4917 DepthDrawCase::BBoxState bboxState; 4918 DepthDrawCase::BBoxSize bboxSize; 4919 } depthCases[] = 4920 { 4921 { 4922 "global_state_bbox_equal", 4923 "Test tight bounding box with global bbox state", 4924 DepthDrawCase::STATE_GLOBAL, 4925 DepthDrawCase::BBOX_EQUAL, 4926 }, 4927 { 4928 "global_state_bbox_larger", 4929 "Test padded bounding box with global bbox state", 4930 DepthDrawCase::STATE_GLOBAL, 4931 DepthDrawCase::BBOX_LARGER, 4932 }, 4933 { 4934 "per_primitive_bbox_equal", 4935 "Test tight bounding box with tessellation output bbox", 4936 DepthDrawCase::STATE_PER_PRIMITIVE, 4937 DepthDrawCase::BBOX_EQUAL, 4938 }, 4939 { 4940 "per_primitive_bbox_larger", 4941 "Test padded bounding box with tessellation output bbox", 4942 DepthDrawCase::STATE_PER_PRIMITIVE, 4943 DepthDrawCase::BBOX_LARGER, 4944 }, 4945 }; 4946 4947 tcu::TestCaseGroup* const depthGroup = new tcu::TestCaseGroup(m_testCtx, "depth", "Test bounding box depth component"); 4948 addChild(depthGroup); 4949 4950 // .builtin_depth 4951 // .user_defined_depth 4952 for (int depthNdx = 0; depthNdx < DE_LENGTH_OF_ARRAY(depthMethods); ++depthNdx) 4953 { 4954 tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, depthMethods[depthNdx].name, depthMethods[depthNdx].description); 4955 depthGroup->addChild(group); 4956 4957 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(depthCases); ++caseNdx) 4958 group->addChild(new DepthDrawCase(m_context, depthCases[caseNdx].name, depthCases[caseNdx].description, depthMethods[depthNdx].depthMethod, depthCases[caseNdx].bboxState, depthCases[caseNdx].bboxSize)); 4959 } 4960 } 4961 4962 // .blit_fbo 4963 { 4964 tcu::TestCaseGroup* const blitFboGroup = new tcu::TestCaseGroup(m_testCtx, "blit_fbo", "Test bounding box does not affect blitting"); 4965 addChild(blitFboGroup); 4966 4967 blitFboGroup->addChild(new BlitFboCase(m_context, "blit_default_to_fbo", "Blit from default fb to fbo", BlitFboCase::TARGET_DEFAULT, BlitFboCase::TARGET_FBO)); 4968 blitFboGroup->addChild(new BlitFboCase(m_context, "blit_fbo_to_default", "Blit from fbo to default fb", BlitFboCase::TARGET_FBO, BlitFboCase::TARGET_DEFAULT)); 4969 blitFboGroup->addChild(new BlitFboCase(m_context, "blit_fbo_to_fbo", "Blit from fbo to fbo", BlitFboCase::TARGET_FBO, BlitFboCase::TARGET_FBO)); 4970 } 4971 4972 // .clear 4973 { 4974 tcu::TestCaseGroup* const clearGroup = new tcu::TestCaseGroup(m_testCtx, "clear", "Test bounding box does not clears"); 4975 addChild(clearGroup); 4976 4977 clearGroup->addChild(new ClearCase(m_context, "full_clear", "Do full clears", 0)); 4978 clearGroup->addChild(new ClearCase(m_context, "full_clear_with_triangles", "Do full clears and render some geometry", ClearCase::DRAW_TRIANGLE_BIT)); 4979 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)); 4980 clearGroup->addChild(new ClearCase(m_context, "scissored_clear", "Do scissored clears", ClearCase::SCISSOR_CLEAR_BIT)); 4981 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)); 4982 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)); 4983 clearGroup->addChild(new ClearCase(m_context, "scissored_full_clear", "Do full clears with enabled scissor", ClearCase::FULLSCREEN_SCISSOR_BIT | ClearCase::SCISSOR_CLEAR_BIT)); 4984 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)); 4985 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)); 4986 } 4987 4988 // .call_order (Khronos bug #13262) 4989 { 4990 tcu::TestCaseGroup* const callOrderGroup = new tcu::TestCaseGroup(m_testCtx, "call_order", "Test viewport and bounding box calls have no effect"); 4991 addChild(callOrderGroup); 4992 4993 callOrderGroup->addChild(new ViewportCallOrderCase(m_context, "viewport_first_bbox_second", "Set up viewport first and bbox after", ViewportCallOrderCase::VIEWPORT_FIRST)); 4994 callOrderGroup->addChild(new ViewportCallOrderCase(m_context, "bbox_first_viewport_second", "Set up bbox first and viewport after", ViewportCallOrderCase::BBOX_FIRST)); 4995 } 4996} 4997 4998} // Functional 4999} // gles31 5000} // deqp 5001