Add tests for GL_EXT_draw_buffers_indexed.
authorMika Isojärvi <misojarvi@google.com>
Tue, 17 Feb 2015 22:13:56 +0000 (14:13 -0800)
committerMika Isojärvi <misojarvi@google.com>
Thu, 5 Mar 2015 19:55:29 +0000 (11:55 -0800)
Add tests for GL_EXT_draw_buffers_indexed extension.

Bug: 18925580
Change-Id: Ia9e3a7a867c5f11a961df4aeaeccc2c5865b58a9

Android.mk
doc/testspecs/GLES31/functional.draw_buffers_indexed.txt [new file with mode: 0644]
modules/gles31/functional/CMakeLists.txt
modules/gles31/functional/es31fDrawBuffersIndexedTests.cpp [new file with mode: 0644]
modules/gles31/functional/es31fDrawBuffersIndexedTests.hpp [new file with mode: 0644]
modules/gles31/functional/es31fFunctionalTests.cpp

index 06adb08..e1235d4 100644 (file)
@@ -314,6 +314,7 @@ LOCAL_SRC_FILES := \
        modules/gles31/functional/es31fVertexAttributeBindingStateQueryTests.cpp \
        modules/gles31/functional/es31fVertexAttributeBindingTests.cpp \
        modules/gles31/functional/es31fCopyImageTests.cpp \
+       modules/gles31/functional/es31fDrawBuffersIndexedTests.cpp \
        modules/gles31/stress/es31sDrawTests.cpp \
        modules/gles31/stress/es31sStressTests.cpp \
        modules/gles31/stress/es31sTessellationGeometryInteractionTests.cpp \
diff --git a/doc/testspecs/GLES31/functional.draw_buffers_indexed.txt b/doc/testspecs/GLES31/functional.draw_buffers_indexed.txt
new file mode 100644 (file)
index 0000000..3c93868
--- /dev/null
@@ -0,0 +1,60 @@
+-------------------------------------------------------------------------
+drawElements Quality Program Test Specification
+-----------------------------------------------
+
+Copyright 2015 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.
+-------------------------------------------------------------------------
+    GL_EXT_draw_buffers_indexed tests.
+
+Tests:
+ + dEQP-GLES31.functional.draw_buffers_indexed.*
+
+Includes:
+ + Blend equations, blend functions, blend enable and color masks.
+ + Setting common state first and then per draw buffer state.
+ + Setting per draw buffer state first and then common state.
+ + Overriding per draw buffer state with common state.
+ + Overriding common state with per draw buffer state.
+ + Random tests for different states and renderbuffer formats.
+
+Excludes:
+ + Query tests.
+ + Exhaustive blend state combinations.
+ + Exhaustive tests for all renderbuffer formats.
+
+Description:
+
+Each test cases begins by creating framebuffer object and required renderbuffer
+objects. Depending on test case it sets some common blend state with original
+non-indexed functions, then some draw buffer specific state with indexed
+functions and finally tries to set some common blend state again with original
+functions. Different test cases set different state before and after setting per
+draw buffer state. Results are verified by rendering single quad using the
+framebuffer object and reading back each renderbuffer, which are then compared
+to reference.
+
+pre_common -group uses two RGBA8 renderbuffers. It first sets some common state
+and changes that same state for second draw buffer using indexed functions.
+
+post_common -group is similar to pre_common -group except it first sets the draw
+buffer specific state for second renderbuffer and then overrides it by using
+non-indexed functions.
+
+min_max_random -group uses specification minimum maximum number of draw buffers
+and randomizes renderbuffer formats, pre- and post-common state and per draw
+buffer state.
+
+max_random -group works as min_max_random -group, but uses the implementation
+maximum number of draw buffers.
index dc34f7e..a4240f1 100644 (file)
@@ -155,6 +155,8 @@ set(DEQP_GLES31_FUNCTIONAL_SRCS
        es31fPrimitiveBoundingBoxTests.hpp
        es31fCopyImageTests.hpp
        es31fCopyImageTests.cpp
+       es31fDrawBuffersIndexedTests.hpp
+       es31fDrawBuffersIndexedTests.cpp
        )
 
 add_library(deqp-gles31-functional STATIC ${DEQP_GLES31_FUNCTIONAL_SRCS})
diff --git a/modules/gles31/functional/es31fDrawBuffersIndexedTests.cpp b/modules/gles31/functional/es31fDrawBuffersIndexedTests.cpp
new file mode 100644 (file)
index 0000000..ceccb05
--- /dev/null
@@ -0,0 +1,1585 @@
+/*-------------------------------------------------------------------------
+ * drawElements Quality Program OpenGL ES 3.1 Module
+ * -------------------------------------------------
+ *
+ * Copyright 2015 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 Indexed blend operation tests (GL_EXT_draw_buffers_indexed)
+ *//*--------------------------------------------------------------------*/
+
+#include "es31fDrawBuffersIndexedTests.hpp"
+
+#include "gluContextInfo.hpp"
+#include "gluDrawUtil.hpp"
+#include "gluObjectWrapper.hpp"
+#include "gluPixelTransfer.hpp"
+#include "gluShaderProgram.hpp"
+#include "gluStrUtil.hpp"
+#include "gluTextureUtil.hpp"
+
+#include "sglrReferenceUtils.hpp"
+
+#include "rrMultisamplePixelBufferAccess.hpp"
+#include "rrRenderer.hpp"
+
+#include "glwEnums.hpp"
+#include "glwFunctions.hpp"
+
+#include "tcuEither.hpp"
+#include "tcuImageCompare.hpp"
+#include "tcuMaybe.hpp"
+#include "tcuResultCollector.hpp"
+#include "tcuTestLog.hpp"
+#include "tcuTexture.hpp"
+#include "tcuTextureUtil.hpp"
+#include "tcuVector.hpp"
+#include "tcuVectorUtil.hpp"
+
+#include "deRandom.hpp"
+#include "deStringUtil.hpp"
+#include "deUniquePtr.hpp"
+
+#include "deInt32.h"
+
+#include <string>
+#include <vector>
+
+using tcu::BVec4;
+using tcu::Either;
+using tcu::IVec2;
+using tcu::IVec4;
+using tcu::Maybe;
+using tcu::TestLog;
+using tcu::TextureFormat;
+using tcu::TextureLevel;
+using tcu::UVec4;
+using tcu::Vec2;
+using tcu::Vec4;
+using tcu::just;
+
+using std::string;
+using std::vector;
+
+using sglr::rr_util::mapGLBlendEquation;
+using sglr::rr_util::mapGLBlendFunc;
+using sglr::rr_util::mapGLBlendEquationAdvanced;
+
+namespace deqp
+{
+namespace gles31
+{
+namespace Functional
+{
+namespace
+{
+
+typedef deUint32 BlendEq;
+
+bool isAdvancedBlendEq (BlendEq eq)
+{
+       switch (eq)
+       {
+               case GL_MULTIPLY_KHR:           return true;
+               case GL_SCREEN_KHR:                     return true;
+               case GL_OVERLAY_KHR:            return true;
+               case GL_DARKEN_KHR:                     return true;
+               case GL_LIGHTEN_KHR:            return true;
+               case GL_COLORDODGE_KHR:         return true;
+               case GL_COLORBURN_KHR:          return true;
+               case GL_HARDLIGHT_KHR:          return true;
+               case GL_SOFTLIGHT_KHR:          return true;
+               case GL_DIFFERENCE_KHR:         return true;
+               case GL_EXCLUSION_KHR:          return true;
+               case GL_HSL_HUE_KHR:            return true;
+               case GL_HSL_SATURATION_KHR:     return true;
+               case GL_HSL_COLOR_KHR:          return true;
+               case GL_HSL_LUMINOSITY_KHR:     return true;
+               default:
+                       return false;
+       }
+}
+
+struct SeparateBlendEq
+{
+       SeparateBlendEq (BlendEq rgb_, BlendEq alpha_)
+               : rgb   (rgb_)
+               , alpha (alpha_)
+       {
+       }
+
+       BlendEq rgb;
+       BlendEq alpha;
+};
+
+struct BlendFunc
+{
+       BlendFunc (deUint32 src_, deUint32 dst_)
+               : src (src_)
+               , dst (dst_)
+       {
+       }
+
+       deUint32 src;
+       deUint32 dst;
+};
+
+struct SeparateBlendFunc
+{
+       SeparateBlendFunc (BlendFunc rgb_, BlendFunc alpha_)
+               : rgb   (rgb_)
+               , alpha (alpha_)
+       {
+       }
+
+       BlendFunc rgb;
+       BlendFunc alpha;
+};
+
+typedef deUint32 DrawBuffer;
+
+struct BlendState
+{
+       BlendState (void) {}
+
+       BlendState (const Maybe<bool>&                                                                  enableBlend_,
+                               const Maybe<Either<BlendEq, SeparateBlendEq> >&         blendEq_,
+                               const Maybe<Either<BlendFunc, SeparateBlendFunc> >&     blendFunc_,
+                               const Maybe<BVec4>&                                                                     colorMask_)
+               : enableBlend   (enableBlend_)
+               , blendEq               (blendEq_)
+               , blendFunc             (blendFunc_)
+               , colorMask             (colorMask_)
+       {
+       }
+
+       bool isEmpty (void) const
+       {
+               return (!enableBlend) && (!blendEq) && (!blendFunc) && (!colorMask);
+       }
+
+       Maybe<bool>                                                                             enableBlend;
+       Maybe<Either<BlendEq, SeparateBlendEq> >                blendEq;
+       Maybe<Either<BlendFunc, SeparateBlendFunc> >    blendFunc;
+       Maybe<BVec4>                                                                    colorMask;
+};
+
+void setCommonBlendState (const glw::Functions& gl, const BlendState& blend)
+{
+       if (blend.enableBlend)
+       {
+               if (*blend.enableBlend)
+                       gl.enable(GL_BLEND);
+               else
+                       gl.disable(GL_BLEND);
+       }
+
+       if (blend.colorMask)
+       {
+               const BVec4& mask = *blend.colorMask;
+
+               gl.colorMask(mask.x(), mask.y(), mask.z(), mask.w());
+       }
+
+       if (blend.blendEq)
+       {
+               const Either<BlendEq, SeparateBlendEq>& blendEq = *blend.blendEq;
+
+               if (blendEq.is<BlendEq>())
+                       gl.blendEquation(blendEq.get<BlendEq>());
+               else if (blendEq.is<SeparateBlendEq>())
+                       gl.blendEquationSeparate(blendEq.get<SeparateBlendEq>().rgb, blendEq.get<SeparateBlendEq>().alpha);
+               else
+                       DE_ASSERT(false);
+       }
+
+       if (blend.blendFunc)
+       {
+               const Either<BlendFunc, SeparateBlendFunc>& blendFunc = *blend.blendFunc;
+
+               if (blendFunc.is<BlendFunc>())
+                       gl.blendFunc(blendFunc.get<BlendFunc>().src, blendFunc.get<BlendFunc>().dst);
+               else if (blendFunc.is<SeparateBlendFunc>())
+                       gl.blendFuncSeparate(blendFunc.get<SeparateBlendFunc>().rgb.src, blendFunc.get<SeparateBlendFunc>().rgb.dst, blendFunc.get<SeparateBlendFunc>().alpha.src, blendFunc.get<SeparateBlendFunc>().alpha.dst);
+               else
+                       DE_ASSERT(false);
+       }
+
+       GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to set common blend state.");
+}
+
+void setIndexedBlendState (const glw::Functions& gl, const BlendState& blend, deUint32 index)
+{
+       if (blend.enableBlend)
+       {
+               if (*blend.enableBlend)
+                       gl.enablei(GL_BLEND, index);
+               else
+                       gl.disablei(GL_BLEND, index);
+       }
+
+       if (blend.colorMask)
+       {
+               const BVec4 mask = *blend.colorMask;
+
+               gl.colorMaski(index, mask.x(), mask.y(), mask.z(), mask.w());
+       }
+
+       if (blend.blendEq)
+       {
+               const Either<BlendEq, SeparateBlendEq>& blendEq = *blend.blendEq;
+
+               if (blendEq.is<BlendEq>())
+                       gl.blendEquationi(index, blendEq.get<BlendEq>());
+               else if (blendEq.is<SeparateBlendEq>())
+                       gl.blendEquationSeparatei(index, blendEq.get<SeparateBlendEq>().rgb, blendEq.get<SeparateBlendEq>().alpha);
+               else
+                       DE_ASSERT(false);
+       }
+
+       if (blend.blendFunc)
+       {
+               const Either<BlendFunc, SeparateBlendFunc>& blendFunc = *blend.blendFunc;
+
+               if (blendFunc.is<BlendFunc>())
+                       gl.blendFunci(index, blendFunc.get<BlendFunc>().src, blendFunc.get<BlendFunc>().dst);
+               else if (blendFunc.is<SeparateBlendFunc>())
+                       gl.blendFuncSeparatei(index, blendFunc.get<SeparateBlendFunc>().rgb.src, blendFunc.get<SeparateBlendFunc>().rgb.dst, blendFunc.get<SeparateBlendFunc>().alpha.src, blendFunc.get<SeparateBlendFunc>().alpha.dst);
+               else
+                       DE_ASSERT(false);
+       }
+
+       GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to set draw buffer specifig blend state.");
+}
+
+class DrawBufferInfo
+{
+public:
+                                                       DrawBufferInfo  (bool                                   render,
+                                                                                        const IVec2&                   size,
+                                                                                        const BlendState&              blendState,
+                                                                                        const TextureFormat&   format);
+
+       const TextureFormat&    getFormat               (void) const { return m_format;         }
+       const IVec2&                    getSize                 (void) const { return m_size;           }
+       const BlendState&               getBlendState   (void) const { return m_blendState;     }
+       bool                                    getRender               (void) const { return m_render;         }
+
+private:
+       bool                                    m_render;
+       IVec2                                   m_size;
+       TextureFormat                   m_format;
+       BlendState                              m_blendState;
+};
+
+DrawBufferInfo::DrawBufferInfo (bool render, const IVec2& size, const BlendState& blendState, const TextureFormat& format)
+       : m_render              (render)
+       , m_size                (size)
+       , m_format              (format)
+       , m_blendState  (blendState)
+{
+}
+
+void clearRenderbuffer (const glw::Functions&                  gl,
+                                               const tcu::TextureFormat&               format,
+                                               int                                                             renderbufferNdx,
+                                               int                                                             renderbufferCount,
+                                               tcu::TextureLevel&                              refRenderbuffer)
+{
+       const tcu::TextureFormatInfo    info            = tcu::getTextureFormatInfo(format);
+
+       // Clear each buffer to different color
+       const float                                             redScale        = float(renderbufferNdx + 1) / float(renderbufferCount);
+       const float                                             blueScale       = float(renderbufferCount - renderbufferNdx) / float(renderbufferCount);
+       const float                                             greenScale      = float(((renderbufferCount/2) + renderbufferNdx) % renderbufferCount) / float(renderbufferCount);
+       // Alpha should never be zero as advanced blend equations assume premultiplied alpha.
+       const float                                             alphaScale      = float(1 + (((renderbufferCount/2) + renderbufferCount - renderbufferNdx) % renderbufferCount)) / float(renderbufferCount);
+
+       switch (tcu::getTextureChannelClass(format.type))
+       {
+               case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
+               {
+                       const float red         = -1000.0f + 2000.0f * redScale;
+                       const float green       = -1000.0f + 2000.0f * greenScale;
+                       const float blue        = -1000.0f + 2000.0f * blueScale;
+                       const float alpha       = -1000.0f + 2000.0f * alphaScale;
+                       const Vec4      color   (red, green, blue, alpha);
+
+                       tcu::clear(refRenderbuffer, color);
+                       gl.clearBufferfv(GL_COLOR, renderbufferNdx, color.getPtr());
+                       break;
+               }
+
+               case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
+               {
+                       const deInt32   red             = deInt32(info.valueMin.x() + (info.valueMax.x() - info.valueMin.x()) * redScale);
+                       const deInt32   green   = deInt32(info.valueMin.y() + (info.valueMax.y() - info.valueMin.y()) * greenScale);
+                       const deInt32   blue    = deInt32(info.valueMin.z() + (info.valueMax.z() - info.valueMin.z()) * blueScale);
+                       const deInt32   alpha   = deInt32(info.valueMin.w() + (info.valueMax.w() - info.valueMin.w()) * alphaScale);
+                       const IVec4             color   (red, green, blue, alpha);
+
+                       tcu::clear(refRenderbuffer, color);
+                       gl.clearBufferiv(GL_COLOR, renderbufferNdx, color.getPtr());
+                       break;
+               }
+
+               case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
+               {
+                       const deUint32  red             = deUint32(info.valueMax.x() * redScale);
+                       const deUint32  green   = deUint32(info.valueMax.y() * greenScale);
+                       const deUint32  blue    = deUint32(info.valueMax.z() * blueScale);
+                       const deUint32  alpha   = deUint32(info.valueMax.w() * alphaScale);
+                       const UVec4             color   (red, green, blue, alpha);
+
+                       tcu::clear(refRenderbuffer, color);
+                       gl.clearBufferuiv(GL_COLOR, renderbufferNdx, color.getPtr());
+                       break;
+               }
+
+               case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
+               {
+                       const float red         = info.valueMin.x() + (info.valueMax.x() - info.valueMin.x()) * redScale;
+                       const float green       = info.valueMin.y() + (info.valueMax.y() - info.valueMin.y()) * greenScale;
+                       const float blue        = info.valueMin.z() + (info.valueMax.z() - info.valueMin.z()) * blueScale;
+                       const float alpha       = info.valueMin.w() + (info.valueMax.w() - info.valueMin.w()) * alphaScale;
+                       const Vec4      color   (red, green, blue, alpha);
+
+                       tcu::clear(refRenderbuffer, color);
+                       gl.clearBufferfv(GL_COLOR, renderbufferNdx, color.getPtr());
+                       break;
+               }
+
+               case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
+               {
+                       const float red         = info.valueMax.x() * redScale;
+                       const float green       = info.valueMax.y() * greenScale;
+                       const float blue        = info.valueMax.z() * blueScale;
+                       const float alpha       = info.valueMax.w() * alphaScale;
+                       const Vec4      color   (red, green, blue, alpha);
+
+                       tcu::clear(refRenderbuffer, color);
+                       gl.clearBufferfv(GL_COLOR, renderbufferNdx, color.getPtr());
+                       break;
+               }
+
+               default:
+                       DE_ASSERT(DE_FALSE);
+       }
+
+       GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to clear renderbuffer.");
+}
+
+void genRenderbuffers (const glw::Functions&                   gl,
+                                          const vector<DrawBufferInfo>&        drawBuffers,
+                                          const glu::Framebuffer&                      framebuffer,
+                                          const glu::RenderbufferVector&       renderbuffers,
+                                          vector<TextureLevel>&                        refRenderbuffers)
+{
+       vector<deUint32> bufs;
+
+       bufs.resize(drawBuffers.size());
+
+       DE_ASSERT(drawBuffers.size() == renderbuffers.size());
+       DE_ASSERT(drawBuffers.size() == refRenderbuffers.size());
+
+       gl.bindFramebuffer(GL_FRAMEBUFFER, *framebuffer);
+
+       for (int renderbufferNdx = 0; renderbufferNdx < (int)drawBuffers.size(); renderbufferNdx++)
+       {
+               const DrawBufferInfo&           drawBuffer      = drawBuffers[renderbufferNdx];
+               const TextureFormat&            format          = drawBuffer.getFormat();
+               const IVec2&                            size            = drawBuffer.getSize();
+               const deUint32                          glFormat        = glu::getInternalFormat(format);
+
+               bufs[renderbufferNdx]                                   = GL_COLOR_ATTACHMENT0 + renderbufferNdx;
+               refRenderbuffers[renderbufferNdx]               = TextureLevel(drawBuffer.getFormat(), size.x(), size.y());
+
+               gl.bindRenderbuffer(GL_RENDERBUFFER, renderbuffers[renderbufferNdx]);
+               gl.renderbufferStorage(GL_RENDERBUFFER, glFormat, size.x(), size.y());
+               gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + renderbufferNdx, GL_RENDERBUFFER, renderbuffers[renderbufferNdx]);
+               GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to create renderbuffer.");
+       }
+
+       gl.drawBuffers(bufs.size(), &(bufs[0]));
+
+       for (int renderbufferNdx = 0; renderbufferNdx < (int)drawBuffers.size(); renderbufferNdx++)
+       {
+               const DrawBufferInfo&           drawBuffer      = drawBuffers[renderbufferNdx];
+               const TextureFormat&            format          = drawBuffer.getFormat();
+
+               clearRenderbuffer(gl, format, renderbufferNdx, (int)refRenderbuffers.size(),  refRenderbuffers[renderbufferNdx]);
+       }
+
+       gl.bindRenderbuffer(GL_RENDERBUFFER, 0);
+       gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+Vec4 getFixedPointFormatThreshold (const tcu::TextureFormat& sourceFormat, const tcu::TextureFormat& readPixelsFormat)
+{
+       DE_ASSERT(tcu::getTextureChannelClass(sourceFormat.type) != tcu::TEXTURECHANNELCLASS_FLOATING_POINT);
+       DE_ASSERT(tcu::getTextureChannelClass(readPixelsFormat.type) != tcu::TEXTURECHANNELCLASS_FLOATING_POINT);
+
+       DE_ASSERT(tcu::getTextureChannelClass(sourceFormat.type) != tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER);
+       DE_ASSERT(tcu::getTextureChannelClass(readPixelsFormat.type) != tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER);
+
+       DE_ASSERT(tcu::getTextureChannelClass(sourceFormat.type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER);
+       DE_ASSERT(tcu::getTextureChannelClass(readPixelsFormat.type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER);
+
+       const tcu::IVec4        srcBits         = tcu::getTextureFormatBitDepth(sourceFormat);
+       const tcu::IVec4        readBits        = tcu::getTextureFormatBitDepth(readPixelsFormat);
+
+       return Vec4(3.0f) / ((tcu::Vector<deUint64, 4>(1) << (tcu::min(srcBits, readBits).cast<deUint64>())) - tcu::Vector<deUint64, 4>(1)).cast<float>();
+}
+
+void verifyRenderbuffer (TestLog&                                      log,
+                                                tcu::ResultCollector&          results,
+                                                const tcu::TextureFormat&      format,
+                                                int                                            renderbufferNdx,
+                                                const tcu::TextureLevel&       refRenderbuffer,
+                                                const tcu::TextureLevel&       result)
+{
+       switch (tcu::getTextureChannelClass(format.type))
+       {
+               case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
+               {
+                       const string    name            = "Renderbuffer" + de::toString(renderbufferNdx);
+                       const string    desc            = "Compare renderbuffer " + de::toString(renderbufferNdx);
+                       const UVec4             threshold       (1, 1, 1, 1);
+
+                       if (!tcu::floatUlpThresholdCompare(log, name.c_str(), desc.c_str(), refRenderbuffer, result, threshold, tcu::COMPARE_LOG_RESULT))
+                               results.fail("Verification of renderbuffer " + de::toString(renderbufferNdx) + " failed.");
+
+                       break;
+               }
+
+               case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
+               case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
+               {
+                       const string    name            = "Renderbuffer" + de::toString(renderbufferNdx);
+                       const string    desc            = "Compare renderbuffer " + de::toString(renderbufferNdx);
+                       const UVec4             threshold       (1, 1, 1, 1);
+
+                       if (!tcu::intThresholdCompare(log, name.c_str(), desc.c_str(), refRenderbuffer, result, threshold, tcu::COMPARE_LOG_RESULT))
+                               results.fail("Verification of renderbuffer " + de::toString(renderbufferNdx) + " failed.");
+
+                       break;
+               }
+
+               case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
+               case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
+               {
+                       const string    name            = "Renderbuffer" + de::toString(renderbufferNdx);
+                       const string    desc            = "Compare renderbuffer " + de::toString(renderbufferNdx);
+                       const Vec4              threshold       = getFixedPointFormatThreshold(format, result.getFormat());
+
+                       if (!tcu::floatThresholdCompare(log, name.c_str(), desc.c_str(), refRenderbuffer, result, threshold, tcu::COMPARE_LOG_RESULT))
+                               results.fail("Verification of renderbuffer " + de::toString(renderbufferNdx) + " failed.");
+
+                       break;
+               }
+
+
+               default:
+                       DE_ASSERT(DE_FALSE);
+       }
+}
+
+TextureFormat getReadPixelFormat (const TextureFormat& format)
+{
+       switch (tcu::getTextureChannelClass(format.type))
+       {
+               case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
+                       return TextureFormat(TextureFormat::RGBA, TextureFormat::UNSIGNED_INT32);
+
+               case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
+                       return TextureFormat(TextureFormat::RGBA, TextureFormat::SIGNED_INT32);
+
+               case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
+               case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
+                       return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8);
+
+               default:
+                       DE_ASSERT(false);
+                       return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8);
+       }
+}
+
+void verifyRenderbuffers (TestLog&                                                     log,
+                                                 tcu::ResultCollector&                         results,
+                                                 glu::RenderContext&                           renderContext,
+                                                 const glu::RenderbufferVector&        renderbuffers,
+                                                 const glu::Framebuffer&                       framebuffer,
+                                                 const vector<TextureLevel>&           refRenderbuffers)
+{
+       const glw::Functions& gl = renderContext.getFunctions();
+
+       DE_ASSERT(renderbuffers.size() == refRenderbuffers.size());
+
+       gl.bindFramebuffer(GL_FRAMEBUFFER, *framebuffer);
+
+       for (int renderbufferNdx = 0; renderbufferNdx < (int)renderbuffers.size(); renderbufferNdx++)
+       {
+               const TextureLevel&     refRenderbuffer = refRenderbuffers[renderbufferNdx];
+               const int                       width                   = refRenderbuffer.getWidth();
+               const int                       height                  = refRenderbuffer.getHeight();
+               const TextureFormat     format                  = refRenderbuffer.getFormat();
+
+               tcu::TextureLevel       result                  (getReadPixelFormat(format), width, height);
+
+               gl.readBuffer(GL_COLOR_ATTACHMENT0 + renderbufferNdx);
+               glu::readPixels(renderContext, 0, 0, result.getAccess());
+               GLU_EXPECT_NO_ERROR(gl.getError(), "Reading pixels from renderbuffer failed.");
+
+               verifyRenderbuffer(log, results, format, renderbufferNdx, refRenderbuffer, result);
+       }
+
+       gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+static const float s_quadCoords[] =
+{
+       -0.5f, -0.5f,
+        0.5f, -0.5f,
+        0.5f,  0.5f,
+
+        0.5f,  0.5f,
+       -0.5f,  0.5f,
+       -0.5f, -0.5f
+};
+
+void setBlendState (rr::FragmentOperationState& fragOps, const BlendState& state)
+{
+       if (state.blendEq)
+       {
+               if (state.blendEq->is<BlendEq>())
+               {
+                       if (isAdvancedBlendEq(state.blendEq->get<BlendEq>()))
+                       {
+                               const rr::BlendEquationAdvanced equation = mapGLBlendEquationAdvanced(state.blendEq->get<BlendEq>());
+
+                               fragOps.blendMode                               = rr::BLENDMODE_ADVANCED;
+                               fragOps.blendEquationAdvaced    = equation;
+                       }
+                       else
+                       {
+                               const rr::BlendEquation equation = mapGLBlendEquation(state.blendEq->get<BlendEq>());
+
+                               fragOps.blendMode                               = rr::BLENDMODE_STANDARD;
+                               fragOps.blendRGBState.equation  = equation;
+                               fragOps.blendAState.equation    = equation;
+                       }
+               }
+               else
+               {
+                       DE_ASSERT(state.blendEq->is<SeparateBlendEq>());
+
+                       fragOps.blendMode                               = rr::BLENDMODE_STANDARD;
+                       fragOps.blendRGBState.equation  = mapGLBlendEquation(state.blendEq->get<SeparateBlendEq>().rgb);
+                       fragOps.blendAState.equation    = mapGLBlendEquation(state.blendEq->get<SeparateBlendEq>().alpha);
+               }
+       }
+
+       if (state.blendFunc)
+       {
+               if (state.blendFunc->is<BlendFunc>())
+               {
+                       const rr::BlendFunc srcFunction = mapGLBlendFunc(state.blendFunc->get<BlendFunc>().src);
+                       const rr::BlendFunc dstFunction = mapGLBlendFunc(state.blendFunc->get<BlendFunc>().dst);
+
+                       fragOps.blendRGBState.srcFunc   = srcFunction;
+                       fragOps.blendRGBState.dstFunc   = dstFunction;
+
+                       fragOps.blendAState.srcFunc             = srcFunction;
+                       fragOps.blendAState.dstFunc             = dstFunction;
+               }
+               else
+               {
+                       DE_ASSERT(state.blendFunc->is<SeparateBlendFunc>());
+
+                       fragOps.blendRGBState.srcFunc   = mapGLBlendFunc(state.blendFunc->get<SeparateBlendFunc>().rgb.src);
+                       fragOps.blendRGBState.dstFunc   = mapGLBlendFunc(state.blendFunc->get<SeparateBlendFunc>().rgb.dst);
+
+                       fragOps.blendAState.srcFunc             = mapGLBlendFunc(state.blendFunc->get<SeparateBlendFunc>().alpha.src);
+                       fragOps.blendAState.dstFunc             = mapGLBlendFunc(state.blendFunc->get<SeparateBlendFunc>().alpha.dst);
+               }
+       }
+
+       if (state.colorMask)
+               fragOps.colorMask = *state.colorMask;
+}
+
+rr::RenderState createRenderState (const BlendState& preCommonBlendState, const BlendState& postCommonBlendState, const DrawBufferInfo& info)
+{
+       const IVec2             size    = info.getSize();
+       rr::RenderState state   (rr::ViewportState(rr::WindowRectangle(0, 0, size.x(), size.y())));
+
+       state.fragOps.blendMode = rr::BLENDMODE_STANDARD;
+
+       setBlendState(state.fragOps, preCommonBlendState);
+       setBlendState(state.fragOps, info.getBlendState());
+       setBlendState(state.fragOps, postCommonBlendState);
+
+       if (postCommonBlendState.enableBlend)
+               state.fragOps.blendMode = (*(postCommonBlendState.enableBlend) ? state.fragOps.blendMode : rr::BLENDMODE_NONE);
+       else  if (info.getBlendState().enableBlend)
+               state.fragOps.blendMode = (*(info.getBlendState().enableBlend) ? state.fragOps.blendMode : rr::BLENDMODE_NONE);
+       else if (preCommonBlendState.enableBlend)
+               state.fragOps.blendMode = (*(preCommonBlendState.enableBlend) ? state.fragOps.blendMode : rr::BLENDMODE_NONE);
+       else
+               state.fragOps.blendMode = rr::BLENDMODE_NONE;
+
+       if (tcu::getTextureChannelClass(info.getFormat().type) != tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT && tcu::getTextureChannelClass(info.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT)
+               state.fragOps.blendMode = rr::BLENDMODE_NONE;
+
+       return state;
+}
+
+class VertexShader : public rr::VertexShader
+{
+public:
+                                       VertexShader    (void);
+       virtual void    shadeVertices   (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const;
+};
+
+VertexShader::VertexShader (void)
+       : rr::VertexShader      (1, 1)
+{
+       m_inputs[0].type        = rr::GENERICVECTYPE_FLOAT;
+       m_outputs[0].type       = rr::GENERICVECTYPE_FLOAT;
+}
+
+void VertexShader::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]       = 0.5f * (Vec4(1.0f) + rr::readVertexAttribFloat(inputs[0], packet.instanceNdx, packet.vertexNdx));
+       }
+}
+
+class FragmentShader : public rr::FragmentShader
+{
+public:
+                       FragmentShader  (int drawBufferNdx, const DrawBufferInfo& info);
+       void    shadeFragments  (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const;
+
+private:
+       const int                               m_drawBufferNdx;
+       const DrawBufferInfo    m_info;
+};
+
+FragmentShader::FragmentShader (int drawBufferNdx, const DrawBufferInfo& info)
+       : rr::FragmentShader    (1, 1)
+       , m_drawBufferNdx               (drawBufferNdx)
+       , m_info                                (info)
+{
+       m_inputs[0].type = rr::GENERICVECTYPE_FLOAT;
+
+       switch (tcu::getTextureChannelClass(m_info.getFormat().type))
+       {
+               case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
+               case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
+               case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
+                       m_outputs[0].type = rr::GENERICVECTYPE_FLOAT;
+                       break;
+
+               case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
+                       m_outputs[0].type = rr::GENERICVECTYPE_UINT32;
+                       break;
+
+               case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
+                       m_outputs[0].type = rr::GENERICVECTYPE_INT32;
+                       break;
+
+               default:
+                       DE_ASSERT(false);
+       };
+}
+
+void FragmentShader::shadeFragments (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const
+{
+       for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
+       {
+               rr::FragmentPacket& packet = packets[packetNdx];
+
+               DE_ASSERT(m_drawBufferNdx >= 0);
+               DE_UNREF(m_info);
+
+               for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
+               {
+                       const Vec2      vColor          = rr::readVarying<float>(packet, context, 0, fragNdx).xy();
+                       const float     values[]        =
+                       {
+                               vColor.x(),
+                               vColor.y(),
+                               (1.0f - vColor.x()),
+                               (1.0f - vColor.y())
+                       };
+
+                       switch (tcu::getTextureChannelClass(m_info.getFormat().type))
+                       {
+                               case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
+                               case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
+                               case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
+                               {
+                                       const Vec4 color (values[(m_drawBufferNdx + 0) % 4],
+                                                                         values[(m_drawBufferNdx + 1) % 4],
+                                                                         values[(m_drawBufferNdx + 2) % 4],
+                                                                         values[(m_drawBufferNdx + 3) % 4]);
+
+                                       rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, color);
+                                       break;
+                               }
+
+                               case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
+                               {
+                                       const UVec4 color ((deUint32)(values[(m_drawBufferNdx + 0) % 4]),
+                                                                          (deUint32)(values[(m_drawBufferNdx + 1) % 4]),
+                                                                          (deUint32)(values[(m_drawBufferNdx + 2) % 4]),
+                                                                          (deUint32)(values[(m_drawBufferNdx + 3) % 4]));
+
+                                       rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, color);
+                                       break;
+                               }
+
+                               case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
+                               {
+                                       const IVec4 color ((deInt32)(values[(m_drawBufferNdx + 0) % 4]),
+                                                                          (deInt32)(values[(m_drawBufferNdx + 1) % 4]),
+                                                                          (deInt32)(values[(m_drawBufferNdx + 2) % 4]),
+                                                                          (deInt32)(values[(m_drawBufferNdx + 3) % 4]));
+
+                                       rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, color);
+                                       break;
+                               }
+
+                               default:
+                                       DE_ASSERT(DE_FALSE);
+                       };
+               }
+       }
+}
+
+rr::VertexAttrib createVertexAttrib (const float* coords)
+{
+       rr::VertexAttrib attrib;
+
+       attrib.type             = rr::VERTEXATTRIBTYPE_FLOAT;
+       attrib.size             = 2;
+       attrib.pointer  = coords;
+
+       return attrib;
+}
+
+void renderRefQuad (const BlendState&                          preCommonBlendState,
+                                       const BlendState&                               postCommonBlendState,
+                                       const vector<DrawBufferInfo>&   drawBuffers,
+                                       vector<TextureLevel>&                   refRenderbuffers)
+{
+       const rr::Renderer                      renderer;
+       const rr::PrimitiveList         primitives              (rr::PRIMITIVETYPE_TRIANGLES, 6, 0);
+       const rr::VertexAttrib          vertexAttribs[] =
+       {
+               createVertexAttrib(s_quadCoords)
+       };
+
+       for (int drawBufferNdx = 0; drawBufferNdx < (int)drawBuffers.size(); drawBufferNdx++)
+       {
+               if (drawBuffers[drawBufferNdx].getRender())
+               {
+                       const rr::RenderState   renderState             (createRenderState(preCommonBlendState, postCommonBlendState, drawBuffers[drawBufferNdx]));
+                       const rr::RenderTarget  renderTarget    (rr::MultisamplePixelBufferAccess::fromSinglesampleAccess(refRenderbuffers[drawBufferNdx].getAccess()));
+                       const VertexShader              vertexShader;
+                       const FragmentShader    fragmentShader  (drawBufferNdx, drawBuffers[drawBufferNdx]);
+                       const rr::Program               program                 (&vertexShader, &fragmentShader);
+                       const rr::DrawCommand   command                 (renderState, renderTarget, program, DE_LENGTH_OF_ARRAY(vertexAttribs), vertexAttribs, primitives);
+
+                       renderer.draw(command);
+               }
+       }
+}
+
+bool requiresAdvancedBlendEq (const BlendState& pre, const BlendState post, const vector<DrawBufferInfo>& drawBuffers)
+{
+       bool requiresAdvancedBlendEq = false;
+
+       if (pre.blendEq && pre.blendEq->is<BlendEq>())
+               requiresAdvancedBlendEq |= isAdvancedBlendEq(pre.blendEq->get<BlendEq>());
+
+       if (post.blendEq && post.blendEq->is<BlendEq>())
+               requiresAdvancedBlendEq |= isAdvancedBlendEq(post.blendEq->get<BlendEq>());
+
+       for (int drawBufferNdx = 0; drawBufferNdx < (int)drawBuffers.size(); drawBufferNdx++)
+       {
+               const BlendState& drawBufferBlendState = drawBuffers[drawBufferNdx].getBlendState();
+
+               if (drawBufferBlendState.blendEq && drawBufferBlendState.blendEq->is<BlendEq>())
+                       requiresAdvancedBlendEq |= isAdvancedBlendEq(drawBufferBlendState.blendEq->get<BlendEq>());
+       }
+
+       return requiresAdvancedBlendEq;
+}
+
+glu::VertexSource genVertexSource (void)
+{
+       const char* const vertexSource =
+               "#version 310 es\n"
+               "layout(location=0) in highp vec2 i_coord;\n"
+               "out highp vec2 v_color;\n"
+               "void main (void)\n"
+               "{\n"
+               "\tv_color = 0.5 * (vec2(1.0) + i_coord);\n"
+               "\tgl_Position = vec4(i_coord, 0.0, 1.0);\n"
+               "}";
+
+       return glu::VertexSource(vertexSource);
+}
+
+glu::FragmentSource genFragmentSource (const BlendState& preCommonBlendState, const BlendState& postCommonBlendState, const vector<DrawBufferInfo>& drawBuffers)
+{
+       std::ostringstream stream;
+
+       stream << "#version 310 es\n";
+
+       if (requiresAdvancedBlendEq(preCommonBlendState, postCommonBlendState, drawBuffers))
+       {
+               stream << "#extension GL_KHR_blend_equation_advanced : require\n"
+                          <<  "layout(blend_support_all_equations) out;\n";
+       }
+
+       stream << "in highp vec2 v_color;\n";
+
+       for (int drawBufferNdx = 0; drawBufferNdx < (int)drawBuffers.size(); drawBufferNdx++)
+       {
+               const DrawBufferInfo&   drawBuffer                      = drawBuffers[drawBufferNdx];
+               const TextureFormat&    format                          = drawBuffer.getFormat();
+
+               stream << "layout(location=" << drawBufferNdx << ") out highp ";
+
+               switch (tcu::getTextureChannelClass(format.type))
+               {
+                       case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
+                       case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
+                       case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
+                               stream << "vec4";
+                               break;
+
+                       case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
+                               stream << "uvec4";
+                               break;
+
+                       case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
+                               stream << "ivec4";
+                               break;
+
+                       default:
+                               DE_ASSERT(DE_FALSE);
+               };
+
+               stream << " o_drawBuffer" <<  drawBufferNdx << ";\n";
+       }
+
+       stream << "void main (void)\n"
+                  << "{\n";
+
+       for (int drawBufferNdx = 0; drawBufferNdx < (int)drawBuffers.size(); drawBufferNdx++)
+       {
+               const DrawBufferInfo&   drawBuffer              = drawBuffers[drawBufferNdx];
+               const TextureFormat&    format                  = drawBuffer.getFormat();
+               const char* const               values[]                =
+               {
+                       "v_color.x",
+                       "v_color.y",
+                       "(1.0 - v_color.x)",
+                       "(1.0 - v_color.y)"
+               };
+
+               stream << "\to_drawBuffer" <<  drawBufferNdx;
+
+               switch (tcu::getTextureChannelClass(format.type))
+               {
+                       case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
+                       case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
+                       case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
+                               stream << " = vec4(" << values[(drawBufferNdx + 0) % 4]
+                                          << ", " << values[(drawBufferNdx + 1) % 4]
+                                          << ", " << values[(drawBufferNdx + 2) % 4]
+                                          << ", " << values[(drawBufferNdx + 3) % 4] << ");\n";
+                               break;
+
+                       case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
+                               stream << " = uvec4(uint(" << values[(drawBufferNdx + 0) % 4]
+                                          << "), uint(" << values[(drawBufferNdx + 1) % 4]
+                                          << "), uint(" << values[(drawBufferNdx + 2) % 4]
+                                          << "), uint(" << values[(drawBufferNdx + 3) % 4] << "));\n";
+                               break;
+
+                       case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
+                               stream << " = ivec4(int(" << values[(drawBufferNdx + 0) % 4]
+                                          << "), int(" << values[(drawBufferNdx + 1) % 4]
+                                          << "), int(" << values[(drawBufferNdx + 2) % 4]
+                                          << "), int(" << values[(drawBufferNdx + 3) % 4] << "));\n";
+                               break;
+
+                       default:
+                               DE_ASSERT(DE_FALSE);
+               };
+       }
+
+       stream << "}";
+
+       return glu::FragmentSource(stream.str());
+}
+
+glu::ProgramSources genShaderSources (const BlendState& preCommonBlendState, const BlendState& postCommonBlendState, const vector<DrawBufferInfo>& drawBuffers)
+{
+       return glu::ProgramSources() << genVertexSource() << genFragmentSource(preCommonBlendState, postCommonBlendState, drawBuffers);
+}
+
+void renderGLQuad (glu::RenderContext&                 renderContext,
+                                  const glu::ShaderProgram&    program)
+{
+       const glu::VertexArrayBinding vertexArrays[] =
+       {
+               glu::VertexArrayBinding(glu::BindingPoint(0), glu::VertexArrayPointer(glu::VTX_COMP_FLOAT, glu::VTX_COMP_CONVERT_NONE, 2, 6, 0, s_quadCoords))
+       };
+
+       glu::draw(renderContext, program.getProgram(), 1, vertexArrays, glu::pr::Triangles(6));
+}
+
+void renderQuad (TestLog&                                              log,
+                                glu::RenderContext&                    renderContext,
+                                const BlendState&                              preCommonBlendState,
+                                const BlendState&                              postCommonBlendState,
+                                const vector<DrawBufferInfo>&  drawBuffers,
+                                const glu::Framebuffer&                framebuffer,
+                                vector<TextureLevel>&                  refRenderbuffers)
+{
+       const glw::Functions&           gl                                              = renderContext.getFunctions();
+       const glu::ShaderProgram        program                                 (gl, genShaderSources(preCommonBlendState, postCommonBlendState, drawBuffers));
+       const IVec2                                     size                                    = drawBuffers[0].getSize();
+       const bool                                      requiresBlendBarriers   = requiresAdvancedBlendEq(preCommonBlendState, postCommonBlendState, drawBuffers);
+
+       vector<deUint32> bufs;
+
+       bufs.resize(drawBuffers.size());
+
+       for (int bufNdx = 0; bufNdx < (int)bufs.size(); bufNdx++)
+               bufs[bufNdx] = (drawBuffers[bufNdx].getRender() ? GL_COLOR_ATTACHMENT0 + bufNdx : GL_NONE);
+
+       log << program;
+
+       gl.viewport(0, 0, size.x(), size.y());
+       gl.useProgram(program.getProgram());
+       gl.bindFramebuffer(GL_FRAMEBUFFER, *framebuffer);
+
+       setCommonBlendState(gl, preCommonBlendState);
+
+       for (int renderbufferNdx = 0; renderbufferNdx < (int)drawBuffers.size(); renderbufferNdx++)
+               setIndexedBlendState(gl, drawBuffers[renderbufferNdx].getBlendState(), renderbufferNdx);
+
+       setCommonBlendState(gl, postCommonBlendState);
+
+       gl.drawBuffers(bufs.size(), &(bufs[0]));
+
+       if (requiresBlendBarriers)
+               gl.blendBarrierKHR();
+
+       renderGLQuad(renderContext, program);
+
+       if (requiresBlendBarriers)
+               gl.blendBarrierKHR();
+
+       gl.drawBuffers(0, 0);
+       gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
+       gl.useProgram(0);
+
+       GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to render");
+
+       renderRefQuad(preCommonBlendState, postCommonBlendState, drawBuffers, refRenderbuffers);
+}
+
+void logBlendState (TestLog&                   log,
+                                       const BlendState&       blend)
+{
+       if (blend.enableBlend)
+       {
+               if (*blend.enableBlend)
+                       log << TestLog::Message << "Enable blending." << TestLog::EndMessage;
+               else
+                       log << TestLog::Message << "Disable blending." << TestLog::EndMessage;
+       }
+
+       if (blend.colorMask)
+       {
+               const BVec4 mask = *blend.colorMask;
+
+               log << TestLog::Message << "Set color mask: " << mask << "." << TestLog::EndMessage;
+       }
+
+
+       if (blend.blendEq)
+       {
+               const Either<BlendEq, SeparateBlendEq>& blendEq = *blend.blendEq;
+
+               if (blendEq.is<BlendEq>())
+                       log << TestLog::Message << "Set blend equation: " << glu::getBlendEquationStr(blendEq.get<BlendEq>()) << "." << TestLog::EndMessage;
+               else if (blendEq.is<SeparateBlendEq>())
+                       log << TestLog::Message << "Set blend equation rgb: " << glu::getBlendEquationStr(blendEq.get<SeparateBlendEq>().rgb) << ", alpha: " << glu::getBlendEquationStr(blendEq.get<SeparateBlendEq>().alpha) << "." << TestLog::EndMessage;
+               else
+                       DE_ASSERT(false);
+       }
+
+       if (blend.blendFunc)
+       {
+               const Either<BlendFunc, SeparateBlendFunc>& blendFunc = *blend.blendFunc;
+
+               if (blendFunc.is<BlendFunc>())
+                       log << TestLog::Message << "Set blend function source: " << glu::getBlendFactorStr(blendFunc.get<BlendFunc>().src) << ", destination: " << glu::getBlendFactorStr(blendFunc.get<BlendFunc>().dst) << "." << TestLog::EndMessage;
+               else if (blendFunc.is<SeparateBlendFunc>())
+               {
+                       log << TestLog::Message << "Set blend function rgb source: " << glu::getBlendFactorStr(blendFunc.get<SeparateBlendFunc>().rgb.src) << ", destination: " << glu::getBlendFactorStr(blendFunc.get<SeparateBlendFunc>().rgb.dst) << "." << TestLog::EndMessage;
+                       log << TestLog::Message << "Set blend function alpha source: " << glu::getBlendFactorStr(blendFunc.get<SeparateBlendFunc>().alpha.src) << ", destination: " << glu::getBlendFactorStr(blendFunc.get<SeparateBlendFunc>().alpha.dst) << "." << TestLog::EndMessage;
+               }
+               else
+                       DE_ASSERT(false);
+       }
+}
+
+void logTestCaseInfo (TestLog&                                         log,
+                                         const BlendState&                             preCommonBlendState,
+                                         const BlendState&                             postCommonBlendState,
+                                         const vector<DrawBufferInfo>& drawBuffers)
+{
+       {
+               tcu::ScopedLogSection drawBuffersSection(log, "DrawBuffers", "Draw buffers");
+
+               for (int drawBufferNdx = 0; drawBufferNdx < (int)drawBuffers.size(); drawBufferNdx++)
+               {
+                       const tcu::ScopedLogSection     drawBufferSection       (log, "DrawBuffer" + de::toString(drawBufferNdx), "Draw Buffer " + de::toString(drawBufferNdx));
+                       const DrawBufferInfo&           drawBuffer                      = drawBuffers[drawBufferNdx];
+
+                       log << TestLog::Message << "Format: " << drawBuffer.getFormat() << TestLog::EndMessage;
+                       log << TestLog::Message << "Size: " << drawBuffer.getSize() << TestLog::EndMessage;
+                       log << TestLog::Message << "Render: " << (drawBuffer.getRender() ? "true" : "false") << TestLog::EndMessage;
+               }
+       }
+
+       if (!preCommonBlendState.isEmpty())
+       {
+               tcu::ScopedLogSection s(log, "PreCommonState", "First set common blend state");
+               logBlendState(log, preCommonBlendState);
+       }
+
+       for (int drawBufferNdx = 0; drawBufferNdx < (int)drawBuffers.size(); drawBufferNdx++)
+       {
+               if (!drawBuffers[drawBufferNdx].getBlendState().isEmpty())
+               {
+                       const tcu::ScopedLogSection s(log, "DrawBufferState" + de::toString(drawBufferNdx), "Set DrawBuffer " + de::toString(drawBufferNdx) + " state to");
+
+                       logBlendState(log, drawBuffers[drawBufferNdx].getBlendState());
+               }
+       }
+
+       if (!postCommonBlendState.isEmpty())
+       {
+               tcu::ScopedLogSection s(log, "PostCommonState", "After set common blend state");
+               logBlendState(log, postCommonBlendState);
+       }
+}
+
+void runTest (TestLog&                                         log,
+                         tcu::ResultCollector&                 results,
+                         glu::RenderContext&                   renderContext,
+
+                         const BlendState&                             preCommonBlendState,
+                         const BlendState&                             postCommonBlendState,
+                         const vector<DrawBufferInfo>& drawBuffers)
+{
+       const glw::Functions&   gl                                      = renderContext.getFunctions();
+       glu::RenderbufferVector renderbuffers           (gl, drawBuffers.size());
+       glu::Framebuffer                framebuffer                     (gl);
+       vector<TextureLevel>    refRenderbuffers        (drawBuffers.size());
+
+       logTestCaseInfo(log, preCommonBlendState, postCommonBlendState, drawBuffers);
+
+       genRenderbuffers(gl, drawBuffers, framebuffer, renderbuffers, refRenderbuffers);
+
+       renderQuad(log, renderContext, preCommonBlendState, postCommonBlendState, drawBuffers, framebuffer, refRenderbuffers);
+
+       verifyRenderbuffers(log, results, renderContext, renderbuffers, framebuffer, refRenderbuffers);
+}
+
+class DrawBuffersIndexedTest : public TestCase
+{
+public:
+                                       DrawBuffersIndexedTest (Context&                                                context,
+                                                                                       const BlendState&                               preCommonBlendState,
+                                                                                       const BlendState&                               postCommonBlendState,
+                                                                                       const vector<DrawBufferInfo>&   drawBuffers,
+                                                                                       const string&                                   name,
+                                                                                       const string&                                   description);
+
+       void                    init                                    (void);
+       IterateResult   iterate                                 (void);
+
+private:
+       const BlendState                                m_preCommonBlendState;
+       const BlendState                                m_postCommonBlendState;
+       const vector<DrawBufferInfo>    m_drawBuffers;
+};
+
+DrawBuffersIndexedTest::DrawBuffersIndexedTest (Context&                                               context,
+                                                                                               const BlendState&                               preCommonBlendState,
+                                                                                               const BlendState&                               postCommonBlendState,
+                                                                                               const vector<DrawBufferInfo>&   drawBuffers,
+                                                                                               const string&                                   name,
+                                                                                               const string&                                   description)
+       : TestCase                                      (context, name.c_str(), description.c_str())
+       , m_preCommonBlendState         (preCommonBlendState)
+       , m_postCommonBlendState        (postCommonBlendState)
+       , m_drawBuffers                         (drawBuffers)
+{
+}
+
+void DrawBuffersIndexedTest::init (void)
+{
+       if (requiresAdvancedBlendEq(m_preCommonBlendState, m_postCommonBlendState, m_drawBuffers) && !m_context.getContextInfo().isExtensionSupported("GL_KHR_blend_equation_advanced"))
+               throw tcu::NotSupportedError("Extension GL_KHR_blend_equation_advanced not supported", "", __FILE__, __LINE__);
+
+       if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_draw_buffers_indexed"))
+               throw tcu::NotSupportedError("Extension GL_EXT_draw_buffers_indexed not supported", "", __FILE__, __LINE__);
+}
+
+TestCase::IterateResult DrawBuffersIndexedTest::iterate (void)
+{
+       TestLog&                                log             = m_testCtx.getLog();
+       tcu::ResultCollector    results (log);
+
+       runTest(log, results, m_context.getRenderContext(), m_preCommonBlendState, m_postCommonBlendState, m_drawBuffers);
+
+       results.setTestContextResult(m_testCtx);
+
+       return STOP;
+}
+
+BlendEq getRandomBlendEq (de::Random& rng)
+{
+       const BlendEq eqs[] =
+       {
+               GL_FUNC_ADD,
+               GL_FUNC_SUBTRACT,
+               GL_FUNC_REVERSE_SUBTRACT,
+               GL_MIN,
+               GL_MAX
+       };
+
+       return de::getSizedArrayElement<DE_LENGTH_OF_ARRAY(eqs)>(eqs, rng.getUint32() % DE_LENGTH_OF_ARRAY(eqs));
+}
+
+BlendFunc getRandomBlendFunc (de::Random& rng)
+{
+       const deUint32 funcs[] =
+       {
+               GL_ZERO,
+               GL_ONE,
+               GL_SRC_COLOR,
+               GL_ONE_MINUS_SRC_COLOR,
+               GL_DST_COLOR,
+               GL_ONE_MINUS_DST_COLOR,
+               GL_SRC_ALPHA,
+               GL_ONE_MINUS_SRC_ALPHA,
+               GL_DST_ALPHA,
+               GL_ONE_MINUS_DST_ALPHA,
+               GL_CONSTANT_COLOR,
+               GL_ONE_MINUS_CONSTANT_COLOR,
+               GL_CONSTANT_ALPHA,
+               GL_ONE_MINUS_CONSTANT_ALPHA,
+               GL_SRC_ALPHA_SATURATE
+       };
+
+       const deUint32 src = de::getSizedArrayElement<DE_LENGTH_OF_ARRAY(funcs)>(funcs, rng.getUint32() % DE_LENGTH_OF_ARRAY(funcs));
+       const deUint32 dst = de::getSizedArrayElement<DE_LENGTH_OF_ARRAY(funcs)>(funcs, rng.getUint32() % DE_LENGTH_OF_ARRAY(funcs));
+
+       return BlendFunc(src, dst);
+}
+
+void genRandomBlendState (de::Random& rng, BlendState& blendState)
+{
+       if (rng.getBool())
+               blendState.enableBlend = rng.getBool();
+
+       if (rng.getBool())
+       {
+               if (rng.getBool())
+                       blendState.blendEq = getRandomBlendEq(rng);
+               else
+               {
+                       const BlendEq   rgb             = getRandomBlendEq(rng);
+                       const BlendEq   alpha   = getRandomBlendEq(rng);
+
+                       blendState.blendEq              = SeparateBlendEq(rgb, alpha);
+               }
+       }
+
+       if (rng.getBool())
+       {
+               if (rng.getBool())
+                       blendState.blendFunc = getRandomBlendFunc(rng);
+               else
+               {
+                       const BlendFunc rgb             = getRandomBlendFunc(rng);
+                       const BlendFunc alpha   = getRandomBlendFunc(rng);
+
+                       blendState.blendFunc    = SeparateBlendFunc(rgb, alpha);
+               }
+       }
+
+       if (rng.getBool())
+       {
+               const bool red          = rng.getBool();
+               const bool green        = rng.getBool();
+               const bool blue         = rng.getBool();
+               const bool alpha        = rng.getBool();
+
+               blendState.colorMask = BVec4(red, blue, green, alpha);
+       }
+}
+
+TextureFormat getRandomFormat (de::Random& rng)
+{
+       const deUint32 glFormats[] =
+       {
+               GL_R8,
+               GL_RG8,
+               GL_RGB8,
+               GL_RGB565,
+               GL_RGBA4,
+               GL_RGB5_A1,
+               GL_RGBA8,
+               GL_RGB10_A2,
+               GL_RGB10_A2UI,
+               GL_R8I,
+               GL_R8UI,
+               GL_R16I,
+               GL_R16UI,
+               GL_R32I,
+               GL_R32UI,
+               GL_RG8I,
+               GL_RG8UI,
+               GL_RG16I,
+               GL_RG16UI,
+               GL_RG32I,
+               GL_RG32UI,
+               GL_RGBA8I,
+               GL_RGBA8UI,
+               GL_RGBA16I,
+               GL_RGBA16UI,
+               GL_RGBA32I,
+               GL_RGBA32UI
+       };
+
+       return glu::mapGLInternalFormat(de::getSizedArrayElement(glFormats, rng.getUint32() % DE_LENGTH_OF_ARRAY(glFormats)));
+}
+
+void genRandomTest (de::Random& rng, BlendState& preCommon, BlendState& postCommon, vector<DrawBufferInfo>& drawBuffers, int maxDrawBufferCount)
+{
+       genRandomBlendState(rng, preCommon);
+       genRandomBlendState(rng, postCommon);
+
+       for (int drawBufferNdx = 0; drawBufferNdx < maxDrawBufferCount; drawBufferNdx++)
+       {
+               const bool                      render          = rng.getFloat() > 0.1f;
+               const IVec2                     size            (64, 64);
+               const TextureFormat     format          (getRandomFormat(rng));
+               BlendState                      blendState;
+
+               genRandomBlendState(rng, blendState);
+               drawBuffers.push_back(DrawBufferInfo(render, size, blendState, format));
+       }
+}
+
+class MaxDrawBuffersIndexedTest : public TestCase
+{
+public:
+                                       MaxDrawBuffersIndexedTest       (Context& contet, int seed);
+
+       void                    init                                            (void);
+       IterateResult   iterate                                         (void);
+
+private:
+       const int               m_seed;
+};
+
+MaxDrawBuffersIndexedTest::MaxDrawBuffersIndexedTest (Context& context, int seed)
+       : TestCase      (context, de::toString(seed).c_str(), de::toString(seed).c_str())
+       , m_seed        (deInt32Hash(seed) ^ 1558001307u)
+{
+}
+
+void MaxDrawBuffersIndexedTest::init (void)
+{
+       if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_draw_buffers_indexed"))
+               throw tcu::NotSupportedError("Extension GL_EXT_draw_buffers_indexed not supported", "", __FILE__, __LINE__);
+}
+
+TestCase::IterateResult MaxDrawBuffersIndexedTest::iterate (void)
+{
+       TestLog&                                log                                             = m_testCtx.getLog();
+       tcu::ResultCollector    results                                 (log);
+       de::Random                              rng                                             (m_seed);
+       BlendState                              preCommonBlendState;
+       BlendState                              postCommonBlendState;
+       vector<DrawBufferInfo>  drawBuffers;
+
+       genRandomTest(rng, preCommonBlendState, postCommonBlendState, drawBuffers, 4);
+
+       runTest(log, results, m_context.getRenderContext(), preCommonBlendState, postCommonBlendState, drawBuffers);
+
+       results.setTestContextResult(m_testCtx);
+
+       return STOP;
+}
+
+class ImplMaxDrawBuffersIndexedTest : public TestCase
+{
+public:
+                                       ImplMaxDrawBuffersIndexedTest   (Context& contet, int seed);
+
+       void                    init                                                    (void);
+       IterateResult   iterate                                                 (void);
+
+private:
+       const int               m_seed;
+};
+
+ImplMaxDrawBuffersIndexedTest::ImplMaxDrawBuffersIndexedTest (Context& context, int seed)
+       : TestCase      (context, de::toString(seed).c_str(), de::toString(seed).c_str())
+       , m_seed        (deInt32Hash(seed) ^ 2686315738u)
+{
+}
+
+void ImplMaxDrawBuffersIndexedTest::init (void)
+{
+       if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_draw_buffers_indexed"))
+               throw tcu::NotSupportedError("Extension GL_EXT_draw_buffers_indexed not supported", "", __FILE__, __LINE__);
+}
+
+TestCase::IterateResult ImplMaxDrawBuffersIndexedTest::iterate (void)
+{
+       TestLog&                                log                                             = m_testCtx.getLog();
+       tcu::ResultCollector    results                                 (log);
+       const glw::Functions&   gl                                              = m_context.getRenderContext().getFunctions();
+       de::Random                              rng                                             (m_seed);
+       deInt32                                 maxDrawBuffers                  = 0;
+       BlendState                              preCommonBlendState;
+       BlendState                              postCommonBlendState;
+       vector<DrawBufferInfo>  drawBuffers;
+
+       gl.getIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
+       GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv(GL_MAX_DRAW_BUFFERS) failed");
+
+       TCU_CHECK(maxDrawBuffers > 0);
+
+       genRandomTest(rng, preCommonBlendState, postCommonBlendState, drawBuffers, maxDrawBuffers);
+
+       runTest(log, results, m_context.getRenderContext(), preCommonBlendState, postCommonBlendState, drawBuffers);
+
+       results.setTestContextResult(m_testCtx);
+
+       return STOP;
+}
+
+enum PrePost
+{
+       PRE,
+       POST
+};
+
+TestCase* createDiffTest (Context& context, PrePost prepost, const char* name, const BlendState& commonState, const BlendState& drawBufferState)
+{
+       const BlendState emptyState = BlendState(tcu::nothing<bool>(), tcu::nothing<Either<BlendEq, SeparateBlendEq> >(), tcu::nothing<Either<BlendFunc, SeparateBlendFunc> >(), tcu::nothing<BVec4>());
+
+       if (prepost == PRE)
+       {
+               const BlendState                preState        = BlendState((commonState.enableBlend ? commonState.enableBlend : just(true)),
+                                                                                                                commonState.blendEq,
+                                                                                                                (commonState.blendFunc ? commonState.blendFunc : just(Either<BlendFunc, SeparateBlendFunc>(BlendFunc(GL_ONE, GL_ONE)))),
+                                                                                                                tcu::nothing<BVec4>());
+               vector<DrawBufferInfo>  drawBuffers;
+
+               drawBuffers.push_back(DrawBufferInfo(true, IVec2(64, 64), emptyState, TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8)));
+               drawBuffers.push_back(DrawBufferInfo(true, IVec2(64, 64), drawBufferState, TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8)));
+
+               return new DrawBuffersIndexedTest(context, preState, emptyState, drawBuffers, name, name);
+       }
+       else if (prepost == POST)
+       {
+               const BlendState                preState        = BlendState(just(true),
+                                                                                                                tcu::nothing<Either<BlendEq, SeparateBlendEq> >(),
+                                                                                                                Maybe<Either<BlendFunc, SeparateBlendFunc> >(BlendFunc(GL_ONE, GL_ONE)),
+                                                                                                                tcu::nothing<BVec4>());
+               vector<DrawBufferInfo>  drawBuffers;
+
+               drawBuffers.push_back(DrawBufferInfo(true, IVec2(64, 64), emptyState, TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8)));
+               drawBuffers.push_back(DrawBufferInfo(true, IVec2(64, 64), drawBufferState, TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8)));
+
+               return new DrawBuffersIndexedTest(context, preState, commonState, drawBuffers, name, name);
+       }
+       else
+       {
+               DE_ASSERT(false);
+               return DE_NULL;
+       }
+}
+
+TestCase* createAdvancedEqDiffTest (Context& context, PrePost prepost, const char* name, const BlendState& commonState, const BlendState& drawBufferState)
+{
+       const BlendState emptyState = BlendState(tcu::nothing<bool>(), tcu::nothing<Either<BlendEq, SeparateBlendEq> >(), tcu::nothing<Either<BlendFunc, SeparateBlendFunc> >(), tcu::nothing<BVec4>());
+
+       if (prepost == PRE)
+       {
+               const BlendState                preState        = BlendState((commonState.enableBlend ? commonState.enableBlend : just(true)),
+                                                                                                                commonState.blendEq,
+                                                                                                                (commonState.blendFunc ? commonState.blendFunc : just(Either<BlendFunc, SeparateBlendFunc>(BlendFunc(GL_ONE, GL_ONE)))),
+                                                                                                                tcu::nothing<BVec4>());
+               vector<DrawBufferInfo>  drawBuffers;
+
+               drawBuffers.push_back(DrawBufferInfo(true, IVec2(64, 64), drawBufferState, TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8)));
+
+               return new DrawBuffersIndexedTest(context, preState, emptyState, drawBuffers, name, name);
+       }
+       else if (prepost == POST)
+       {
+               const BlendState                preState        = BlendState(just(true),
+                                                                                                                tcu::nothing<Either<BlendEq, SeparateBlendEq> >(),
+                                                                                                                Maybe<Either<BlendFunc, SeparateBlendFunc> >(BlendFunc(GL_ONE, GL_ONE)),
+                                                                                                                tcu::nothing<BVec4>());
+               vector<DrawBufferInfo>  drawBuffers;
+
+               drawBuffers.push_back(DrawBufferInfo(true, IVec2(64, 64), drawBufferState, TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8)));
+
+               return new DrawBuffersIndexedTest(context, preState, commonState, drawBuffers, name, name);
+       }
+       else
+       {
+               DE_ASSERT(false);
+               return DE_NULL;
+       }
+}
+
+void addDrawBufferCommonTests (TestCaseGroup* root, PrePost prepost)
+{
+       const BlendState                emptyState      = BlendState(Maybe<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
+
+       {
+               const BlendState        disableState    = BlendState(just(false), Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
+               const BlendState        enableState             = BlendState(just(true), Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
+
+               root->addChild(createDiffTest(root->getContext(), prepost, "common_enable_buffer_enable",       enableState,    enableState));
+               root->addChild(createDiffTest(root->getContext(), prepost, "common_disable_buffer_disable",     disableState,   disableState));
+               root->addChild(createDiffTest(root->getContext(), prepost, "common_disable_buffer_enable",      disableState,   enableState));
+               root->addChild(createDiffTest(root->getContext(), prepost, "common_enable_buffer_disable",      enableState,    disableState));
+       }
+
+       {
+               const BlendState        eqStateA                        = BlendState(tcu::nothing<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(GL_FUNC_ADD), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
+               const BlendState        eqStateB                        = BlendState(tcu::nothing<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(GL_FUNC_SUBTRACT), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
+
+               const BlendState        separateEqStateA        = BlendState(tcu::nothing<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(SeparateBlendEq(GL_FUNC_ADD, GL_FUNC_SUBTRACT)), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
+               const BlendState        separateEqStateB        = BlendState(tcu::nothing<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(SeparateBlendEq(GL_FUNC_SUBTRACT, GL_FUNC_ADD)), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
+
+               const BlendState        advancedEqStateA        = BlendState(tcu::nothing<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(GL_DIFFERENCE_KHR), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
+               const BlendState        advancedEqStateB        = BlendState(tcu::nothing<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(GL_SCREEN_KHR), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
+
+               root->addChild(createDiffTest(root->getContext(), prepost, "common_blend_eq_buffer_blend_eq", eqStateA, eqStateB));
+               root->addChild(createDiffTest(root->getContext(), prepost, "common_blend_eq_buffer_separate_blend_eq", eqStateA, separateEqStateB));
+               root->addChild(createAdvancedEqDiffTest(root->getContext(), prepost, "common_blend_eq_buffer_advanced_blend_eq", eqStateA, advancedEqStateB));
+
+               root->addChild(createDiffTest(root->getContext(), prepost, "common_separate_blend_eq_buffer_blend_eq", separateEqStateA, eqStateB));
+               root->addChild(createDiffTest(root->getContext(), prepost, "common_separate_blend_eq_buffer_separate_blend_eq", separateEqStateA, separateEqStateB));
+               root->addChild(createAdvancedEqDiffTest(root->getContext(), prepost, "common_separate_blend_eq_buffer_advanced_blend_eq", separateEqStateA, advancedEqStateB));
+
+               root->addChild(createAdvancedEqDiffTest(root->getContext(), prepost, "common_advanced_blend_eq_buffer_blend_eq", advancedEqStateA, eqStateB));
+               root->addChild(createAdvancedEqDiffTest(root->getContext(), prepost, "common_advanced_blend_eq_buffer_separate_blend_eq", advancedEqStateA, separateEqStateB));
+               root->addChild(createAdvancedEqDiffTest(root->getContext(), prepost, "common_advanced_blend_eq_buffer_advanced_blend_eq", advancedEqStateA, advancedEqStateB));
+       }
+
+       {
+               const BlendState        funcStateA                      = BlendState(tcu::nothing<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(BlendFunc(GL_SRC_ALPHA, GL_DST_ALPHA)), Maybe<BVec4>());
+               const BlendState        funcStateB                      = BlendState(tcu::nothing<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(BlendFunc(GL_DST_ALPHA, GL_SRC_ALPHA)), Maybe<BVec4>());
+               const BlendState        separateFuncStateA      = BlendState(tcu::nothing<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(SeparateBlendFunc(BlendFunc(GL_SRC_ALPHA, GL_DST_ALPHA), BlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA))), Maybe<BVec4>());
+               const BlendState        separateFuncStateB      = BlendState(tcu::nothing<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(SeparateBlendFunc(BlendFunc(GL_DST_ALPHA, GL_SRC_ALPHA), BlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA))), Maybe<BVec4>());
+
+               root->addChild(createDiffTest(root->getContext(), prepost, "common_blend_func_buffer_blend_func",                                       funcStateA,                     funcStateB));
+               root->addChild(createDiffTest(root->getContext(), prepost, "common_blend_func_buffer_separate_blend_func",                      funcStateA,                     separateFuncStateB));
+               root->addChild(createDiffTest(root->getContext(), prepost, "common_separate_blend_func_buffer_blend_func",                      separateFuncStateA,     funcStateB));
+               root->addChild(createDiffTest(root->getContext(), prepost, "common_separate_blend_func_buffer_separate_blend_func",     separateFuncStateA,     separateFuncStateB));
+       }
+
+       {
+               const BlendState        commonColorMaskState    = BlendState(tcu::nothing<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>(BVec4(true, false, true, false)));
+               const BlendState        bufferColorMaskState    = BlendState(tcu::nothing<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>(BVec4(false, true, false, true)));
+
+               root->addChild(createDiffTest(root->getContext(), prepost, "common_color_mask_buffer_color_mask", commonColorMaskState, bufferColorMaskState));
+       }
+}
+
+void addRandomMaxTest (TestCaseGroup* root)
+{
+       for (int i = 0; i < 20; i++)
+               root->addChild(new MaxDrawBuffersIndexedTest(root->getContext(), i));
+}
+
+void addRandomImplMaxTest (TestCaseGroup* root)
+{
+       for (int i = 0; i < 20; i++)
+               root->addChild(new ImplMaxDrawBuffersIndexedTest(root->getContext(), i));
+}
+
+} // anonymous
+
+TestCaseGroup* createDrawBuffersIndexedTests (Context& context)
+{
+       const BlendState                emptyState              = BlendState(Maybe<bool>(), Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
+       TestCaseGroup* const    group                   = new TestCaseGroup(context, "draw_buffers_indexed", "Test for indexed draw buffers. GL_EXT_draw_buffers_indexed.");
+
+       TestCaseGroup* const    preGroup                = new TestCaseGroup(context, "overwrite_common", "Set common state and overwrite it with draw buffer blend state.");
+       TestCaseGroup* const    postGroup               = new TestCaseGroup(context, "overwrite_indexed", "Set indexed blend state and overwrite it ith common state.");
+       TestCaseGroup* const    randomGroup             = new TestCaseGroup(context, "random", "Random indexed blend state tests.");
+       TestCaseGroup* const    maxGroup                = new TestCaseGroup(context, "max_required_draw_buffers", "Random tests using minimum maximum number of draw buffers.");
+       TestCaseGroup* const    maxImplGroup    = new TestCaseGroup(context, "max_implementation_draw_buffers", "Random tests using maximum number of draw buffers reported by implementation.");
+
+       group->addChild(preGroup);
+       group->addChild(postGroup);
+       group->addChild(randomGroup);
+
+       randomGroup->addChild(maxGroup);
+       randomGroup->addChild(maxImplGroup);
+
+       addDrawBufferCommonTests(preGroup, PRE);
+       addDrawBufferCommonTests(postGroup, POST);
+       addRandomMaxTest(maxGroup);
+       addRandomImplMaxTest(maxImplGroup);
+
+       return group;
+}
+
+} // Functional
+} // gles31
+} // deqp
diff --git a/modules/gles31/functional/es31fDrawBuffersIndexedTests.hpp b/modules/gles31/functional/es31fDrawBuffersIndexedTests.hpp
new file mode 100644 (file)
index 0000000..5d716f9
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef _ES31FDRAWBUFFERSINDEXEDTESTS_HPP
+#define _ES31FDRAWBUFFERSINDEXEDTESTS_HPP
+/*-------------------------------------------------------------------------
+ * drawElements Quality Program OpenGL ES 3.1 Module
+ * -------------------------------------------------
+ *
+ * Copyright 2015 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 Indexed blend operation tests (GL_EXT_draw_buffers_indexed)
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tes31TestCase.hpp"
+
+namespace deqp
+{
+namespace gles31
+{
+namespace Functional
+{
+
+TestCaseGroup* createDrawBuffersIndexedTests (Context& context);
+
+} // Functional
+} // gles31
+} // deqp
+
+#endif // _ES31FDRAWBUFFERSINDEXEDTESTS_HPP
index 0a9b0b7..3720cc0 100644 (file)
@@ -85,6 +85,7 @@
 #include "es31fPrimitiveBoundingBoxTests.hpp"
 #include "es31fAndroidExtensionPackES31ATests.hpp"
 #include "es31fCopyImageTests.hpp"
+#include "es31fDrawBuffersIndexedTests.hpp"
 
 namespace deqp
 {
@@ -328,6 +329,7 @@ void FunctionalTests::init (void)
        addChild(new PrimitiveBoundingBoxTests                          (m_context));
        addChild(new AndroidExtensionPackES31ATests                     (m_context));
        addChild(createCopyImageTests                                           (m_context));
+       addChild(createDrawBuffersIndexedTests                          (m_context));
 }
 
 } // Functional