/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 3.0 Module * ------------------------------------------------- * * Copyright 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Framebuffer Object Tests. *//*--------------------------------------------------------------------*/ #include "es3fFboRenderTest.hpp" #include "sglrContextUtil.hpp" #include "sglrGLContext.hpp" #include "sglrReferenceContext.hpp" #include "es3fFboTestUtil.hpp" #include "tcuSurface.hpp" #include "tcuImageCompare.hpp" #include "tcuTextureUtil.hpp" #include "tcuVectorUtil.hpp" #include "tcuRenderTarget.hpp" #include "gluPixelTransfer.hpp" #include "gluTextureUtil.hpp" #include "gluStrUtil.hpp" #include "deRandom.h" #include "deString.h" #include "glwDefs.hpp" #include "glwEnums.hpp" #include <sstream> using std::vector; using std::string; using tcu::TestLog; using tcu::Vec2; using tcu::Vec3; using tcu::Vec4; using tcu::IVec2; using tcu::IVec3; using tcu::IVec4; using tcu::RGBA; using tcu::Surface; namespace deqp { namespace gles3 { namespace Functional { using glw::GLenum; using namespace FboTestUtil; class FboConfig { public: FboConfig (deUint32 buffers_, deUint32 colorType_, deUint32 colorFormat_, deUint32 depthStencilType_, deUint32 depthStencilFormat_, int width_ = 0, int height_ = 0, int samples_ = 0) : buffers (buffers_) , colorType (colorType_) , colorFormat (colorFormat_) , depthStencilType (depthStencilType_) , depthStencilFormat (depthStencilFormat_) , width (width_) , height (height_) , samples (samples_) { } FboConfig (void) : buffers (0) , colorType (GL_NONE) , colorFormat (GL_NONE) , depthStencilType (GL_NONE) , depthStencilFormat (GL_NONE) , width (0) , height (0) , samples (0) { } std::string getName (void) const; deUint32 buffers; //!< Buffer bit mask (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|...) GLenum colorType; //!< GL_TEXTURE_2D, GL_TEXTURE_CUBE_MAP, GL_RENDERBUFFER GLenum colorFormat; //!< Internal format for color buffer texture or renderbuffer GLenum depthStencilType; GLenum depthStencilFormat; int width; int height; int samples; }; static const char* getTypeName (GLenum type) { switch (type) { case GL_TEXTURE_2D: return "tex2d"; case GL_RENDERBUFFER: return "rbo"; default: TCU_FAIL("Unknown type"); } } std::string FboConfig::getName (void) const { std::ostringstream name; DE_ASSERT(buffers & GL_COLOR_BUFFER_BIT); name << getTypeName(colorType) << "_" << getFormatName(colorFormat); if (buffers & GL_DEPTH_BUFFER_BIT) name << "_depth"; if (buffers & GL_STENCIL_BUFFER_BIT) name << "_stencil"; if (buffers & (GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT)) name << "_" << getTypeName(depthStencilType) << "_" << getFormatName(depthStencilFormat); return name.str(); } class Framebuffer { public: Framebuffer (sglr::Context& context, const FboConfig& config, int width, int height, deUint32 fbo = 0, deUint32 colorBuffer = 0, deUint32 depthStencilBuffer = 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 getDepthStencilBuffer (void) const { return m_depthStencilBuffer; } void checkCompleteness (void); private: deUint32 createTex2D (deUint32 name, GLenum format, int width, int height); deUint32 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_depthStencilBuffer; }; static std::vector<std::string> getEnablingExtensions (deUint32 format) { std::vector<std::string> out; switch (format) { case GL_RGB16F: out.push_back("GL_EXT_color_buffer_half_float"); break; case GL_RGBA16F: case GL_RG16F: case GL_R16F: out.push_back("GL_EXT_color_buffer_half_float"); case GL_RGBA32F: case GL_RGB32F: case GL_R11F_G11F_B10F: case GL_RG32F: case GL_R32F: out.push_back("GL_EXT_color_buffer_float"); default: break; } return out; } 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 bool isAnyExtensionSupported (sglr::Context& context, const std::vector<std::string>& requiredExts) { if (requiredExts.empty()) return true; for (std::vector<std::string>::const_iterator iter = requiredExts.begin(); iter != requiredExts.end(); iter++) { const std::string& extension = *iter; if (isExtensionSupported(context, extension.c_str())) return true; } return false; } template<typename T> static std::string join (const std::vector<T>& list, const std::string& sep) { std::ostringstream out; for (typename std::vector<T>::const_iterator iter = list.begin(); iter != list.end(); iter++) { if (iter != list.begin()) out << sep; out << *iter; } return out.str(); } static void checkColorFormatSupport (sglr::Context& context, deUint32 sizedFormat) { const std::vector<std::string> requiredExts = getEnablingExtensions(sizedFormat); if (!isAnyExtensionSupported(context, requiredExts)) { std::string errMsg = "Format not supported, requires " + ((requiredExts.size() == 1) ? requiredExts[0] : " one of the following: " + join(requiredExts, ", ")); throw tcu::NotSupportedError(errMsg); } } Framebuffer::Framebuffer (sglr::Context& context, const FboConfig& config, int width, int height, deUint32 fbo, deUint32 colorBufferName, deUint32 depthStencilBufferName) : m_config (config) , m_context (context) , m_framebuffer (fbo) , m_colorBuffer (0) , m_depthStencilBuffer (0) { // Verify that color format is supported checkColorFormatSupport(context, config.colorFormat); if (m_framebuffer == 0) context.genFramebuffers(1, &m_framebuffer); context.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer); if (m_config.buffers & (GL_COLOR_BUFFER_BIT)) { switch (m_config.colorType) { case GL_TEXTURE_2D: m_colorBuffer = createTex2D(colorBufferName, m_config.colorFormat, width, height); context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0); break; case GL_RENDERBUFFER: m_colorBuffer = createRbo(colorBufferName, m_config.colorFormat, width, height); context.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_colorBuffer); break; default: TCU_FAIL("Unsupported type"); } } if (m_config.buffers & (GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT)) { switch (m_config.depthStencilType) { case GL_TEXTURE_2D: m_depthStencilBuffer = createTex2D(depthStencilBufferName, m_config.depthStencilFormat, width, height); break; case GL_RENDERBUFFER: m_depthStencilBuffer = createRbo(depthStencilBufferName, m_config.depthStencilFormat, width, height); break; default: TCU_FAIL("Unsupported type"); } } for (int ndx = 0; ndx < 2; ndx++) { deUint32 bit = ndx ? GL_STENCIL_BUFFER_BIT : GL_DEPTH_BUFFER_BIT; deUint32 point = ndx ? GL_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT; if ((m_config.buffers & bit) == 0) continue; /* Not used. */ switch (m_config.depthStencilType) { case GL_TEXTURE_2D: context.framebufferTexture2D(GL_FRAMEBUFFER, point, GL_TEXTURE_2D, m_depthStencilBuffer, 0); break; case GL_RENDERBUFFER: context.framebufferRenderbuffer(GL_FRAMEBUFFER, point, GL_RENDERBUFFER, m_depthStencilBuffer); break; default: DE_ASSERT(false); } } GLenum err = m_context.getError(); if (err != GL_NO_ERROR) throw glu::Error(err, glu::getErrorStr(err).toString().c_str(), "", __FILE__, __LINE__); context.bindFramebuffer(GL_FRAMEBUFFER, 0); } Framebuffer::~Framebuffer (void) { m_context.deleteFramebuffers(1, &m_framebuffer); destroyBuffer(m_colorBuffer, m_config.colorType); destroyBuffer(m_depthStencilBuffer, m_config.depthStencilType); } 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(status, __FILE__, __LINE__); } deUint32 Framebuffer::createTex2D (deUint32 name, GLenum format, int width, int height) { if (name == 0) m_context.genTextures(1, &name); m_context.bindTexture(GL_TEXTURE_2D, name); m_context.texImage2D(GL_TEXTURE_2D, 0, format, width, height); if (!deIsPowerOfTwo32(width) || !deIsPowerOfTwo32(height)) { // Set wrap mode to clamp for NPOT FBOs m_context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); m_context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } m_context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); m_context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); return name; } deUint32 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); return name; } 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; bool compare (const tcu::Surface& reference, const tcu::Surface& result); protected: const 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) { tcu::Vec4 clearColor = tcu::Vec4(0.125f, 0.25f, 0.5f, 1.0f); glu::RenderContext& renderCtx = m_context.getRenderContext(); const tcu::RenderTarget& renderTarget = renderCtx.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 gles3Frame (width, height); tcu::Surface refFrame (width, height); GLenum gles3Error; GLenum refError; // Render using GLES3 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, gles3Frame); // Call actual render func gles3Error = 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 = (gles3Error == refError); if (!errorCodesOk) { log << tcu::TestLog::Message << "Error code mismatch: got " << glu::getErrorStr(gles3Error) << ", expected " << glu::getErrorStr(refError) << tcu::TestLog::EndMessage; failReason = "Got unexpected error"; } // Compare images bool imagesOk = compare(refFrame, gles3Frame); 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; } bool FboRenderCase::compare (const tcu::Surface& reference, const tcu::Surface& result) { const tcu::RGBA threshold (tcu::max(getFormatThreshold(m_config.colorFormat), tcu::RGBA(12, 12, 12, 12))); return tcu::bilinearCompare(m_testCtx.getLog(), "ComparisonResult", "Image comparison result", reference.getAccess(), result.getAccess(), threshold, tcu::COMPARE_LOG_RESULT); } namespace FboCases { class StencilClearsTest : public FboRenderCase { public: StencilClearsTest (Context& context, const FboConfig& config); virtual ~StencilClearsTest (void) {}; void render (sglr::Context& context, Surface& dst); }; StencilClearsTest::StencilClearsTest (Context& context, const FboConfig& config) : FboRenderCase (context, config.getName().c_str(), "Stencil clears", config) { } void StencilClearsTest::render (sglr::Context& context, Surface& dst) { tcu::TextureFormat colorFormat = glu::mapGLInternalFormat(m_config.colorFormat); glu::DataType fboSamplerType = glu::getSampler2DType(colorFormat); glu::DataType fboOutputType = getFragmentOutputType(colorFormat); tcu::TextureFormatInfo fboRangeInfo = tcu::getTextureFormatInfo(colorFormat); Vec4 fboOutScale = fboRangeInfo.valueMax - fboRangeInfo.valueMin; Vec4 fboOutBias = fboRangeInfo.valueMin; Texture2DShader texToFboShader (DataTypes() << glu::TYPE_SAMPLER_2D, fboOutputType); Texture2DShader texFromFboShader (DataTypes() << fboSamplerType, glu::TYPE_FLOAT_VEC4); deUint32 texToFboShaderID = context.createProgram(&texToFboShader); deUint32 texFromFboShaderID = context.createProgram(&texFromFboShader); deUint32 metaballsTex = 1; deUint32 quadsTex = 2; int width = 128; int height = 128; texToFboShader.setOutScaleBias(fboOutScale, fboOutBias); texFromFboShader.setTexScaleBias(0, fboRangeInfo.lookupScale, fboRangeInfo.lookupBias); createQuadsTex2D(context, quadsTex, GL_RGBA, GL_UNSIGNED_BYTE, width, height); createMetaballsTex2D(context, metaballsTex, GL_RGBA, GL_UNSIGNED_BYTE, width, height); Framebuffer fbo(context, m_config, 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.enable(GL_STENCIL_TEST); context.bindTexture(GL_TEXTURE_2D, quadsTex); context.stencilFunc(GL_EQUAL, 1, 0xffu); texToFboShader.setUniforms(context, texToFboShaderID); sglr::drawQuad(context, texToFboShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(+1.0f, +1.0f, 0.0f)); context.bindTexture(GL_TEXTURE_2D, metaballsTex); context.stencilFunc(GL_EQUAL, 2, 0xffu); texToFboShader.setUniforms(context, texToFboShaderID); sglr::drawQuad(context, texToFboShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(+1.0f, +1.0f, 0.0f)); context.disable(GL_STENCIL_TEST); if (fbo.getConfig().colorType == GL_TEXTURE_2D) { context.bindFramebuffer(GL_FRAMEBUFFER, 0); context.bindTexture(GL_TEXTURE_2D, fbo.getColorBuffer()); context.viewport(0, 0, context.getWidth(), context.getHeight()); texFromFboShader.setUniforms(context, texFromFboShaderID); sglr::drawQuad(context, texFromFboShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f)); context.readPixels(dst, 0, 0, context.getWidth(), context.getHeight()); } else readPixels(context, dst, 0, 0, width, height, colorFormat, fboRangeInfo.lookupScale, fboRangeInfo.lookupBias); } 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) { Texture2DShader texShader (DataTypes() << glu::TYPE_SAMPLER_2D, glu::TYPE_FLOAT_VEC4); FlatColorShader flatShader (glu::TYPE_FLOAT_VEC4); deUint32 texShaderID = context.createProgram(&texShader); deUint32 flatShaderID = context.createProgram(&flatShader); int width = 128; int height = 128; deUint32 quadsTex = 1; deUint32 metaballsTex = 2; bool stencil = (m_config.buffers & GL_STENCIL_BUFFER_BIT) != 0; context.disable(GL_DITHER); // Textures 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); // Fbo A Framebuffer fboA(context, m_config, width, height); fboA.checkCompleteness(); // Fbo B - don't create colorbuffer FboConfig cfg = m_config; cfg.buffers &= ~GL_COLOR_BUFFER_BIT; cfg.colorType = GL_NONE; cfg.colorFormat = GL_NONE; Framebuffer fboB(context, cfg, width, height); // Attach color buffer from fbo A context.bindFramebuffer(GL_FRAMEBUFFER, fboB.getFramebuffer()); switch (m_config.colorType) { 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(DE_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); } texShader.setUniforms(context, texShaderID); context.enable(GL_DEPTH_TEST); sglr::drawQuad(context, texShaderID, 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, texShaderID, 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, texShaderID, Vec3(0.5f, 0.5f, 0.5f), Vec3(1.0f, 1.0f, 0.5f)); context.disable(GL_DEPTH_TEST); if (stencil) { 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().colorType == GL_TEXTURE_2D) { texShader.setUniforms(context, texShaderID); context.bindFramebuffer(GL_FRAMEBUFFER, 0); context.bindTexture(GL_TEXTURE_2D, fboA.getColorBuffer()); context.viewport(0, 0, context.getWidth(), context.getHeight()); sglr::drawQuad(context, texShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f)); context.readPixels(dst, 0, 0, context.getWidth(), context.getHeight()); } else readPixels(context, dst, 0, 0, width, height, glu::mapGLInternalFormat(fboA.getConfig().colorFormat), Vec4(1.0f), Vec4(0.0f)); } class SharedColorbufferClearsTest : public FboRenderCase { public: SharedColorbufferClearsTest (Context& context, const FboConfig& config); virtual ~SharedColorbufferClearsTest (void) {} void render (sglr::Context& context, Surface& dst); }; SharedColorbufferClearsTest::SharedColorbufferClearsTest (Context& context, const FboConfig& config) : FboRenderCase (context, config.getName().c_str(), "Shared colorbuffer clears", config) { } void SharedColorbufferClearsTest::render (sglr::Context& context, Surface& dst) { tcu::TextureFormat colorFormat = glu::mapGLInternalFormat(m_config.colorFormat); glu::DataType fboSamplerType = glu::getSampler2DType(colorFormat); int width = 128; int height = 128; deUint32 colorbuffer = 1; // Check for format support. checkColorFormatSupport(context, m_config.colorFormat); // Single colorbuffer if (m_config.colorType == GL_TEXTURE_2D) { context.bindTexture(GL_TEXTURE_2D, colorbuffer); context.texImage2D(GL_TEXTURE_2D, 0, m_config.colorFormat, width, height); context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } else { DE_ASSERT(m_config.colorType == GL_RENDERBUFFER); context.bindRenderbuffer(GL_RENDERBUFFER, colorbuffer); context.renderbufferStorage(GL_RENDERBUFFER, m_config.colorFormat, width, height); } // Multiple framebuffers sharing the colorbuffer for (int fbo = 1; fbo <= 3; fbo++) { context.bindFramebuffer(GL_FRAMEBUFFER, fbo); if (m_config.colorType == 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(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 (m_config.colorType == GL_TEXTURE_2D) { Texture2DShader shader(DataTypes() << fboSamplerType, glu::TYPE_FLOAT_VEC4); deUint32 shaderID = context.createProgram(&shader); shader.setUniforms(context, shaderID); 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 readPixels(context, dst, 0, 0, width, height, colorFormat, Vec4(1.0f), Vec4(0.0f)); } class SharedDepthStencilTest : public FboRenderCase { public: SharedDepthStencilTest (Context& context, const FboConfig& config); virtual ~SharedDepthStencilTest (void) {}; static bool isConfigSupported (const FboConfig& config); void render (sglr::Context& context, Surface& dst); }; SharedDepthStencilTest::SharedDepthStencilTest (Context& context, const FboConfig& config) : FboRenderCase (context, config.getName().c_str(), "Shared depth/stencilbuffer", config) { } bool SharedDepthStencilTest::isConfigSupported (const FboConfig& config) { return (config.buffers & (GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT)) != 0; } void SharedDepthStencilTest::render (sglr::Context& context, Surface& dst) { Texture2DShader texShader (DataTypes() << glu::TYPE_SAMPLER_2D, glu::TYPE_FLOAT_VEC4); FlatColorShader flatShader (glu::TYPE_FLOAT_VEC4); deUint32 texShaderID = context.createProgram(&texShader); deUint32 flatShaderID = context.createProgram(&flatShader); int width = 128; int height = 128; // bool depth = (m_config.buffers & GL_DEPTH_BUFFER_BIT) != 0; bool stencil = (m_config.buffers & GL_STENCIL_BUFFER_BIT) != 0; // 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, m_config, width, height); fboA.checkCompleteness(); // Fbo B FboConfig cfg = m_config; cfg.buffers &= ~(GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); cfg.depthStencilType = GL_NONE; cfg.depthStencilFormat = GL_NONE; Framebuffer fboB(context, cfg, width, height); // Bind depth/stencil buffers from fbo A to fbo B context.bindFramebuffer(GL_FRAMEBUFFER, fboB.getFramebuffer()); for (int ndx = 0; ndx < 2; ndx++) { deUint32 bit = ndx ? GL_STENCIL_BUFFER_BIT : GL_DEPTH_BUFFER_BIT; deUint32 point = ndx ? GL_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT; if ((m_config.buffers & bit) == 0) continue; switch (m_config.depthStencilType) { case GL_TEXTURE_2D: context.framebufferTexture2D(GL_FRAMEBUFFER, point, GL_TEXTURE_2D, fboA.getDepthStencilBuffer(), 0); break; case GL_RENDERBUFFER: context.framebufferRenderbuffer(GL_FRAMEBUFFER, point, GL_RENDERBUFFER, fboA.getDepthStencilBuffer()); break; default: TCU_FAIL("Not implemented"); } } // Setup uniforms texShader.setUniforms(context, texShaderID); // Clear color to red and stencil to 1 in fbo B. 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); context.enable(GL_DEPTH_TEST); // Render quad to fbo A context.bindFramebuffer(GL_FRAMEBUFFER, fboA.getFramebuffer()); 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 (stencil) { // Clear subset of stencil buffer to 0 in fbo A 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 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) { // Render quad with stencil mask == 0 context.enable(GL_STENCIL_TEST); context.stencilFunc(GL_EQUAL, 0, 0xffu); context.useProgram(flatShaderID); flatShader.setColor(context, flatShaderID, Vec4(0.0f, 1.0f, 0.0f, 1.0f)); sglr::drawQuad(context, flatShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(+1.0f, +1.0f, 0.0f)); context.disable(GL_STENCIL_TEST); } if (m_config.colorType == 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 readPixels(context, dst, 0, 0, width, height, glu::mapGLInternalFormat(m_config.colorFormat), Vec4(1.0f), Vec4(0.0f)); } } #if 0 class TexSubImageAfterRenderTest : public FboRenderCase { public: TexSubImageAfterRenderTest (Context& context, const FboConfig& config); virtual ~TexSubImageAfterRenderTest (void) {} 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) { } void TexSubImageAfterRenderTest::render (sglr::Context& context, Surface& dst) { using sglr::TexturedQuadOp; bool isRGBA = true; Surface fourQuads(Surface::PIXELFORMAT_RGB, 64, 64); tcu::SurfaceUtil::fillWithFourQuads(fourQuads); Surface metaballs(isRGBA ? Surface::PIXELFORMAT_RGBA : Surface::PIXELFORMAT_RGB, 64, 64); tcu::SurfaceUtil::fillWithMetaballs(metaballs, 5, 3); 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, fourQuads); 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); context.draw(TexturedQuadOp(-1.0f, -1.0f, 1.0f, 1.0f, 0)); // Update texture using TexSubImage2D context.bindTexture(GL_TEXTURE_2D, fboTex); context.texSubImage2D(GL_TEXTURE_2D, 0, 32, 32, metaballs); // Draw to screen context.bindFramebuffer(GL_FRAMEBUFFER, 0); context.viewport(0, 0, context.getWidth(), context.getHeight()); context.draw(TexturedQuadOp(-1.0f, -1.0f, 1.0f, 1.0f, 0)); context.readPixels(dst, 0, 0, context.getWidth(), context.getHeight()); } class TexSubImageBetweenRenderTest : public FboRenderCase { public: TexSubImageBetweenRenderTest (Context& context, const FboConfig& config); virtual ~TexSubImageBetweenRenderTest (void) {} 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) { } void TexSubImageBetweenRenderTest::render (sglr::Context& context, Surface& dst) { using sglr::TexturedQuadOp; using sglr::BlendTextureOp; bool isRGBA = true; Surface fourQuads(Surface::PIXELFORMAT_RGB, 64, 64); tcu::SurfaceUtil::fillWithFourQuads(fourQuads); Surface metaballs(isRGBA ? Surface::PIXELFORMAT_RGBA : Surface::PIXELFORMAT_RGB, 64, 64); tcu::SurfaceUtil::fillWithMetaballs(metaballs, 5, 3); Surface metaballs2(Surface::PIXELFORMAT_RGBA, 64, 64); tcu::SurfaceUtil::fillWithMetaballs(metaballs2, 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, metaballs2); 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, fourQuads); 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); context.draw(TexturedQuadOp(-1.0f, -1.0f, 1.0f, 1.0f, 0)); // Update texture using TexSubImage2D context.bindTexture(GL_TEXTURE_2D, fboTex); context.texSubImage2D(GL_TEXTURE_2D, 0, 32, 32, metaballs); // Render again to fbo context.bindTexture(GL_TEXTURE_2D, metaballsTex); context.draw(BlendTextureOp(0)); // Draw to screen context.bindFramebuffer(GL_FRAMEBUFFER, 0); context.viewport(0, 0, context.getWidth(), context.getHeight()); context.bindTexture(GL_TEXTURE_2D, fboTex); context.draw(TexturedQuadOp(-1.0f, -1.0f, 1.0f, 1.0f, 0)); context.readPixels(dst, 0, 0, context.getWidth(), context.getHeight()); } #endif 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) { tcu::TextureFormat colorFormat = glu::mapGLInternalFormat(m_config.colorFormat); glu::DataType fboSamplerType = glu::getSampler2DType(colorFormat); glu::DataType fboOutputType = getFragmentOutputType(colorFormat); tcu::TextureFormatInfo fboRangeInfo = tcu::getTextureFormatInfo(colorFormat); Vec4 fboOutScale = fboRangeInfo.valueMax - fboRangeInfo.valueMin; Vec4 fboOutBias = fboRangeInfo.valueMin; Texture2DShader texToFboShader (DataTypes() << glu::TYPE_SAMPLER_2D, fboOutputType); Texture2DShader texFromFboShader (DataTypes() << fboSamplerType, glu::TYPE_FLOAT_VEC4); FlatColorShader flatShader (fboOutputType); deUint32 texToFboShaderID = context.createProgram(&texToFboShader); deUint32 texFromFboShaderID = context.createProgram(&texFromFboShader); deUint32 flatShaderID = context.createProgram(&flatShader); deUint32 quadsTex = 1; deUint32 metaballsTex = 2; bool depth = (m_config.buffers & GL_DEPTH_BUFFER_BIT) != 0; bool stencil = (m_config.buffers & GL_STENCIL_BUFFER_BIT) != 0; int initialWidth = 128; int initialHeight = 128; int newWidth = 64; int newHeight = 32; texToFboShader.setOutScaleBias(fboOutScale, fboOutBias); texFromFboShader.setTexScaleBias(0, fboRangeInfo.lookupScale, fboRangeInfo.lookupBias); createQuadsTex2D(context, quadsTex, GL_RGB, GL_UNSIGNED_BYTE, 64, 64); createMetaballsTex2D(context, metaballsTex, GL_RGB, GL_UNSIGNED_BYTE, 32, 32); Framebuffer fbo(context, m_config, initialWidth, initialHeight); fbo.checkCompleteness(); // Setup shaders texToFboShader.setUniforms (context, texToFboShaderID); texFromFboShader.setUniforms(context, texFromFboShaderID); flatShader.setColor (context, flatShaderID, Vec4(0.0f, 1.0f, 0.0f, 1.0f) * fboOutScale + fboOutBias); // Render quads context.bindFramebuffer(GL_FRAMEBUFFER, fbo.getFramebuffer()); context.viewport(0, 0, initialWidth, initialHeight); clearColorBuffer(context, colorFormat, tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f)); context.clear(GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); context.bindTexture(GL_TEXTURE_2D, quadsTex); sglr::drawQuad(context, texToFboShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f)); if (fbo.getConfig().colorType == 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, texFromFboShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f)); // Restore binding context.bindFramebuffer(GL_FRAMEBUFFER, fbo.getFramebuffer()); } // Resize buffers switch (fbo.getConfig().colorType) { case GL_TEXTURE_2D: context.bindTexture(GL_TEXTURE_2D, fbo.getColorBuffer()); context.texImage2D(GL_TEXTURE_2D, 0, fbo.getConfig().colorFormat, newWidth, newHeight); break; case GL_RENDERBUFFER: context.bindRenderbuffer(GL_RENDERBUFFER, fbo.getColorBuffer()); context.renderbufferStorage(GL_RENDERBUFFER, fbo.getConfig().colorFormat, newWidth, newHeight); break; default: DE_ASSERT(DE_FALSE); } if (depth || stencil) { switch (fbo.getConfig().depthStencilType) { case GL_TEXTURE_2D: context.bindTexture(GL_TEXTURE_2D, fbo.getDepthStencilBuffer()); context.texImage2D(GL_TEXTURE_2D, 0, fbo.getConfig().depthStencilFormat, newWidth, newHeight); break; case GL_RENDERBUFFER: context.bindRenderbuffer(GL_RENDERBUFFER, fbo.getDepthStencilBuffer()); context.renderbufferStorage(GL_RENDERBUFFER, fbo.getConfig().depthStencilFormat, newWidth, newHeight); break; default: DE_ASSERT(false); } } // Render to resized fbo context.viewport(0, 0, newWidth, newHeight); clearColorBuffer(context, colorFormat, tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f)); context.clear(GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); context.enable(GL_DEPTH_TEST); context.bindTexture(GL_TEXTURE_2D, metaballsTex); sglr::drawQuad(context, texToFboShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(+1.0f, +1.0f, 0.0f)); context.bindTexture(GL_TEXTURE_2D, quadsTex); sglr::drawQuad(context, texToFboShaderID, 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.clearStencil(1); context.scissor(10, 10, 5, 15); 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, flatShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(+1.0f, +1.0f, 0.0f)); context.disable(GL_STENCIL_TEST); } if (m_config.colorType == 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, texFromFboShaderID, Vec3(-0.5f, -0.5f, 0.0f), Vec3(0.5f, 0.5f, 0.0f)); context.readPixels(dst, 0, 0, context.getWidth(), context.getHeight()); } else readPixels(context, dst, 0, 0, newWidth, newHeight, colorFormat, fboRangeInfo.lookupScale, fboRangeInfo.lookupBias); } class RecreateBuffersTest : public FboRenderCase { public: RecreateBuffersTest (Context& context, const FboConfig& config, deUint32 buffers, bool rebind); virtual ~RecreateBuffersTest (void) {} void render (sglr::Context& context, Surface& dst); private: deUint32 m_buffers; bool m_rebind; }; RecreateBuffersTest::RecreateBuffersTest (Context& context, const FboConfig& config, deUint32 buffers, bool rebind) : FboRenderCase (context, (string(config.getName()) + (rebind ? "" : "_no_rebind")).c_str(), "Recreate buffers", config) , m_buffers (buffers) , m_rebind (rebind) { } void RecreateBuffersTest::render (sglr::Context& ctx, Surface& dst) { tcu::TextureFormat colorFormat = glu::mapGLInternalFormat(m_config.colorFormat); glu::DataType fboSamplerType = glu::getSampler2DType(colorFormat); glu::DataType fboOutputType = getFragmentOutputType(colorFormat); tcu::TextureFormatInfo fboRangeInfo = tcu::getTextureFormatInfo(colorFormat); Vec4 fboOutScale = fboRangeInfo.valueMax - fboRangeInfo.valueMin; Vec4 fboOutBias = fboRangeInfo.valueMin; Texture2DShader texToFboShader (DataTypes() << glu::TYPE_SAMPLER_2D, fboOutputType); Texture2DShader texFromFboShader (DataTypes() << fboSamplerType, glu::TYPE_FLOAT_VEC4); FlatColorShader flatShader (fboOutputType); deUint32 texToFboShaderID = ctx.createProgram(&texToFboShader); deUint32 texFromFboShaderID = ctx.createProgram(&texFromFboShader); deUint32 flatShaderID = ctx.createProgram(&flatShader); int width = 128; int height = 128; deUint32 metaballsTex = 1; deUint32 quadsTex = 2; bool stencil = (m_config.buffers & GL_STENCIL_BUFFER_BIT) != 0; createQuadsTex2D(ctx, quadsTex, GL_RGB, GL_UNSIGNED_BYTE, 64, 64); createMetaballsTex2D(ctx, metaballsTex, GL_RGB, GL_UNSIGNED_BYTE, 64, 64); Framebuffer fbo(ctx, m_config, width, height); fbo.checkCompleteness(); // Setup shaders texToFboShader.setOutScaleBias(fboOutScale, fboOutBias); texFromFboShader.setTexScaleBias(0, fboRangeInfo.lookupScale, fboRangeInfo.lookupBias); texToFboShader.setUniforms (ctx, texToFboShaderID); texFromFboShader.setUniforms(ctx, texFromFboShaderID); flatShader.setColor (ctx, flatShaderID, Vec4(0.0f, 0.0f, 1.0f, 1.0f) * fboOutScale + fboOutBias); // Draw scene ctx.bindFramebuffer(GL_FRAMEBUFFER, fbo.getFramebuffer()); ctx.viewport(0, 0, width, height); clearColorBuffer(ctx, colorFormat, tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f)); ctx.clear(GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); ctx.enable(GL_DEPTH_TEST); ctx.bindTexture(GL_TEXTURE_2D, quadsTex); sglr::drawQuad(ctx, texToFboShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f)); ctx.disable(GL_DEPTH_TEST); 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); DE_ASSERT((m_buffers & (GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT)) == 0 || (m_buffers & (GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT)) == (m_config.buffers & (GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT))); // Recreate. for (int ndx = 0; ndx < 2; ndx++) { deUint32 bit = ndx == 0 ? GL_COLOR_BUFFER_BIT : (GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); deUint32 type = ndx == 0 ? fbo.getConfig().colorType : fbo.getConfig().depthStencilType; deUint32 format = ndx == 0 ? fbo.getConfig().colorFormat : fbo.getConfig().depthStencilFormat; deUint32 buf = ndx == 0 ? fbo.getColorBuffer() : fbo.getDepthStencilBuffer(); if ((m_buffers & bit) == 0) continue; switch (type) { case GL_TEXTURE_2D: ctx.deleteTextures(1, &buf); ctx.bindTexture(GL_TEXTURE_2D, buf); ctx.texImage2D(GL_TEXTURE_2D, 0, format, width, height); ctx.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); ctx.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); break; case GL_RENDERBUFFER: ctx.deleteRenderbuffers(1, &buf); ctx.bindRenderbuffer(GL_RENDERBUFFER, buf); ctx.renderbufferStorage(GL_RENDERBUFFER, format, width, height); break; default: DE_ASSERT(false); } } // Rebind. if (m_rebind) { for (int ndx = 0; ndx < 3; ndx++) { deUint32 bit = ndx == 0 ? GL_COLOR_BUFFER_BIT : ndx == 1 ? GL_DEPTH_BUFFER_BIT : ndx == 2 ? GL_STENCIL_BUFFER_BIT : 0; deUint32 point = ndx == 0 ? GL_COLOR_ATTACHMENT0 : ndx == 1 ? GL_DEPTH_ATTACHMENT : ndx == 2 ? GL_STENCIL_ATTACHMENT : 0; deUint32 type = ndx == 0 ? fbo.getConfig().colorType : fbo.getConfig().depthStencilType; deUint32 buf = ndx == 0 ? fbo.getColorBuffer() : fbo.getDepthStencilBuffer(); if ((m_buffers & bit) == 0) continue; switch (type) { case GL_TEXTURE_2D: ctx.framebufferTexture2D(GL_FRAMEBUFFER, point, GL_TEXTURE_2D, buf, 0); break; case GL_RENDERBUFFER: ctx.framebufferRenderbuffer(GL_FRAMEBUFFER, point, GL_RENDERBUFFER, buf); break; default: DE_ASSERT(false); } } } if (!m_rebind) ctx.bindFramebuffer(GL_FRAMEBUFFER, fbo.getFramebuffer()); ctx.clearStencil(0); ctx.clear(m_buffers & (GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT)); // \note Clear only buffers that were re-created if (m_buffers & GL_COLOR_BUFFER_BIT) { // Clearing of integer buffers is undefined so do clearing by rendering flat color. sglr::drawQuad(ctx, flatShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f)); } ctx.enable(GL_DEPTH_TEST); 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, texToFboShaderID, 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); if (fbo.getConfig().colorType == 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, texFromFboShaderID, 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 readPixels(ctx, dst, 0, 0, width, height, colorFormat, fboRangeInfo.lookupScale, fboRangeInfo.lookupBias); } } } // FboCases FboRenderTestGroup::FboRenderTestGroup (Context& context) : TestCaseGroup(context, "render", "Rendering Tests") { } FboRenderTestGroup::~FboRenderTestGroup (void) { } void FboRenderTestGroup::init (void) { static const deUint32 objectTypes[] = { GL_TEXTURE_2D, GL_RENDERBUFFER }; enum FormatType { FORMATTYPE_FLOAT = 0, FORMATTYPE_FIXED, FORMATTYPE_INT, FORMATTYPE_UINT, FORMATTYPE_LAST }; // Required by specification. static const struct { deUint32 format; FormatType type; } colorFormats[] = { { GL_RGBA32F, FORMATTYPE_FLOAT }, { GL_RGBA32I, FORMATTYPE_INT }, { GL_RGBA32UI, FORMATTYPE_UINT }, { GL_RGBA16F, FORMATTYPE_FLOAT }, { GL_RGBA16I, FORMATTYPE_INT }, { GL_RGBA16UI, FORMATTYPE_UINT }, { GL_RGB16F, FORMATTYPE_FLOAT }, { GL_RGBA8, FORMATTYPE_FIXED }, { GL_RGBA8I, FORMATTYPE_INT }, { GL_RGBA8UI, FORMATTYPE_UINT }, { GL_SRGB8_ALPHA8, FORMATTYPE_FIXED }, { GL_RGB10_A2, FORMATTYPE_FIXED }, { GL_RGB10_A2UI, FORMATTYPE_UINT }, { GL_RGBA4, FORMATTYPE_FIXED }, { GL_RGB5_A1, FORMATTYPE_FIXED }, { GL_RGB8, FORMATTYPE_FIXED }, { GL_RGB565, FORMATTYPE_FIXED }, { GL_R11F_G11F_B10F, FORMATTYPE_FLOAT }, { GL_RG32F, FORMATTYPE_FLOAT }, { GL_RG32I, FORMATTYPE_INT }, { GL_RG32UI, FORMATTYPE_UINT }, { GL_RG16F, FORMATTYPE_FLOAT }, { GL_RG16I, FORMATTYPE_INT }, { GL_RG16UI, FORMATTYPE_UINT }, { GL_RG8, FORMATTYPE_FLOAT }, { GL_RG8I, FORMATTYPE_INT }, { GL_RG8UI, FORMATTYPE_UINT }, { GL_R32F, FORMATTYPE_FLOAT }, { GL_R32I, FORMATTYPE_INT }, { GL_R32UI, FORMATTYPE_UINT }, { GL_R16F, FORMATTYPE_FLOAT }, { GL_R16I, FORMATTYPE_INT }, { GL_R16UI, FORMATTYPE_UINT }, { GL_R8, FORMATTYPE_FLOAT }, { GL_R8I, FORMATTYPE_INT }, { GL_R8UI, FORMATTYPE_UINT } }; static const struct { deUint32 format; bool depth; bool stencil; } depthStencilFormats[] = { { GL_DEPTH_COMPONENT32F, true, false }, { GL_DEPTH_COMPONENT24, true, false }, { GL_DEPTH_COMPONENT16, true, false }, { GL_DEPTH32F_STENCIL8, true, true }, { GL_DEPTH24_STENCIL8, true, true }, { GL_STENCIL_INDEX8, false, true } }; using namespace FboCases; // .stencil_clear tcu::TestCaseGroup* stencilClearGroup = new tcu::TestCaseGroup(m_testCtx, "stencil_clear", "Stencil buffer clears"); addChild(stencilClearGroup); for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(depthStencilFormats); fmtNdx++) { deUint32 colorType = GL_TEXTURE_2D; deUint32 stencilType = GL_RENDERBUFFER; deUint32 colorFmt = GL_RGBA8; if (!depthStencilFormats[fmtNdx].stencil) continue; FboConfig config(GL_COLOR_BUFFER_BIT|GL_STENCIL_BUFFER_BIT, colorType, colorFmt, stencilType, depthStencilFormats[fmtNdx].format); stencilClearGroup->addChild(new StencilClearsTest(m_context, config)); } // .shared_colorbuffer_clear tcu::TestCaseGroup* sharedColorbufferClearGroup = new tcu::TestCaseGroup(m_testCtx, "shared_colorbuffer_clear", "Shader colorbuffer clears"); addChild(sharedColorbufferClearGroup); for (int colorFmtNdx = 0; colorFmtNdx < DE_LENGTH_OF_ARRAY(colorFormats); colorFmtNdx++) { // Clearing of integer buffers is undefined. if (colorFormats[colorFmtNdx].type == FORMATTYPE_INT || colorFormats[colorFmtNdx].type == FORMATTYPE_UINT) continue; for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(objectTypes); typeNdx++) { FboConfig config(GL_COLOR_BUFFER_BIT, objectTypes[typeNdx], colorFormats[colorFmtNdx].format, GL_NONE, GL_NONE); sharedColorbufferClearGroup->addChild(new SharedColorbufferClearsTest(m_context, config)); } } // .shared_colorbuffer tcu::TestCaseGroup* sharedColorbufferGroup = new tcu::TestCaseGroup(m_testCtx, "shared_colorbuffer", "Shared colorbuffer tests"); addChild(sharedColorbufferGroup); for (int colorFmtNdx = 0; colorFmtNdx < DE_LENGTH_OF_ARRAY(colorFormats); colorFmtNdx++) { deUint32 depthStencilType = GL_RENDERBUFFER; deUint32 depthStencilFormat = GL_DEPTH24_STENCIL8; // Blending with integer buffers and fp32 targets is not supported. if (colorFormats[colorFmtNdx].type == FORMATTYPE_INT || colorFormats[colorFmtNdx].type == FORMATTYPE_UINT || colorFormats[colorFmtNdx].format == GL_RGBA32F || colorFormats[colorFmtNdx].format == GL_RGB32F || colorFormats[colorFmtNdx].format == GL_RG32F || colorFormats[colorFmtNdx].format == GL_R32F) continue; for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(objectTypes); typeNdx++) { FboConfig colorOnlyConfig (GL_COLOR_BUFFER_BIT, objectTypes[typeNdx], colorFormats[colorFmtNdx].format, GL_NONE, GL_NONE); FboConfig colorDepthConfig (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT, objectTypes[typeNdx], colorFormats[colorFmtNdx].format, depthStencilType, depthStencilFormat); FboConfig colorDepthStencilConfig (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT, objectTypes[typeNdx], colorFormats[colorFmtNdx].format, depthStencilType, depthStencilFormat); sharedColorbufferGroup->addChild(new SharedColorbufferTest(m_context, colorOnlyConfig)); sharedColorbufferGroup->addChild(new SharedColorbufferTest(m_context, colorDepthConfig)); sharedColorbufferGroup->addChild(new SharedColorbufferTest(m_context, colorDepthStencilConfig)); } } // .shared_depth_stencil tcu::TestCaseGroup* sharedDepthStencilGroup = new tcu::TestCaseGroup(m_testCtx, "shared_depth_stencil", "Shared depth and stencil buffers"); addChild(sharedDepthStencilGroup); for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(depthStencilFormats); fmtNdx++) { deUint32 colorType = GL_TEXTURE_2D; deUint32 colorFmt = GL_RGBA8; bool depth = depthStencilFormats[fmtNdx].depth; bool stencil = depthStencilFormats[fmtNdx].stencil; if (!depth) continue; // Not verified. // Depth and stencil: both rbo and textures for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(objectTypes); typeNdx++) { FboConfig config(GL_COLOR_BUFFER_BIT|(depth ? GL_DEPTH_BUFFER_BIT : 0)|(stencil ? GL_STENCIL_BUFFER_BIT : 0), colorType, colorFmt, objectTypes[typeNdx], depthStencilFormats[fmtNdx].format); sharedDepthStencilGroup->addChild(new SharedDepthStencilTest(m_context, config)); } } // .resize tcu::TestCaseGroup* resizeGroup = new tcu::TestCaseGroup(m_testCtx, "resize", "FBO resize tests"); addChild(resizeGroup); for (int colorFmtNdx = 0; colorFmtNdx < DE_LENGTH_OF_ARRAY(colorFormats); colorFmtNdx++) { deUint32 colorFormat = colorFormats[colorFmtNdx].format; // Color-only. for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(objectTypes); typeNdx++) { FboConfig config(GL_COLOR_BUFFER_BIT, objectTypes[typeNdx], colorFormat, GL_NONE, GL_NONE); resizeGroup->addChild(new ResizeTest(m_context, config)); } // For selected color formats tests depth & stencil variants. if (colorFormat == GL_RGBA8 || colorFormat == GL_RGBA16F) { for (int depthStencilFmtNdx = 0; depthStencilFmtNdx < DE_LENGTH_OF_ARRAY(depthStencilFormats); depthStencilFmtNdx++) { deUint32 colorType = GL_TEXTURE_2D; bool depth = depthStencilFormats[depthStencilFmtNdx].depth; bool stencil = depthStencilFormats[depthStencilFmtNdx].stencil; // Depth and stencil: both rbo and textures for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(objectTypes); typeNdx++) { if (!depth && objectTypes[typeNdx] != GL_RENDERBUFFER) continue; // Not supported. FboConfig config(GL_COLOR_BUFFER_BIT|(depth ? GL_DEPTH_BUFFER_BIT : 0)|(stencil ? GL_STENCIL_BUFFER_BIT : 0), colorType, colorFormat, objectTypes[typeNdx], depthStencilFormats[depthStencilFmtNdx].format); resizeGroup->addChild(new ResizeTest(m_context, config)); } } } } // .recreate_color tcu::TestCaseGroup* recreateColorGroup = new tcu::TestCaseGroup(m_testCtx, "recreate_color", "Recreate colorbuffer tests"); addChild(recreateColorGroup); for (int colorFmtNdx = 0; colorFmtNdx < DE_LENGTH_OF_ARRAY(colorFormats); colorFmtNdx++) { deUint32 colorFormat = colorFormats[colorFmtNdx].format; deUint32 depthStencilFormat = GL_DEPTH24_STENCIL8; deUint32 depthStencilType = GL_RENDERBUFFER; // Color-only. for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(objectTypes); typeNdx++) { FboConfig config(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT, objectTypes[typeNdx], colorFormat, depthStencilType, depthStencilFormat); recreateColorGroup->addChild(new RecreateBuffersTest(m_context, config, GL_COLOR_BUFFER_BIT, true /* rebind */)); } } // .recreate_depth_stencil tcu::TestCaseGroup* recreateDepthStencilGroup = new tcu::TestCaseGroup(m_testCtx, "recreate_depth_stencil", "Recreate depth and stencil buffers"); addChild(recreateDepthStencilGroup); for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(depthStencilFormats); fmtNdx++) { deUint32 colorType = GL_TEXTURE_2D; deUint32 colorFmt = GL_RGBA8; bool depth = depthStencilFormats[fmtNdx].depth; bool stencil = depthStencilFormats[fmtNdx].stencil; // Depth and stencil: both rbo and textures for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(objectTypes); typeNdx++) { if (!depth && objectTypes[typeNdx] != GL_RENDERBUFFER) continue; FboConfig config(GL_COLOR_BUFFER_BIT|(depth ? GL_DEPTH_BUFFER_BIT : 0)|(stencil ? GL_STENCIL_BUFFER_BIT : 0), colorType, colorFmt, objectTypes[typeNdx], depthStencilFormats[fmtNdx].format); recreateDepthStencilGroup->addChild(new RecreateBuffersTest(m_context, config, (depth ? GL_DEPTH_BUFFER_BIT : 0)|(stencil ? GL_STENCIL_BUFFER_BIT : 0), true /* rebind */)); } } } } // Functional } // gles3 } // deqp