1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL (ES) Module 3 * ----------------------------------------------- 4 * 5 * Copyright 2014 The Android Open Source Project 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 *//*! 20 * \file 21 * \brief Common object lifetime tests. 22 *//*--------------------------------------------------------------------*/ 23 24 #include "glsLifetimeTests.hpp" 25 26 #include "deString.h" 27 #include "deRandom.hpp" 28 #include "deSTLUtil.hpp" 29 #include "deStringUtil.hpp" 30 #include "tcuRGBA.hpp" 31 #include "tcuImageCompare.hpp" 32 #include "tcuRenderTarget.hpp" 33 #include "tcuStringTemplate.hpp" 34 #include "tcuTestLog.hpp" 35 #include "gluDrawUtil.hpp" 36 #include "gluObjectWrapper.hpp" 37 #include "gluPixelTransfer.hpp" 38 #include "gluShaderProgram.hpp" 39 #include "gluDefs.hpp" 40 #include "glwFunctions.hpp" 41 42 #include <vector> 43 #include <map> 44 #include <algorithm> 45 #include <sstream> 46 47 namespace deqp 48 { 49 namespace gls 50 { 51 namespace LifetimeTests 52 { 53 namespace details 54 { 55 56 using std::map; 57 using std::string; 58 using std::ostringstream; 59 using de::Random; 60 using tcu::RenderTarget; 61 using tcu::RGBA; 62 using tcu::StringTemplate; 63 using tcu::TestCase; 64 typedef TestCase::IterateResult IterateResult; 65 using tcu::TestLog; 66 using tcu::ScopedLogSection; 67 using glu::Program; 68 using glu::Shader; 69 using glu::Framebuffer; 70 using glu::SHADERTYPE_VERTEX; 71 using glu::SHADERTYPE_FRAGMENT; 72 using namespace glw; 73 74 enum { VIEWPORT_SIZE = 128, FRAMEBUFFER_SIZE = 128 }; 75 76 GLint getInteger (ContextWrapper& gl, GLenum queryParam) 77 { 78 GLint ret = 0; 79 GLU_CHECK_CALL_ERROR( 80 gl.glGetIntegerv(queryParam, &ret), 81 gl.glGetError()); 82 gl.log() << TestLog::Message << "// Single integer output: " << ret << TestLog::EndMessage; 83 return ret; 84 } 85 86 #define GLSL100_SRC(BODY) ("#version 100\n" #BODY "\n") 87 88 static const char* const s_vertexShaderSrc = GLSL100_SRC( 89 attribute vec2 pos; 90 void main() 91 { 92 gl_Position = vec4(pos.xy, 0.0, 1.0); 93 } 94 ); 95 96 static const char* const s_fragmentShaderSrc = GLSL100_SRC( 97 void main() 98 { 99 gl_FragColor = vec4(1.0); 100 } 101 ); 102 103 class CheckedShader : public Shader 104 { 105 public: 106 CheckedShader (const RenderContext& renderCtx, glu::ShaderType type, const string& src) 107 : Shader (renderCtx, type) 108 { 109 const char* const srcStr = src.c_str(); 110 setSources(1, &srcStr, DE_NULL); 111 compile(); 112 TCU_CHECK(getCompileStatus()); 113 } 114 }; 115 116 class CheckedProgram : public Program 117 { 118 public: 119 CheckedProgram (const RenderContext& renderCtx, GLuint vtxShader, GLuint fragShader) 120 : Program (renderCtx) 121 { 122 attachShader(vtxShader); 123 attachShader(fragShader); 124 link(); 125 TCU_CHECK(getLinkStatus()); 126 } 127 }; 128 129 ContextWrapper::ContextWrapper (const Context& ctx) 130 : CallLogWrapper (ctx.gl(), ctx.log()) 131 , m_ctx (ctx) 132 { 133 enableLogging(true); 134 } 135 136 void SimpleBinder::bind (GLuint name) 137 { 138 (this->*m_bindFunc)(m_bindTarget, name); 139 } 140 141 GLuint SimpleBinder::getBinding (void) 142 { 143 return getInteger(*this, m_bindingParam); 144 } 145 146 GLuint SimpleType::gen (void) 147 { 148 GLuint ret; 149 (this->*m_genFunc)(1, &ret); 150 return ret; 151 } 152 153 class VertexArrayBinder : public SimpleBinder 154 { 155 public: 156 VertexArrayBinder (Context& ctx) 157 : SimpleBinder (ctx, 0, GL_NONE, GL_VERTEX_ARRAY_BINDING, true) {} 158 void bind (GLuint name) { glBindVertexArray(name); } 159 }; 160 161 class QueryBinder : public Binder 162 { 163 public: 164 QueryBinder (Context& ctx) : Binder(ctx) {} 165 void bind (GLuint name) 166 { 167 if (name != 0) 168 glBeginQuery(GL_ANY_SAMPLES_PASSED, name); 169 else 170 glEndQuery(GL_ANY_SAMPLES_PASSED); 171 } 172 GLuint getBinding (void) { return 0; } 173 }; 174 175 bool ProgramType::isDeleteFlagged (GLuint name) 176 { 177 GLint deleteFlagged = 0; 178 glGetProgramiv(name, GL_DELETE_STATUS, &deleteFlagged); 179 return deleteFlagged != 0; 180 } 181 182 bool ShaderType::isDeleteFlagged (GLuint name) 183 { 184 GLint deleteFlagged = 0; 185 glGetShaderiv(name, GL_DELETE_STATUS, &deleteFlagged); 186 return deleteFlagged != 0; 187 } 188 189 void setupFbo (const Context& ctx, GLuint seed, GLuint fbo) 190 { 191 const Functions& gl = ctx.getRenderContext().getFunctions(); 192 193 GLU_CHECK_CALL_ERROR(gl.bindFramebuffer(GL_FRAMEBUFFER, fbo), 194 gl.getError()); 195 196 if (seed == 0) 197 { 198 gl.clearColor(0.0, 0.0, 0.0, 1.0); 199 GLU_CHECK_CALL_ERROR(gl.clear(GL_COLOR_BUFFER_BIT), gl.getError()); 200 } 201 else 202 { 203 Random rnd (seed); 204 const GLsizei width = rnd.getInt(0, FRAMEBUFFER_SIZE); 205 const GLsizei height = rnd.getInt(0, FRAMEBUFFER_SIZE); 206 const GLint x = rnd.getInt(0, FRAMEBUFFER_SIZE - width); 207 const GLint y = rnd.getInt(0, FRAMEBUFFER_SIZE - height); 208 const GLfloat r1 = rnd.getFloat(); 209 const GLfloat g1 = rnd.getFloat(); 210 const GLfloat b1 = rnd.getFloat(); 211 const GLfloat a1 = rnd.getFloat(); 212 const GLfloat r2 = rnd.getFloat(); 213 const GLfloat g2 = rnd.getFloat(); 214 const GLfloat b2 = rnd.getFloat(); 215 const GLfloat a2 = rnd.getFloat(); 216 217 GLU_CHECK_CALL_ERROR(gl.clearColor(r1, g1, b1, a1), gl.getError()); 218 GLU_CHECK_CALL_ERROR(gl.clear(GL_COLOR_BUFFER_BIT), gl.getError()); 219 gl.scissor(x, y, width, height); 220 gl.enable(GL_SCISSOR_TEST); 221 gl.clearColor(r2, g2, b2, a2); 222 gl.clear(GL_COLOR_BUFFER_BIT); 223 gl.disable(GL_SCISSOR_TEST); 224 } 225 226 gl.bindFramebuffer(GL_FRAMEBUFFER, 0); 227 GLU_CHECK_ERROR(gl.getError()); 228 } 229 230 void drawFbo (const Context& ctx, GLuint fbo, Surface& dst) 231 { 232 const RenderContext& renderCtx = ctx.getRenderContext(); 233 const Functions& gl = renderCtx.getFunctions(); 234 235 GLU_CHECK_CALL_ERROR( 236 gl.bindFramebuffer(GL_FRAMEBUFFER, fbo), 237 gl.getError()); 238 239 dst.setSize(FRAMEBUFFER_SIZE, FRAMEBUFFER_SIZE); 240 glu::readPixels(renderCtx, 0, 0, dst.getAccess()); 241 GLU_EXPECT_NO_ERROR(gl.getError(), "Read pixels from framebuffer"); 242 243 GLU_CHECK_CALL_ERROR( 244 gl.bindFramebuffer(GL_FRAMEBUFFER, 0), 245 gl.getError()); 246 } 247 248 GLuint getFboAttachment (const Functions& gl, GLuint fbo, GLenum requiredType) 249 { 250 GLint type = 0, name = 0; 251 gl.bindFramebuffer(GL_FRAMEBUFFER, fbo); 252 GLU_CHECK_CALL_ERROR( 253 gl.getFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 254 GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, 255 &type), 256 gl.getError()); 257 258 if (GLenum(type) != requiredType || GLenum(type) == GL_NONE) 259 return 0; 260 261 GLU_CHECK_CALL_ERROR( 262 gl.getFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 263 GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, 264 &name), 265 gl.getError()); 266 gl.bindFramebuffer(GL_FRAMEBUFFER, 0); 267 GLU_CHECK_ERROR(gl.getError()); 268 269 return name; 270 } 271 272 void FboAttacher::initAttachment (GLuint seed, GLuint element) 273 { 274 Binder& binder = *getElementType().binder(); 275 Framebuffer fbo(getRenderContext()); 276 277 enableLogging(false); 278 279 binder.enableLogging(false); 280 binder.bind(element); 281 initStorage(); 282 binder.bind(0); 283 binder.enableLogging(true); 284 285 attach(element, *fbo); 286 setupFbo(getContext(), seed, *fbo); 287 detach(element, *fbo); 288 289 enableLogging(true); 290 291 log() << TestLog::Message 292 << "// Drew to " << getElementType().getName() << " " << element 293 << " with seed " << seed << "." 294 << TestLog::EndMessage; 295 } 296 297 void FboInputAttacher::drawContainer (GLuint fbo, Surface& dst) 298 { 299 drawFbo(getContext(), fbo, dst); 300 log() << TestLog::Message 301 << "// Read pixels from framebuffer " << fbo << " to output image." 302 << TestLog::EndMessage; 303 } 304 305 void FboOutputAttacher::setupContainer (GLuint seed, GLuint fbo) 306 { 307 setupFbo(getContext(), seed, fbo); 308 log() << TestLog::Message 309 << "// Drew to framebuffer " << fbo << " with seed " << seed << "." 310 << TestLog::EndMessage; 311 } 312 313 void FboOutputAttacher::drawAttachment (GLuint element, Surface& dst) 314 { 315 Framebuffer fbo(getRenderContext()); 316 m_attacher.enableLogging(false); 317 m_attacher.attach(element, *fbo); 318 drawFbo(getContext(), *fbo, dst); 319 m_attacher.detach(element, *fbo); 320 m_attacher.enableLogging(true); 321 log() << TestLog::Message 322 << "// Read pixels from " << m_attacher.getElementType().getName() << " " << element 323 << " to output image." 324 << TestLog::EndMessage; 325 GLU_CHECK_ERROR(gl().getError()); 326 } 327 328 void TextureFboAttacher::attach (GLuint texture, GLuint fbo) 329 { 330 GLU_CHECK_CALL_ERROR( 331 glBindFramebuffer(GL_FRAMEBUFFER, fbo), 332 gl().getError()); 333 GLU_CHECK_CALL_ERROR( 334 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 335 GL_TEXTURE_2D, texture, 0), 336 gl().getError()); 337 GLU_CHECK_CALL_ERROR( 338 glBindFramebuffer(GL_FRAMEBUFFER, 0), 339 gl().getError()); 340 } 341 342 void TextureFboAttacher::detach (GLuint texture, GLuint fbo) 343 { 344 DE_UNREF(texture); 345 GLU_CHECK_CALL_ERROR( 346 glBindFramebuffer(GL_FRAMEBUFFER, fbo), 347 gl().getError()); 348 GLU_CHECK_CALL_ERROR( 349 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0), 350 gl().getError()); 351 GLU_CHECK_CALL_ERROR( 352 glBindFramebuffer(GL_FRAMEBUFFER, 0), 353 gl().getError()); 354 } 355 356 GLuint TextureFboAttacher::getAttachment (GLuint fbo) 357 { 358 return getFboAttachment(gl(), fbo, GL_TEXTURE); 359 } 360 361 void TextureFboAttacher::initStorage (void) 362 { 363 GLU_CHECK_CALL_ERROR( 364 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, FRAMEBUFFER_SIZE, FRAMEBUFFER_SIZE, 0, 365 GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, DE_NULL), 366 gl().getError()); 367 } 368 369 void RboFboAttacher::initStorage (void) 370 { 371 GLU_CHECK_CALL_ERROR( 372 glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, FRAMEBUFFER_SIZE, FRAMEBUFFER_SIZE), 373 gl().getError()); 374 } 375 376 void RboFboAttacher::attach (GLuint rbo, GLuint fbo) 377 { 378 GLU_CHECK_CALL_ERROR( 379 glBindFramebuffer(GL_FRAMEBUFFER, fbo), 380 gl().getError()); 381 GLU_CHECK_CALL_ERROR( 382 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo), 383 gl().getError()); 384 GLU_CHECK_CALL_ERROR( 385 glBindFramebuffer(GL_FRAMEBUFFER, 0), 386 gl().getError()); 387 } 388 389 void RboFboAttacher::detach (GLuint rbo, GLuint fbo) 390 { 391 DE_UNREF(rbo); 392 GLU_CHECK_CALL_ERROR( 393 glBindFramebuffer(GL_FRAMEBUFFER, fbo), 394 gl().getError()); 395 GLU_CHECK_CALL_ERROR( 396 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0), 397 gl().getError()); 398 GLU_CHECK_CALL_ERROR( 399 glBindFramebuffer(GL_FRAMEBUFFER, 0), 400 gl().getError()); 401 } 402 403 GLuint RboFboAttacher::getAttachment (GLuint fbo) 404 { 405 return getFboAttachment(gl(), fbo, GL_RENDERBUFFER); 406 } 407 408 static const char* const s_fragmentShaderTemplate = GLSL100_SRC( 409 void main() 410 { 411 gl_FragColor = vec4(${RED}, ${GREEN}, ${BLUE}, 1.0); 412 } 413 ); 414 415 void ShaderProgramAttacher::initAttachment (GLuint seed, GLuint shader) 416 { 417 using de::insert; 418 using de::floatToString; 419 420 Random rnd(seed); 421 map<string, string> params; 422 const StringTemplate sourceTmpl (s_fragmentShaderTemplate); 423 424 insert(params, "RED", floatToString(rnd.getFloat(), 4)); 425 insert(params, "GREEN", floatToString(rnd.getFloat(), 4)); 426 insert(params, "BLUE", floatToString(rnd.getFloat(), 4)); 427 428 { 429 const string source = sourceTmpl.specialize(params); 430 const char* const sourceStr = source.c_str(); 431 432 GLU_CHECK_CALL_ERROR(glShaderSource(shader, 1, &sourceStr, DE_NULL), gl().getError()); 433 GLU_CHECK_CALL_ERROR(glCompileShader(shader), gl().getError()); 434 435 { 436 GLint compileStatus = 0; 437 gl().getShaderiv(shader, GL_COMPILE_STATUS, &compileStatus); 438 TCU_CHECK_MSG(compileStatus != 0, sourceStr); 439 } 440 } 441 } 442 443 void ShaderProgramAttacher::attach (GLuint shader, GLuint program) 444 { 445 GLU_CHECK_CALL_ERROR( 446 glAttachShader(program, shader), 447 gl().getError()); 448 } 449 450 void ShaderProgramAttacher::detach (GLuint shader, GLuint program) 451 { 452 GLU_CHECK_CALL_ERROR( 453 glDetachShader(program, shader), 454 gl().getError()); 455 } 456 457 GLuint ShaderProgramAttacher::getAttachment (GLuint program) 458 { 459 GLuint shaders[2] = { 0, 0 }; 460 const GLsizei shadersLen = DE_LENGTH_OF_ARRAY(shaders); 461 GLsizei numShaders = 0; 462 GLuint ret = 0; 463 464 gl().getAttachedShaders(program, shadersLen, &numShaders, shaders); 465 466 // There should ever be at most one attached shader in normal use, but if 467 // something is wrong, the temporary vertex shader might not have been 468 // detached properly, so let's find the fragment shader explicitly. 469 for (int ndx = 0; ndx < de::min<GLsizei>(shadersLen, numShaders); ++ndx) 470 { 471 GLint shaderType = GL_NONE; 472 gl().getShaderiv(shaders[ndx], GL_SHADER_TYPE, &shaderType); 473 474 if (shaderType == GL_FRAGMENT_SHADER) 475 { 476 ret = shaders[ndx]; 477 break; 478 } 479 } 480 481 return ret; 482 } 483 484 void setViewport (const RenderContext& renderCtx, const Rectangle& rect) 485 { 486 renderCtx.getFunctions().viewport(rect.x, rect.y, rect.width, rect.height); 487 } 488 489 void readRectangle (const RenderContext& renderCtx, const Rectangle& rect, Surface& dst) 490 { 491 dst.setSize(rect.width, rect.height); 492 glu::readPixels(renderCtx, rect.x, rect.y, dst.getAccess()); 493 } 494 495 Rectangle randomViewport (const RenderContext& ctx, GLint maxWidth, GLint maxHeight, 496 Random& rnd) 497 { 498 const RenderTarget& target = ctx.getRenderTarget(); 499 const GLint width = de::min(target.getWidth(), maxWidth); 500 const GLint xOff = rnd.getInt(0, target.getWidth() - width); 501 const GLint height = de::min(target.getHeight(), maxHeight); 502 const GLint yOff = rnd.getInt(0, target.getHeight() - height); 503 504 return Rectangle(xOff, yOff, width, height); 505 } 506 507 void ShaderProgramInputAttacher::drawContainer (GLuint program, Surface& dst) 508 { 509 static const float s_vertices[6] = { -1.0, 0.0, 1.0, 1.0, 0.0, -1.0 }; 510 Random rnd (program); 511 CheckedShader vtxShader (getRenderContext(), 512 SHADERTYPE_VERTEX, s_vertexShaderSrc); 513 const Rectangle viewport = randomViewport(getRenderContext(), 514 VIEWPORT_SIZE, VIEWPORT_SIZE, rnd); 515 516 gl().attachShader(program, vtxShader.getShader()); 517 gl().linkProgram(program); 518 519 { 520 GLint linkStatus = 0; 521 gl().getProgramiv(program, GL_LINK_STATUS, &linkStatus); 522 TCU_CHECK(linkStatus != 0); 523 } 524 525 log() << TestLog::Message 526 << "// Attached a temporary vertex shader and linked program " << program 527 << TestLog::EndMessage; 528 529 setViewport(getRenderContext(), viewport); 530 log() << TestLog::Message << "// Positioned viewport randomly" << TestLog::EndMessage; 531 532 glUseProgram(program); 533 { 534 GLint posLoc = gl().getAttribLocation(program, "pos"); 535 TCU_CHECK(posLoc >= 0); 536 537 gl().enableVertexAttribArray(posLoc); 538 539 gl().clearColor(0, 0, 0, 1); 540 gl().clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 541 gl().vertexAttribPointer(posLoc, 2, GL_FLOAT, GL_FALSE, 0, s_vertices); 542 gl().drawArrays(GL_TRIANGLES, 0, 3); 543 544 gl().disableVertexAttribArray(posLoc); 545 log () << TestLog::Message << "// Drew a fixed triangle" << TestLog::EndMessage; 546 } 547 glUseProgram(0); 548 549 readRectangle(getRenderContext(), viewport, dst); 550 log() << TestLog::Message << "// Copied viewport to output image" << TestLog::EndMessage; 551 552 gl().detachShader(program, vtxShader.getShader()); 553 log() << TestLog::Message << "// Removed temporary vertex shader" << TestLog::EndMessage; 554 } 555 556 ES2Types::ES2Types (const Context& ctx) 557 : Types (ctx) 558 , m_bufferBind (ctx, &CallLogWrapper::glBindBuffer, 559 GL_ARRAY_BUFFER, GL_ARRAY_BUFFER_BINDING) 560 , m_bufferType (ctx, "buffer", &CallLogWrapper::glGenBuffers, 561 &CallLogWrapper::glDeleteBuffers, 562 &CallLogWrapper::glIsBuffer, &m_bufferBind) 563 , m_textureBind (ctx, &CallLogWrapper::glBindTexture, GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D) 564 , m_textureType (ctx, "texture", &CallLogWrapper::glGenTextures, 565 &CallLogWrapper::glDeleteTextures, 566 &CallLogWrapper::glIsTexture, &m_textureBind) 567 , m_rboBind (ctx, &CallLogWrapper::glBindRenderbuffer, 568 GL_RENDERBUFFER, GL_RENDERBUFFER_BINDING) 569 , m_rboType (ctx, "renderbuffer", 570 &CallLogWrapper::glGenRenderbuffers, 571 &CallLogWrapper::glDeleteRenderbuffers, 572 &CallLogWrapper::glIsRenderbuffer, &m_rboBind) 573 , m_fboBind (ctx, &CallLogWrapper::glBindFramebuffer, 574 GL_FRAMEBUFFER, GL_FRAMEBUFFER_BINDING) 575 , m_fboType (ctx, "framebuffer", 576 &CallLogWrapper::glGenFramebuffers, 577 &CallLogWrapper::glDeleteFramebuffers, 578 &CallLogWrapper::glIsFramebuffer, &m_fboBind) 579 , m_shaderType (ctx) 580 , m_programType (ctx) 581 , m_texFboAtt (ctx, m_textureType, m_fboType) 582 , m_texFboInAtt (m_texFboAtt) 583 , m_texFboOutAtt(m_texFboAtt) 584 , m_rboFboAtt (ctx, m_rboType, m_fboType) 585 , m_rboFboInAtt (m_rboFboAtt) 586 , m_rboFboOutAtt(m_rboFboAtt) 587 , m_shaderAtt (ctx, m_shaderType, m_programType) 588 , m_shaderInAtt (m_shaderAtt) 589 { 590 Type* const types[] = 591 { 592 &m_bufferType, &m_textureType, &m_rboType, &m_fboType, &m_shaderType, &m_programType 593 }; 594 m_types.insert(m_types.end(), DE_ARRAY_BEGIN(types), DE_ARRAY_END(types)); 595 596 m_attachers.push_back(&m_texFboAtt); 597 m_attachers.push_back(&m_rboFboAtt); 598 m_attachers.push_back(&m_shaderAtt); 599 600 m_inAttachers.push_back(&m_texFboInAtt); 601 m_inAttachers.push_back(&m_rboFboInAtt); 602 m_inAttachers.push_back(&m_shaderInAtt); 603 604 m_outAttachers.push_back(&m_texFboOutAtt); 605 m_outAttachers.push_back(&m_rboFboOutAtt); 606 } 607 608 class Name 609 { 610 public: 611 Name (Type& type) : m_type(type), m_name(type.gen()) {} 612 Name (Type& type, GLuint name) : m_type(type), m_name(name) {} 613 ~Name (void) { m_type.release(m_name); } 614 GLuint operator* (void) const { return m_name; } 615 616 private: 617 Type& m_type; 618 const GLuint m_name; 619 }; 620 621 class ResultCollector 622 { 623 public: 624 ResultCollector (TestContext& testCtx); 625 bool check (bool cond, const char* msg); 626 void fail (const char* msg); 627 void warn (const char* msg); 628 ~ResultCollector (void); 629 630 private: 631 void addResult (qpTestResult result, const char* msg); 632 633 TestContext& m_testCtx; 634 TestLog& m_log; 635 qpTestResult m_result; 636 const char* m_message; 637 }; 638 639 ResultCollector::ResultCollector (TestContext& testCtx) 640 : m_testCtx (testCtx) 641 , m_log (testCtx.getLog()) 642 , m_result (QP_TEST_RESULT_PASS) 643 , m_message ("Pass") 644 { 645 } 646 647 bool ResultCollector::check (bool cond, const char* msg) 648 { 649 if (!cond) 650 fail(msg); 651 return cond; 652 } 653 654 void ResultCollector::addResult (qpTestResult result, const char* msg) 655 { 656 m_log << TestLog::Message << "// Fail: " << msg << TestLog::EndMessage; 657 if (m_result == QP_TEST_RESULT_PASS) 658 { 659 m_result = result; 660 m_message = msg; 661 } 662 else 663 { 664 if (result == QP_TEST_RESULT_FAIL) 665 m_result = result; 666 m_message = "Multiple problems, see log for details"; 667 } 668 } 669 670 void ResultCollector::fail (const char* msg) 671 { 672 addResult(QP_TEST_RESULT_FAIL, msg); 673 } 674 675 void ResultCollector::warn (const char* msg) 676 { 677 addResult(QP_TEST_RESULT_QUALITY_WARNING, msg); 678 } 679 680 ResultCollector::~ResultCollector (void) 681 { 682 m_testCtx.setTestResult(m_result, m_message); 683 } 684 685 class TestBase : public TestCase, protected CallLogWrapper 686 { 687 protected: 688 TestBase (const char* name, 689 const char* description, 690 const Context& ctx); 691 692 // Copy ContextWrapper since MI (except for CallLogWrapper) is a no-no. 693 const Context& getContext (void) const { return m_ctx; } 694 const RenderContext& getRenderContext (void) const { return m_ctx.getRenderContext(); } 695 const Functions& gl (void) const { return m_ctx.gl(); } 696 TestLog& log (void) const { return m_ctx.log(); } 697 void init (void); 698 699 Context m_ctx; 700 Random m_rnd; 701 }; 702 703 TestBase::TestBase (const char* name, const char* description, const Context& ctx) 704 : TestCase (ctx.getTestContext(), name, description) 705 , CallLogWrapper (ctx.gl(), ctx.log()) 706 , m_ctx (ctx) 707 , m_rnd (deStringHash(name)) 708 { 709 enableLogging(true); 710 } 711 712 void TestBase::init (void) 713 { 714 m_rnd = Random(deStringHash(getName())); 715 } 716 717 class LifeTest : public TestBase 718 { 719 public: 720 typedef void (LifeTest::*TestFunction) (void); 721 722 LifeTest (const char* name, 723 const char* description, 724 Type& type, 725 TestFunction test) 726 : TestBase (name, description, type.getContext()) 727 , m_type (type) 728 , m_test (test) {} 729 730 IterateResult iterate (void); 731 732 void testGen (void); 733 void testDelete (void); 734 void testBind (void); 735 void testDeleteBound (void); 736 void testBindNoGen (void); 737 void testDeleteUsed (void); 738 739 private: 740 Binder& binder (void) { return *m_type.binder(); } 741 742 Type& m_type; 743 TestFunction m_test; 744 }; 745 746 IterateResult LifeTest::iterate (void) 747 { 748 (this->*m_test)(); 749 return STOP; 750 } 751 752 void LifeTest::testGen (void) 753 { 754 ResultCollector errors (getTestContext()); 755 Name name (m_type); 756 757 if (m_type.genCreates()) 758 errors.check(m_type.exists(*name), "Gen* should have created an object, but didn't"); 759 else 760 errors.check(!m_type.exists(*name), "Gen* should not have created an object, but did"); 761 } 762 763 void LifeTest::testDelete (void) 764 { 765 ResultCollector errors (getTestContext()); 766 GLuint name = m_type.gen(); 767 768 m_type.release(name); 769 errors.check(!m_type.exists(name), "Object still exists after deletion"); 770 } 771 772 void LifeTest::testBind (void) 773 { 774 ResultCollector errors (getTestContext()); 775 Name name (m_type); 776 777 binder().bind(*name); 778 GLU_EXPECT_NO_ERROR(gl().getError(), "Bind failed"); 779 errors.check(m_type.exists(*name), "Object does not exist after binding"); 780 binder().bind(0); 781 } 782 783 void LifeTest::testDeleteBound (void) 784 { 785 const GLuint id = m_type.gen(); 786 ResultCollector errors (getTestContext()); 787 788 binder().bind(id); 789 m_type.release(id); 790 791 if (m_type.nameLingers()) 792 { 793 errors.check(gl().getError() == GL_NO_ERROR, "Deleting bound object failed"); 794 errors.check(binder().getBinding() == id, 795 "Deleting bound object did not retain binding"); 796 errors.check(m_type.exists(id), 797 "Deleting bound object made its name invalid"); 798 errors.check(m_type.isDeleteFlagged(id), 799 "Deleting bound object did not flag the object for deletion"); 800 binder().bind(0); 801 } 802 else 803 { 804 errors.check(gl().getError() == GL_NO_ERROR, "Deleting bound object failed"); 805 errors.check(binder().getBinding() == 0, 806 "Deleting bound object did not remove binding"); 807 errors.check(!m_type.exists(id), 808 "Deleting bound object did not make its name invalid"); 809 binder().bind(0); 810 } 811 812 errors.check(binder().getBinding() == 0, "Unbinding didn't remove binding"); 813 errors.check(!m_type.exists(id), "Name is still valid after deleting and unbinding"); 814 } 815 816 void LifeTest::testBindNoGen (void) 817 { 818 ResultCollector errors (getTestContext()); 819 const GLuint id = m_rnd.getUint32(); 820 821 if (!errors.check(!m_type.exists(id), "Randomly chosen identifier already exists")) 822 return; 823 824 Name name (m_type, id); 825 binder().bind(*name); 826 827 if (binder().genRequired()) 828 { 829 errors.check(glGetError() == GL_INVALID_OPERATION, 830 "Did not fail when binding a name not generated by Gen* call"); 831 errors.check(!m_type.exists(*name), 832 "Bind* created an object for a name not generated by a Gen* call"); 833 } 834 else 835 { 836 errors.check(glGetError() == GL_NO_ERROR, 837 "Failed when binding a name not generated by Gen* call"); 838 errors.check(m_type.exists(*name), 839 "Object was not created by the Bind* call"); 840 } 841 } 842 843 void LifeTest::testDeleteUsed (void) 844 { 845 ResultCollector errors(getTestContext()); 846 GLuint programId = 0; 847 848 { 849 CheckedShader vtxShader (getRenderContext(), 850 SHADERTYPE_VERTEX, s_vertexShaderSrc); 851 CheckedShader fragShader (getRenderContext(), 852 SHADERTYPE_FRAGMENT, s_fragmentShaderSrc); 853 CheckedProgram program (getRenderContext(), 854 vtxShader.getShader(), fragShader.getShader()); 855 856 programId = program.getProgram(); 857 858 log() << TestLog::Message << "// Created and linked program " << programId 859 << TestLog::EndMessage; 860 GLU_CHECK_CALL_ERROR(glUseProgram(programId), gl().getError()); 861 862 log() << TestLog::Message << "// Deleted program " << programId 863 << TestLog::EndMessage; 864 } 865 TCU_CHECK(glIsProgram(programId)); 866 { 867 GLint deleteFlagged = 0; 868 glGetProgramiv(programId, GL_DELETE_STATUS, &deleteFlagged); 869 errors.check(deleteFlagged != 0, "Program object was not flagged as deleted"); 870 } 871 GLU_CHECK_CALL_ERROR(glUseProgram(0), gl().getError()); 872 errors.check(!gl().isProgram(programId), 873 "Deleted program name still valid after being made non-current"); 874 } 875 876 class AttachmentTest : public TestBase 877 { 878 public: 879 typedef void (AttachmentTest::*TestFunction) (void); 880 AttachmentTest (const char* name, 881 const char* description, 882 Attacher& attacher, 883 TestFunction test) 884 : TestBase (name, description, attacher.getContext()) 885 , m_attacher (attacher) 886 , m_test (test) {} 887 IterateResult iterate (void); 888 889 void testDeletedNames (void); 890 void testDeletedBinding (void); 891 void testDeletedReattach (void); 892 893 private: 894 Attacher& m_attacher; 895 const TestFunction m_test; 896 }; 897 898 IterateResult AttachmentTest::iterate (void) 899 { 900 (this->*m_test)(); 901 return STOP; 902 } 903 904 GLuint getAttachment (Attacher& attacher, GLuint container) 905 { 906 const GLuint queriedAttachment = attacher.getAttachment(container); 907 attacher.log() << TestLog::Message 908 << "// Result of query for " << attacher.getElementType().getName() 909 << " attached to " << attacher.getContainerType().getName() << " " 910 << container << ": " << queriedAttachment << "." 911 << TestLog::EndMessage; 912 return queriedAttachment; 913 } 914 915 void AttachmentTest::testDeletedNames (void) 916 { 917 Type& elemType = m_attacher.getElementType(); 918 Type& containerType = m_attacher.getContainerType(); 919 Name container (containerType); 920 ResultCollector errors (getTestContext()); 921 GLuint elementId = 0; 922 923 { 924 Name element(elemType); 925 elementId = *element; 926 m_attacher.initAttachment(0, *element); 927 m_attacher.attach(*element, *container); 928 errors.check(getAttachment(m_attacher, *container) == elementId, 929 "Attachment name not returned by query even before deletion."); 930 } 931 932 // "Such a container or other context may continue using the object, and 933 // may still contain state identifying its name as being currently bound" 934 // 935 // We here interpret "may" to mean that whenever the container has a 936 // deleted object attached to it, a query will return that object's former 937 // name. 938 errors.check(getAttachment(m_attacher, *container) == elementId, 939 "Attachment name not returned by query after attachment was deleted."); 940 941 if (elemType.nameLingers()) 942 errors.check(elemType.exists(elementId), 943 "Attached object name no longer valid after deletion."); 944 else 945 errors.check(!elemType.exists(elementId), 946 "Attached object name still valid after deletion."); 947 948 m_attacher.detach(elementId, *container); 949 errors.check(getAttachment(m_attacher, *container) == 0, 950 "Attachment name returned by query even after detachment."); 951 errors.check(!elemType.exists(elementId), 952 "Deleted attached object name still usable after detachment."); 953 }; 954 955 class InputAttachmentTest : public TestBase 956 { 957 public: 958 InputAttachmentTest (const char* name, 959 const char* description, 960 InputAttacher& inputAttacher) 961 : TestBase (name, description, inputAttacher.getContext()) 962 , m_inputAttacher (inputAttacher) {} 963 964 IterateResult iterate (void); 965 966 private: 967 InputAttacher& m_inputAttacher; 968 }; 969 970 GLuint replaceName (Type& type, GLuint oldName, TestLog& log) 971 { 972 const Binder* const binder = type.binder(); 973 const bool genRequired = binder == DE_NULL || binder->genRequired(); 974 975 if (genRequired) 976 return type.gen(); 977 978 log << TestLog::Message 979 << "// Type does not require Gen* for binding, reusing old id " << oldName << "." 980 << TestLog::EndMessage; 981 982 return oldName; 983 } 984 985 IterateResult InputAttachmentTest::iterate (void) 986 { 987 Attacher& attacher = m_inputAttacher.getAttacher(); 988 Type& containerType = attacher.getContainerType(); 989 Type& elementType = attacher.getElementType(); 990 Name container (containerType); 991 GLuint elementId = 0; 992 const GLuint refSeed = m_rnd.getUint32(); 993 const GLuint newSeed = m_rnd.getUint32(); 994 ResultCollector errors (getTestContext()); 995 996 Surface refSurface; // Surface from drawing with refSeed-seeded attachment 997 Surface delSurface; // Surface from drawing with deleted refSeed attachment 998 Surface newSurface; // Surface from drawing with newSeed-seeded attachment 999 1000 log() << TestLog::Message 1001 << "Testing if writing to a newly created object modifies a deleted attachment" 1002 << TestLog::EndMessage; 1003 1004 { 1005 ScopedLogSection section (log(), 1006 "Write to original", "Writing to an original attachment"); 1007 const Name element (elementType); 1008 1009 elementId = *element; 1010 attacher.initAttachment(refSeed, elementId); 1011 attacher.attach(elementId, *container); 1012 m_inputAttacher.drawContainer(*container, refSurface); 1013 // element gets deleted here 1014 log() << TestLog::Message << "// Deleting attachment"; 1015 } 1016 { 1017 ScopedLogSection section (log(), "Write to new", 1018 "Writing to a new attachment after deleting the original"); 1019 const GLuint newId = replaceName(elementType, elementId, log()); 1020 const Name newElement (elementType, newId); 1021 1022 attacher.initAttachment(newSeed, newId); 1023 1024 m_inputAttacher.drawContainer(*container, delSurface); 1025 attacher.detach(elementId, *container); 1026 1027 attacher.attach(newId, *container); 1028 m_inputAttacher.drawContainer(*container, newSurface); 1029 attacher.detach(newId, *container); 1030 } 1031 { 1032 const bool surfacesMatch = tcu::pixelThresholdCompare( 1033 log(), "Reading from deleted", 1034 "Comparison result from reading from a container with a deleted attachment " 1035 "before and after writing to a fresh object.", 1036 refSurface, delSurface, RGBA(0, 0, 0, 0), tcu::COMPARE_LOG_RESULT); 1037 1038 errors.check( 1039 surfacesMatch, 1040 "Writing to a fresh object modified the container with a deleted attachment."); 1041 1042 if (!surfacesMatch) 1043 log() << TestLog::Image("New attachment", 1044 "Container state after attached to the fresh object", 1045 newSurface); 1046 } 1047 1048 return STOP; 1049 } 1050 1051 class OutputAttachmentTest : public TestBase 1052 { 1053 public: 1054 OutputAttachmentTest (const char* name, 1055 const char* description, 1056 OutputAttacher& outputAttacher) 1057 : TestBase (name, description, 1058 outputAttacher.getContext()) 1059 , m_outputAttacher (outputAttacher) {} 1060 IterateResult iterate (void); 1061 1062 private: 1063 OutputAttacher& m_outputAttacher; 1064 }; 1065 1066 IterateResult OutputAttachmentTest::iterate (void) 1067 { 1068 Attacher& attacher = m_outputAttacher.getAttacher(); 1069 Type& containerType = attacher.getContainerType(); 1070 Type& elementType = attacher.getElementType(); 1071 Name container (containerType); 1072 GLuint elementId = 0; 1073 const GLuint refSeed = m_rnd.getUint32(); 1074 const GLuint newSeed = m_rnd.getUint32(); 1075 ResultCollector errors (getTestContext()); 1076 Surface refSurface; // Surface drawn from attachment to refSeed container 1077 Surface newSurface; // Surface drawn from attachment to newSeed container 1078 Surface delSurface; // Like newSurface, after writing to a deleted attachment 1079 1080 log() << TestLog::Message 1081 << "Testing if writing to a container with a deleted attachment " 1082 << "modifies a newly created object" 1083 << TestLog::EndMessage; 1084 1085 { 1086 ScopedLogSection section (log(), "Write to existing", 1087 "Writing to a container with an existing attachment"); 1088 const Name element (elementType); 1089 1090 elementId = *element; 1091 attacher.initAttachment(0, elementId); 1092 attacher.attach(elementId, *container); 1093 1094 // For reference purposes, make note of what refSeed looks like. 1095 m_outputAttacher.setupContainer(refSeed, *container); 1096 m_outputAttacher.drawAttachment(elementId, refSurface); 1097 } 1098 { 1099 ScopedLogSection section (log(), "Write to deleted", 1100 "Writing to a container after deletion of attachment"); 1101 const GLuint newId = replaceName(elementType, elementId, log()); 1102 const Name newElement (elementType, newId); 1103 1104 log() << TestLog::Message 1105 << "Creating a new object " << newId 1106 << TestLog::EndMessage; 1107 1108 log() << TestLog::Message 1109 << "Recording state of new object before writing to container" 1110 << TestLog::EndMessage; 1111 attacher.initAttachment(newSeed, newId); 1112 m_outputAttacher.drawAttachment(newId, newSurface); 1113 1114 log() << TestLog::Message 1115 << "Writing to container" 1116 << TestLog::EndMessage; 1117 1118 // Now re-write refSeed to the container. 1119 m_outputAttacher.setupContainer(refSeed, *container); 1120 // Does it affect the newly created attachment object? 1121 m_outputAttacher.drawAttachment(newId, delSurface); 1122 } 1123 attacher.detach(elementId, *container); 1124 1125 const bool surfacesMatch = tcu::pixelThresholdCompare( 1126 log(), "Writing to deleted", 1127 "Comparison result from reading from a fresh object before and after " 1128 "writing to a container with a deleted attachment", 1129 newSurface, delSurface, RGBA(0, 0, 0, 0), tcu::COMPARE_LOG_RESULT); 1130 1131 errors.check(surfacesMatch, 1132 "Writing to container with deleted attachment modified a new object."); 1133 1134 if (!surfacesMatch) 1135 log() << TestLog::Image( 1136 "Original attachment", 1137 "Result of container modification on original attachment before deletion.", 1138 refSurface); 1139 return STOP; 1140 }; 1141 1142 struct LifeTestSpec 1143 { 1144 const char* name; 1145 LifeTest::TestFunction func; 1146 bool needBind; 1147 }; 1148 1149 MovePtr<TestCaseGroup> createLifeTestGroup (TestContext& testCtx, 1150 const LifeTestSpec& spec, 1151 const vector<Type*>& types) 1152 { 1153 MovePtr<TestCaseGroup> group(new TestCaseGroup(testCtx, spec.name, spec.name)); 1154 1155 for (vector<Type*>::const_iterator it = types.begin(); it != types.end(); ++it) 1156 { 1157 Type& type = **it; 1158 const char* name = type.getName(); 1159 if (!spec.needBind || type.binder() != DE_NULL) 1160 group->addChild(new LifeTest(name, name, type, spec.func)); 1161 } 1162 1163 return group; 1164 } 1165 1166 static const LifeTestSpec s_lifeTests[] = 1167 { 1168 { "gen", &LifeTest::testGen, false }, 1169 { "delete", &LifeTest::testDelete, false }, 1170 { "bind", &LifeTest::testBind, true }, 1171 { "delete_bound", &LifeTest::testDeleteBound, true }, 1172 { "bind_no_gen", &LifeTest::testBindNoGen, true }, 1173 }; 1174 1175 string attacherName (Attacher& attacher) 1176 { 1177 ostringstream os; 1178 os << attacher.getElementType().getName() << "_" << attacher.getContainerType().getName(); 1179 return os.str(); 1180 } 1181 1182 void addTestCases (TestCaseGroup& group, Types& types) 1183 { 1184 TestContext& testCtx = types.getTestContext(); 1185 1186 for (const LifeTestSpec* it = DE_ARRAY_BEGIN(s_lifeTests); 1187 it != DE_ARRAY_END(s_lifeTests); ++it) 1188 group.addChild(createLifeTestGroup(testCtx, *it, types.getTypes()).release()); 1189 1190 { 1191 TestCaseGroup* const delUsedGroup = 1192 new TestCaseGroup(testCtx, "delete_used", "Delete current program"); 1193 group.addChild(delUsedGroup); 1194 1195 delUsedGroup->addChild( 1196 new LifeTest("program", "program", types.getProgramType(), 1197 &LifeTest::testDeleteUsed)); 1198 } 1199 1200 { 1201 TestCaseGroup* const attGroup = new TestCaseGroup( 1202 testCtx, "attach", "Attachment tests"); 1203 group.addChild(attGroup); 1204 1205 { 1206 TestCaseGroup* const nameGroup = new TestCaseGroup( 1207 testCtx, "deleted_name", "Name of deleted attachment"); 1208 attGroup->addChild(nameGroup); 1209 1210 const vector<Attacher*>& atts = types.getAttachers(); 1211 for (vector<Attacher*>::const_iterator it = atts.begin(); it != atts.end(); ++it) 1212 { 1213 const string name = attacherName(**it); 1214 nameGroup->addChild(new AttachmentTest(name.c_str(), name.c_str(), **it, 1215 &AttachmentTest::testDeletedNames)); 1216 } 1217 } 1218 { 1219 TestCaseGroup* inputGroup = new TestCaseGroup( 1220 testCtx, "deleted_input", "Input from deleted attachment"); 1221 attGroup->addChild(inputGroup); 1222 1223 const vector<InputAttacher*>& inAtts = types.getInputAttachers(); 1224 for (vector<InputAttacher*>::const_iterator it = inAtts.begin(); 1225 it != inAtts.end(); ++it) 1226 { 1227 const string name = attacherName((*it)->getAttacher()); 1228 inputGroup->addChild(new InputAttachmentTest(name.c_str(), name.c_str(), **it)); 1229 } 1230 } 1231 { 1232 TestCaseGroup* outputGroup = new TestCaseGroup( 1233 testCtx, "deleted_output", "Output to deleted attachment"); 1234 attGroup->addChild(outputGroup); 1235 1236 const vector<OutputAttacher*>& outAtts = types.getOutputAttachers(); 1237 for (vector<OutputAttacher*>::const_iterator it = outAtts.begin(); 1238 it != outAtts.end(); ++it) 1239 { 1240 string name = attacherName((*it)->getAttacher()); 1241 outputGroup->addChild(new OutputAttachmentTest(name.c_str(), name.c_str(), 1242 **it)); 1243 } 1244 } 1245 } 1246 } 1247 1248 } // details 1249 } // LifetimeTests 1250 } // gls 1251 } // deqp 1252