Create repro cases for FBO clear bug
authorPyry Haulos <phaulos@google.com>
Mon, 3 Nov 2014 23:13:31 +0000 (15:13 -0800)
committerPyry Haulos <phaulos@google.com>
Mon, 3 Nov 2014 23:13:31 +0000 (15:13 -0800)
Color clears typically enjoy special handling in drivers, especially on
tile-based renderers, where fullscreen clears are typically just state
that gets processed when tile is initialized. That requires further
special handling when the clear is the only command issued to a render
target.

Sure enough there appears to be some devices where following doesn't
work as expected:

Repeat N times:
  Clear FBO A with some iteration-specific color
  Bind FBO B
  Sample from FBO A

The result is that sampling might not return color of that iteration,
but instead for example the claer color of first, or previous iteration.

These tests attempt to reproduce that implementation bug.

Bug: 18169662
Change-Id: I869138c5108d66fdf0cf497f730b2b3518d1686d

modules/gles2/functional/es2fFboRenderTest.cpp
modules/gles3/functional/es3fFboColorbufferTests.cpp

index 42173bf..dedbcc8 100644 (file)
@@ -37,7 +37,7 @@
 #include "gluPixelTransfer.hpp"
 #include "gluTextureUtil.hpp"
 #include "gluStrUtil.hpp"
-#include "deRandom.h"
+#include "deRandom.hpp"
 #include "deString.h"
 
 #include "glwFunctions.hpp"
@@ -1987,6 +1987,90 @@ void RecreateBuffersTest<Buffers>::render (sglr::Context& ctx, Surface& dst)
        }
 }
 
+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)
@@ -2105,6 +2189,14 @@ void FboRenderTestGroup::init (void)
        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
index b1763f5..28e71c7 100644 (file)
@@ -812,6 +812,206 @@ private:
        deUint32        m_dstAlpha;
 };
 
+class FboRepeatedClearSampleTex2DCase : public FboColorbufferCase
+{
+public:
+       FboRepeatedClearSampleTex2DCase (Context& context, const char* name, const char* desc, deUint32 format)
+               : FboColorbufferCase(context, name, desc, format)
+       {
+       }
+
+protected:
+       void preCheck (void)
+       {
+               checkFormatSupport(m_format);
+       }
+
+       void render (tcu::Surface& dst)
+       {
+               const tcu::TextureFormat                fboFormat               = glu::mapGLInternalFormat(m_format);
+               const tcu::TextureFormatInfo    fmtInfo                 = tcu::getTextureFormatInfo(fboFormat);
+               const int                                               numRowsCols             = 4;
+               const int                                               cellSize                = 16;
+               const int                                               fboSizes[]              = { cellSize, cellSize*numRowsCols };
+
+               Texture2DShader                                 fboBlitShader   (DataTypes() << glu::getSampler2DType(fboFormat), getFragmentOutputType(fboFormat), Vec4(1.0f), Vec4(0.0f));
+               const deUint32                                  fboBlitShaderID = getCurrentContext()->createProgram(&fboBlitShader);
+
+               de::Random                                              rnd                             (18169662);
+               deUint32                                                fbos[]                  = { 0, 0 };
+               deUint32                                                textures[]              = { 0, 0 };
+
+               glGenFramebuffers(2, &fbos[0]);
+               glGenTextures(2, &textures[0]);
+
+               for (int fboNdx = 0; fboNdx < DE_LENGTH_OF_ARRAY(fbos); fboNdx++)
+               {
+                       glBindTexture(GL_TEXTURE_2D, textures[fboNdx]);
+                       glTexStorage2D(GL_TEXTURE_2D, 1, m_format, fboSizes[fboNdx], fboSizes[fboNdx]);
+                       glTexParameteri(GL_TEXTURE_2D,  GL_TEXTURE_WRAP_S,              GL_CLAMP_TO_EDGE);
+                       glTexParameteri(GL_TEXTURE_2D,  GL_TEXTURE_WRAP_T,              GL_CLAMP_TO_EDGE);
+                       glTexParameteri(GL_TEXTURE_2D,  GL_TEXTURE_MIN_FILTER,  GL_NEAREST);
+                       glTexParameteri(GL_TEXTURE_2D,  GL_TEXTURE_MAG_FILTER,  GL_NEAREST);
+                       checkError();
+
+                       glBindFramebuffer(GL_FRAMEBUFFER, fbos[fboNdx]);
+                       glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[fboNdx], 0);
+                       checkError();
+                       checkFramebufferStatus(GL_FRAMEBUFFER);
+               }
+
+               // larger fbo bound -- clear to transparent black
+               clearColorBuffer(fboFormat, Vec4(0.0f));
+
+               fboBlitShader.setUniforms(*getCurrentContext(), fboBlitShaderID);
+               glBindTexture(GL_TEXTURE_2D, textures[0]);
+
+               for (int cellY = 0; cellY < numRowsCols; cellY++)
+               for (int cellX = 0; cellX < numRowsCols; cellX++)
+               {
+                       const Vec4      color   = randomVector<4>(rnd, fmtInfo.valueMin, fmtInfo.valueMax);
+
+                       glBindFramebuffer(GL_FRAMEBUFFER, fbos[0]);
+                       clearColorBuffer(fboFormat, color);
+
+                       glBindFramebuffer(GL_FRAMEBUFFER, fbos[1]);
+                       glViewport(cellX*cellSize, cellY*cellSize, cellSize, cellSize);
+                       sglr::drawQuad(*getCurrentContext(), fboBlitShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
+               }
+
+               readPixels(dst, 0, 0, fboSizes[1], fboSizes[1], fboFormat, fmtInfo.lookupScale, fmtInfo.lookupBias);
+               checkError();
+       }
+};
+
+class FboRepeatedClearBlitTex2DCase : public FboColorbufferCase
+{
+public:
+       FboRepeatedClearBlitTex2DCase (Context& context, const char* name, const char* desc, deUint32 format)
+               : FboColorbufferCase(context, name, desc, format)
+       {
+       }
+
+protected:
+       void preCheck (void)
+       {
+               checkFormatSupport(m_format);
+       }
+
+       void render (tcu::Surface& dst)
+       {
+               const tcu::TextureFormat                fboFormat               = glu::mapGLInternalFormat(m_format);
+               const tcu::TextureFormatInfo    fmtInfo                 = tcu::getTextureFormatInfo(fboFormat);
+               const int                                               numRowsCols             = 4;
+               const int                                               cellSize                = 16;
+               const int                                               fboSizes[]              = { cellSize, cellSize*numRowsCols };
+
+               de::Random                                              rnd                             (18169662);
+               deUint32                                                fbos[]                  = { 0, 0 };
+               deUint32                                                textures[]              = { 0, 0 };
+
+               glGenFramebuffers(2, &fbos[0]);
+               glGenTextures(2, &textures[0]);
+
+               for (int fboNdx = 0; fboNdx < DE_LENGTH_OF_ARRAY(fbos); fboNdx++)
+               {
+                       glBindTexture(GL_TEXTURE_2D, textures[fboNdx]);
+                       glTexStorage2D(GL_TEXTURE_2D, 1, m_format, fboSizes[fboNdx], fboSizes[fboNdx]);
+                       glTexParameteri(GL_TEXTURE_2D,  GL_TEXTURE_WRAP_S,              GL_CLAMP_TO_EDGE);
+                       glTexParameteri(GL_TEXTURE_2D,  GL_TEXTURE_WRAP_T,              GL_CLAMP_TO_EDGE);
+                       glTexParameteri(GL_TEXTURE_2D,  GL_TEXTURE_MIN_FILTER,  GL_NEAREST);
+                       glTexParameteri(GL_TEXTURE_2D,  GL_TEXTURE_MAG_FILTER,  GL_NEAREST);
+                       checkError();
+
+                       glBindFramebuffer(GL_FRAMEBUFFER, fbos[fboNdx]);
+                       glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[fboNdx], 0);
+                       checkError();
+                       checkFramebufferStatus(GL_FRAMEBUFFER);
+               }
+
+               // larger fbo bound -- clear to transparent black
+               clearColorBuffer(fboFormat, Vec4(0.0f));
+
+               for (int cellY = 0; cellY < numRowsCols; cellY++)
+               for (int cellX = 0; cellX < numRowsCols; cellX++)
+               {
+                       const Vec4      color   = randomVector<4>(rnd, fmtInfo.valueMin, fmtInfo.valueMax);
+
+                       glBindFramebuffer(GL_FRAMEBUFFER, fbos[0]);
+                       clearColorBuffer(fboFormat, color);
+
+                       glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[1]);
+                       glBlitFramebuffer(0, 0, cellSize, cellSize, cellX*cellSize, cellY*cellSize, (cellX+1)*cellSize, (cellY+1)*cellSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+               }
+
+               glBindFramebuffer(GL_FRAMEBUFFER, fbos[1]);
+               readPixels(dst, 0, 0, fboSizes[1], fboSizes[1], fboFormat, fmtInfo.lookupScale, fmtInfo.lookupBias);
+               checkError();
+       }
+};
+
+class FboRepeatedClearBlitRboCase : public FboColorbufferCase
+{
+public:
+       FboRepeatedClearBlitRboCase (Context& context, const char* name, const char* desc, deUint32 format)
+               : FboColorbufferCase(context, name, desc, format)
+       {
+       }
+
+protected:
+       void preCheck (void)
+       {
+               checkFormatSupport(m_format);
+       }
+
+       void render (tcu::Surface& dst)
+       {
+               const tcu::TextureFormat                fboFormat               = glu::mapGLInternalFormat(m_format);
+               const tcu::TextureFormatInfo    fmtInfo                 = tcu::getTextureFormatInfo(fboFormat);
+               const int                                               numRowsCols             = 4;
+               const int                                               cellSize                = 16;
+               const int                                               fboSizes[]              = { cellSize, cellSize*numRowsCols };
+
+               de::Random                                              rnd                             (18169662);
+               deUint32                                                fbos[]                  = { 0, 0 };
+               deUint32                                                rbos[]                  = { 0, 0 };
+
+               glGenFramebuffers(2, &fbos[0]);
+               glGenRenderbuffers(2, &rbos[0]);
+
+               for (int fboNdx = 0; fboNdx < DE_LENGTH_OF_ARRAY(fbos); fboNdx++)
+               {
+                       glBindRenderbuffer(GL_RENDERBUFFER, rbos[fboNdx]);
+                       glRenderbufferStorage(GL_RENDERBUFFER, m_format, fboSizes[fboNdx], fboSizes[fboNdx]);
+                       checkError();
+
+                       glBindFramebuffer(GL_FRAMEBUFFER, fbos[fboNdx]);
+                       glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbos[fboNdx]);
+                       checkError();
+                       checkFramebufferStatus(GL_FRAMEBUFFER);
+               }
+
+               // larger fbo bound -- clear to transparent black
+               clearColorBuffer(fboFormat, Vec4(0.0f));
+
+               for (int cellY = 0; cellY < numRowsCols; cellY++)
+               for (int cellX = 0; cellX < numRowsCols; cellX++)
+               {
+                       const Vec4      color   = randomVector<4>(rnd, fmtInfo.valueMin, fmtInfo.valueMax);
+
+                       glBindFramebuffer(GL_FRAMEBUFFER, fbos[0]);
+                       clearColorBuffer(fboFormat, color);
+
+                       glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[1]);
+                       glBlitFramebuffer(0, 0, cellSize, cellSize, cellX*cellSize, cellY*cellSize, (cellX+1)*cellSize, (cellY+1)*cellSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+               }
+
+               glBindFramebuffer(GL_FRAMEBUFFER, fbos[1]);
+               readPixels(dst, 0, 0, fboSizes[1], fboSizes[1], fboFormat, fmtInfo.lookupScale, fmtInfo.lookupBias);
+               checkError();
+       }
+};
+
 FboColorTests::FboColorTests (Context& context)
        : TestCaseGroup(context, "color", "Colorbuffer tests")
 {
@@ -944,6 +1144,51 @@ void FboColorTests::init (void)
                        blendGroup->addChild(new FboBlendCase(m_context, (fmtName + "_src_over").c_str(), "", format, IVec2(127, 111), GL_FUNC_ADD, GL_FUNC_ADD, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE));
                }
        }
+
+       // .repeated_clear
+       {
+               tcu::TestCaseGroup* const repeatedClearGroup = new tcu::TestCaseGroup(m_testCtx, "repeated_clear", "Repeated clears and blits");
+               addChild(repeatedClearGroup);
+
+               // .sample.tex2d
+               {
+                       tcu::TestCaseGroup* const sampleGroup = new tcu::TestCaseGroup(m_testCtx, "sample", "Read by sampling");
+                       repeatedClearGroup->addChild(sampleGroup);
+
+                       tcu::TestCaseGroup* const tex2DGroup = new tcu::TestCaseGroup(m_testCtx, "tex2d", "2D Texture");
+                       sampleGroup->addChild(tex2DGroup);
+
+                       for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(colorFormats); fmtNdx++)
+                               tex2DGroup->addChild(new FboRepeatedClearSampleTex2DCase(m_context, getFormatName(colorFormats[fmtNdx]),
+                                                                                                                                                "", colorFormats[fmtNdx]));
+               }
+
+               // .blit
+               {
+                       tcu::TestCaseGroup* const blitGroup = new tcu::TestCaseGroup(m_testCtx, "blit", "Blitted");
+                       repeatedClearGroup->addChild(blitGroup);
+
+                       // .tex2d
+                       {
+                               tcu::TestCaseGroup* const tex2DGroup = new tcu::TestCaseGroup(m_testCtx, "tex2d", "2D Texture");
+                               blitGroup->addChild(tex2DGroup);
+
+                               for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(colorFormats); fmtNdx++)
+                                       tex2DGroup->addChild(new FboRepeatedClearBlitTex2DCase(m_context, getFormatName(colorFormats[fmtNdx]),
+                                                                                                                                                  "", colorFormats[fmtNdx]));
+                       }
+
+                       // .rbo
+                       {
+                               tcu::TestCaseGroup* const rboGroup = new tcu::TestCaseGroup(m_testCtx, "rbo", "Renderbuffer");
+                               blitGroup->addChild(rboGroup);
+
+                               for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(colorFormats); fmtNdx++)
+                                       rboGroup->addChild(new FboRepeatedClearBlitRboCase(m_context, getFormatName(colorFormats[fmtNdx]),
+                                                                                                                                          "", colorFormats[fmtNdx]));
+                       }
+               }
+       }
 }
 
 } // Functional