/*------------------------------------------------------------------------- * 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