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 40namespace deqp 41{ 42namespace gles3 43{ 44namespace Functional 45{ 46namespace 47{ 48 49using std::vector; 50using de::MovePtr; 51using de::Random; 52using tcu::RenderTarget; 53using tcu::Surface; 54using tcu::TestContext; 55using tcu::TestLog; 56using glu::CallLogWrapper; 57using glu::RenderContext; 58using glu::ProgramSources; 59using glu::VertexArray; 60using glu::Buffer; 61namespace lt = gls::LifetimeTests; 62using namespace lt; 63using namespace glw; 64typedef TestCase::IterateResult IterateResult; 65 66enum { VIEWPORT_SIZE = 128 }; 67 68class ScaleProgram : public glu::ShaderProgram 69{ 70public: 71 ScaleProgram (lt::Context& ctx); 72 void draw (GLuint vao, GLfloat scale, bool tf, Surface* dst); 73 void setPos (GLuint buffer, GLuint vao); 74 75private: 76 ProgramSources getSources (void); 77 78 const RenderContext& m_renderCtx; 79 GLint m_scaleLoc; 80 GLint m_posLoc; 81}; 82 83enum { NUM_COMPONENTS = 4, NUM_VERTICES = 3 }; 84 85ScaleProgram::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 97static 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 107static 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 115ProgramSources 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 126void 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 155void 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 169class VertexArrayBinder : public SimpleBinder 170{ 171public: 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 177class SamplerBinder : public Binder 178{ 179public: 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 193class QueryBinder : public Binder 194{ 195public: 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 207class BufferVAOAttacher : public Attacher 208{ 209public: 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 221private: 222 ScaleProgram& m_program; 223}; 224 225static 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 232void 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 257void 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 264void 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 272void BufferVAOAttacher::detach (GLuint buffer, GLuint varr) 273{ 274 DE_UNREF(buffer); 275 attach(0, varr); 276} 277 278GLuint 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 288class BufferVAOInputAttacher : public InputAttacher 289{ 290public: 291 BufferVAOInputAttacher (BufferVAOAttacher& attacher) 292 : InputAttacher (attacher) 293 , m_program (attacher.getProgram()) {} 294 void drawContainer (GLuint container, Surface& dst); 295 296private: 297 ScaleProgram& m_program; 298}; 299 300void 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 307class BufferTfAttacher : public Attacher 308{ 309public: 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 319void 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 326void 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 334void 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 343GLuint 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 353class BufferTfOutputAttacher : public OutputAttacher 354{ 355public: 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 362private: 363 ScaleProgram& m_program; 364}; 365 366void 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 378void 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 395class ES3Types : public ES2Types 396{ 397public: 398 ES3Types (lt::Context& ctx); 399private: 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 415ES3Types::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 450class 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 458TfDeleteActiveTest::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 467class ScopedTransformFeedbackFeedback 468{ 469public: 470 ScopedTransformFeedbackFeedback (glu::CallLogWrapper& gl, GLenum type); 471 ~ScopedTransformFeedbackFeedback (void); 472 473private: 474 glu::CallLogWrapper& m_gl; 475}; 476 477ScopedTransformFeedbackFeedback::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 484ScopedTransformFeedbackFeedback::~ScopedTransformFeedbackFeedback (void) 485{ 486 m_gl.glEndTransformFeedback(); 487} 488 489IterateResult 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 553class TestGroup : public TestCaseGroup 554{ 555public: 556 TestGroup (gles3::Context& context) 557 : TestCaseGroup (context, "lifetime", "Object lifetime tests") 558 {} 559 void init (void); 560private: 561 MovePtr<Types> m_types; 562}; 563 564void 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 582TestCaseGroup* createLifetimeTests (Context& context) 583{ 584 return new TestGroup(context); 585} 586 587} // Functional 588} // gles3 589} // deqp 590