blob: 2ce8ccad6fd36109515043957b61ecd335002605 [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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 de::MovePtr;
using de::Random;
using glu::Buffer;
using glu::CallLogWrapper;
using glu::ProgramSources;
using glu::RenderContext;
using glu::VertexArray;
using std::vector;
using tcu::RenderTarget;
using tcu::Surface;
using tcu::TestContext;
using tcu::TestLog;
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);
if (gl.getError() == GL_INVALID_OPERATION)
return;
}
GLU_CHECK_CALL_ERROR(gl.drawArrays(GL_TRIANGLES, 0, 3), gl.getError());
if (tf)
gl.endTransformFeedback();
if (dst != nullptr)
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, nullptr), 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, nullptr);
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]), nullptr, 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"));
}
} // namespace
TestCaseGroup *createLifetimeTests(Context &context)
{
return new TestGroup(context);
}
} // namespace Functional
} // namespace gles3
} // namespace deqp