Add tests for GL_EXT_draw_buffers_indexed.

Add tests for GL_EXT_draw_buffers_indexed extension.

Bug: 18925580
Change-Id: Ia9e3a7a867c5f11a961df4aeaeccc2c5865b58a9
diff --git a/Android.mk b/Android.mk
index 06adb08..e1235d4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -314,6 +314,7 @@
 	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
index 0000000..3c93868
--- /dev/null
+++ b/doc/testspecs/GLES31/functional.draw_buffers_indexed.txt
@@ -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.
diff --git a/modules/gles31/functional/CMakeLists.txt b/modules/gles31/functional/CMakeLists.txt
index dc34f7e..a4240f1 100644
--- a/modules/gles31/functional/CMakeLists.txt
+++ b/modules/gles31/functional/CMakeLists.txt
@@ -155,6 +155,8 @@
 	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
index 0000000..ceccb05
--- /dev/null
+++ b/modules/gles31/functional/es31fDrawBuffersIndexedTests.cpp
@@ -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
index 0000000..5d716f9
--- /dev/null
+++ b/modules/gles31/functional/es31fDrawBuffersIndexedTests.hpp
@@ -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
diff --git a/modules/gles31/functional/es31fFunctionalTests.cpp b/modules/gles31/functional/es31fFunctionalTests.cpp
index 0a9b0b7..3720cc0 100644
--- a/modules/gles31/functional/es31fFunctionalTests.cpp
+++ b/modules/gles31/functional/es31fFunctionalTests.cpp
@@ -85,6 +85,7 @@
 #include "es31fPrimitiveBoundingBoxTests.hpp"
 #include "es31fAndroidExtensionPackES31ATests.hpp"
 #include "es31fCopyImageTests.hpp"
+#include "es31fDrawBuffersIndexedTests.hpp"
 
 namespace deqp
 {
@@ -328,6 +329,7 @@
 	addChild(new PrimitiveBoundingBoxTests				(m_context));
 	addChild(new AndroidExtensionPackES31ATests			(m_context));
 	addChild(createCopyImageTests						(m_context));
+	addChild(createDrawBuffersIndexedTests				(m_context));
 }
 
 } // Functional