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 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 272void 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 297void 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 305void 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 313void 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 328void 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 342void 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 356GLuint TextureFboAttacher::getAttachment (GLuint fbo) 357{ 358 return getFboAttachment(gl(), fbo, GL_TEXTURE); 359} 360 361void 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 369void RboFboAttacher::initStorage (void) 370{ 371 GLU_CHECK_CALL_ERROR( 372 glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, FRAMEBUFFER_SIZE, FRAMEBUFFER_SIZE), 373 gl().getError()); 374} 375 376void 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 389void 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 403GLuint RboFboAttacher::getAttachment (GLuint fbo) 404{ 405 return getFboAttachment(gl(), fbo, GL_RENDERBUFFER); 406} 407 408static const char* const s_fragmentShaderTemplate = GLSL100_SRC( 409 void main() 410 { 411 gl_FragColor = vec4(${RED}, ${GREEN}, ${BLUE}, 1.0); 412 } 413 ); 414 415void 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 443void ShaderProgramAttacher::attach (GLuint shader, GLuint program) 444{ 445 GLU_CHECK_CALL_ERROR( 446 glAttachShader(program, shader), 447 gl().getError()); 448} 449 450void ShaderProgramAttacher::detach (GLuint shader, GLuint program) 451{ 452 GLU_CHECK_CALL_ERROR( 453 glDetachShader(program, shader), 454 gl().getError()); 455} 456 457GLuint 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 484void setViewport (const RenderContext& renderCtx, const Rectangle& rect) 485{ 486 renderCtx.getFunctions().viewport(rect.x, rect.y, rect.width, rect.height); 487} 488 489void 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 495Rectangle 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 507void 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 556ES2Types::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 608class Name 609{ 610public: 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 616private: 617 Type& m_type; 618 const GLuint m_name; 619}; 620 621class ResultCollector 622{ 623public: 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 630private: 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 639ResultCollector::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 647bool ResultCollector::check (bool cond, const char* msg) 648{ 649 if (!cond) 650 fail(msg); 651 return cond; 652} 653 654void 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 670void ResultCollector::fail (const char* msg) 671{ 672 addResult(QP_TEST_RESULT_FAIL, msg); 673} 674 675void ResultCollector::warn (const char* msg) 676{ 677 addResult(QP_TEST_RESULT_QUALITY_WARNING, msg); 678} 679 680ResultCollector::~ResultCollector (void) 681{ 682 m_testCtx.setTestResult(m_result, m_message); 683} 684 685class TestBase : public TestCase, protected CallLogWrapper 686{ 687protected: 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 703TestBase::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 712void TestBase::init (void) 713{ 714 m_rnd = Random(deStringHash(getName())); 715} 716 717class LifeTest : public TestBase 718{ 719public: 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 739private: 740 Binder& binder (void) { return *m_type.binder(); } 741 742 Type& m_type; 743 TestFunction m_test; 744}; 745 746IterateResult LifeTest::iterate (void) 747{ 748 (this->*m_test)(); 749 return STOP; 750} 751 752void 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 763void 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 772void 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 783void 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 816void 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 843void 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 876class AttachmentTest : public TestBase 877{ 878public: 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 893private: 894 Attacher& m_attacher; 895 const TestFunction m_test; 896}; 897 898IterateResult AttachmentTest::iterate (void) 899{ 900 (this->*m_test)(); 901 return STOP; 902} 903 904GLuint 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 915void 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 955class InputAttachmentTest : public TestBase 956{ 957public: 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 966private: 967 InputAttacher& m_inputAttacher; 968}; 969 970GLuint 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 985IterateResult 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 1051class OutputAttachmentTest : public TestBase 1052{ 1053public: 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 1062private: 1063 OutputAttacher& m_outputAttacher; 1064}; 1065 1066IterateResult 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 1142struct LifeTestSpec 1143{ 1144 const char* name; 1145 LifeTest::TestFunction func; 1146 bool needBind; 1147}; 1148 1149MovePtr<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 1166static 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 1175string attacherName (Attacher& attacher) 1176{ 1177 ostringstream os; 1178 os << attacher.getElementType().getName() << "_" << attacher.getContainerType().getName(); 1179 return os.str(); 1180} 1181 1182void 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