/*-------------------------------------------------------------------------
* drawElements Quality Program OpenGL ES 2.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 Framebuffer Object Tests.
*
* Notes:
* + Like in API tests, tcu::sgl2s::Context class is used.
* + ReferenceContext is used to generate reference images.
* + API calls can be logged \todo [pyry] Implement.
*//*--------------------------------------------------------------------*/
#include "es2fFboRenderTest.hpp"
#include "sglrContextUtil.hpp"
#include "sglrGLContext.hpp"
#include "sglrReferenceContext.hpp"
#include "tcuSurface.hpp"
#include "tcuTextureUtil.hpp"
#include "tcuImageCompare.hpp"
#include "tcuRenderTarget.hpp"
#include "gluPixelTransfer.hpp"
#include "gluTextureUtil.hpp"
#include "gluStrUtil.hpp"
#include "deRandom.hpp"
#include "deString.h"
#include "glwFunctions.hpp"
#include "glwEnums.hpp"
using std::vector;
using std::string;
using tcu::Vec2;
using tcu::Vec3;
using tcu::Vec4;
using tcu::RGBA;
using tcu::Surface;
using namespace glw; // GL types
namespace deqp
{
namespace gles2
{
namespace Functional
{
// Shaders.
class FlatColorShader : public sglr::ShaderProgram
{
public:
FlatColorShader (void)
: sglr::ShaderProgram(sglr::pdec::ShaderProgramDeclaration()
<< sglr::pdec::VertexAttribute("a_position", rr::GENERICVECTYPE_FLOAT)
<< sglr::pdec::FragmentOutput(rr::GENERICVECTYPE_FLOAT)
<< sglr::pdec::Uniform("u_color", glu::TYPE_FLOAT_VEC4)
<< sglr::pdec::VertexSource(
"attribute highp vec4 a_position;\n"
"void main (void)\n"
"{\n"
" gl_Position = a_position;\n"
"}\n")
<< sglr::pdec::FragmentSource(
"uniform mediump vec4 u_color;\n"
"void main (void)\n"
"{\n"
" gl_FragColor = u_color;\n"
"}\n"))
{
}
void setColor (sglr::Context& gl, deUint32 program, const tcu::Vec4& color)
{
gl.useProgram(program);
gl.uniform4fv(gl.getUniformLocation(program, "u_color"), 1, color.getPtr());
}
void shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const
{
for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
packets[packetNdx]->position = rr::readVertexAttribFloat(inputs[0], packets[packetNdx]->instanceNdx, packets[packetNdx]->vertexNdx);
}
void shadeFragments (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const
{
const tcu::Vec4 color(m_uniforms[0].value.f4);
DE_UNREF(packets);
for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, color);
}
};
class SingleTex2DShader : public sglr::ShaderProgram
{
public:
SingleTex2DShader (void)
: sglr::ShaderProgram(sglr::pdec::ShaderProgramDeclaration()
<< sglr::pdec::VertexAttribute("a_position", rr::GENERICVECTYPE_FLOAT)
<< sglr::pdec::VertexAttribute("a_coord", rr::GENERICVECTYPE_FLOAT)
<< sglr::pdec::VertexToFragmentVarying(rr::GENERICVECTYPE_FLOAT)
<< sglr::pdec::FragmentOutput(rr::GENERICVECTYPE_FLOAT)
<< sglr::pdec::Uniform("u_sampler0", glu::TYPE_SAMPLER_2D)
<< sglr::pdec::VertexSource(
"attribute highp vec4 a_position;\n"
"attribute mediump vec2 a_coord;\n"
"varying mediump vec2 v_coord;\n"
"void main (void)\n"
"{\n"
" gl_Position = a_position;\n"
" v_coord = a_coord;\n"
"}\n")
<< sglr::pdec::FragmentSource(
"uniform sampler2D u_sampler0;\n"
"varying mediump vec2 v_coord;\n"
"void main (void)\n"
"{\n"
" gl_FragColor = texture2D(u_sampler0, v_coord);\n"
"}\n"))
{
}
void setUnit (sglr::Context& gl, deUint32 program, int unitNdx)
{
gl.useProgram(program);
gl.uniform1i(gl.getUniformLocation(program, "u_sampler0"), unitNdx);
}
void shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const
{
for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
{
rr::VertexPacket& packet = *packets[packetNdx];
packet.position = rr::readVertexAttribFloat(inputs[0], packet.instanceNdx, packet.vertexNdx);
packet.outputs[0] = rr::readVertexAttribFloat(inputs[1], packet.instanceNdx, packet.vertexNdx);
}
}
void shadeFragments (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const
{
for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
{
const tcu::Vec4 v_coord = rr::readVarying<float>(packets[packetNdx], context, 0, fragNdx);
const float lod = 0.0f;
rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, this->m_uniforms[0].sampler.tex2D->sample(v_coord.x(), v_coord.y(), lod));
}
}
};
class MixTexturesShader : public sglr::ShaderProgram
{
public:
MixTexturesShader (void)
: sglr::ShaderProgram(sglr::pdec::ShaderProgramDeclaration()
<< sglr::pdec::VertexAttribute("a_position", rr::GENERICVECTYPE_FLOAT)
<< sglr::pdec::VertexAttribute("a_coord", rr::GENERICVECTYPE_FLOAT)
<< sglr::pdec::VertexToFragmentVarying(rr::GENERICVECTYPE_FLOAT)
<< sglr::pdec::FragmentOutput(rr::GENERICVECTYPE_FLOAT)
<< sglr::pdec::Uniform("u_sampler0", glu::TYPE_SAMPLER_2D)
<< sglr::pdec::Uniform("u_sampler1", glu::TYPE_SAMPLER_2D)
<< sglr::pdec::VertexSource(
"attribute highp vec4 a_position;\n"
"attribute mediump vec2 a_coord;\n"
"varying mediump vec2 v_coord;\n"
"void main (void)\n"
"{\n"
" gl_Position = a_position;\n"
" v_coord = a_coord;\n"
"}\n")
<< sglr::pdec::FragmentSource(
"uniform sampler2D u_sampler0;\n"
"uniform sampler2D u_sampler1;\n"
"varying mediump vec2 v_coord;\n"
"void main (void)\n"
"{\n"
" gl_FragColor = texture2D(u_sampler0, v_coord)*0.5 + texture2D(u_sampler1, v_coord)*0.5;\n"
"}\n"))
{
}
void setUnits (sglr::Context& gl, deUint32 program, int unit0, int unit1)
{
gl.useProgram(program);
gl.uniform1i(gl.getUniformLocation(program, "u_sampler0"), unit0);
gl.uniform1i(gl.getUniformLocation(program, "u_sampler1"), unit1);
}
void shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const
{
for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
{
rr::VertexPacket& packet = *packets[packetNdx];
packet.position = rr::readVertexAttribFloat(inputs[0], packet.instanceNdx, packet.vertexNdx);
packet.outputs[0] = rr::readVertexAttribFloat(inputs[1], packet.instanceNdx, packet.vertexNdx);
}
}
void shadeFragments (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const
{
for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
{
const tcu::Vec4 v_coord = rr::readVarying<float>(packets[packetNdx], context, 0, fragNdx);
const float lod = 0.0f;
rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, this->m_uniforms[0].sampler.tex2D->sample(v_coord.x(), v_coord.y(), lod) * 0.5f
+ this->m_uniforms[1].sampler.tex2D->sample(v_coord.x(), v_coord.y(), lod) * 0.5f);
}
}
};
// Framebuffer config.
class FboConfig
{
public:
FboConfig (void)
: colorbufferType (GL_NONE)
, colorbufferFormat (GL_NONE)
, depthbufferType (GL_NONE)
, depthbufferFormat (GL_NONE)
, stencilbufferType (GL_NONE)
, stencilbufferFormat (GL_NONE)
{
}
std::string getName (void) const;
GLenum colorbufferType; //!< GL_TEXTURE_2D, GL_TEXTURE_CUBE_MAP, GL_RENDERBUFFER
GLenum colorbufferFormat; //!< Internal format for color buffer texture or renderbuffer
GLenum depthbufferType; //!< GL_RENDERBUFFER
GLenum depthbufferFormat;
GLenum stencilbufferType; //!< GL_RENDERBUFFER
GLenum stencilbufferFormat;
private:
static const char* getFormatName (GLenum format);
};
const char* FboConfig::getFormatName (GLenum format)
{
switch (format)
{
case GL_RGB: return "rgb";
case GL_RGBA: return "rgba";
case GL_ALPHA: return "alpha";
case GL_LUMINANCE: return "luminance";
case GL_LUMINANCE_ALPHA: return "luminance_alpha";
case GL_RGB565: return "rgb565";
case GL_RGB5_A1: return "rgb5_a1";
case GL_RGBA4: return "rgba4";
case GL_RGBA16F: return "rgba16f";
case GL_RGB16F: return "rgb16f";
case GL_DEPTH_COMPONENT16: return "depth_component16";
case GL_STENCIL_INDEX8: return "stencil_index8";
default: DE_ASSERT(false); return DE_NULL;
}
}
std::string FboConfig::getName (void) const
{
std::string name = "";
if (colorbufferType != GL_NONE)
{
switch (colorbufferType)
{
case GL_TEXTURE_2D: name += "tex2d_"; break;
case GL_TEXTURE_CUBE_MAP: name += "texcube_"; break;
case GL_RENDERBUFFER: name += "rbo_"; break;
default: DE_ASSERT(false); break;
}
name += getFormatName(colorbufferFormat);
}
if (depthbufferType != GL_NONE)
{
DE_ASSERT(depthbufferType == GL_RENDERBUFFER);
if (name.length() > 0)
name += "_";
name += getFormatName(depthbufferFormat);
}
if (stencilbufferType != GL_NONE)
{
DE_ASSERT(stencilbufferType == GL_RENDERBUFFER);
if (name.length() > 0)
name += "_";
name += getFormatName(stencilbufferFormat);
}
return name;
}
class FboIncompleteException : public tcu::TestError
{
public:
FboIncompleteException (const FboConfig& config, GLenum reason, const char* file, int line);
virtual ~FboIncompleteException (void) throw() {}
const FboConfig& getConfig (void) const { return m_config; }
GLenum getReason (void) const { return m_reason; }
private:
FboConfig m_config;
GLenum m_reason;
};
static const char* getFboIncompleteReasonName (GLenum reason)
{
switch (reason)
{
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: return "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
case GL_FRAMEBUFFER_UNSUPPORTED: return "GL_FRAMEBUFFER_UNSUPPORTED";
case GL_FRAMEBUFFER_COMPLETE: return "GL_FRAMEBUFFER_COMPLETE";
default: return "UNKNOWN";
}
}
FboIncompleteException::FboIncompleteException (const FboConfig& config, GLenum reason, const char* file, int line)
: TestError("Framebuffer is not complete", getFboIncompleteReasonName(reason), file, line)
, m_config(config)
, m_reason(reason)
{
}
class Framebuffer
{
public:
Framebuffer (sglr::Context& context, const FboConfig& config, int width, int height, deUint32 fbo = 0, deUint32 colorbuffer = 0, deUint32 depthbuffer = 0, deUint32 stencilbuffer = 0);
~Framebuffer (void);
const FboConfig& getConfig (void) const { return m_config; }
deUint32 getFramebuffer (void) const { return m_framebuffer; }
deUint32 getColorbuffer (void) const { return m_colorbuffer; }
deUint32 getDepthbuffer (void) const { return m_depthbuffer; }
deUint32 getStencilbuffer (void) const { return m_stencilbuffer; }
void checkCompleteness (void);
private:
void createRbo (deUint32& name, GLenum format, int width, int height);
void destroyBuffer (deUint32 name, GLenum type);
FboConfig m_config;
sglr::Context& m_context;
deUint32 m_framebuffer;
deUint32 m_colorbuffer;
deUint32 m_depthbuffer;
deUint32 m_stencilbuffer;
};
static bool isExtensionSupported (sglr::Context& context, const char* name)
{
std::istringstream extensions(context.getString(GL_EXTENSIONS));
std::string extension;
while (std::getline(extensions, extension, ' '))
{
if (extension == name)
return true;
}
return false;
}
static void checkColorFormatSupport (sglr::Context& context, deUint32 sizedFormat)
{
switch (sizedFormat)
{
case GL_RGBA16F:
case GL_RGB16F:
case GL_RG16F:
case GL_R16F:
if (!isExtensionSupported(context, "GL_EXT_color_buffer_half_float"))
throw tcu::NotSupportedError("GL_EXT_color_buffer_half_float is not supported");
default:
break;
}
}
Framebuffer::Framebuffer (sglr::Context& context, const FboConfig& config, int width, int height, deUint32 fbo, deUint32 colorbuffer, deUint32 depthbuffer, deUint32 stencilbuffer)
: m_config (config)
, m_context (context)
, m_framebuffer (fbo)
, m_colorbuffer (colorbuffer)
, m_depthbuffer (depthbuffer)
, m_stencilbuffer (stencilbuffer)
{
// Verify that color format is supported
checkColorFormatSupport(context, config.colorbufferFormat);
if (m_framebuffer == 0)
context.genFramebuffers(1, &m_framebuffer);
context.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
switch (m_config.colorbufferType)
{
case GL_TEXTURE_2D:
if (m_colorbuffer == 0)
context.genTextures(1, &m_colorbuffer);
context.bindTexture(GL_TEXTURE_2D, m_colorbuffer);
context.texImage2D(GL_TEXTURE_2D, 0, m_config.colorbufferFormat, width, height);
context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
if (!deIsPowerOfTwo32(width) || !deIsPowerOfTwo32(height))
{
// Set wrap mode to clamp for NPOT FBOs
context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorbuffer, 0);
break;
case GL_TEXTURE_CUBE_MAP:
DE_FATAL("TODO");
break;
case GL_RENDERBUFFER:
createRbo(m_colorbuffer, m_config.colorbufferFormat, width, height);
context.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_colorbuffer);
break;
default:
DE_ASSERT(m_config.colorbufferType == GL_NONE);
break;
}
if (m_config.depthbufferType == GL_RENDERBUFFER)
{
createRbo(m_depthbuffer, m_config.depthbufferFormat, width, height);
context.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthbuffer);
}
else
DE_ASSERT(m_config.depthbufferType == GL_NONE);
if (m_config.stencilbufferType == GL_RENDERBUFFER)
{
createRbo(m_stencilbuffer, m_config.stencilbufferFormat, width, height);
context.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_stencilbuffer);
}
else
DE_ASSERT(m_config.stencilbufferType == GL_NONE);
context.bindFramebuffer(GL_FRAMEBUFFER, 0);
}
Framebuffer::~Framebuffer (void)
{
m_context.deleteFramebuffers(1, &m_framebuffer);
destroyBuffer(m_colorbuffer, m_config.colorbufferType);
destroyBuffer(m_depthbuffer, m_config.depthbufferType);
destroyBuffer(m_stencilbuffer, m_config.stencilbufferType);
}
void Framebuffer::checkCompleteness (void)
{
m_context.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
GLenum status = m_context.checkFramebufferStatus(GL_FRAMEBUFFER);
m_context.bindFramebuffer(GL_FRAMEBUFFER, 0);
if (status != GL_FRAMEBUFFER_COMPLETE)
throw FboIncompleteException(m_config, status, __FILE__, __LINE__);
}
void Framebuffer::createRbo (deUint32& name, GLenum format, int width, int height)
{
if (name == 0)
m_context.genRenderbuffers(1, &name);
m_context.bindRenderbuffer(GL_RENDERBUFFER, name);
m_context.renderbufferStorage(GL_RENDERBUFFER, format, width, height);
}
void Framebuffer::destroyBuffer (deUint32 name, GLenum type)
{
if (type == GL_TEXTURE_2D || type == GL_TEXTURE_CUBE_MAP)
m_context.deleteTextures(1, &name);
else if (type == GL_RENDERBUFFER)
m_context.deleteRenderbuffers(1, &name);
else
DE_ASSERT(type == GL_NONE);
}
static void createMetaballsTex2D (sglr::Context& context, deUint32 name, GLenum format, GLenum dataType, int width, int height)
{
tcu::TextureFormat texFormat = glu::mapGLTransferFormat(format, dataType);
tcu::TextureLevel level (texFormat, width, height);
tcu::fillWithMetaballs(level.getAccess(), 5, name ^ width ^ height);
context.bindTexture(GL_TEXTURE_2D, name);
context.texImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, dataType, level.getAccess().getDataPtr());
context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
static void createQuadsTex2D (sglr::Context& context, deUint32 name, GLenum format, GLenum dataType, int width, int height)
{
tcu::TextureFormat texFormat = glu::mapGLTransferFormat(format, dataType);
tcu::TextureLevel level (texFormat, width, height);
tcu::fillWithRGBAQuads(level.getAccess());
context.bindTexture(GL_TEXTURE_2D, name);
context.texImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, dataType, level.getAccess().getDataPtr());
context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
class FboRenderCase : public TestCase
{
public:
FboRenderCase (Context& context, const char* name, const char* description, const FboConfig& config);
virtual ~FboRenderCase (void) {}
virtual IterateResult iterate (void);
virtual void render (sglr::Context& fboContext, Surface& dst) = DE_NULL;
const FboConfig& getConfig (void) const { return m_config; }
static bool isConfigSupported (const FboConfig& config) { DE_UNREF(config); return true; }
private:
FboConfig m_config;
};
FboRenderCase::FboRenderCase (Context& context, const char* name, const char* description, const FboConfig& config)
: TestCase(context, name, description)
, m_config(config)
{
}
TestCase::IterateResult FboRenderCase::iterate (void)
{
Vec4 clearColor (0.125f, 0.25f, 0.5f, 1.0f);
glu::RenderContext& renderCtx = m_context.getRenderContext();
const tcu::RenderTarget& renderTarget = m_context.getRenderTarget();
tcu::TestLog& log = m_testCtx.getLog();
const char* failReason = DE_NULL;
// Position & size for context
deRandom rnd;
deRandom_init(&rnd, deStringHash(getName()));
int width = deMin32(renderTarget.getWidth(), 128);
int height = deMin32(renderTarget.getHeight(), 128);
int xMax = renderTarget.getWidth()-width+1;
int yMax = renderTarget.getHeight()-height+1;
int x = deRandom_getUint32(&rnd) % xMax;
int y = deRandom_getUint32(&rnd) % yMax;
tcu::Surface gles2Frame (width, height);
tcu::Surface refFrame (width, height);
GLenum gles2Error;
GLenum refError;
// Render using GLES2
try
{
sglr::GLContext context(renderCtx, log, sglr::GLCONTEXT_LOG_CALLS, tcu::IVec4(x, y, width, height));
context.clearColor(clearColor.x(), clearColor.y(), clearColor.z(), clearColor.w());
context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
render(context, gles2Frame); // Call actual render func
gles2Error = context.getError();
}
catch (const FboIncompleteException& e)
{
if (e.getReason() == GL_FRAMEBUFFER_UNSUPPORTED)
{
// Mark test case as unsupported
log << e;
m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not supported");
return STOP;
}
else
throw; // Propagate error
}
// Render reference image
{
sglr::ReferenceContextBuffers buffers (tcu::PixelFormat(8,8,8,renderTarget.getPixelFormat().alphaBits?8:0), renderTarget.getDepthBits(), renderTarget.getStencilBits(), width, height);
sglr::ReferenceContext context (sglr::ReferenceContextLimits(renderCtx), buffers.getColorbuffer(), buffers.getDepthbuffer(), buffers.getStencilbuffer());
context.clearColor(clearColor.x(), clearColor.y(), clearColor.z(), clearColor.w());
context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
render(context, refFrame);
refError = context.getError();
}
// Compare error codes
bool errorCodesOk = (gles2Error == refError);
if (!errorCodesOk)
{
log << tcu::TestLog::Message << "Error code mismatch: got " << glu::getErrorStr(gles2Error) << ", expected " << glu::getErrorStr(refError) << tcu::TestLog::EndMessage;
failReason = "Got unexpected error";
}
// Compare images
const float threshold = 0.03f;
bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, gles2Frame, threshold, tcu::COMPARE_LOG_RESULT);
if (!imagesOk && !failReason)
failReason = "Image comparison failed";
// Store test result
bool isOk = errorCodesOk && imagesOk;
m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
isOk ? "Pass" : failReason);
return STOP;
}
namespace FboCases
{
class ColorClearsTest : public FboRenderCase
{
public:
ColorClearsTest (Context& context, const FboConfig& config);
~ColorClearsTest (void) {}
void render (sglr::Context& context, Surface& dst);
};
ColorClearsTest::ColorClearsTest (Context& context, const FboConfig& config)
: FboRenderCase(context, config.getName().c_str(), "Color buffer clears", config)
{
}
void ColorClearsTest::render (sglr::Context& context, Surface& dst)
{
int width = 128;
int height = 128;
deRandom rnd;
deRandom_init(&rnd, 0);
// Create framebuffer
Framebuffer fbo(context, getConfig(), width, height);
fbo.checkCompleteness();
// Clear fbo
context.bindFramebuffer(GL_FRAMEBUFFER, fbo.getFramebuffer());
context.viewport(0, 0, width, height);
context.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
// Enable scissor test.
context.enable(GL_SCISSOR_TEST);
// Do 10 random color clears
for (int i = 0; i < 15; i++)
{
int cX = (int)(deRandom_getUint32(&rnd) & 0x7fffffff) % width;
int cY = (int)(deRandom_getUint32(&rnd) & 0x7fffffff) % height;
int cWidth = (int)(deRandom_getUint32(&rnd) & 0x7fffffff) % (width-cX);
int cHeight = (int)(deRandom_getUint32(&rnd) & 0x7fffffff) % (height-cY);
Vec4 color = RGBA(deRandom_getUint32(&rnd)).toVec();
context.scissor(cX, cY, cWidth, cHeight);
context.clearColor(color.x(), color.y(), color.z(), color.w());
context.clear(GL_COLOR_BUFFER_BIT);
}
// Disable scissor.
context.disable(GL_SCISSOR_TEST);
if (fbo.getConfig().colorbufferType == GL_TEXTURE_2D)
{
// Unbind fbo
context.bindFramebuffer(GL_FRAMEBUFFER, 0);
// Draw to screen
SingleTex2DShader shader;
deUint32 shaderID = context.createProgram(&shader);
shader.setUnit(context, shaderID, 0);
context.bindTexture(GL_TEXTURE_2D, fbo.getColorbuffer());
context.viewport(0, 0, context.getWidth(), context.getHeight());
sglr::drawQuad(context, shaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
// Read from screen
context.readPixels(dst, 0, 0, context.getWidth(), context.getHeight());
}
else
{
// clear alpha channel for GL_RGB5_A1 format because test
// thresholds for the alpha channel do not account for dithering
if(getConfig().colorbufferFormat == GL_RGB5_A1)
{
context.colorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
context.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
context.clear(GL_COLOR_BUFFER_BIT);
context.colorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
}
// Read from fbo
context.readPixels(dst, 0, 0, width, height);
}
}
class IntersectingQuadsTest : public FboRenderCase
{
public:
IntersectingQuadsTest (Context& context, const FboConfig& config, bool npot = false);
virtual ~IntersectingQuadsTest (void) {}
virtual void render (sglr::Context& context, Surface& dst);
static bool isConfigSupported (const FboConfig& config);
private:
int m_fboWidth;
int m_fboHeight;
};
class IntersectingQuadsNpotTest : public IntersectingQuadsTest
{
public:
IntersectingQuadsNpotTest (Context& context, const FboConfig& config)
: IntersectingQuadsTest(context, config, true)
{
}
};
IntersectingQuadsTest::IntersectingQuadsTest (Context& context, const FboConfig& config, bool npot)
: FboRenderCase (context, (string(npot ? "npot_" : "") + config.getName()).c_str(), "Intersecting textured quads", config)
, m_fboWidth (npot ? 127 : 128)
, m_fboHeight (npot ? 95 : 128)
{
}
bool IntersectingQuadsTest::isConfigSupported (const FboConfig& config)
{
// \note Disabled for stencil configurations since doesn't exercise stencil buffer
return config.depthbufferType != GL_NONE &&
config.stencilbufferType == GL_NONE;
}
void IntersectingQuadsTest::render (sglr::Context& ctx, Surface& dst)
{
SingleTex2DShader texShader;
deUint32 texShaderID = ctx.createProgram(&texShader);
deUint32 metaballsTex = 1;
deUint32 quadsTex = 2;
createMetaballsTex2D(ctx, metaballsTex, GL_RGB, GL_UNSIGNED_BYTE, 64, 64);
createQuadsTex2D(ctx, quadsTex, GL_RGB, GL_UNSIGNED_BYTE, 64, 64);
int width = m_fboWidth;
int height = m_fboHeight;
Framebuffer fbo(ctx, getConfig(), width, height);
fbo.checkCompleteness();
// Setup shaders
texShader.setUnit(ctx, texShaderID, 0);
// Draw scene
ctx.bindFramebuffer(GL_FRAMEBUFFER, fbo.getFramebuffer());
ctx.viewport(0, 0, width, height);
ctx.clearColor(1.0f, 0.0f, 0.0f, 1.0f);
ctx.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
ctx.enable(GL_DEPTH_TEST);
ctx.bindTexture(GL_TEXTURE_2D, metaballsTex);
sglr::drawQuad(ctx, texShaderID, Vec3(-1.0f, -1.0f, -1.0f), Vec3(1.0f, 1.0f, 1.0f));
ctx.bindTexture(GL_TEXTURE_2D, quadsTex);
sglr::drawQuad(ctx, texShaderID, Vec3(-1.0f, -1.0f, 1.0f), Vec3(1.0f, 1.0f, -1.0f));
ctx.disable(GL_DEPTH_TEST);
if (fbo.getConfig().colorbufferType == GL_TEXTURE_2D)
{
// Unbind fbo
ctx.bindFramebuffer(GL_FRAMEBUFFER, 0);
// Draw to screen
ctx.bindTexture(GL_TEXTURE_2D, fbo.getColorbuffer());
ctx.viewport(0, 0, ctx.getWidth(), ctx.getHeight());
sglr::drawQuad(ctx, texShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
// Read from screen
ctx.readPixels(dst, 0, 0, ctx.getWidth(), ctx.getHeight());
}
else
{
// Read from fbo
ctx.readPixels(dst, 0, 0, width, height);
}
}
class MixTest : public FboRenderCase
{
public:
MixTest (Context& context, const FboConfig& config, bool npot = false);
virtual ~MixTest (void) {}
void render (sglr::Context& context, Surface& dst);
static bool isConfigSupported (const FboConfig& config);
private:
int m_fboAWidth;
int m_fboAHeight;
int m_fboBWidth;
int m_fboBHeight;
};
class MixNpotTest : public MixTest
{
public:
MixNpotTest (Context& context, const FboConfig& config)
: MixTest(context, config, true)
{
}
};
MixTest::MixTest (Context& context, const FboConfig& config, bool npot)
: FboRenderCase (context, (string(npot ? "mix_npot_" : "mix_") + config.getName()).c_str(), "Use two fbos as sources in draw operation", config)
, m_fboAWidth (npot ? 127 : 128)
, m_fboAHeight (npot ? 95 : 128)
, m_fboBWidth (npot ? 55 : 64)
, m_fboBHeight (npot ? 63 : 64)
{
}
bool MixTest::isConfigSupported (const FboConfig& config)
{
// \note Disabled for stencil configurations since doesn't exercise stencil buffer
return config.colorbufferType == GL_TEXTURE_2D &&
config.stencilbufferType == GL_NONE;
}
void MixTest::render (sglr::Context& context, Surface& dst)
{
SingleTex2DShader singleTexShader;
MixTexturesShader mixShader;
deUint32 singleTexShaderID = context.createProgram(&singleTexShader);
deUint32 mixShaderID = context.createProgram(&mixShader);
// Texture with metaballs
deUint32 metaballsTex = 1;
context.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
createMetaballsTex2D(context, metaballsTex, GL_RGB, GL_UNSIGNED_BYTE, 64, 64);
// Setup shaders
singleTexShader.setUnit(context, singleTexShaderID, 0);
mixShader.setUnits(context, mixShaderID, 0, 1);
// Fbo, quad with metaballs texture
Framebuffer fboA(context, getConfig(), m_fboAWidth, m_fboAHeight);
fboA.checkCompleteness();
context.bindFramebuffer(GL_FRAMEBUFFER, fboA.getFramebuffer());
context.viewport(0, 0, m_fboAWidth, m_fboAHeight);
context.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
context.bindTexture(GL_TEXTURE_2D, metaballsTex);
sglr::drawQuad(context, singleTexShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
// Fbo, color clears
Framebuffer fboB(context, getConfig(), m_fboBWidth, m_fboBHeight);
fboB.checkCompleteness();
context.bindFramebuffer(GL_FRAMEBUFFER, fboB.getFramebuffer());
context.viewport(0, 0, m_fboBWidth, m_fboBHeight);
context.enable(GL_SCISSOR_TEST);
context.scissor(0, 0, 32, 64);
context.clearColor(1.0f, 0.0f, 0.0f, 1.0f);
context.clear(GL_COLOR_BUFFER_BIT);
context.scissor(32, 0, 32, 64);
context.clearColor(0.0f, 1.0f, 0.0f, 1.0f);
context.clear(GL_COLOR_BUFFER_BIT);
context.disable(GL_SCISSOR_TEST);
// Final mix op
context.activeTexture(GL_TEXTURE0);
context.bindTexture(GL_TEXTURE_2D, fboA.getColorbuffer());
context.activeTexture(GL_TEXTURE1);
context.bindTexture(GL_TEXTURE_2D, fboB.getColorbuffer());
context.bindFramebuffer(GL_FRAMEBUFFER, 0);
context.viewport(0, 0, context.getWidth(), context.getHeight());
sglr::drawQuad(context, mixShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
context.readPixels(dst, 0, 0, context.getWidth(), context.getHeight());
}
class BlendTest : public FboRenderCase
{
public:
BlendTest (Context& context, const FboConfig& config, bool npot = false);
virtual ~BlendTest (void) {}
void render (sglr::Context& context, Surface& dst);
static bool isConfigSupported (const FboConfig& config);
private:
int m_fboWidth;
int m_fboHeight;
};
class BlendNpotTest : public BlendTest
{
public:
BlendNpotTest (Context& context, const FboConfig& config)
: BlendTest(context, config, true)
{
}
};
BlendTest::BlendTest (Context& context, const FboConfig& config, bool npot)
: FboRenderCase (context, (string(npot ? "blend_npot_" : "blend_") + config.getName()).c_str(), "Blend to fbo", config)
, m_fboWidth (npot ? 111 : 128)
, m_fboHeight (npot ? 122 : 128)
{
}
bool BlendTest::isConfigSupported (const FboConfig& config)
{
// \note Disabled for stencil configurations since doesn't exercise stencil buffer
return config.stencilbufferType == GL_NONE;
}
void BlendTest::render (sglr::Context& context, Surface& dst)
{
SingleTex2DShader shader;
deUint32 shaderID = context.createProgram(&shader);
int width = m_fboWidth;
int height = m_fboHeight;
deUint32 metaballsTex = 1;
createMetaballsTex2D(context, metaballsTex, GL_RGBA, GL_UNSIGNED_BYTE, 64, 64);
Framebuffer fbo(context, getConfig(), width, height);
fbo.checkCompleteness();
shader.setUnit(context, shaderID, 0);
context.bindFramebuffer(GL_FRAMEBUFFER, fbo.getFramebuffer());
context.viewport(0, 0, width, height);
context.bindTexture(GL_TEXTURE_2D, metaballsTex);
context.clearColor(0.6f, 0.0f, 0.6f, 1.0f);
context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
context.enable(GL_BLEND);
context.blendEquation(GL_FUNC_ADD);
context.blendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
sglr::drawQuad(context, shaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
context.disable(GL_BLEND);
if (fbo.getConfig().colorbufferType == GL_TEXTURE_2D)
{
context.bindFramebuffer(GL_FRAMEBUFFER, 0);
context.bindTexture(GL_TEXTURE_2D, fbo.getColorbuffer());
context.viewport(0, 0, context.getWidth(), context.getHeight());
sglr::drawQuad(context, shaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
context.readPixels(dst, 0, 0, context.getWidth(), context.getHeight());
}
else
context.readPixels(dst, 0, 0, width, height);
}
class StencilClearsTest : public FboRenderCase
{
public:
StencilClearsTest (Context& context, const FboConfig& config);
virtual ~StencilClearsTest (void) {};
void render (sglr::Context& context, Surface& dst);
static bool isConfigSupported (const FboConfig& config);
};
StencilClearsTest::StencilClearsTest (Context& context, const FboConfig& config)
: FboRenderCase(context, config.getName().c_str(), "Stencil clears", config)
{
}
void StencilClearsTest::render (sglr::Context& context, Surface& dst)
{
SingleTex2DShader shader;
deUint32 shaderID = context.createProgram(&shader);
int width = 128;
int height = 128;
deUint32 quadsTex = 1;
deUint32 metaballsTex = 2;
createQuadsTex2D(context, quadsTex, GL_RGBA, GL_UNSIGNED_BYTE, width, height);
createMetaballsTex2D(context, metaballsTex, GL_RGBA, GL_UNSIGNED_BYTE, width, height);
Framebuffer fbo(context, getConfig(), width, height);
fbo.checkCompleteness();
// Bind framebuffer and clear
context.bindFramebuffer(GL_FRAMEBUFFER, fbo.getFramebuffer());
context.viewport(0, 0, width, height);
context.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
// Do stencil clears
context.enable(GL_SCISSOR_TEST);
context.scissor(10, 16, 32, 120);
context.clearStencil(1);
context.clear(GL_STENCIL_BUFFER_BIT);
context.scissor(16, 32, 100, 64);
context.clearStencil(2);
context.clear(GL_STENCIL_BUFFER_BIT);
context.disable(GL_SCISSOR_TEST);
// Draw 2 textures with stecil tests
context.activeTexture(GL_TEXTURE0);
context.bindTexture(GL_TEXTURE_2D, quadsTex);
context.activeTexture(GL_TEXTURE1);
context.bindTexture(GL_TEXTURE_2D, metaballsTex);
context.enable(GL_STENCIL_TEST);
context.stencilFunc(GL_EQUAL, 1, 0xffffffffu);
shader.setUnit(context, shaderID, 0);
sglr::drawQuad(context, shaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
context.stencilFunc(GL_EQUAL, 2, 0xffffffffu);
shader.setUnit(context, shaderID, 1);
sglr::drawQuad(context, shaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
context.disable(GL_STENCIL_TEST);
if (fbo.getConfig().colorbufferType == GL_TEXTURE_2D)
{
context.bindFramebuffer(GL_FRAMEBUFFER, 0);
context.activeTexture(GL_TEXTURE0);
context.bindTexture(GL_TEXTURE_2D, fbo.getColorbuffer());
context.viewport(0, 0, context.getWidth(), context.getHeight());
shader.setUnit(context, shaderID, 0);
sglr::drawQuad(context, shaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
context.readPixels(dst, 0, 0, context.getWidth(), context.getHeight());
}
else
{
// clear alpha channel for GL_RGB5_A1 format because test
// thresholds for the alpha channel do not account for dithering
if(getConfig().colorbufferFormat == GL_RGB5_A1)
{
context.colorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
context.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
context.clear(GL_COLOR_BUFFER_BIT);
context.colorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
}
context.readPixels(dst, 0, 0, width, height);
}
}
bool StencilClearsTest::isConfigSupported (const FboConfig& config)
{
return config.stencilbufferType != GL_NONE;
}
class StencilTest : public FboRenderCase
{
public:
StencilTest (Context& context, const FboConfig& config, bool npot = false);
virtual ~StencilTest (void) {};
void render (sglr::Context& context, Surface& dst);
static bool isConfigSupported (const FboConfig& config);
private:
int m_fboWidth;
int m_fboHeight;
};
class StencilNpotTest : public StencilTest
{
public:
StencilNpotTest (Context& context, const FboConfig& config)
: StencilTest(context, config, true)
{
}
};
StencilTest::StencilTest (Context& context, const FboConfig& config, bool npot)
: FboRenderCase (context, (string(npot ? "npot_" : "") + config.getName()).c_str(), "Stencil ops", config)
, m_fboWidth (npot ? 99 : 128)
, m_fboHeight (npot ? 110 : 128)
{
}
bool StencilTest::isConfigSupported (const FboConfig& config)
{
return config.stencilbufferType != GL_NONE;
}
void StencilTest::render (sglr::Context& ctx, Surface& dst)
{
FlatColorShader colorShader;
SingleTex2DShader texShader;
deUint32 colorShaderID = ctx.createProgram(&colorShader);
deUint32 texShaderID = ctx.createProgram(&texShader);
int width = m_fboWidth;
int height = m_fboHeight;
int texWidth = 64;
int texHeight = 64;
deUint32 quadsTex = 1;
deUint32 metaballsTex = 2;
bool depth = getConfig().depthbufferType != GL_NONE;
createQuadsTex2D(ctx, quadsTex, GL_RGB, GL_UNSIGNED_BYTE, texWidth, texHeight);
createMetaballsTex2D(ctx, metaballsTex, GL_RGB, GL_UNSIGNED_BYTE, texWidth, texHeight);
Framebuffer fbo(ctx, getConfig(), width, height);
fbo.checkCompleteness();
// Bind framebuffer and clear
ctx.bindFramebuffer(GL_FRAMEBUFFER, fbo.getFramebuffer());
ctx.viewport(0, 0, width, height);
ctx.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
ctx.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
// Render intersecting quads - increment stencil on depth pass
ctx.enable(GL_DEPTH_TEST);
ctx.enable(GL_STENCIL_TEST);
ctx.stencilFunc(GL_ALWAYS, 0, 0xffu);
ctx.stencilOp(GL_KEEP, GL_KEEP, GL_INCR);
colorShader.setColor(ctx, colorShaderID, Vec4(0.0f, 0.0f, 1.0f, 1.0f));
sglr::drawQuad(ctx, colorShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(+1.0f, +1.0f, 0.0f));
ctx.bindTexture(GL_TEXTURE_2D, quadsTex);
texShader.setUnit(ctx, texShaderID, 0);
sglr::drawQuad(ctx, texShaderID, Vec3(-1.0f, -1.0f, -1.0f), Vec3(+1.0f, +1.0f, +1.0f));
// Draw quad with stencil test (stencil == 1 or 2 depending on depth) - decrement on stencil failure
ctx.disable(GL_DEPTH_TEST);
ctx.stencilFunc(GL_EQUAL, depth ? 2 : 1, 0xffu);
ctx.stencilOp(GL_DECR, GL_KEEP, GL_KEEP);
colorShader.setColor(ctx, colorShaderID, Vec4(0.0f, 1.0f, 0.0f, 1.0f));
sglr::drawQuad(ctx, colorShaderID, Vec3(-0.5f, -0.5f, 0.0f), Vec3(+0.5f, +0.5f, 0.0f));
// Draw metaballs with stencil test where stencil > 1 or 2 depending on depth buffer
ctx.bindTexture(GL_TEXTURE_2D, metaballsTex);
ctx.stencilFunc(GL_GREATER, depth ? 1 : 2, 0xffu);
ctx.stencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
sglr::drawQuad(ctx, texShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(+1.0f, +1.0f, 0.0f));
ctx.disable(GL_STENCIL_TEST);
if (fbo.getConfig().colorbufferType == GL_TEXTURE_2D)
{
ctx.bindFramebuffer(GL_FRAMEBUFFER, 0);
ctx.activeTexture(GL_TEXTURE0);
ctx.bindTexture(GL_TEXTURE_2D, fbo.getColorbuffer());
ctx.viewport(0, 0, ctx.getWidth(), ctx.getHeight());
sglr::drawQuad(ctx, texShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
ctx.readPixels(dst, 0, 0, ctx.getWidth(), ctx.getHeight());
}
else
ctx.readPixels(dst, 0, 0, width, height);
}
class SharedColorbufferTest : public FboRenderCase
{
public:
SharedColorbufferTest (Context& context, const FboConfig& config);
virtual ~SharedColorbufferTest (void) {};
void render (sglr::Context& context, Surface& dst);
};
SharedColorbufferTest::SharedColorbufferTest (Context& context, const FboConfig& config)
: FboRenderCase(context, config.getName().c_str(), "Shared colorbuffer", config)
{
}
void SharedColorbufferTest::render (sglr::Context& context, Surface& dst)
{
SingleTex2DShader shader;
deUint32 shaderID = context.createProgram(&shader);
int width = 128;
int height = 128;
// bool depth = getConfig().depthbufferFormat != GL_NONE;
bool stencil = getConfig().stencilbufferFormat != GL_NONE;
// Textures
deUint32 quadsTex = 1;
deUint32 metaballsTex = 2;
createQuadsTex2D(context, quadsTex, GL_RGB, GL_UNSIGNED_BYTE, 64, 64);
createMetaballsTex2D(context, metaballsTex, GL_RGBA, GL_UNSIGNED_BYTE, 64, 64);
context.viewport(0, 0, width, height);
shader.setUnit(context, shaderID, 0);
// Fbo A
Framebuffer fboA(context, getConfig(), width, height);
fboA.checkCompleteness();
// Fbo B - don't create colorbuffer
FboConfig cfg = getConfig();
cfg.colorbufferType = GL_NONE;
cfg.colorbufferFormat = GL_NONE;
Framebuffer fboB(context, cfg, width, height);
// Attach color buffer from fbo A
context.bindFramebuffer(GL_FRAMEBUFFER, fboB.getFramebuffer());
switch (getConfig().colorbufferType)
{
case GL_TEXTURE_2D:
context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboA.getColorbuffer(), 0);
break;
case GL_RENDERBUFFER:
context.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, fboA.getColorbuffer());
break;
default:
DE_ASSERT(false);
}
// Clear depth and stencil in fbo B
context.clear(GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
// Render quads to fbo 1, with depth 0.0
context.bindFramebuffer(GL_FRAMEBUFFER, fboA.getFramebuffer());
context.bindTexture(GL_TEXTURE_2D, quadsTex);
context.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
if (stencil)
{
// Stencil to 1 in fbo A
context.clearStencil(1);
context.clear(GL_STENCIL_BUFFER_BIT);
}
context.enable(GL_DEPTH_TEST);
sglr::drawQuad(context, shaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
context.disable(GL_DEPTH_TEST);
// Blend metaballs to fbo 2
context.bindFramebuffer(GL_FRAMEBUFFER, fboB.getFramebuffer());
context.bindTexture(GL_TEXTURE_2D, metaballsTex);
context.enable(GL_BLEND);
context.blendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
sglr::drawQuad(context, shaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
// Render small quad that is only visible if depth buffer is not shared with fbo A - or there is no depth bits
context.bindTexture(GL_TEXTURE_2D, quadsTex);
context.enable(GL_DEPTH_TEST);
sglr::drawQuad(context, shaderID, Vec3(0.5f, 0.5f, 0.5f), Vec3(1.0f, 1.0f, 0.5f));
context.disable(GL_DEPTH_TEST);
if (stencil)
{
FlatColorShader flatShader;
deUint32 flatShaderID = context.createProgram(&flatShader);
flatShader.setColor(context, flatShaderID, Vec4(0.0f, 1.0f, 0.0f, 1.0f));
// Clear subset of stencil buffer to 1
context.enable(GL_SCISSOR_TEST);
context.scissor(10, 10, 12, 25);
context.clearStencil(1);
context.clear(GL_STENCIL_BUFFER_BIT);
context.disable(GL_SCISSOR_TEST);
// Render quad with stencil mask == 1
context.enable(GL_STENCIL_TEST);
context.stencilFunc(GL_EQUAL, 1, 0xffu);
sglr::drawQuad(context, flatShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
context.disable(GL_STENCIL_TEST);
}
// Get results
if (fboA.getConfig().colorbufferType == GL_TEXTURE_2D)
{
context.bindFramebuffer(GL_FRAMEBUFFER, 0);
context.bindTexture(GL_TEXTURE_2D, fboA.getColorbuffer());
context.viewport(0, 0, context.getWidth(), context.getHeight());
sglr::drawQuad(context, shaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
context.readPixels(dst, 0, 0, context.getWidth(), context.getHeight());
}
else
context.readPixels(dst, 0, 0, width, height);
}
class SharedColorbufferClearsTest : public FboRenderCase
{
public:
SharedColorbufferClearsTest (Context& context, const FboConfig& config);
virtual ~SharedColorbufferClearsTest (void) {}
static bool isConfigSupported (const FboConfig& config);
void render (sglr::Context& context, Surface& dst);
};
SharedColorbufferClearsTest::SharedColorbufferClearsTest (Context& context, const FboConfig& config)
: FboRenderCase(context, config.getName().c_str(), "Shared colorbuffer clears", config)
{
}
bool SharedColorbufferClearsTest::isConfigSupported (const FboConfig& config)
{
return config.colorbufferType != GL_NONE &&
config.depthbufferType == GL_NONE &&
config.stencilbufferType == GL_NONE;
}
void SharedColorbufferClearsTest::render (sglr::Context& context, Surface& dst)
{
int width = 128;
int height = 128;
deUint32 colorbuffer = 1;
checkColorFormatSupport(context, getConfig().colorbufferFormat);
// Single colorbuffer
if (getConfig().colorbufferType == GL_TEXTURE_2D)
{
context.bindTexture(GL_TEXTURE_2D, colorbuffer);
context.texImage2D(GL_TEXTURE_2D, 0, getConfig().colorbufferFormat, width, height);
context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
else
{
DE_ASSERT(getConfig().colorbufferType == GL_RENDERBUFFER);
context.bindRenderbuffer(GL_RENDERBUFFER, colorbuffer);
context.renderbufferStorage(GL_RENDERBUFFER, getConfig().colorbufferFormat, width, height);
}
// Multiple framebuffers sharing the colorbuffer
for (int fbo = 1; fbo <= 3; fbo++)
{
context.bindFramebuffer(GL_FRAMEBUFFER, fbo);
if (getConfig().colorbufferType == GL_TEXTURE_2D)
context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer, 0);
else
context.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuffer);
}
context.bindFramebuffer(GL_FRAMEBUFFER, 1);
// Check completeness
{
GLenum status = context.checkFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
throw FboIncompleteException(getConfig(), status, __FILE__, __LINE__);
}
// Render to them
context.viewport(0, 0, width, height);
context.clearColor(0.0f, 0.0f, 1.0f, 1.0f);
context.clear(GL_COLOR_BUFFER_BIT);
context.enable(GL_SCISSOR_TEST);
context.bindFramebuffer(GL_FRAMEBUFFER, 2);
context.clearColor(0.6f, 0.0f, 0.0f, 1.0f);
context.scissor(10, 10, 64, 64);
context.clear(GL_COLOR_BUFFER_BIT);
context.clearColor(0.0f, 0.6f, 0.0f, 1.0f);
context.scissor(60, 60, 40, 20);
context.clear(GL_COLOR_BUFFER_BIT);
context.bindFramebuffer(GL_FRAMEBUFFER, 3);
context.clearColor(0.0f, 0.0f, 0.6f, 1.0f);
context.scissor(20, 20, 100, 10);
context.clear(GL_COLOR_BUFFER_BIT);
context.bindFramebuffer(GL_FRAMEBUFFER, 1);
context.clearColor(0.6f, 0.0f, 0.6f, 1.0f);
context.scissor(20, 20, 5, 100);
context.clear(GL_COLOR_BUFFER_BIT);
context.disable(GL_SCISSOR_TEST);
if (getConfig().colorbufferType == GL_TEXTURE_2D)
{
SingleTex2DShader shader;
deUint32 shaderID = context.createProgram(&shader);
shader.setUnit(context, shaderID, 0);
context.bindFramebuffer(GL_FRAMEBUFFER, 0);
context.viewport(0, 0, context.getWidth(), context.getHeight());
sglr::drawQuad(context, shaderID, Vec3(-0.9f, -0.9f, 0.0f), Vec3(0.9f, 0.9f, 0.0f));
context.readPixels(dst, 0, 0, context.getWidth(), context.getHeight());
}
else
context.readPixels(dst, 0, 0, width, height);
}
class SharedDepthbufferTest : public FboRenderCase
{
public:
SharedDepthbufferTest (Context& context, const FboConfig& config);
virtual ~SharedDepthbufferTest (void) {};
static bool isConfigSupported (const FboConfig& config);
void render (sglr::Context& context, Surface& dst);
};
SharedDepthbufferTest::SharedDepthbufferTest (Context& context, const FboConfig& config)
: FboRenderCase(context, config.getName().c_str(), "Shared depthbuffer", config)
{
}
bool SharedDepthbufferTest::isConfigSupported (const FboConfig& config)
{
return config.depthbufferType == GL_RENDERBUFFER;
}
void SharedDepthbufferTest::render (sglr::Context& context, Surface& dst)
{
SingleTex2DShader texShader;
FlatColorShader colorShader;
deUint32 texShaderID = context.createProgram(&texShader);
deUint32 colorShaderID = context.createProgram(&colorShader);
int width = 128;
int height = 128;
bool stencil = getConfig().stencilbufferType != GL_NONE;
// Setup shaders
texShader.setUnit(context, texShaderID, 0);
colorShader.setColor(context, colorShaderID, Vec4(0.0f, 1.0f, 0.0f, 1.0f));
// Textures
deUint32 metaballsTex = 5;
deUint32 quadsTex = 6;
createMetaballsTex2D(context, metaballsTex, GL_RGB, GL_UNSIGNED_BYTE, 64, 64);
createQuadsTex2D(context, quadsTex, GL_RGB, GL_UNSIGNED_BYTE, 64, 64);
context.viewport(0, 0, width, height);
// Fbo A
Framebuffer fboA(context, getConfig(), width, height);
fboA.checkCompleteness();
// Fbo B
FboConfig cfg = getConfig();
cfg.depthbufferType = GL_NONE;
cfg.depthbufferFormat = GL_NONE;
Framebuffer fboB(context, cfg, width, height);
// Bind depth buffer from fbo A to fbo B
DE_ASSERT(fboA.getConfig().depthbufferType == GL_RENDERBUFFER);
context.bindFramebuffer(GL_FRAMEBUFFER, fboB.getFramebuffer());
context.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fboA.getDepthbuffer());
// Clear fbo B color to red and stencil to 1
context.clearColor(1.0f, 0.0f, 0.0f, 1.0f);
context.clearStencil(1);
context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
// Enable depth test.
context.enable(GL_DEPTH_TEST);
// Render quad to fbo A
context.bindFramebuffer(GL_FRAMEBUFFER, fboA.getFramebuffer());
context.bindTexture(GL_TEXTURE_2D, quadsTex);
context.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
sglr::drawQuad(context, texShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
// Render metaballs to fbo B
context.bindFramebuffer(GL_FRAMEBUFFER, fboB.getFramebuffer());
context.bindTexture(GL_TEXTURE_2D, metaballsTex);
sglr::drawQuad(context, texShaderID, Vec3(-1.0f, -1.0f, -1.0f), Vec3(1.0f, 1.0f, 1.0f));
context.disable(GL_DEPTH_TEST);
if (stencil)
{
// Clear subset of stencil buffer to 0
context.enable(GL_SCISSOR_TEST);
context.scissor(10, 10, 12, 25);
context.clearStencil(0);
context.clear(GL_STENCIL_BUFFER_BIT);
context.disable(GL_SCISSOR_TEST);
// Render quad with stencil mask == 0
context.enable(GL_STENCIL_TEST);
context.stencilFunc(GL_EQUAL, 0, 0xffu);
sglr::drawQuad(context, colorShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
context.disable(GL_STENCIL_TEST);
}
if (getConfig().colorbufferType == GL_TEXTURE_2D)
{
// Render both to screen
context.bindFramebuffer(GL_FRAMEBUFFER, 0);
context.viewport(0, 0, context.getWidth(), context.getHeight());
context.bindTexture(GL_TEXTURE_2D, fboA.getColorbuffer());
sglr::drawQuad(context, texShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(0.0f, 1.0f, 0.0f));
context.bindTexture(GL_TEXTURE_2D, fboB.getColorbuffer());
sglr::drawQuad(context, texShaderID, Vec3(0.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
context.readPixels(dst, 0, 0, context.getWidth(), context.getHeight());
}
else
{
// Read results from fbo B
context.readPixels(dst, 0, 0, width, height);
}
}
class TexSubImageAfterRenderTest : public FboRenderCase
{
public:
TexSubImageAfterRenderTest (Context& context, const FboConfig& config);
virtual ~TexSubImageAfterRenderTest (void) {}
static bool isConfigSupported (const FboConfig& config);
void render (sglr::Context& context, Surface& dst);
};
TexSubImageAfterRenderTest::TexSubImageAfterRenderTest (Context& context, const FboConfig& config)
: FboRenderCase(context, (string("after_render_") + config.getName()).c_str(), "TexSubImage after rendering to texture", config)
{
}
bool TexSubImageAfterRenderTest::isConfigSupported (const FboConfig& config)
{
return config.colorbufferType == GL_TEXTURE_2D &&
(config.colorbufferFormat == GL_RGB || config.colorbufferFormat == GL_RGBA) &&
config.depthbufferType == GL_NONE &&
config.stencilbufferType == GL_NONE;
}
void TexSubImageAfterRenderTest::render (sglr::Context& context, Surface& dst)
{
SingleTex2DShader shader;
deUint32 shaderID = context.createProgram(&shader);
bool isRGBA = getConfig().colorbufferFormat == GL_RGBA;
tcu::TextureLevel fourQuads(tcu::TextureFormat(tcu::TextureFormat::RGB, tcu::TextureFormat::UNORM_INT8), 64, 64);
tcu::fillWithRGBAQuads(fourQuads.getAccess());
tcu::TextureLevel metaballs(tcu::TextureFormat(isRGBA ? tcu::TextureFormat::RGBA : tcu::TextureFormat::RGB, tcu::TextureFormat::UNORM_INT8), 64, 64);
tcu::fillWithMetaballs(metaballs.getAccess(), 5, 3);
shader.setUnit(context, shaderID, 0);
deUint32 fourQuadsTex = 1;
context.bindTexture(GL_TEXTURE_2D, fourQuadsTex);
context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
context.texImage2D(GL_TEXTURE_2D, 0, GL_RGB, 64, 64, 0, GL_RGB, GL_UNSIGNED_BYTE, fourQuads.getAccess().getDataPtr());
context.bindFramebuffer(GL_FRAMEBUFFER, 1);
deUint32 fboTex = 2;
context.bindTexture(GL_TEXTURE_2D, fboTex);
context.texImage2D(GL_TEXTURE_2D, 0, isRGBA ? GL_RGBA : GL_RGB, 128, 128);
context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTex, 0);
// Render to fbo
context.viewport(0, 0, 128, 128);
context.bindTexture(GL_TEXTURE_2D, fourQuadsTex);
sglr::drawQuad(context, shaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
// Update texture using TexSubImage2D
context.bindTexture(GL_TEXTURE_2D, fboTex);
context.texSubImage2D(GL_TEXTURE_2D, 0, 32, 32, 64, 64, isRGBA ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, metaballs.getAccess().getDataPtr());
// Draw to screen
context.bindFramebuffer(GL_FRAMEBUFFER, 0);
context.viewport(0, 0, context.getWidth(), context.getHeight());
sglr::drawQuad(context, shaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
context.readPixels(dst, 0, 0, context.getWidth(), context.getHeight());
}
class TexSubImageBetweenRenderTest : public FboRenderCase
{
public:
TexSubImageBetweenRenderTest (Context& context, const FboConfig& config);
virtual ~TexSubImageBetweenRenderTest (void) {}
static bool isConfigSupported (const FboConfig& config);
void render (sglr::Context& context, Surface& dst);
};
TexSubImageBetweenRenderTest::TexSubImageBetweenRenderTest (Context& context, const FboConfig& config)
: FboRenderCase(context, (string("between_render_") + config.getName()).c_str(), "TexSubImage between rendering calls", config)
{
}
bool TexSubImageBetweenRenderTest::isConfigSupported (const FboConfig& config)
{
return config.colorbufferType == GL_TEXTURE_2D &&
(config.colorbufferFormat == GL_RGB || config.colorbufferFormat == GL_RGBA) &&
config.depthbufferType == GL_NONE &&
config.stencilbufferType == GL_NONE;
}
void TexSubImageBetweenRenderTest::render (sglr::Context& context, Surface& dst)
{
SingleTex2DShader shader;
deUint32 shaderID = context.createProgram(&shader);
bool isRGBA = getConfig().colorbufferFormat == GL_RGBA;
tcu::TextureLevel fourQuads(tcu::TextureFormat(tcu::TextureFormat::RGB, tcu::TextureFormat::UNORM_INT8), 64, 64);
tcu::fillWithRGBAQuads(fourQuads.getAccess());
tcu::TextureLevel metaballs(tcu::TextureFormat(isRGBA ? tcu::TextureFormat::RGBA : tcu::TextureFormat::RGB, tcu::TextureFormat::UNORM_INT8), 64, 64);
tcu::fillWithMetaballs(metaballs.getAccess(), 5, 3);
tcu::TextureLevel metaballs2(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), 64, 64);
tcu::fillWithMetaballs(metaballs2.getAccess(), 5, 4);
deUint32 metaballsTex = 3;
context.bindTexture(GL_TEXTURE_2D, metaballsTex);
context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
context.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, metaballs2.getAccess().getDataPtr());
deUint32 fourQuadsTex = 1;
context.bindTexture(GL_TEXTURE_2D, fourQuadsTex);
context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
context.texImage2D(GL_TEXTURE_2D, 0, GL_RGB, 64, 64, 0, GL_RGB, GL_UNSIGNED_BYTE, fourQuads.getAccess().getDataPtr());
context.bindFramebuffer(GL_FRAMEBUFFER, 1);
deUint32 fboTex = 2;
context.bindTexture(GL_TEXTURE_2D, fboTex);
context.texImage2D(GL_TEXTURE_2D, 0, isRGBA ? GL_RGBA : GL_RGB, 128, 128);
context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTex, 0);
shader.setUnit(context, shaderID, 0);
// Render to fbo
context.viewport(0, 0, 128, 128);
context.bindTexture(GL_TEXTURE_2D, fourQuadsTex);
sglr::drawQuad(context, shaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
// Update texture using TexSubImage2D
context.bindTexture(GL_TEXTURE_2D, fboTex);
context.texSubImage2D(GL_TEXTURE_2D, 0, 32, 32, 64, 64, isRGBA ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, metaballs.getAccess().getDataPtr());
// Render again to fbo
context.bindTexture(GL_TEXTURE_2D, metaballsTex);
context.enable(GL_BLEND);
context.blendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
sglr::drawQuad(context, shaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
context.disable(GL_BLEND);
// Draw to screen
context.bindFramebuffer(GL_FRAMEBUFFER, 0);
context.viewport(0, 0, context.getWidth(), context.getHeight());
context.bindTexture(GL_TEXTURE_2D, fboTex);
sglr::drawQuad(context, shaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
context.readPixels(dst, 0, 0, context.getWidth(), context.getHeight());
}
class ResizeTest : public FboRenderCase
{
public:
ResizeTest (Context& context, const FboConfig& config);
virtual ~ResizeTest (void) {}
void render (sglr::Context& context, Surface& dst);
};
ResizeTest::ResizeTest (Context& context, const FboConfig& config)
: FboRenderCase(context, config.getName().c_str(), "Resize framebuffer", config)
{
}
void ResizeTest::render (sglr::Context& context, Surface& dst)
{
SingleTex2DShader texShader;
FlatColorShader colorShader;
deUint32 texShaderID = context.createProgram(&texShader);
deUint32 colorShaderID = context.createProgram(&colorShader);
deUint32 quadsTex = 1;
deUint32 metaballsTex = 2;
bool depth = getConfig().depthbufferType != GL_NONE;
bool stencil = getConfig().stencilbufferType != GL_NONE;
createQuadsTex2D(context, quadsTex, GL_RGB, GL_UNSIGNED_BYTE, 64, 64);
createMetaballsTex2D(context, metaballsTex, GL_RGB, GL_UNSIGNED_BYTE, 32, 32);
Framebuffer fbo(context, getConfig(), 128, 128);
fbo.checkCompleteness();
// Setup shaders
texShader.setUnit(context, texShaderID, 0);
colorShader.setColor(context, colorShaderID, Vec4(0.0f, 1.0f, 0.0f, 1.0f));
// Render quads
context.bindFramebuffer(GL_FRAMEBUFFER, fbo.getFramebuffer());
context.viewport(0, 0, 128, 128);
context.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
context.bindTexture(GL_TEXTURE_2D, quadsTex);
sglr::drawQuad(context, texShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
if (fbo.getConfig().colorbufferType == GL_TEXTURE_2D)
{
// Render fbo to screen
context.bindFramebuffer(GL_FRAMEBUFFER, 0);
context.viewport(0, 0, context.getWidth(), context.getHeight());
context.bindTexture(GL_TEXTURE_2D, fbo.getColorbuffer());
sglr::drawQuad(context, texShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
// Restore binding
context.bindFramebuffer(GL_FRAMEBUFFER, fbo.getFramebuffer());
}
int newWidth = 64;
int newHeight = 32;
// Resize buffers
switch (fbo.getConfig().colorbufferType)
{
case GL_TEXTURE_2D:
context.bindTexture(GL_TEXTURE_2D, fbo.getColorbuffer());
context.texImage2D(GL_TEXTURE_2D, 0, fbo.getConfig().colorbufferFormat, newWidth, newHeight);
break;
case GL_RENDERBUFFER:
context.bindRenderbuffer(GL_RENDERBUFFER, fbo.getColorbuffer());
context.renderbufferStorage(GL_RENDERBUFFER, fbo.getConfig().colorbufferFormat, newWidth, newHeight);
break;
default:
DE_ASSERT(false);
}
if (depth)
{
DE_ASSERT(fbo.getConfig().depthbufferType == GL_RENDERBUFFER);
context.bindRenderbuffer(GL_RENDERBUFFER, fbo.getDepthbuffer());
context.renderbufferStorage(GL_RENDERBUFFER, fbo.getConfig().depthbufferFormat, newWidth, newHeight);
}
if (stencil)
{
DE_ASSERT(fbo.getConfig().stencilbufferType == GL_RENDERBUFFER);
context.bindRenderbuffer(GL_RENDERBUFFER, fbo.getStencilbuffer());
context.renderbufferStorage(GL_RENDERBUFFER, fbo.getConfig().stencilbufferFormat, newWidth, newHeight);
}
// Render to resized fbo
context.viewport(0, 0, newWidth, newHeight);
context.clearColor(1.0f, 0.0f, 0.0f, 1.0f);
context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
context.enable(GL_DEPTH_TEST);
context.bindTexture(GL_TEXTURE_2D, metaballsTex);
sglr::drawQuad(context, texShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(+1.0f, +1.0f, 0.0f));
context.bindTexture(GL_TEXTURE_2D, quadsTex);
sglr::drawQuad(context, texShaderID, Vec3(0.0f, 0.0f, -1.0f), Vec3(+1.0f, +1.0f, 1.0f));
context.disable(GL_DEPTH_TEST);
if (stencil)
{
context.enable(GL_SCISSOR_TEST);
context.scissor(10, 10, 5, 15);
context.clearStencil(1);
context.clear(GL_STENCIL_BUFFER_BIT);
context.disable(GL_SCISSOR_TEST);
context.enable(GL_STENCIL_TEST);
context.stencilFunc(GL_EQUAL, 1, 0xffu);
sglr::drawQuad(context, colorShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(+1.0f, +1.0f, 0.0f));
context.disable(GL_STENCIL_TEST);
}
if (getConfig().colorbufferType == GL_TEXTURE_2D)
{
context.bindFramebuffer(GL_FRAMEBUFFER, 0);
context.viewport(0, 0, context.getWidth(), context.getHeight());
context.bindTexture(GL_TEXTURE_2D, fbo.getColorbuffer());
sglr::drawQuad(context, texShaderID, Vec3(-0.5f, -0.5f, 0.0f), Vec3(0.5f, 0.5f, 0.0f));
context.readPixels(dst, 0, 0, context.getWidth(), context.getHeight());
}
else
context.readPixels(dst, 0, 0, newWidth, newHeight);
}
template <GLenum Buffers>
class RecreateBuffersTest : public FboRenderCase
{
public:
RecreateBuffersTest (Context& context, const FboConfig& config, bool rebind);
virtual ~RecreateBuffersTest (void) {}
static bool isConfigSupported (const FboConfig& config);
void render (sglr::Context& context, Surface& dst);
private:
bool m_rebind;
};
template <GLenum Buffers>
class RecreateBuffersNoRebindTest : public RecreateBuffersTest<Buffers>
{
public:
RecreateBuffersNoRebindTest (Context& context, const FboConfig& config)
: RecreateBuffersTest<Buffers>(context, config, false)
{
}
};
template <GLenum Buffers>
class RecreateBuffersRebindTest : public RecreateBuffersTest<Buffers>
{
public:
RecreateBuffersRebindTest (Context& context, const FboConfig& config)
: RecreateBuffersTest<Buffers>(context, config, true)
{
}
};
template <GLenum Buffers>
RecreateBuffersTest<Buffers>::RecreateBuffersTest (Context& context, const FboConfig& config, bool rebind)
: FboRenderCase (context, (string(rebind ? "rebind_" : "no_rebind_") + config.getName()).c_str(), "Recreate buffers", config)
, m_rebind (rebind)
{
}
template <GLenum Buffers>
bool RecreateBuffersTest<Buffers>::isConfigSupported (const FboConfig& config)
{
if ((Buffers & GL_COLOR_BUFFER_BIT) && config.colorbufferType == GL_NONE)
return false;
if ((Buffers & GL_DEPTH_BUFFER_BIT) && config.depthbufferType == GL_NONE)
return false;
if ((Buffers & GL_STENCIL_BUFFER_BIT) && config.stencilbufferType == GL_NONE)
return false;
return true;
}
template <GLenum Buffers>
void RecreateBuffersTest<Buffers>::render (sglr::Context& ctx, Surface& dst)
{
SingleTex2DShader texShader;
deUint32 texShaderID = ctx.createProgram(&texShader);
int width = 128;
int height = 128;
deUint32 metaballsTex = 1;
deUint32 quadsTex = 2;
bool stencil = getConfig().stencilbufferType != GL_NONE;
createQuadsTex2D(ctx, quadsTex, GL_RGB, GL_UNSIGNED_BYTE, 64, 64);
createMetaballsTex2D(ctx, metaballsTex, GL_RGB, GL_UNSIGNED_BYTE, 64, 64);
Framebuffer fbo(ctx, getConfig(), width, height);
fbo.checkCompleteness();
// Setup shader
texShader.setUnit(ctx, texShaderID, 0);
// Draw scene
ctx.bindFramebuffer(GL_FRAMEBUFFER, fbo.getFramebuffer());
ctx.viewport(0, 0, width, height);
ctx.clearColor(1.0f, 0.0f, 0.0f, 1.0f);
ctx.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
ctx.enable(GL_DEPTH_TEST);
ctx.bindTexture(GL_TEXTURE_2D, quadsTex);
sglr::drawQuad(ctx, texShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
if (stencil)
{
ctx.enable(GL_SCISSOR_TEST);
ctx.scissor(width/4, height/4, width/2, height/2);
ctx.clearStencil(1);
ctx.clear(GL_STENCIL_BUFFER_BIT);
ctx.disable(GL_SCISSOR_TEST);
}
// Recreate buffers
if (!m_rebind)
ctx.bindFramebuffer(GL_FRAMEBUFFER, 0);
if (Buffers & GL_COLOR_BUFFER_BIT)
{
deUint32 colorbuf = fbo.getColorbuffer();
switch (fbo.getConfig().colorbufferType)
{
case GL_TEXTURE_2D:
ctx.deleteTextures(1, &colorbuf);
ctx.bindTexture(GL_TEXTURE_2D, colorbuf);
ctx.texImage2D(GL_TEXTURE_2D, 0, fbo.getConfig().colorbufferFormat, width, height);
ctx.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
if (m_rebind)
ctx.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuf, 0);
break;
case GL_RENDERBUFFER:
ctx.deleteRenderbuffers(1, &colorbuf);
ctx.bindRenderbuffer(GL_RENDERBUFFER, colorbuf);
ctx.renderbufferStorage(GL_RENDERBUFFER, fbo.getConfig().colorbufferFormat, width, height);
if (m_rebind)
ctx.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuf);
break;
default:
DE_ASSERT(false);
}
}
if (Buffers & GL_DEPTH_BUFFER_BIT)
{
deUint32 depthbuf = fbo.getDepthbuffer();
DE_ASSERT(fbo.getConfig().depthbufferType == GL_RENDERBUFFER);
ctx.deleteRenderbuffers(1, &depthbuf);
ctx.bindRenderbuffer(GL_RENDERBUFFER, depthbuf);
ctx.renderbufferStorage(GL_RENDERBUFFER, fbo.getConfig().depthbufferFormat, width, height);
if (m_rebind)
ctx.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthbuf);
}
if (Buffers & GL_STENCIL_BUFFER_BIT)
{
deUint32 stencilbuf = fbo.getStencilbuffer();
DE_ASSERT(fbo.getConfig().stencilbufferType == GL_RENDERBUFFER);
ctx.deleteRenderbuffers(1, &stencilbuf);
ctx.bindRenderbuffer(GL_RENDERBUFFER, stencilbuf);
ctx.renderbufferStorage(GL_RENDERBUFFER, fbo.getConfig().stencilbufferFormat, width, height);
if (m_rebind)
ctx.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, stencilbuf);
}
if (!m_rebind)
ctx.bindFramebuffer(GL_FRAMEBUFFER, fbo.getFramebuffer());
ctx.clearColor(0.0f, 0.0f, 1.0f, 0.0f);
ctx.clearStencil(0);
ctx.clear(Buffers); // \note Clear only buffers that were re-created
if (stencil)
{
// \note Stencil test enabled only if we have stencil buffer
ctx.enable(GL_STENCIL_TEST);
ctx.stencilFunc(GL_EQUAL, 0, 0xffu);
}
ctx.bindTexture(GL_TEXTURE_2D, metaballsTex);
sglr::drawQuad(ctx, texShaderID, Vec3(-1.0f, -1.0f, 1.0f), Vec3(1.0f, 1.0f, -1.0f));
if (stencil)
ctx.disable(GL_STENCIL_TEST);
ctx.disable(GL_DEPTH_TEST);
// Read from fbo
ctx.readPixels(dst, 0, 0, width, height);
}
class RepeatedClearCase : public FboRenderCase
{
private:
static FboConfig makeConfig (deUint32 format)
{
FboConfig cfg;
cfg.colorbufferType = GL_TEXTURE_2D;
cfg.colorbufferFormat = format;
cfg.depthbufferType = GL_NONE;
cfg.stencilbufferType = GL_NONE;
return cfg;
}
public:
RepeatedClearCase (Context& context, deUint32 format)
: FboRenderCase(context, makeConfig(format).getName().c_str(), "Repeated clears", makeConfig(format))
{
}
protected:
void render (sglr::Context& ctx, Surface& dst)
{
const int numRowsCols = 4;
const int cellSize = 16;
const int fboSizes[] = { cellSize, cellSize*numRowsCols };
SingleTex2DShader fboBlitShader;
const deUint32 fboBlitShaderID = ctx.createProgram(&fboBlitShader);
de::Random rnd (18169662);
deUint32 fbos[] = { 0, 0 };
deUint32 textures[] = { 0, 0 };
ctx.genFramebuffers(2, &fbos[0]);
ctx.genTextures(2, &textures[0]);
for (int fboNdx = 0; fboNdx < DE_LENGTH_OF_ARRAY(fbos); fboNdx++)
{
ctx.bindTexture(GL_TEXTURE_2D, textures[fboNdx]);
ctx.texImage2D(GL_TEXTURE_2D, 0, getConfig().colorbufferFormat, fboSizes[fboNdx], fboSizes[fboNdx], 0,
getConfig().colorbufferFormat, GL_UNSIGNED_BYTE, DE_NULL);
ctx.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
ctx.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
ctx.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
ctx.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
ctx.bindFramebuffer(GL_FRAMEBUFFER, fbos[fboNdx]);
ctx.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[fboNdx], 0);
{
const GLenum status = ctx.checkFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
throw FboIncompleteException(getConfig(), status, __FILE__, __LINE__);
}
}
// larger fbo bound -- clear to transparent black
ctx.clearColor(0.0f, 0.0f, 0.0f, 0.0f);
ctx.clear(GL_COLOR_BUFFER_BIT);
fboBlitShader.setUnit(ctx, fboBlitShaderID, 0);
ctx.bindTexture(GL_TEXTURE_2D, textures[0]);
for (int cellY = 0; cellY < numRowsCols; cellY++)
for (int cellX = 0; cellX < numRowsCols; cellX++)
{
const float r = rnd.getFloat();
const float g = rnd.getFloat();
const float b = rnd.getFloat();
const float a = rnd.getFloat();
ctx.bindFramebuffer(GL_FRAMEBUFFER, fbos[0]);
ctx.clearColor(r, g, b, a);
ctx.clear(GL_COLOR_BUFFER_BIT);
ctx.bindFramebuffer(GL_FRAMEBUFFER, fbos[1]);
ctx.viewport(cellX*cellSize, cellY*cellSize, cellSize, cellSize);
sglr::drawQuad(ctx, fboBlitShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
}
ctx.readPixels(dst, 0, 0, fboSizes[1], fboSizes[1]);
}
};
} // FboCases
FboRenderTestGroup::FboRenderTestGroup (Context& context)
: TestCaseGroup(context, "render", "Rendering Tests")
{
}
FboRenderTestGroup::~FboRenderTestGroup (void)
{
}
namespace
{
struct TypeFormatPair
{
GLenum type;
GLenum format;
};
template <typename CaseType>
void addChildVariants (deqp::gles2::TestCaseGroup* group)
{
TypeFormatPair colorbufferConfigs[] =
{
// { GL_TEXTURE_2D, GL_ALPHA },
// { GL_TEXTURE_2D, GL_LUMINANCE },
// { GL_TEXTURE_2D, GL_LUMINANCE_ALPHA },
{ GL_TEXTURE_2D, GL_RGB },
{ GL_TEXTURE_2D, GL_RGBA },
{ GL_RENDERBUFFER, GL_RGB565 },
{ GL_RENDERBUFFER, GL_RGB5_A1 },
{ GL_RENDERBUFFER, GL_RGBA4 },
// { GL_RENDERBUFFER, GL_RGBA16F },
// { GL_RENDERBUFFER, GL_RGB16F }
};
TypeFormatPair depthbufferConfigs[] =
{
{ GL_NONE, GL_NONE },
{ GL_RENDERBUFFER, GL_DEPTH_COMPONENT16 }
};
TypeFormatPair stencilbufferConfigs[] =
{
{ GL_NONE, GL_NONE },
{ GL_RENDERBUFFER, GL_STENCIL_INDEX8 }
};
for (int colorbufferNdx = 0; colorbufferNdx < DE_LENGTH_OF_ARRAY(colorbufferConfigs); colorbufferNdx++)
for (int depthbufferNdx = 0; depthbufferNdx < DE_LENGTH_OF_ARRAY(depthbufferConfigs); depthbufferNdx++)
for (int stencilbufferNdx = 0; stencilbufferNdx < DE_LENGTH_OF_ARRAY(stencilbufferConfigs); stencilbufferNdx++)
{
FboConfig config;
config.colorbufferType = colorbufferConfigs[colorbufferNdx].type;
config.colorbufferFormat = colorbufferConfigs[colorbufferNdx].format;
config.depthbufferType = depthbufferConfigs[depthbufferNdx].type;
config.depthbufferFormat = depthbufferConfigs[depthbufferNdx].format;
config.stencilbufferType = stencilbufferConfigs[stencilbufferNdx].type;
config.stencilbufferFormat = stencilbufferConfigs[stencilbufferNdx].format;
if (CaseType::isConfigSupported(config))
group->addChild(new CaseType(group->getContext(), config));
}
}
template <typename CaseType>
void createChildGroup (deqp::gles2::TestCaseGroup* parent, const char* name, const char* description)
{
deqp::gles2::TestCaseGroup* tmpGroup = new deqp::gles2::TestCaseGroup(parent->getContext(), name, description);
parent->addChild(tmpGroup);
addChildVariants<CaseType>(tmpGroup);
}
template <GLbitfield Buffers>
void createRecreateBuffersGroup (deqp::gles2::TestCaseGroup* parent, const char* name, const char* description)
{
deqp::gles2::TestCaseGroup* tmpGroup = new deqp::gles2::TestCaseGroup(parent->getContext(), name, description);
parent->addChild(tmpGroup);
addChildVariants<FboCases::RecreateBuffersRebindTest<Buffers> > (tmpGroup);
addChildVariants<FboCases::RecreateBuffersNoRebindTest<Buffers> > (tmpGroup);
}
} // anonymous
void FboRenderTestGroup::init (void)
{
createChildGroup<FboCases::ColorClearsTest> (this, "color_clear", "Color buffer clears");
createChildGroup<FboCases::StencilClearsTest> (this, "stencil_clear", "Stencil buffer clears");
deqp::gles2::TestCaseGroup* colorGroup = new deqp::gles2::TestCaseGroup(m_context, "color", "Color buffer tests");
addChild(colorGroup);
addChildVariants<FboCases::MixTest> (colorGroup);
addChildVariants<FboCases::MixNpotTest> (colorGroup);
addChildVariants<FboCases::BlendTest> (colorGroup);
addChildVariants<FboCases::BlendNpotTest> (colorGroup);
deqp::gles2::TestCaseGroup* depthGroup = new deqp::gles2::TestCaseGroup(m_context, "depth", "Depth bufer tests");
addChild(depthGroup);
addChildVariants<FboCases::IntersectingQuadsTest> (depthGroup);
addChildVariants<FboCases::IntersectingQuadsNpotTest> (depthGroup);
deqp::gles2::TestCaseGroup* stencilGroup = new deqp::gles2::TestCaseGroup(m_context, "stencil", "Stencil buffer tests");
addChild(stencilGroup);
addChildVariants<FboCases::StencilTest> (stencilGroup);
addChildVariants<FboCases::StencilNpotTest> (stencilGroup);
createChildGroup<FboCases::SharedColorbufferClearsTest> (this, "shared_colorbuffer_clear", "Shared colorbuffer clears");
createChildGroup<FboCases::SharedColorbufferTest> (this, "shared_colorbuffer", "Shared colorbuffer tests");
createChildGroup<FboCases::SharedDepthbufferTest> (this, "shared_depthbuffer", "Shared depthbuffer tests");
createChildGroup<FboCases::ResizeTest> (this, "resize", "FBO resize tests");
createRecreateBuffersGroup<GL_COLOR_BUFFER_BIT> (this, "recreate_colorbuffer", "Recreate colorbuffer tests");
createRecreateBuffersGroup<GL_DEPTH_BUFFER_BIT> (this, "recreate_depthbuffer", "Recreate depthbuffer tests");
createRecreateBuffersGroup<GL_STENCIL_BUFFER_BIT> (this, "recreate_stencilbuffer", "Recreate stencilbuffer tests");
deqp::gles2::TestCaseGroup* texSubImageGroup = new deqp::gles2::TestCaseGroup(m_context, "texsubimage", "TexSubImage interop with FBO colorbuffer texture");
addChild(texSubImageGroup);
addChildVariants<FboCases::TexSubImageAfterRenderTest> (texSubImageGroup);
addChildVariants<FboCases::TexSubImageBetweenRenderTest> (texSubImageGroup);
{
tcu::TestCaseGroup* const repeatedClearGroup = new tcu::TestCaseGroup(m_testCtx, "repeated_clear", "Repeated FBO clears");
addChild(repeatedClearGroup);
repeatedClearGroup->addChild(new FboCases::RepeatedClearCase(m_context, GL_RGB));
repeatedClearGroup->addChild(new FboCases::RepeatedClearCase(m_context, GL_RGBA));
}
}
} // Functional
} // gles2
} // deqp