| /*------------------------------------------------------------------------- |
| * drawElements Quality Program OpenGL ES 3.0 Module |
| * ------------------------------------------------- |
| * |
| * Copyright 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| *//*! |
| * \file |
| * \brief Object lifetime tests. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "es3fLifetimeTests.hpp" |
| |
| #include "deRandom.hpp" |
| #include "deUniquePtr.hpp" |
| #include "tcuRenderTarget.hpp" |
| #include "tcuSurface.hpp" |
| #include "gluDrawUtil.hpp" |
| #include "gluObjectWrapper.hpp" |
| #include "gluPixelTransfer.hpp" |
| #include "gluShaderProgram.hpp" |
| #include "glsLifetimeTests.hpp" |
| #include "glwEnums.hpp" |
| #include "glwFunctions.hpp" |
| |
| #include <vector> |
| |
| namespace deqp |
| { |
| namespace gles3 |
| { |
| namespace Functional |
| { |
| namespace |
| { |
| |
| using std::vector; |
| using de::MovePtr; |
| using de::Random; |
| using tcu::RenderTarget; |
| using tcu::Surface; |
| using tcu::TestContext; |
| using tcu::TestLog; |
| using glu::CallLogWrapper; |
| using glu::RenderContext; |
| using glu::ProgramSources; |
| using glu::VertexArray; |
| using glu::Buffer; |
| namespace lt = gls::LifetimeTests; |
| using namespace lt; |
| using namespace glw; |
| typedef TestCase::IterateResult IterateResult; |
| |
| enum { VIEWPORT_SIZE = 128 }; |
| |
| class ScaleProgram : public glu::ShaderProgram |
| { |
| public: |
| ScaleProgram (lt::Context& ctx); |
| void draw (GLuint vao, GLfloat scale, bool tf, Surface* dst); |
| void setPos (GLuint buffer, GLuint vao); |
| |
| private: |
| ProgramSources getSources (void); |
| |
| const RenderContext& m_renderCtx; |
| GLint m_scaleLoc; |
| GLint m_posLoc; |
| }; |
| |
| enum { NUM_COMPONENTS = 4, NUM_VERTICES = 3 }; |
| |
| ScaleProgram::ScaleProgram (lt::Context& ctx) |
| : glu::ShaderProgram (ctx.getRenderContext(), getSources()) |
| , m_renderCtx (ctx.getRenderContext()) |
| { |
| const Functions& gl = m_renderCtx.getFunctions(); |
| TCU_CHECK(isOk()); |
| m_scaleLoc = gl.getUniformLocation(getProgram(), "scale"); |
| m_posLoc = gl.getAttribLocation(getProgram(), "pos"); |
| } |
| |
| #define GLSL(VERSION, BODY) ("#version " #VERSION "\n" #BODY "\n") |
| |
| static const char* const s_vertexShaderSrc = GLSL( |
| 100, |
| attribute vec4 pos; |
| uniform float scale; |
| void main () |
| { |
| gl_Position = vec4(scale * pos.xy, pos.zw); |
| } |
| ); |
| |
| static const char* const s_fragmentShaderSrc = GLSL( |
| 100, |
| void main () |
| { |
| gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0); |
| } |
| ); |
| |
| ProgramSources ScaleProgram::getSources (void) |
| { |
| using namespace glu; |
| ProgramSources sources; |
| sources << VertexSource(s_vertexShaderSrc) |
| << FragmentSource(s_fragmentShaderSrc) |
| << TransformFeedbackMode(GL_INTERLEAVED_ATTRIBS) |
| << TransformFeedbackVarying("gl_Position"); |
| return sources; |
| } |
| |
| void ScaleProgram::draw (GLuint vao, GLfloat scale, bool tf, Surface* dst) |
| { |
| const Functions& gl = m_renderCtx.getFunctions(); |
| de::Random rnd (vao); |
| Rectangle viewport = randomViewport(m_renderCtx, |
| VIEWPORT_SIZE, VIEWPORT_SIZE, rnd); |
| setViewport(m_renderCtx, viewport); |
| gl.clearColor(0, 0, 0, 1); |
| gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| |
| gl.bindVertexArray(vao); |
| gl.enableVertexAttribArray(m_posLoc); |
| GLU_CHECK_CALL_ERROR(gl.useProgram(getProgram()), |
| gl.getError()); |
| |
| gl.uniform1f(m_scaleLoc, scale); |
| |
| if (tf) |
| gl.beginTransformFeedback(GL_TRIANGLES); |
| GLU_CHECK_CALL_ERROR(gl.drawArrays(GL_TRIANGLES, 0, 3), gl.getError()); |
| if (tf) |
| gl.endTransformFeedback(); |
| |
| if (dst != DE_NULL) |
| readRectangle(m_renderCtx, viewport, *dst); |
| |
| gl.bindVertexArray(0); |
| } |
| |
| void ScaleProgram::setPos (GLuint buffer, GLuint vao) |
| { |
| const Functions& gl = m_renderCtx.getFunctions(); |
| |
| gl.bindBuffer(GL_ARRAY_BUFFER, buffer); |
| gl.bindVertexArray(vao); |
| GLU_CHECK_CALL_ERROR( |
| gl.vertexAttribPointer(m_posLoc, NUM_COMPONENTS, GL_FLOAT, false, 0, DE_NULL), |
| gl.getError()); |
| gl.bindVertexArray(0); |
| gl.bindBuffer(GL_ARRAY_BUFFER, 0); |
| GLU_CHECK_ERROR(gl.getError()); |
| } |
| |
| class VertexArrayBinder : public SimpleBinder |
| { |
| public: |
| VertexArrayBinder (lt::Context& ctx) |
| : SimpleBinder (ctx, 0, GL_NONE, GL_VERTEX_ARRAY_BINDING, true) {} |
| void bind (GLuint name) { glBindVertexArray(name); } |
| }; |
| |
| class SamplerBinder : public Binder |
| { |
| public: |
| SamplerBinder (lt::Context& ctx) : Binder(ctx) {} |
| void bind (GLuint name) { glBindSampler(0, name); } |
| GLuint getBinding (void) |
| { |
| GLint arr[32] = {}; |
| glGetIntegerv(GL_SAMPLER_BINDING, arr); |
| log() << TestLog::Message << "// First output integer: " << arr[0] |
| << TestLog::EndMessage; |
| return arr[0]; |
| } |
| bool genRequired (void) const { return true; } |
| }; |
| |
| class QueryBinder : public Binder |
| { |
| public: |
| QueryBinder (lt::Context& ctx) : Binder(ctx) {} |
| void bind (GLuint name) |
| { |
| if (name != 0) |
| glBeginQuery(GL_ANY_SAMPLES_PASSED, name); |
| else |
| glEndQuery(GL_ANY_SAMPLES_PASSED); |
| } |
| GLuint getBinding (void) { return 0; } |
| }; |
| |
| class BufferVAOAttacher : public Attacher |
| { |
| public: |
| BufferVAOAttacher (lt::Context& ctx, Type& elementType, |
| Type& varrType, ScaleProgram& program) |
| : Attacher (ctx, elementType, varrType) |
| , m_program (program) {} |
| void initAttachment (GLuint seed, GLuint element); |
| void attach (GLuint element, GLuint container); |
| void detach (GLuint element, GLuint container); |
| bool canAttachDeleted (void) const { return false; } |
| ScaleProgram& getProgram (void) { return m_program; } |
| GLuint getAttachment (GLuint container); |
| |
| private: |
| ScaleProgram& m_program; |
| }; |
| |
| static const GLfloat s_varrData[NUM_VERTICES * NUM_COMPONENTS] = |
| { |
| -1.0, 0.0, 0.0, 1.0, |
| 1.0, 1.0, 0.0, 1.0, |
| 0.0, -1.0, 0.0, 1.0 |
| }; |
| |
| void initBuffer (const Functions& gl, GLuint seed, GLenum usage, GLuint buffer) |
| { |
| gl.bindBuffer(GL_ARRAY_BUFFER, buffer); |
| if (seed == 0) |
| gl.bufferData(GL_ARRAY_BUFFER, sizeof(s_varrData), s_varrData, usage); |
| else |
| { |
| Random rnd (seed); |
| GLfloat data[DE_LENGTH_OF_ARRAY(s_varrData)]; |
| |
| for (int ndx = 0; ndx < NUM_VERTICES; ndx++) |
| { |
| GLfloat* vertex = &data[ndx * NUM_COMPONENTS]; |
| vertex[0] = 2.0f * (rnd.getFloat() - 0.5f); |
| vertex[1] = 2.0f * (rnd.getFloat() - 0.5f); |
| DE_STATIC_ASSERT(NUM_COMPONENTS == 4); |
| vertex[2] = 0.0f; |
| vertex[3] = 1.0f; |
| } |
| gl.bufferData(GL_ARRAY_BUFFER, sizeof(data), data, usage); |
| } |
| gl.bindBuffer(GL_ARRAY_BUFFER, 0); |
| GLU_CHECK_ERROR(gl.getError()); |
| } |
| |
| void BufferVAOAttacher::initAttachment (GLuint seed, GLuint buffer) |
| { |
| initBuffer(gl(), seed, GL_STATIC_DRAW, buffer); |
| log() << TestLog::Message << "// Initialized buffer " << buffer << " from seed " << seed |
| << TestLog::EndMessage; |
| } |
| |
| void BufferVAOAttacher::attach (GLuint buffer, GLuint vao) |
| { |
| m_program.setPos(buffer, vao); |
| log() << TestLog::Message |
| << "// Set the `pos` attribute in VAO " << vao << " to buffer " << buffer |
| << TestLog::EndMessage; |
| } |
| |
| void BufferVAOAttacher::detach (GLuint buffer, GLuint varr) |
| { |
| DE_UNREF(buffer); |
| attach(0, varr); |
| } |
| |
| GLuint BufferVAOAttacher::getAttachment (GLuint varr) |
| { |
| GLint name = 0; |
| gl().bindVertexArray(varr); |
| gl().getVertexAttribiv(0, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &name); |
| gl().bindVertexArray(0); |
| GLU_CHECK_ERROR(gl().getError()); |
| return GLuint(name); |
| } |
| |
| class BufferVAOInputAttacher : public InputAttacher |
| { |
| public: |
| BufferVAOInputAttacher (BufferVAOAttacher& attacher) |
| : InputAttacher (attacher) |
| , m_program (attacher.getProgram()) {} |
| void drawContainer (GLuint container, Surface& dst); |
| |
| private: |
| ScaleProgram& m_program; |
| }; |
| |
| void BufferVAOInputAttacher::drawContainer (GLuint vao, Surface& dst) |
| { |
| m_program.draw(vao, 1.0, false, &dst); |
| log() << TestLog::Message << "// Drew an output image with VAO " << vao |
| << TestLog::EndMessage; |
| }; |
| |
| class BufferTfAttacher : public Attacher |
| { |
| public: |
| BufferTfAttacher (lt::Context& ctx, Type& bufferType, Type& tfType) |
| : Attacher (ctx, bufferType, tfType) {} |
| void initAttachment (GLuint seed, GLuint element); |
| void attach (GLuint buffer, GLuint tf); |
| void detach (GLuint buffer, GLuint tf); |
| bool canAttachDeleted (void) const { return false; } |
| GLuint getAttachment (GLuint tf); |
| }; |
| |
| void BufferTfAttacher::initAttachment (GLuint seed, GLuint buffer) |
| { |
| initBuffer(gl(), seed, GL_DYNAMIC_READ, buffer); |
| log() << TestLog::Message << "// Initialized buffer " << buffer << " from seed " << seed |
| << TestLog::EndMessage; |
| } |
| |
| void BufferTfAttacher::attach (GLuint buffer, GLuint tf) |
| { |
| glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tf); |
| glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buffer); |
| glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0); |
| GLU_CHECK_ERROR(gl().getError()); |
| } |
| |
| void BufferTfAttacher::detach (GLuint buffer, GLuint tf) |
| { |
| DE_UNREF(buffer); |
| glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tf); |
| glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0); |
| glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0); |
| GLU_CHECK_ERROR(gl().getError()); |
| } |
| |
| GLuint BufferTfAttacher::getAttachment (GLuint tf) |
| { |
| GLint ret = 0; |
| gl().bindTransformFeedback(GL_TRANSFORM_FEEDBACK, tf); |
| gl().getIntegeri_v(GL_TRANSFORM_FEEDBACK_BUFFER_BINDING, 0, &ret); |
| gl().bindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0); |
| GLU_CHECK_ERROR(gl().getError()); |
| return GLuint(ret); |
| } |
| |
| class BufferTfOutputAttacher : public OutputAttacher |
| { |
| public: |
| BufferTfOutputAttacher (BufferTfAttacher& attacher, ScaleProgram& program) |
| : OutputAttacher (attacher) |
| , m_program (program) {} |
| void setupContainer (GLuint seed, GLuint container); |
| void drawAttachment (GLuint attachment, Surface& dst); |
| |
| private: |
| ScaleProgram& m_program; |
| }; |
| |
| void BufferTfOutputAttacher::drawAttachment (GLuint buffer, Surface& dst) |
| { |
| VertexArray vao(getRenderContext()); |
| |
| m_program.setPos(buffer, *vao); |
| m_program.draw(*vao, 1.0, false, &dst); |
| log() << TestLog::Message |
| << "// Drew output image with vertices from buffer " << buffer |
| << TestLog::EndMessage; |
| GLU_CHECK_ERROR(gl().getError()); |
| } |
| |
| void BufferTfOutputAttacher::setupContainer (GLuint seed, GLuint tf) |
| { |
| Buffer posBuf (getRenderContext()); |
| VertexArray vao (getRenderContext()); |
| |
| initBuffer(gl(), seed, GL_STATIC_DRAW, *posBuf); |
| m_program.setPos(*posBuf, *vao); |
| |
| glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tf); |
| m_program.draw(*vao, -1.0, true, DE_NULL); |
| log() << TestLog::Message |
| << "// Drew an image with seed " << seed << " with transform feedback to " << tf |
| << TestLog::EndMessage; |
| glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0); |
| GLU_CHECK_ERROR(gl().getError()); |
| } |
| |
| class ES3Types : public ES2Types |
| { |
| public: |
| ES3Types (lt::Context& ctx); |
| private: |
| ScaleProgram m_program; |
| QueryBinder m_queryBind; |
| SimpleType m_queryType; |
| SimpleBinder m_tfBind; |
| SimpleType m_tfType; |
| VertexArrayBinder m_varrBind; |
| SimpleType m_varrType; |
| SamplerBinder m_samplerBind; |
| SimpleType m_samplerType; |
| BufferVAOAttacher m_bufVarrAtt; |
| BufferVAOInputAttacher m_bufVarrInAtt; |
| BufferTfAttacher m_bufTfAtt; |
| BufferTfOutputAttacher m_bufTfOutAtt; |
| }; |
| |
| ES3Types::ES3Types (lt::Context& ctx) |
| : ES2Types (ctx) |
| , m_program (ctx) |
| , m_queryBind (ctx) |
| , m_queryType (ctx, "query", &CallLogWrapper::glGenQueries, |
| &CallLogWrapper::glDeleteQueries, |
| &CallLogWrapper::glIsQuery, &m_queryBind) |
| , m_tfBind (ctx, &CallLogWrapper::glBindTransformFeedback, GL_TRANSFORM_FEEDBACK, |
| GL_TRANSFORM_FEEDBACK_BINDING, true) |
| , m_tfType (ctx, "transform_feedback", &CallLogWrapper::glGenTransformFeedbacks, |
| &CallLogWrapper::glDeleteTransformFeedbacks, |
| &CallLogWrapper::glIsTransformFeedback, &m_tfBind) |
| , m_varrBind (ctx) |
| , m_varrType (ctx, "vertex_array", &CallLogWrapper::glGenVertexArrays, |
| &CallLogWrapper::glDeleteVertexArrays, |
| &CallLogWrapper::glIsVertexArray, &m_varrBind) |
| , m_samplerBind (ctx) |
| , m_samplerType (ctx, "sampler", &CallLogWrapper::glGenSamplers, |
| &CallLogWrapper::glDeleteSamplers, |
| &CallLogWrapper::glIsSampler, &m_samplerBind, true) |
| , m_bufVarrAtt (ctx, m_bufferType, m_varrType, m_program) |
| , m_bufVarrInAtt(m_bufVarrAtt) |
| , m_bufTfAtt (ctx, m_bufferType, m_tfType) |
| , m_bufTfOutAtt (m_bufTfAtt, m_program) |
| { |
| Type* types[] = { &m_queryType, &m_tfType, &m_varrType, &m_samplerType }; |
| m_types.insert(m_types.end(), DE_ARRAY_BEGIN(types), DE_ARRAY_END(types)); |
| |
| m_attachers.push_back(&m_bufVarrAtt); |
| m_attachers.push_back(&m_bufTfAtt); |
| |
| m_inAttachers.push_back(&m_bufVarrInAtt); |
| m_outAttachers.push_back(&m_bufTfOutAtt); |
| } |
| |
| class TfDeleteActiveTest : public TestCase, private CallLogWrapper |
| { |
| public: |
| TfDeleteActiveTest (gles3::Context& context, |
| const char* name, const char* description); |
| IterateResult iterate (void); |
| }; |
| |
| TfDeleteActiveTest::TfDeleteActiveTest (gles3::Context& context, |
| const char* name, const char* description) |
| : TestCase (context, name, description) |
| , CallLogWrapper (context.getRenderContext().getFunctions(), |
| context.getTestContext().getLog()) |
| { |
| enableLogging(true); |
| } |
| |
| class ScopedTransformFeedbackFeedback |
| { |
| public: |
| ScopedTransformFeedbackFeedback (glu::CallLogWrapper& gl, GLenum type); |
| ~ScopedTransformFeedbackFeedback (void); |
| |
| private: |
| glu::CallLogWrapper& m_gl; |
| }; |
| |
| ScopedTransformFeedbackFeedback::ScopedTransformFeedbackFeedback (glu::CallLogWrapper& gl, GLenum type) |
| : m_gl(gl) |
| { |
| m_gl.glBeginTransformFeedback(type); |
| GLU_EXPECT_NO_ERROR(m_gl.glGetError(), "glBeginTransformFeedback"); |
| } |
| |
| ScopedTransformFeedbackFeedback::~ScopedTransformFeedbackFeedback (void) |
| { |
| m_gl.glEndTransformFeedback(); |
| } |
| |
| IterateResult TfDeleteActiveTest::iterate (void) |
| { |
| static const char* const s_xfbVertexSource = "#version 300 es\n" |
| "void main ()\n" |
| "{\n" |
| " gl_Position = vec4(float(gl_VertexID) / 2.0, float(gl_VertexID % 2) / 2.0, 0.0, 1.0);\n" |
| "}\n"; |
| static const char* const s_xfbFragmentSource = "#version 300 es\n" |
| "layout(location=0) out mediump vec4 dEQP_FragColor;\n" |
| "void main ()\n" |
| "{\n" |
| " dEQP_FragColor = vec4(1.0, 1.0, 0.0, 1.0);\n" |
| "}\n"; |
| |
| glu::Buffer buf (m_context.getRenderContext()); |
| GLuint tf = 0; |
| glu::ShaderProgram program (m_context.getRenderContext(), |
| glu::ProgramSources() |
| << glu::VertexSource(s_xfbVertexSource) |
| << glu::FragmentSource(s_xfbFragmentSource) |
| << glu::TransformFeedbackMode(GL_INTERLEAVED_ATTRIBS) |
| << glu::TransformFeedbackVarying("gl_Position")); |
| |
| if (!program.isOk()) |
| { |
| m_testCtx.getLog() << program; |
| throw tcu::TestError("failed to build program"); |
| } |
| |
| try |
| { |
| GLU_CHECK_CALL(glUseProgram(program.getProgram())); |
| GLU_CHECK_CALL(glGenTransformFeedbacks(1, &tf)); |
| GLU_CHECK_CALL(glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tf)); |
| GLU_CHECK_CALL(glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, *buf)); |
| GLU_CHECK_CALL(glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 3 * sizeof(glw::GLfloat[4]), DE_NULL, GL_DYNAMIC_COPY)); |
| |
| { |
| ScopedTransformFeedbackFeedback xfb(static_cast<glu::CallLogWrapper&>(*this), GL_TRIANGLES); |
| |
| glDeleteTransformFeedbacks(1, &tf); |
| { |
| GLenum err = glGetError(); |
| if (err != GL_INVALID_OPERATION) |
| getTestContext().setTestResult( |
| QP_TEST_RESULT_FAIL, |
| "Deleting active transform feedback did not produce GL_INVALID_OPERATION"); |
| else |
| getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| } |
| } |
| GLU_CHECK(); // ScopedTransformFeedbackFeedback::dtor might modify error state |
| |
| GLU_CHECK_CALL(glDeleteTransformFeedbacks(1, &tf)); |
| } |
| catch (const glu::Error&) |
| { |
| glDeleteTransformFeedbacks(1, &tf); |
| throw; |
| } |
| |
| return STOP; |
| } |
| |
| class TestGroup : public TestCaseGroup |
| { |
| public: |
| TestGroup (gles3::Context& context) |
| : TestCaseGroup (context, "lifetime", "Object lifetime tests") |
| {} |
| void init (void); |
| private: |
| MovePtr<Types> m_types; |
| }; |
| |
| void TestGroup::init (void) |
| { |
| gles3::Context& ctx = getContext(); |
| lt::Context ltCtx (ctx.getRenderContext(), ctx.getTestContext()); |
| |
| m_types = MovePtr<Types>(new ES3Types(ltCtx)); |
| |
| addTestCases(*this, *m_types); |
| |
| TestCaseGroup* deleteActiveGroup = |
| new TestCaseGroup(ctx, "delete_active", "Delete active object"); |
| addChild(deleteActiveGroup); |
| deleteActiveGroup->addChild( |
| new TfDeleteActiveTest(ctx, "transform_feedback", "Transform Feedback")); |
| } |
| |
| } // anonymous |
| |
| TestCaseGroup* createLifetimeTests (Context& context) |
| { |
| return new TestGroup(context); |
| } |
| |
| } // Functional |
| } // gles3 |
| } // deqp |