Merge vk-gl-cts/opengl-es-cts-3.2.8 into vk-gl-cts/main
authorMatthew Netsch <quic_mnetsch@quicinc.com>
Thu, 2 Jun 2022 21:22:12 +0000 (21:22 +0000)
committerMatthew Netsch <quic_mnetsch@quicinc.com>
Thu, 2 Jun 2022 21:22:12 +0000 (21:22 +0000)
Change-Id: I011f48b25cfd0f2b951425ea770220a6dcaa3e1a

1  2 
modules/gles3/functional/es3fDrawBuffersIndexedTests.cpp

index 7310de8,0000000..d6e3957
mode 100644,000000..100644
--- /dev/null
@@@ -1,1675 -1,0 +1,1687 @@@
-       return Vec4(3.0f) / ((tcu::Vector<deUint64, 4>(1) << (tcu::min(srcBits, readBits).cast<deUint64>())) - tcu::Vector<deUint64, 4>(1)).cast<float>();
 +/*-------------------------------------------------------------------------
 + * drawElements Quality Program OpenGL ES 3 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 "es3fDrawBuffersIndexedTests.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 "tcuStringTemplate.hpp"
 +#include "tcuTestLog.hpp"
 +#include "tcuTexture.hpp"
 +#include "tcuTextureUtil.hpp"
 +#include "tcuVector.hpp"
 +#include "tcuVectorUtil.hpp"
 +#include "tcuFloat.hpp"
 +
 +#include "deRandom.hpp"
 +#include "deArrayUtil.hpp"
 +#include "deStringUtil.hpp"
 +#include "deUniquePtr.hpp"
 +
 +#include "deInt32.h"
 +
 +#include <string>
 +#include <vector>
 +#include <map>
 +
 +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 std::map;
 +
 +using sglr::rr_util::mapGLBlendEquation;
 +using sglr::rr_util::mapGLBlendFunc;
 +using sglr::rr_util::mapGLBlendEquationAdvanced;
 +
 +namespace deqp
 +{
 +namespace gles3
 +{
 +namespace Functional
 +{
 +namespace
 +{
 +
 +typedef deUint32 BlendEq;
 +
 +bool isAdvancedBlendEq (BlendEq eq)
 +{
 +      switch (eq)
 +      {
 +              case GL_MULTIPLY:               return true;
 +              case GL_SCREEN:                 return true;
 +              case GL_OVERLAY:                return true;
 +              case GL_DARKEN:                 return true;
 +              case GL_LIGHTEN:                return true;
 +              case GL_COLORDODGE:             return true;
 +              case GL_COLORBURN:              return true;
 +              case GL_HARDLIGHT:              return true;
 +              case GL_SOFTLIGHT:              return true;
 +              case GL_DIFFERENCE:             return true;
 +              case GL_EXCLUSION:              return true;
 +              case GL_HSL_HUE:                return true;
 +              case GL_HSL_SATURATION: return true;
 +              case GL_HSL_COLOR:              return true;
 +              case GL_HSL_LUMINOSITY: 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;
 +};
 +
 +static bool checkES32orGL45Support(Context& ctx)
 +{
 +      auto ctxType = ctx.getRenderContext().getType();
 +      return contextSupports(ctxType, glu::ApiType::es(3, 2)) ||
 +                 contextSupports(ctxType, glu::ApiType::core(4, 5));
 +}
 +
 +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((glw::GLsizei)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);
 +
++      Vec4                            threshold       = Vec4(0.0f);
++
++      for (int i = 0; i < 4; i++)
++      {
++              const int bits = de::min(srcBits[i], readBits[i]);
++
++              if (bits > 0)
++              {
++                      threshold[i] = 3.0f / static_cast<float>(((1ul << bits) - 1ul));
++              }
++      }
++
++      return threshold;
 +}
 +
 +UVec4 getFloatULPThreshold (const tcu::TextureFormat& sourceFormat, const tcu::TextureFormat& readPixelsFormat)
 +{
 +      const tcu::IVec4        srcMantissaBits         = tcu::getTextureFormatMantissaBitDepth(sourceFormat);
 +      const tcu::IVec4        readMantissaBits        = tcu::getTextureFormatMantissaBitDepth(readPixelsFormat);
 +      tcu::IVec4                      ULPDiff(0);
 +
 +      for (int i = 0; i < 4; i++)
 +              if (readMantissaBits[i] >= srcMantissaBits[i])
 +                      ULPDiff[i] = readMantissaBits[i] - srcMantissaBits[i];
 +
 +      return UVec4(4) * (UVec4(1) << (ULPDiff.cast<deUint32>()));
 +}
 +
 +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       = getFloatULPThreshold(format, result.getFormat());
 +
 +                      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);
 +
 +              case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
 +                      return TextureFormat(TextureFormat::RGBA, TextureFormat::FLOAT);
 +
 +              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, int subpixelBits)
 +{
 +      const IVec2             size    = info.getSize();
 +      rr::RenderState state   (rr::ViewportState(rr::WindowRectangle(0, 0, size.x(), size.y())), subpixelBits);
 +
 +      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
 +              && tcu::getTextureChannelClass(info.getFormat().type) != tcu::TEXTURECHANNELCLASS_FLOATING_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,
 +                                      const int                                               subpixelBits,
 +                                      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], subpixelBits));
 +                      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 (glu::RenderContext& renderContext)
 +{
 +      const bool supportsES32 = glu::contextSupports(renderContext.getType(), glu::ApiType::es(3, 2));
 +
 +      const char* const vertexSource =
 +              "${GLSL_VERSION_DECL}\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"
 +              "}";
 +
 +      map<string, string> args;
 +      args["GLSL_VERSION_DECL"] = supportsES32 ? getGLSLVersionDeclaration(glu::GLSL_VERSION_320_ES) : getGLSLVersionDeclaration(glu::GLSL_VERSION_300_ES);
 +
 +      return glu::VertexSource(tcu::StringTemplate(vertexSource).specialize(args));
 +}
 +
 +glu::FragmentSource genFragmentSource (const BlendState& preCommonBlendState, const BlendState& postCommonBlendState, const vector<DrawBufferInfo>& drawBuffers, glu::RenderContext& renderContext)
 +{
 +      std::ostringstream stream;
 +      const bool supportsES32 = glu::contextSupports(renderContext.getType(), glu::ApiType::es(3, 2));
 +
 +      stream << "${GLSL_VERSION_DECL}\n";
 +
 +      if (requiresAdvancedBlendEq(preCommonBlendState, postCommonBlendState, drawBuffers))
 +      {
 +              stream << "${GLSL_EXTENSION}"
 +                         <<  "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 << "}";
 +
 +      map<string, string> args;
 +      args["GLSL_VERSION_DECL"] = supportsES32 ? getGLSLVersionDeclaration(glu::GLSL_VERSION_320_ES) : getGLSLVersionDeclaration(glu::GLSL_VERSION_300_ES);
 +      args["GLSL_EXTENSION"] = supportsES32 ? "\n" : "#extension GL_KHR_blend_equation_advanced : require\n";
 +
 +      return glu::FragmentSource(tcu::StringTemplate(stream.str()).specialize(args));
 +}
 +
 +glu::ProgramSources genShaderSources (const BlendState& preCommonBlendState, const BlendState& postCommonBlendState, const vector<DrawBufferInfo>& drawBuffers, glu::RenderContext& renderContext)
 +{
 +      return glu::ProgramSources() << genVertexSource(renderContext) << genFragmentSource(preCommonBlendState, postCommonBlendState, drawBuffers, renderContext);
 +}
 +
 +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, renderContext));
 +      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((glw::GLsizei)bufs.size(), &(bufs[0]));
 +
 +      if (requiresBlendBarriers)
 +              gl.blendBarrier();
 +
 +      renderGLQuad(renderContext, program);
 +
 +      if (requiresBlendBarriers)
 +              gl.blendBarrier();
 +
 +      gl.drawBuffers(0, 0);
 +      gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
 +      gl.useProgram(0);
 +
 +      GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to render");
 +
 +      int subpixelBits = 0;
 +      gl.getIntegerv(GL_SUBPIXEL_BITS, &subpixelBits);
 +
 +      renderRefQuad(preCommonBlendState, postCommonBlendState, drawBuffers, subpixelBits, 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)
 +{
 +      const bool supportsES32orGL45 = checkES32orGL45Support(m_context);
 +
 +      if (!supportsES32orGL45)
 +      {
 +              if (requiresAdvancedBlendEq(m_preCommonBlendState, m_postCommonBlendState, m_drawBuffers) && !m_context.getContextInfo().isExtensionSupported("GL_KHR_blend_equation_advanced"))
 +                      TCU_THROW(NotSupportedError, "Extension GL_KHR_blend_equation_advanced not supported");
 +
 +              if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_draw_buffers_indexed"))
 +                      TCU_THROW(NotSupportedError, "Extension GL_EXT_draw_buffers_indexed not supported");
 +      }
 +}
 +
 +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, Context& context)
 +{
 +      const bool supportsES32orGL45 = checkES32orGL45Support(context);
 +
 +      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,
 +              GL_RGBA16F,
 +              GL_R32F,
 +              GL_RG32F,
 +              GL_RGBA32F,
 +              GL_R11F_G11F_B10F
 +      };
 +
 +      if (supportsES32orGL45)
 +              return glu::mapGLInternalFormat(de::getArrayElement(glFormats, rng.getUint32() % DE_LENGTH_OF_ARRAY(glFormats)));
 +      else
 +      {
 +              DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(glFormats) == 32);
 +              return glu::mapGLInternalFormat(de::getArrayElement(glFormats, rng.getUint32() % (DE_LENGTH_OF_ARRAY(glFormats) - 5)));
 +      }
 +}
 +
 +void genRandomTest (de::Random& rng, BlendState& preCommon, BlendState& postCommon, vector<DrawBufferInfo>& drawBuffers, int maxDrawBufferCount, Context& context)
 +{
 +      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, context));
 +              BlendState                      blendState;
 +
 +              genRandomBlendState(rng, blendState);
 +
 +              // 32bit float formats don't support blending in GLES32
 +              if (format.type == tcu::TextureFormat::FLOAT)
 +              {
 +                      // If format is 32bit float post common can't enable blending
 +                      if (postCommon.enableBlend && *postCommon.enableBlend)
 +                      {
 +                              // Either don't set enable blend or disable blending
 +                              if (rng.getBool())
 +                                      postCommon.enableBlend = tcu::Nothing;
 +                              else
 +                                      postCommon.enableBlend = tcu::just(false);
 +                      }
 +
 +                      // If post common doesn't disable blending, per attachment state or
 +                      // pre common must.
 +                      if (!postCommon.enableBlend)
 +                      {
 +                              // If pre common enables blend per attachment must disable it
 +                              // If per attachment state changes blend state it must disable it
 +                              if ((preCommon.enableBlend && *preCommon.enableBlend)
 +                                      || blendState.enableBlend)
 +                                      blendState.enableBlend = tcu::just(false);
 +                      }
 +              }
 +
 +              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)
 +{
 +      const bool supportsES32orGL45 = checkES32orGL45Support(m_context);
 +
 +      if (!supportsES32orGL45 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_draw_buffers_indexed"))
 +              TCU_THROW(NotSupportedError, "Extension GL_EXT_draw_buffers_indexed not supported");
 +}
 +
 +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, m_context);
 +
 +      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)
 +{
 +      const bool supportsES32orGL45 = checkES32orGL45Support(m_context);
 +
 +      if (!supportsES32orGL45 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_draw_buffers_indexed"))
 +              TCU_THROW(NotSupportedError, "Extension GL_EXT_draw_buffers_indexed not supported");
 +}
 +
 +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, m_context);
 +
 +      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, tcu::Nothing, tcu::Nothing, tcu::Nothing);
 +
 +      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);
 +              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,
 +                                                                                                               Maybe<Either<BlendFunc, SeparateBlendFunc> >(BlendFunc(GL_ONE, GL_ONE)),
 +                                                                                                               tcu::Nothing);
 +              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, tcu::Nothing, tcu::Nothing, tcu::Nothing);
 +
 +      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);
 +              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,
 +                                                                                                               Maybe<Either<BlendFunc, SeparateBlendFunc> >(BlendFunc(GL_ONE, GL_ONE)),
 +                                                                                                               tcu::Nothing);
 +              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, Maybe<Either<BlendEq, SeparateBlendEq> >(GL_FUNC_ADD), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
 +              const BlendState        eqStateB                        = BlendState(tcu::Nothing, Maybe<Either<BlendEq, SeparateBlendEq> >(GL_FUNC_SUBTRACT), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
 +
 +              const BlendState        separateEqStateA        = BlendState(tcu::Nothing, Maybe<Either<BlendEq, SeparateBlendEq> >(SeparateBlendEq(GL_FUNC_ADD, GL_FUNC_SUBTRACT)), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
 +              const BlendState        separateEqStateB        = BlendState(tcu::Nothing, Maybe<Either<BlendEq, SeparateBlendEq> >(SeparateBlendEq(GL_FUNC_SUBTRACT, GL_FUNC_ADD)), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
 +
 +              const BlendState        advancedEqStateA        = BlendState(tcu::Nothing, Maybe<Either<BlendEq, SeparateBlendEq> >(GL_DIFFERENCE), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>());
 +              const BlendState        advancedEqStateB        = BlendState(tcu::Nothing, Maybe<Either<BlendEq, SeparateBlendEq> >(GL_SCREEN), 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, Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(BlendFunc(GL_SRC_ALPHA, GL_DST_ALPHA)), Maybe<BVec4>());
 +              const BlendState        funcStateB                      = BlendState(tcu::Nothing, Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(BlendFunc(GL_DST_ALPHA, GL_SRC_ALPHA)), Maybe<BVec4>());
 +              const BlendState        separateFuncStateA      = BlendState(tcu::Nothing, 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, 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, Maybe<Either<BlendEq, SeparateBlendEq> >(), Maybe<Either<BlendFunc, SeparateBlendFunc> >(), Maybe<BVec4>(BVec4(true, false, true, false)));
 +              const BlendState        bufferColorMaskState    = BlendState(tcu::Nothing, 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 with 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
 +} // gles3
 +} // deqp