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