1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL ES 3.0 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 Object lifetime tests. 22 *//*--------------------------------------------------------------------*/ 23 24 #include "es3fLifetimeTests.hpp" 25 26 #include "deRandom.hpp" 27 #include "deUniquePtr.hpp" 28 #include "tcuRenderTarget.hpp" 29 #include "tcuSurface.hpp" 30 #include "gluDrawUtil.hpp" 31 #include "gluObjectWrapper.hpp" 32 #include "gluPixelTransfer.hpp" 33 #include "gluShaderProgram.hpp" 34 #include "glsLifetimeTests.hpp" 35 #include "glwEnums.hpp" 36 #include "glwFunctions.hpp" 37 38 #include <vector> 39 40 namespace deqp 41 { 42 namespace gles3 43 { 44 namespace Functional 45 { 46 namespace 47 { 48 49 using std::vector; 50 using de::MovePtr; 51 using de::Random; 52 using tcu::RenderTarget; 53 using tcu::Surface; 54 using tcu::TestContext; 55 using tcu::TestLog; 56 using glu::CallLogWrapper; 57 using glu::RenderContext; 58 using glu::ProgramSources; 59 using glu::VertexArray; 60 using glu::Buffer; 61 namespace lt = gls::LifetimeTests; 62 using namespace lt; 63 using namespace glw; 64 typedef TestCase::IterateResult IterateResult; 65 66 enum { VIEWPORT_SIZE = 128 }; 67 68 class ScaleProgram : public glu::ShaderProgram 69 { 70 public: 71 ScaleProgram (lt::Context& ctx); 72 void draw (GLuint vao, GLfloat scale, bool tf, Surface* dst); 73 void setPos (GLuint buffer, GLuint vao); 74 75 private: 76 ProgramSources getSources (void); 77 78 const RenderContext& m_renderCtx; 79 GLint m_scaleLoc; 80 GLint m_posLoc; 81 }; 82 83 enum { NUM_COMPONENTS = 4, NUM_VERTICES = 3 }; 84 85 ScaleProgram::ScaleProgram (lt::Context& ctx) 86 : glu::ShaderProgram (ctx.getRenderContext(), getSources()) 87 , m_renderCtx (ctx.getRenderContext()) 88 { 89 const Functions& gl = m_renderCtx.getFunctions(); 90 TCU_CHECK(isOk()); 91 m_scaleLoc = gl.getUniformLocation(getProgram(), "scale"); 92 m_posLoc = gl.getAttribLocation(getProgram(), "pos"); 93 } 94 95 #define GLSL(VERSION, BODY) ("#version " #VERSION "\n" #BODY "\n") 96 97 static const char* const s_vertexShaderSrc = GLSL( 98 100, 99 attribute vec4 pos; 100 uniform float scale; 101 void main () 102 { 103 gl_Position = vec4(scale * pos.xy, pos.zw); 104 } 105 ); 106 107 static const char* const s_fragmentShaderSrc = GLSL( 108 100, 109 void main () 110 { 111 gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0); 112 } 113 ); 114 115 ProgramSources ScaleProgram::getSources (void) 116 { 117 using namespace glu; 118 ProgramSources sources; 119 sources << VertexSource(s_vertexShaderSrc) 120 << FragmentSource(s_fragmentShaderSrc) 121 << TransformFeedbackMode(GL_INTERLEAVED_ATTRIBS) 122 << TransformFeedbackVarying("gl_Position"); 123 return sources; 124 } 125 126 void ScaleProgram::draw (GLuint vao, GLfloat scale, bool tf, Surface* dst) 127 { 128 const Functions& gl = m_renderCtx.getFunctions(); 129 de::Random rnd (vao); 130 Rectangle viewport = randomViewport(m_renderCtx, 131 VIEWPORT_SIZE, VIEWPORT_SIZE, rnd); 132 setViewport(m_renderCtx, viewport); 133 gl.clearColor(0, 0, 0, 1); 134 gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 135 136 gl.bindVertexArray(vao); 137 gl.enableVertexAttribArray(m_posLoc); 138 GLU_CHECK_CALL_ERROR(gl.useProgram(getProgram()), 139 gl.getError()); 140 141 gl.uniform1f(m_scaleLoc, scale); 142 143 if (tf) 144 gl.beginTransformFeedback(GL_TRIANGLES); 145 GLU_CHECK_CALL_ERROR(gl.drawArrays(GL_TRIANGLES, 0, 3), gl.getError()); 146 if (tf) 147 gl.endTransformFeedback(); 148 149 if (dst != DE_NULL) 150 readRectangle(m_renderCtx, viewport, *dst); 151 152 gl.bindVertexArray(0); 153 } 154 155 void ScaleProgram::setPos (GLuint buffer, GLuint vao) 156 { 157 const Functions& gl = m_renderCtx.getFunctions(); 158 159 gl.bindBuffer(GL_ARRAY_BUFFER, buffer); 160 gl.bindVertexArray(vao); 161 GLU_CHECK_CALL_ERROR( 162 gl.vertexAttribPointer(m_posLoc, NUM_COMPONENTS, GL_FLOAT, false, 0, DE_NULL), 163 gl.getError()); 164 gl.bindVertexArray(0); 165 gl.bindBuffer(GL_ARRAY_BUFFER, 0); 166 GLU_CHECK_ERROR(gl.getError()); 167 } 168 169 class VertexArrayBinder : public SimpleBinder 170 { 171 public: 172 VertexArrayBinder (lt::Context& ctx) 173 : SimpleBinder (ctx, 0, GL_NONE, GL_VERTEX_ARRAY_BINDING, true) {} 174 void bind (GLuint name) { glBindVertexArray(name); } 175 }; 176 177 class SamplerBinder : public Binder 178 { 179 public: 180 SamplerBinder (lt::Context& ctx) : Binder(ctx) {} 181 void bind (GLuint name) { glBindSampler(0, name); } 182 GLuint getBinding (void) 183 { 184 GLint arr[32] = {}; 185 glGetIntegerv(GL_SAMPLER_BINDING, arr); 186 log() << TestLog::Message << "// First output integer: " << arr[0] 187 << TestLog::EndMessage; 188 return arr[0]; 189 } 190 bool genRequired (void) const { return true; } 191 }; 192 193 class QueryBinder : public Binder 194 { 195 public: 196 QueryBinder (lt::Context& ctx) : Binder(ctx) {} 197 void bind (GLuint name) 198 { 199 if (name != 0) 200 glBeginQuery(GL_ANY_SAMPLES_PASSED, name); 201 else 202 glEndQuery(GL_ANY_SAMPLES_PASSED); 203 } 204 GLuint getBinding (void) { return 0; } 205 }; 206 207 class BufferVAOAttacher : public Attacher 208 { 209 public: 210 BufferVAOAttacher (lt::Context& ctx, Type& elementType, 211 Type& varrType, ScaleProgram& program) 212 : Attacher (ctx, elementType, varrType) 213 , m_program (program) {} 214 void initAttachment (GLuint seed, GLuint element); 215 void attach (GLuint element, GLuint container); 216 void detach (GLuint element, GLuint container); 217 bool canAttachDeleted (void) const { return false; } 218 ScaleProgram& getProgram (void) { return m_program; } 219 GLuint getAttachment (GLuint container); 220 221 private: 222 ScaleProgram& m_program; 223 }; 224 225 static const GLfloat s_varrData[NUM_VERTICES * NUM_COMPONENTS] = 226 { 227 -1.0, 0.0, 0.0, 1.0, 228 1.0, 1.0, 0.0, 1.0, 229 0.0, -1.0, 0.0, 1.0 230 }; 231 232 void initBuffer (const Functions& gl, GLuint seed, GLenum usage, GLuint buffer) 233 { 234 gl.bindBuffer(GL_ARRAY_BUFFER, buffer); 235 if (seed == 0) 236 gl.bufferData(GL_ARRAY_BUFFER, sizeof(s_varrData), s_varrData, usage); 237 else 238 { 239 Random rnd (seed); 240 GLfloat data[DE_LENGTH_OF_ARRAY(s_varrData)]; 241 242 for (int ndx = 0; ndx < NUM_VERTICES; ndx++) 243 { 244 GLfloat* vertex = &data[ndx * NUM_COMPONENTS]; 245 vertex[0] = 2.0f * (rnd.getFloat() - 0.5f); 246 vertex[1] = 2.0f * (rnd.getFloat() - 0.5f); 247 DE_STATIC_ASSERT(NUM_COMPONENTS == 4); 248 vertex[2] = 0.0f; 249 vertex[3] = 1.0f; 250 } 251 gl.bufferData(GL_ARRAY_BUFFER, sizeof(data), data, usage); 252 } 253 gl.bindBuffer(GL_ARRAY_BUFFER, 0); 254 GLU_CHECK_ERROR(gl.getError()); 255 } 256 257 void BufferVAOAttacher::initAttachment (GLuint seed, GLuint buffer) 258 { 259 initBuffer(gl(), seed, GL_STATIC_DRAW, buffer); 260 log() << TestLog::Message << "// Initialized buffer " << buffer << " from seed " << seed 261 << TestLog::EndMessage; 262 } 263 264 void BufferVAOAttacher::attach (GLuint buffer, GLuint vao) 265 { 266 m_program.setPos(buffer, vao); 267 log() << TestLog::Message 268 << "// Set the `pos` attribute in VAO " << vao << " to buffer " << buffer 269 << TestLog::EndMessage; 270 } 271 272 void BufferVAOAttacher::detach (GLuint buffer, GLuint varr) 273 { 274 DE_UNREF(buffer); 275 attach(0, varr); 276 } 277 278 GLuint BufferVAOAttacher::getAttachment (GLuint varr) 279 { 280 GLint name = 0; 281 gl().bindVertexArray(varr); 282 gl().getVertexAttribiv(0, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &name); 283 gl().bindVertexArray(0); 284 GLU_CHECK_ERROR(gl().getError()); 285 return GLuint(name); 286 } 287 288 class BufferVAOInputAttacher : public InputAttacher 289 { 290 public: 291 BufferVAOInputAttacher (BufferVAOAttacher& attacher) 292 : InputAttacher (attacher) 293 , m_program (attacher.getProgram()) {} 294 void drawContainer (GLuint container, Surface& dst); 295 296 private: 297 ScaleProgram& m_program; 298 }; 299 300 void BufferVAOInputAttacher::drawContainer (GLuint vao, Surface& dst) 301 { 302 m_program.draw(vao, 1.0, false, &dst); 303 log() << TestLog::Message << "// Drew an output image with VAO " << vao 304 << TestLog::EndMessage; 305 }; 306 307 class BufferTfAttacher : public Attacher 308 { 309 public: 310 BufferTfAttacher (lt::Context& ctx, Type& bufferType, Type& tfType) 311 : Attacher (ctx, bufferType, tfType) {} 312 void initAttachment (GLuint seed, GLuint element); 313 void attach (GLuint buffer, GLuint tf); 314 void detach (GLuint buffer, GLuint tf); 315 bool canAttachDeleted (void) const { return false; } 316 GLuint getAttachment (GLuint tf); 317 }; 318 319 void BufferTfAttacher::initAttachment (GLuint seed, GLuint buffer) 320 { 321 initBuffer(gl(), seed, GL_DYNAMIC_READ, buffer); 322 log() << TestLog::Message << "// Initialized buffer " << buffer << " from seed " << seed 323 << TestLog::EndMessage; 324 } 325 326 void BufferTfAttacher::attach (GLuint buffer, GLuint tf) 327 { 328 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tf); 329 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buffer); 330 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0); 331 GLU_CHECK_ERROR(gl().getError()); 332 } 333 334 void BufferTfAttacher::detach (GLuint buffer, GLuint tf) 335 { 336 DE_UNREF(buffer); 337 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tf); 338 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0); 339 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0); 340 GLU_CHECK_ERROR(gl().getError()); 341 } 342 343 GLuint BufferTfAttacher::getAttachment (GLuint tf) 344 { 345 GLint ret = 0; 346 gl().bindTransformFeedback(GL_TRANSFORM_FEEDBACK, tf); 347 gl().getIntegeri_v(GL_TRANSFORM_FEEDBACK_BUFFER_BINDING, 0, &ret); 348 gl().bindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0); 349 GLU_CHECK_ERROR(gl().getError()); 350 return GLuint(ret); 351 } 352 353 class BufferTfOutputAttacher : public OutputAttacher 354 { 355 public: 356 BufferTfOutputAttacher (BufferTfAttacher& attacher, ScaleProgram& program) 357 : OutputAttacher (attacher) 358 , m_program (program) {} 359 void setupContainer (GLuint seed, GLuint container); 360 void drawAttachment (GLuint attachment, Surface& dst); 361 362 private: 363 ScaleProgram& m_program; 364 }; 365 366 void BufferTfOutputAttacher::drawAttachment (GLuint buffer, Surface& dst) 367 { 368 VertexArray vao(getRenderContext()); 369 370 m_program.setPos(buffer, *vao); 371 m_program.draw(*vao, 1.0, false, &dst); 372 log() << TestLog::Message 373 << "// Drew output image with vertices from buffer " << buffer 374 << TestLog::EndMessage; 375 GLU_CHECK_ERROR(gl().getError()); 376 } 377 378 void BufferTfOutputAttacher::setupContainer (GLuint seed, GLuint tf) 379 { 380 Buffer posBuf (getRenderContext()); 381 VertexArray vao (getRenderContext()); 382 383 initBuffer(gl(), seed, GL_STATIC_DRAW, *posBuf); 384 m_program.setPos(*posBuf, *vao); 385 386 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tf); 387 m_program.draw(*vao, -1.0, true, DE_NULL); 388 log() << TestLog::Message 389 << "// Drew an image with seed " << seed << " with transform feedback to " << tf 390 << TestLog::EndMessage; 391 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0); 392 GLU_CHECK_ERROR(gl().getError()); 393 } 394 395 class ES3Types : public ES2Types 396 { 397 public: 398 ES3Types (lt::Context& ctx); 399 private: 400 ScaleProgram m_program; 401 QueryBinder m_queryBind; 402 SimpleType m_queryType; 403 SimpleBinder m_tfBind; 404 SimpleType m_tfType; 405 VertexArrayBinder m_varrBind; 406 SimpleType m_varrType; 407 SamplerBinder m_samplerBind; 408 SimpleType m_samplerType; 409 BufferVAOAttacher m_bufVarrAtt; 410 BufferVAOInputAttacher m_bufVarrInAtt; 411 BufferTfAttacher m_bufTfAtt; 412 BufferTfOutputAttacher m_bufTfOutAtt; 413 }; 414 415 ES3Types::ES3Types (lt::Context& ctx) 416 : ES2Types (ctx) 417 , m_program (ctx) 418 , m_queryBind (ctx) 419 , m_queryType (ctx, "query", &CallLogWrapper::glGenQueries, 420 &CallLogWrapper::glDeleteQueries, 421 &CallLogWrapper::glIsQuery, &m_queryBind) 422 , m_tfBind (ctx, &CallLogWrapper::glBindTransformFeedback, GL_TRANSFORM_FEEDBACK, 423 GL_TRANSFORM_FEEDBACK_BINDING, true) 424 , m_tfType (ctx, "transform_feedback", &CallLogWrapper::glGenTransformFeedbacks, 425 &CallLogWrapper::glDeleteTransformFeedbacks, 426 &CallLogWrapper::glIsTransformFeedback, &m_tfBind) 427 , m_varrBind (ctx) 428 , m_varrType (ctx, "vertex_array", &CallLogWrapper::glGenVertexArrays, 429 &CallLogWrapper::glDeleteVertexArrays, 430 &CallLogWrapper::glIsVertexArray, &m_varrBind) 431 , m_samplerBind (ctx) 432 , m_samplerType (ctx, "sampler", &CallLogWrapper::glGenSamplers, 433 &CallLogWrapper::glDeleteSamplers, 434 &CallLogWrapper::glIsSampler, &m_samplerBind, true) 435 , m_bufVarrAtt (ctx, m_bufferType, m_varrType, m_program) 436 , m_bufVarrInAtt(m_bufVarrAtt) 437 , m_bufTfAtt (ctx, m_bufferType, m_tfType) 438 , m_bufTfOutAtt (m_bufTfAtt, m_program) 439 { 440 Type* types[] = { &m_queryType, &m_tfType, &m_varrType, &m_samplerType }; 441 m_types.insert(m_types.end(), DE_ARRAY_BEGIN(types), DE_ARRAY_END(types)); 442 443 m_attachers.push_back(&m_bufVarrAtt); 444 m_attachers.push_back(&m_bufTfAtt); 445 446 m_inAttachers.push_back(&m_bufVarrInAtt); 447 m_outAttachers.push_back(&m_bufTfOutAtt); 448 } 449 450 class TfDeleteActiveTest : public TestCase, private CallLogWrapper 451 { 452 public: 453 TfDeleteActiveTest (gles3::Context& context, 454 const char* name, const char* description); 455 IterateResult iterate (void); 456 }; 457 458 TfDeleteActiveTest::TfDeleteActiveTest (gles3::Context& context, 459 const char* name, const char* description) 460 : TestCase (context, name, description) 461 , CallLogWrapper (context.getRenderContext().getFunctions(), 462 context.getTestContext().getLog()) 463 { 464 enableLogging(true); 465 } 466 467 class ScopedTransformFeedbackFeedback 468 { 469 public: 470 ScopedTransformFeedbackFeedback (glu::CallLogWrapper& gl, GLenum type); 471 ~ScopedTransformFeedbackFeedback (void); 472 473 private: 474 glu::CallLogWrapper& m_gl; 475 }; 476 477 ScopedTransformFeedbackFeedback::ScopedTransformFeedbackFeedback (glu::CallLogWrapper& gl, GLenum type) 478 : m_gl(gl) 479 { 480 m_gl.glBeginTransformFeedback(type); 481 GLU_EXPECT_NO_ERROR(m_gl.glGetError(), "glBeginTransformFeedback"); 482 } 483 484 ScopedTransformFeedbackFeedback::~ScopedTransformFeedbackFeedback (void) 485 { 486 m_gl.glEndTransformFeedback(); 487 } 488 489 IterateResult TfDeleteActiveTest::iterate (void) 490 { 491 static const char* const s_xfbVertexSource = "#version 300 es\n" 492 "void main ()\n" 493 "{\n" 494 " gl_Position = vec4(float(gl_VertexID) / 2.0, float(gl_VertexID % 2) / 2.0, 0.0, 1.0);\n" 495 "}\n"; 496 static const char* const s_xfbFragmentSource = "#version 300 es\n" 497 "layout(location=0) out mediump vec4 dEQP_FragColor;\n" 498 "void main ()\n" 499 "{\n" 500 " dEQP_FragColor = vec4(1.0, 1.0, 0.0, 1.0);\n" 501 "}\n"; 502 503 glu::Buffer buf (m_context.getRenderContext()); 504 GLuint tf = 0; 505 glu::ShaderProgram program (m_context.getRenderContext(), 506 glu::ProgramSources() 507 << glu::VertexSource(s_xfbVertexSource) 508 << glu::FragmentSource(s_xfbFragmentSource) 509 << glu::TransformFeedbackMode(GL_INTERLEAVED_ATTRIBS) 510 << glu::TransformFeedbackVarying("gl_Position")); 511 512 if (!program.isOk()) 513 { 514 m_testCtx.getLog() << program; 515 throw tcu::TestError("failed to build program"); 516 } 517 518 try 519 { 520 GLU_CHECK_CALL(glUseProgram(program.getProgram())); 521 GLU_CHECK_CALL(glGenTransformFeedbacks(1, &tf)); 522 GLU_CHECK_CALL(glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tf)); 523 GLU_CHECK_CALL(glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, *buf)); 524 GLU_CHECK_CALL(glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 3 * sizeof(glw::GLfloat[4]), DE_NULL, GL_DYNAMIC_COPY)); 525 526 { 527 ScopedTransformFeedbackFeedback xfb(static_cast<glu::CallLogWrapper&>(*this), GL_TRIANGLES); 528 529 glDeleteTransformFeedbacks(1, &tf); 530 { 531 GLenum err = glGetError(); 532 if (err != GL_INVALID_OPERATION) 533 getTestContext().setTestResult( 534 QP_TEST_RESULT_FAIL, 535 "Deleting active transform feedback did not produce GL_INVALID_OPERATION"); 536 else 537 getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); 538 } 539 } 540 GLU_CHECK(); // ScopedTransformFeedbackFeedback::dtor might modify error state 541 542 GLU_CHECK_CALL(glDeleteTransformFeedbacks(1, &tf)); 543 } 544 catch (const glu::Error&) 545 { 546 glDeleteTransformFeedbacks(1, &tf); 547 throw; 548 } 549 550 return STOP; 551 } 552 553 class TestGroup : public TestCaseGroup 554 { 555 public: 556 TestGroup (gles3::Context& context) 557 : TestCaseGroup (context, "lifetime", "Object lifetime tests") 558 {} 559 void init (void); 560 private: 561 MovePtr<Types> m_types; 562 }; 563 564 void TestGroup::init (void) 565 { 566 gles3::Context& ctx = getContext(); 567 lt::Context ltCtx (ctx.getRenderContext(), ctx.getTestContext()); 568 569 m_types = MovePtr<Types>(new ES3Types(ltCtx)); 570 571 addTestCases(*this, *m_types); 572 573 TestCaseGroup* deleteActiveGroup = 574 new TestCaseGroup(ctx, "delete_active", "Delete active object"); 575 addChild(deleteActiveGroup); 576 deleteActiveGroup->addChild( 577 new TfDeleteActiveTest(ctx, "transform_feedback", "Transform Feedback")); 578 } 579 580 } // anonymous 581 582 TestCaseGroup* createLifetimeTests (Context& context) 583 { 584 return new TestGroup(context); 585 } 586 587 } // Functional 588 } // gles3 589 } // deqp 590