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