C++程序  |  1159行  |  38.04 KB

/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL ES 3.0 Module
 * -------------------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Depth & stencil tests.
 *//*--------------------------------------------------------------------*/

#include "es3fDepthStencilTests.hpp"
#include "glsFragmentOpUtil.hpp"
#include "gluPixelTransfer.hpp"
#include "gluStrUtil.hpp"
#include "tcuSurface.hpp"
#include "tcuRenderTarget.hpp"
#include "tcuTestLog.hpp"
#include "tcuImageCompare.hpp"
#include "tcuCommandLine.hpp"
#include "tcuTextureUtil.hpp"
#include "deRandom.hpp"
#include "deStringUtil.hpp"
#include "deMemory.h"
#include "deString.h"
#include "rrFragmentOperations.hpp"
#include "sglrReferenceUtils.hpp"

#include <algorithm>
#include <sstream>

#include "glw.h"

namespace deqp
{
namespace gles3
{
namespace Functional
{

using std::vector;
using tcu::IVec2;
using tcu::Vec2;
using tcu::Vec4;
using tcu::TestLog;
using std::ostringstream;

enum
{
	VIEWPORT_WIDTH			= 4*3*4,
	VIEWPORT_HEIGHT			= 4*12,

	NUM_RANDOM_CASES		= 25,
	NUM_RANDOM_SUB_CASES	= 10
};

namespace DepthStencilCaseUtil
{

struct StencilParams
{
	deUint32	function;
	int			reference;
	deUint32	compareMask;

	deUint32	stencilFailOp;
	deUint32	depthFailOp;
	deUint32	depthPassOp;

	deUint32	writeMask;

	StencilParams (void)
		: function		(0)
		, reference		(0)
		, compareMask	(0)
		, stencilFailOp	(0)
		, depthFailOp	(0)
		, depthPassOp	(0)
		, writeMask		(0)
	{
	}
};

struct DepthStencilParams
{
	rr::FaceType	visibleFace;			//!< Quad visible face.

	bool			stencilTestEnabled;
	StencilParams	stencil[rr::FACETYPE_LAST];

	bool			depthTestEnabled;
	deUint32		depthFunc;
	float			depth;
	bool			depthWriteMask;

	DepthStencilParams (void)
		: visibleFace			(rr::FACETYPE_LAST)
		, stencilTestEnabled	(false)
		, depthTestEnabled		(false)
		, depthFunc				(0)
		, depth					(0.0f)
		, depthWriteMask		(false)
	{
	}
};

tcu::TestLog& operator<< (tcu::TestLog& log, const StencilParams& params)
{
	log << TestLog::Message << "  func = " << glu::getCompareFuncStr(params.function) << "\n"
							<< "  ref = " << params.reference << "\n"
							<< "  compare mask = " << tcu::toHex(params.compareMask) << "\n"
							<< "  stencil fail = " << glu::getStencilOpStr(params.stencilFailOp) << "\n"
							<< "  depth fail = " << glu::getStencilOpStr(params.depthFailOp) << "\n"
							<< "  depth pass = " << glu::getStencilOpStr(params.depthPassOp) << "\n"
							<< "  write mask = " << tcu::toHex(params.writeMask) << "\n"
		<< TestLog::EndMessage;
	return log;
}

tcu::TestLog& operator<< (tcu::TestLog& log, const DepthStencilParams& params)
{
	log << TestLog::Message << "Stencil test: " << (params.stencilTestEnabled ? "enabled" : "disabled") << TestLog::EndMessage;
	if (params.stencilTestEnabled)
	{
		log << TestLog::Message << "Front-face stencil state: " << TestLog::EndMessage;
		log << params.stencil[rr::FACETYPE_FRONT];

		log << TestLog::Message << "Back-face stencil state: " << TestLog::EndMessage;
		log << params.stencil[rr::FACETYPE_BACK];
	}

	log << TestLog::Message << "Depth test: " << (params.depthTestEnabled ? "enabled" : "disabled") << TestLog::EndMessage;
	if (params.depthTestEnabled)
	{
		log << TestLog::Message << "  func = " << glu::getCompareFuncStr(params.depthFunc) << "\n"
								   "  depth value = " << params.depth << "\n"
								   "  write mask = " << (params.depthWriteMask ? "true" : "false") << "\n"
			<< TestLog::EndMessage;
	}

	log << TestLog::Message << "Triangles are " << (params.visibleFace == rr::FACETYPE_FRONT ? "front" : "back") << "-facing" << TestLog::EndMessage;

	return log;
}

struct ClearCommand
{
	rr::WindowRectangle	rect;
	deUint32			buffers;
	tcu::Vec4			color;
	int					stencil;
	// \note No depth here - don't use clears for setting depth values; use quad rendering instead. Cleared depths are in [0, 1] to begin with,
	//		 whereas rendered depths are given in [-1, 1] and then mapped to [0, 1]; this discrepancy could cause precision issues in depth tests.

	ClearCommand (void)
		: rect		(0, 0, 0, 0)
		, buffers	(0)
		, stencil	(0)
	{
	}

	ClearCommand (const rr::WindowRectangle&	rect_,
				  deUint32						buffers_,
				  const tcu::Vec4&				color_,
				  int							stencil_)
		: rect		(rect_)
		, buffers	(buffers_)
		, color		(color_)
		, stencil	(stencil_)
	{
	}
};

struct RenderCommand
{
	DepthStencilParams		params;
	rr::WindowRectangle		rect;
	tcu::Vec4				color;
	tcu::BVec4				colorMask;

	RenderCommand (void)
		: rect(0, 0, 0, 0)
	{
	}
};

struct RefRenderCommand
{
	gls::FragmentOpUtil::IntegerQuad	quad;
	rr::FragmentOperationState			state;
};

struct TestRenderTarget
{
	int		width;
	int		height;
	int		depthBits;
	int		stencilBits;

	TestRenderTarget (int width_,
					  int height_,
					  int depthBits_,
					  int stencilBits_)
		: width			(width_)
		, height		(height_)
		, depthBits		(depthBits_)
		, stencilBits	(stencilBits_)
	{
	}

	TestRenderTarget (void)
		: width			(0)
		, height		(0)
		, depthBits		(0)
		, stencilBits	(0)
	{
	}
};

void getStencilTestValues (int stencilBits, int numValues, int* values)
{
	int numLowest		= numValues/2;
	int	numHighest		= numValues-numLowest;
	int	maxVal			= (1<<stencilBits)-1;

	for (int ndx = 0; ndx < numLowest; ndx++)
		values[ndx] = ndx;

	for (int ndx = 0; ndx < numHighest; ndx++)
		values[numValues-ndx-1] = maxVal-ndx;
}

void generateBaseClearAndDepthCommands (const TestRenderTarget& target, vector<ClearCommand>& clearCommands, vector<RenderCommand>& renderCommands)
{
	DE_ASSERT(clearCommands.empty());
	DE_ASSERT(renderCommands.empty());

	const int		numL0CellsX		= 4;
	const int		numL0CellsY		= 4;
	const int		numL1CellsX		= 3;
	const int		numL1CellsY		= 1;
	int				cellL0Width		= target.width/numL0CellsX;
	int				cellL0Height	= target.height/numL0CellsY;
	int				cellL1Width		= cellL0Width/numL1CellsX;
	int				cellL1Height	= cellL0Height/numL1CellsY;

	int				stencilValues[numL0CellsX*numL0CellsY];
	float			depthValues[numL1CellsX*numL1CellsY];

	if (cellL0Width <= 0 || cellL1Width <= 0 || cellL0Height <= 0 || cellL1Height <= 0)
		throw tcu::NotSupportedError("Too small render target");

	// Fullscreen clear to black.
	clearCommands.push_back(ClearCommand(rr::WindowRectangle(0, 0, target.width, target.height), GL_COLOR_BUFFER_BIT, Vec4(0.0f, 0.0f, 0.0f, 1.0f), 0));

	// Compute stencil values: numL0CellsX*numL0CellsY combinations of lowest and highest bits.
	getStencilTestValues(target.stencilBits, numL0CellsX*numL0CellsY, &stencilValues[0]);

	// Compute depth values
	{
		int		numValues		= DE_LENGTH_OF_ARRAY(depthValues);
		float	depthStep		= 2.0f/(float)(numValues-1);

		for (int ndx = 0; ndx < numValues; ndx++)
			depthValues[ndx] = -1.0f + depthStep*(float)ndx;
	}

	for (int y0 = 0; y0 < numL0CellsY; y0++)
	{
		for (int x0 = 0; x0 < numL0CellsX; x0++)
		{
			int stencilValue = stencilValues[y0*numL0CellsX + x0];

			for (int y1 = 0; y1 < numL1CellsY; y1++)
			{
				for (int x1 = 0; x1 < numL1CellsX; x1++)
				{
					int					x			= x0*cellL0Width + x1*cellL1Width;
					int					y			= y0*cellL0Height + y1*cellL1Height;
					rr::WindowRectangle	cellL1Rect	(x, y, cellL1Width, cellL1Height);

					clearCommands.push_back(ClearCommand(cellL1Rect, GL_STENCIL_BUFFER_BIT, Vec4(0), stencilValue));

					RenderCommand renderCmd;
					renderCmd.params.visibleFace		= rr::FACETYPE_FRONT;
					renderCmd.params.depth				= depthValues[y1*numL1CellsX + x1];;
					renderCmd.params.depthTestEnabled	= true;
					renderCmd.params.depthFunc			= GL_ALWAYS;
					renderCmd.params.depthWriteMask		= true;
					renderCmd.colorMask					= tcu::BVec4(false);
					renderCmd.rect						= cellL1Rect;

					renderCommands.push_back(renderCmd);
				}
			}
		}
	}
}

void generateDepthVisualizeCommands (const TestRenderTarget& target, vector<RenderCommand>& commands)
{
	const float			epsilon			= -0.05f;
	static const float	depthSteps[]	= {-1.0f, -0.5f, 0.0f, 0.5f, 1.0f};
	int					numSteps		= DE_LENGTH_OF_ARRAY(depthSteps);
	const float			colorStep		= 1.0f / (float)(numSteps-1);

	for (int ndx = 0; ndx < numSteps; ndx++)
	{
		RenderCommand cmd;

		cmd.params.visibleFace		= rr::FACETYPE_FRONT;
		cmd.rect					= rr::WindowRectangle(0, 0, target.width, target.height);
		cmd.color					= Vec4(0.0f, 0.0f, colorStep*(float)ndx, 0.0f);
		cmd.colorMask				= tcu::BVec4(false, false, true, false);
		cmd.params.depth			= depthSteps[ndx]+epsilon;
		cmd.params.depthTestEnabled	= true;
		cmd.params.depthFunc		= GL_LESS;
		cmd.params.depthWriteMask	= false;

		commands.push_back(cmd);
	}
}

void generateStencilVisualizeCommands (const TestRenderTarget& target, vector<RenderCommand>& commands)
{
	const int	numValues		= 4*4;
	float		colorStep		= 1.0f / numValues; // 0 is reserved for non-matching.
	int			stencilValues[numValues];

	getStencilTestValues(target.stencilBits, numValues, &stencilValues[0]);

	for (int ndx = 0; ndx < numValues; ndx++)
	{
		RenderCommand cmd;

		cmd.params.visibleFace							= rr::FACETYPE_FRONT;
		cmd.rect										= rr::WindowRectangle(0, 0, target.width, target.height);
		cmd.color										= Vec4(0.0f, colorStep*float(ndx+1), 0.0f, 0.0f);
		cmd.colorMask									= tcu::BVec4(false, true, false, false);
		cmd.params.stencilTestEnabled					= true;

		cmd.params.stencil[rr::FACETYPE_FRONT].function			= GL_EQUAL;
		cmd.params.stencil[rr::FACETYPE_FRONT].reference		= stencilValues[ndx];
		cmd.params.stencil[rr::FACETYPE_FRONT].compareMask		= ~0u;
		cmd.params.stencil[rr::FACETYPE_FRONT].stencilFailOp	= GL_KEEP;
		cmd.params.stencil[rr::FACETYPE_FRONT].depthFailOp		= GL_KEEP;
		cmd.params.stencil[rr::FACETYPE_FRONT].depthPassOp		= GL_KEEP;
		cmd.params.stencil[rr::FACETYPE_FRONT].writeMask		= 0u;

		cmd.params.stencil[rr::FACETYPE_BACK] = cmd.params.stencil[rr::FACETYPE_FRONT];

		commands.push_back(cmd);
	}
}

void translateStencilState (const StencilParams& src, rr::StencilState& dst)
{
	dst.func		= sglr::rr_util::mapGLTestFunc(src.function);
	dst.ref			= src.reference;
	dst.compMask	= src.compareMask;
	dst.sFail		= sglr::rr_util::mapGLStencilOp(src.stencilFailOp);
	dst.dpFail		= sglr::rr_util::mapGLStencilOp(src.depthFailOp);
	dst.dpPass		= sglr::rr_util::mapGLStencilOp(src.depthPassOp);
	dst.writeMask	= src.writeMask;
}

void translateCommand (const RenderCommand& src, RefRenderCommand& dst, const TestRenderTarget& renderTarget)
{
	const float		far				= 1.0f;
	const float		near			= 0.0f;
	bool			hasDepth		= renderTarget.depthBits > 0;
	bool			hasStencil		= renderTarget.stencilBits > 0;
	bool			isFrontFacing	= src.params.visibleFace == rr::FACETYPE_FRONT;

	dst.quad.posA = IVec2(isFrontFacing ? src.rect.left : (src.rect.left+src.rect.width-1), src.rect.bottom);
	dst.quad.posB = IVec2(isFrontFacing ? (src.rect.left+src.rect.width-1) : src.rect.left, src.rect.bottom+src.rect.height-1);

	std::fill(DE_ARRAY_BEGIN(dst.quad.color), DE_ARRAY_END(dst.quad.color), src.color);
	std::fill(DE_ARRAY_BEGIN(dst.quad.depth), DE_ARRAY_END(dst.quad.depth), ((far-near)/2.0f) * src.params.depth + (near+far)/2.0f);

	dst.state.colorMask = src.colorMask;

	dst.state.scissorTestEnabled		= false;
	dst.state.stencilTestEnabled		= hasStencil && src.params.stencilTestEnabled;
	dst.state.depthTestEnabled			= hasDepth && src.params.depthTestEnabled;
	dst.state.blendMode					= rr::BLENDMODE_NONE;
	dst.state.numStencilBits			= renderTarget.stencilBits;

	if (dst.state.depthTestEnabled)
	{
		dst.state.depthFunc					= sglr::rr_util::mapGLTestFunc(src.params.depthFunc);
		dst.state.depthMask					= src.params.depthWriteMask;
	}

	if (dst.state.stencilTestEnabled)
	{
		translateStencilState(src.params.stencil[rr::FACETYPE_BACK],	dst.state.stencilStates[rr::FACETYPE_BACK]);
		translateStencilState(src.params.stencil[rr::FACETYPE_FRONT],	dst.state.stencilStates[rr::FACETYPE_FRONT]);
	}
}

void render (const vector<ClearCommand>& clears, int viewportX, int viewportY)
{
	glEnable(GL_SCISSOR_TEST);

	for (int ndx = 0; ndx < (int)clears.size(); ndx++)
	{
		const ClearCommand& clear = clears[ndx];

		if (clear.buffers & GL_COLOR_BUFFER_BIT)	glClearColor(clear.color.x(), clear.color.y(), clear.color.z(), clear.color.w());
		if (clear.buffers & GL_STENCIL_BUFFER_BIT)	glClearStencil(clear.stencil);

		DE_ASSERT(clear.buffers == (clear.buffers & (GL_COLOR_BUFFER_BIT|GL_STENCIL_BUFFER_BIT))); // \note Don't use clear for depths.

		glScissor(clear.rect.left+viewportX, clear.rect.bottom+viewportY, clear.rect.width, clear.rect.height);
		glClear(clear.buffers);
	}

	glDisable(GL_SCISSOR_TEST);
}

void render (gls::FragmentOpUtil::QuadRenderer& renderer, const RenderCommand& command, int viewportX, int viewportY)
{
	if (command.params.stencilTestEnabled)
	{
		glEnable(GL_STENCIL_TEST);

		for (int face = 0; face < rr::FACETYPE_LAST; face++)
		{
			deUint32				glFace	= face == rr::FACETYPE_BACK ? GL_BACK : GL_FRONT;
			const StencilParams&	sParams	= command.params.stencil[face];

			glStencilFuncSeparate(glFace, sParams.function, sParams.reference, sParams.compareMask);
			glStencilOpSeparate(glFace, sParams.stencilFailOp, sParams.depthFailOp, sParams.depthPassOp);
			glStencilMaskSeparate(glFace, sParams.writeMask);
		}
	}
	else
		glDisable(GL_STENCIL_TEST);

	if (command.params.depthTestEnabled)
	{
		glEnable(GL_DEPTH_TEST);
		glDepthFunc(command.params.depthFunc);
		glDepthMask(command.params.depthWriteMask ? GL_TRUE : GL_FALSE);
	}
	else
		glDisable(GL_DEPTH_TEST);

	glColorMask(command.colorMask[0] ? GL_TRUE : GL_FALSE,
				command.colorMask[1] ? GL_TRUE : GL_FALSE,
				command.colorMask[2] ? GL_TRUE : GL_FALSE,
				command.colorMask[3] ? GL_TRUE : GL_FALSE);
	glViewport(command.rect.left+viewportX, command.rect.bottom+viewportY, command.rect.width, command.rect.height);

	gls::FragmentOpUtil::Quad quad;

	bool isFrontFacing = command.params.visibleFace == rr::FACETYPE_FRONT;
	quad.posA = Vec2(isFrontFacing ? -1.0f :  1.0f, -1.0f);
	quad.posB = Vec2(isFrontFacing ?  1.0f : -1.0f,  1.0f);

	std::fill(DE_ARRAY_BEGIN(quad.color), DE_ARRAY_END(quad.color), command.color);
	std::fill(DE_ARRAY_BEGIN(quad.depth), DE_ARRAY_END(quad.depth), command.params.depth);

	renderer.render(quad);
	GLU_CHECK();
}

void renderReference (const vector<ClearCommand>& clears, const tcu::PixelBufferAccess& dstColor, const tcu::PixelBufferAccess& dstStencil, int stencilBits)
{
	for (int ndx = 0; ndx < (int)clears.size(); ndx++)
	{
		const ClearCommand& clear = clears[ndx];

		if (clear.buffers & GL_COLOR_BUFFER_BIT)
			tcu::clear(tcu::getSubregion(dstColor, clear.rect.left, clear.rect.bottom, clear.rect.width, clear.rect.height), clear.color);

		if (clear.buffers & GL_STENCIL_BUFFER_BIT && stencilBits > 0)
		{
			int maskedVal = clear.stencil & ((1<<stencilBits)-1);
			tcu::clearStencil(tcu::getSubregion(dstStencil, clear.rect.left, clear.rect.bottom, clear.rect.width, clear.rect.height), maskedVal);
		}

		DE_ASSERT(clear.buffers == (clear.buffers & (GL_COLOR_BUFFER_BIT|GL_STENCIL_BUFFER_BIT))); // \note Don't use clear for depths.
	}
}

} // DepthStencilCaseUtil

using namespace DepthStencilCaseUtil;

class DepthStencilCase : public TestCase
{
public:
							DepthStencilCase		(Context& context, const char* name, const char* desc, const std::vector<DepthStencilParams>& cases);
							~DepthStencilCase		(void);

	void					init					(void);
	void					deinit					(void);

	IterateResult			iterate					(void);

private:
							DepthStencilCase		(const DepthStencilCase& other);
	DepthStencilCase&		operator=				(const DepthStencilCase& other);

	std::vector<DepthStencilParams>					m_cases;

	TestRenderTarget								m_renderTarget;
	std::vector<ClearCommand>						m_baseClears;
	std::vector<RenderCommand>						m_baseDepthRenders;
	std::vector<RenderCommand>						m_visualizeCommands;
	std::vector<RefRenderCommand>					m_refBaseDepthRenders;
	std::vector<RefRenderCommand>					m_refVisualizeCommands;

	gls::FragmentOpUtil::QuadRenderer*				m_renderer;
	tcu::Surface*									m_refColorBuffer;
	tcu::TextureLevel*								m_refDepthBuffer;
	tcu::TextureLevel*								m_refStencilBuffer;
	gls::FragmentOpUtil::ReferenceQuadRenderer*		m_refRenderer;

	int												m_iterNdx;
};

DepthStencilCase::DepthStencilCase (Context& context, const char* name, const char* desc, const std::vector<DepthStencilParams>& cases)
	: TestCase				(context, name, desc)
	, m_cases				(cases)
	, m_renderer			(DE_NULL)
	, m_refColorBuffer		(DE_NULL)
	, m_refDepthBuffer		(DE_NULL)
	, m_refStencilBuffer	(DE_NULL)
	, m_refRenderer			(DE_NULL)
	, m_iterNdx				(0)
{
}

DepthStencilCase::~DepthStencilCase (void)
{
	delete m_renderer;
	delete m_refColorBuffer;
	delete m_refDepthBuffer;
	delete m_refStencilBuffer;
	delete m_refRenderer;
}

void DepthStencilCase::init (void)
{
	DE_ASSERT(!m_renderer && !m_refColorBuffer && !m_refDepthBuffer && !m_refStencilBuffer && !m_refRenderer);

	// Compute render target.
	int viewportW	= de::min<int>(m_context.getRenderTarget().getWidth(), VIEWPORT_WIDTH);
	int viewportH	= de::min<int>(m_context.getRenderTarget().getHeight(), VIEWPORT_HEIGHT);
	m_renderTarget	= TestRenderTarget(viewportW, viewportH, m_context.getRenderTarget().getDepthBits(), m_context.getRenderTarget().getStencilBits());

	// Compute base clears & visualization commands.
	generateBaseClearAndDepthCommands(m_renderTarget, m_baseClears, m_baseDepthRenders);
	generateDepthVisualizeCommands(m_renderTarget, m_visualizeCommands);
	generateStencilVisualizeCommands(m_renderTarget, m_visualizeCommands);

	// Translate to ref commands.
	m_refBaseDepthRenders.resize(m_baseDepthRenders.size());
	for (int ndx = 0; ndx < (int)m_baseDepthRenders.size(); ndx++)
		translateCommand(m_baseDepthRenders[ndx], m_refBaseDepthRenders[ndx], m_renderTarget);

	m_refVisualizeCommands.resize(m_visualizeCommands.size());
	for (int ndx = 0; ndx < (int)m_visualizeCommands.size(); ndx++)
		translateCommand(m_visualizeCommands[ndx], m_refVisualizeCommands[ndx], m_renderTarget);

	m_renderer			= new gls::FragmentOpUtil::QuadRenderer(m_context.getRenderContext(), glu::GLSL_VERSION_300_ES);
	m_refColorBuffer	= new tcu::Surface(viewportW, viewportH);
	m_refDepthBuffer	= new tcu::TextureLevel(tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::FLOAT),			viewportW, viewportH);
	m_refStencilBuffer	= new tcu::TextureLevel(tcu::TextureFormat(tcu::TextureFormat::S, tcu::TextureFormat::UNSIGNED_INT32),	viewportW, viewportH);
	m_refRenderer		= new gls::FragmentOpUtil::ReferenceQuadRenderer();

	m_iterNdx			= 0;
	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}

void DepthStencilCase::deinit (void)
{
	delete m_renderer;
	delete m_refColorBuffer;
	delete m_refDepthBuffer;
	delete m_refStencilBuffer;
	delete m_refRenderer;

	m_renderer			= DE_NULL;
	m_refColorBuffer	= DE_NULL;
	m_refDepthBuffer	= DE_NULL;
	m_refStencilBuffer	= DE_NULL;
	m_refRenderer		= DE_NULL;

	m_baseClears.clear();
	m_baseDepthRenders.clear();
	m_visualizeCommands.clear();
	m_refBaseDepthRenders.clear();
	m_refVisualizeCommands.clear();
}

DepthStencilCase::IterateResult DepthStencilCase::iterate (void)
{
	de::Random				rnd				(deStringHash(getName()) ^ deInt32Hash(m_iterNdx));
	int						viewportX		= rnd.getInt(0, m_context.getRenderTarget().getWidth()-m_renderTarget.width);
	int						viewportY		= rnd.getInt(0, m_context.getRenderTarget().getHeight()-m_renderTarget.height);
	RenderCommand			testCmd;

	tcu::Surface			renderedImg		(m_renderTarget.width, m_renderTarget.height);
	tcu::RGBA				threshold		= m_context.getRenderTarget().getPixelFormat().getColorThreshold();

	// Fill in test command for this iteration.
	testCmd.color		= Vec4(1.0f, 0.0f, 0.0f, 1.0f);
	testCmd.colorMask	= tcu::BVec4(true);
	testCmd.rect		= rr::WindowRectangle(0, 0, m_renderTarget.width, m_renderTarget.height);
	testCmd.params		= m_cases[m_iterNdx];

	if (m_iterNdx == 0)
	{
		m_testCtx.getLog() << TestLog::Message << "Channels:\n"
													"  RED: passing pixels\n"
													"  GREEN: stencil values\n"
													"  BLUE: depth values"
							<< TestLog::EndMessage;
	}

	if (m_cases.size() > 1)
		m_testCtx.getLog() << TestLog::Message << "Iteration " << m_iterNdx << "..." << TestLog::EndMessage;

	m_testCtx.getLog() << m_cases[m_iterNdx];

	// Submit render commands to gl GL.

	// Base clears.
	render(m_baseClears, viewportX, viewportY);

	// Base depths.
	for (vector<RenderCommand>::const_iterator cmd = m_baseDepthRenders.begin(); cmd != m_baseDepthRenders.end(); ++cmd)
		render(*m_renderer, *cmd, viewportX, viewportY);

	// Test command.
	render(*m_renderer, testCmd, viewportX, viewportY);

	// Visualization commands.
	for (vector<RenderCommand>::const_iterator cmd = m_visualizeCommands.begin(); cmd != m_visualizeCommands.end(); ++cmd)
		render(*m_renderer, *cmd, viewportX, viewportY);

	// Re-enable all write masks.
	glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
	glDepthMask(GL_TRUE);
	glStencilMask(~0u);

	// Ask GPU to start rendering.
	glFlush();

	// Render reference while GPU is doing work.
	{
		RefRenderCommand refTestCmd;
		translateCommand(testCmd, refTestCmd, m_renderTarget);

		// Base clears.
		renderReference(m_baseClears, m_refColorBuffer->getAccess(), m_refStencilBuffer->getAccess(), m_renderTarget.depthBits);

		// Base depths.
		for (vector<RefRenderCommand>::const_iterator cmd = m_refBaseDepthRenders.begin(); cmd != m_refBaseDepthRenders.end(); ++cmd)
			m_refRenderer->render(gls::FragmentOpUtil::getMultisampleAccess(m_refColorBuffer->getAccess()),
								  gls::FragmentOpUtil::getMultisampleAccess(m_refDepthBuffer->getAccess()),
								  gls::FragmentOpUtil::getMultisampleAccess(m_refStencilBuffer->getAccess()),
								  cmd->quad, cmd->state);

		// Test command.
		m_refRenderer->render(gls::FragmentOpUtil::getMultisampleAccess(m_refColorBuffer->getAccess()),
							  gls::FragmentOpUtil::getMultisampleAccess(m_refDepthBuffer->getAccess()),
							  gls::FragmentOpUtil::getMultisampleAccess(m_refStencilBuffer->getAccess()),
							  refTestCmd.quad, refTestCmd.state);

		// Visualization commands.
		for (vector<RefRenderCommand>::const_iterator cmd = m_refVisualizeCommands.begin(); cmd != m_refVisualizeCommands.end(); ++cmd)
			m_refRenderer->render(gls::FragmentOpUtil::getMultisampleAccess(m_refColorBuffer->getAccess()),
								  gls::FragmentOpUtil::getMultisampleAccess(m_refDepthBuffer->getAccess()),
								  gls::FragmentOpUtil::getMultisampleAccess(m_refStencilBuffer->getAccess()),
								  cmd->quad, cmd->state);
	}

	// Read rendered image.
	glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, renderedImg.getAccess());

	m_iterNdx += 1;

	// Compare to reference.
	bool	isLastIter	= m_iterNdx >= (int)m_cases.size();
	bool	compareOk	= tcu::pixelThresholdCompare(m_testCtx.getLog(), "CompareResult", "Image Comparison Result", *m_refColorBuffer, renderedImg, threshold,
													 tcu::COMPARE_LOG_RESULT);

	m_testCtx.getLog() << TestLog::Message << (compareOk ? "  Passed." : "  FAILED!") << TestLog::EndMessage;
	if (!compareOk)
		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");

	if (compareOk && !isLastIter)
		return CONTINUE;
	else
		return STOP;
}

DepthStencilTests::DepthStencilTests (Context& context)
	: TestCaseGroup(context, "depth_stencil", "Depth and Stencil Op Tests")
{
}

DepthStencilTests::~DepthStencilTests (void)
{
}

static void randomDepthStencilState (de::Random& rnd, DepthStencilParams& params)
{
	const float stencilTestProbability	= 0.8f;
	const float depthTestProbability	= 0.7f;

	static const deUint32 compareFuncs[] =
	{
		GL_NEVER,
		GL_ALWAYS,
		GL_LESS,
		GL_LEQUAL,
		GL_EQUAL,
		GL_GEQUAL,
		GL_GREATER,
		GL_NOTEQUAL
	};

	static const deUint32 stencilOps[] =
	{
		GL_KEEP,
		GL_ZERO,
		GL_REPLACE,
		GL_INCR,
		GL_DECR,
		GL_INVERT,
		GL_INCR_WRAP,
		GL_DECR_WRAP
	};

	static const float depthValues[] = { -1.0f, -0.8f, -0.6f, -0.4f, -0.2f, 0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f };

	params.visibleFace			= rnd.getBool() ? rr::FACETYPE_FRONT : rr::FACETYPE_BACK;
	params.stencilTestEnabled	= rnd.getFloat() < stencilTestProbability;
	params.depthTestEnabled		= !params.stencilTestEnabled || (rnd.getFloat() < depthTestProbability);

	if (params.stencilTestEnabled)
	{
		for (int ndx = 0; ndx < 2; ndx++)
		{
			params.stencil[ndx].function		= rnd.choose<deUint32>(DE_ARRAY_BEGIN(compareFuncs), DE_ARRAY_END(compareFuncs));
			params.stencil[ndx].reference		= rnd.getInt(-2, 260);
			params.stencil[ndx].compareMask		= rnd.getUint32();
			params.stencil[ndx].stencilFailOp	= rnd.choose<deUint32>(DE_ARRAY_BEGIN(stencilOps), DE_ARRAY_END(stencilOps));
			params.stencil[ndx].depthFailOp		= rnd.choose<deUint32>(DE_ARRAY_BEGIN(stencilOps), DE_ARRAY_END(stencilOps));
			params.stencil[ndx].depthPassOp		= rnd.choose<deUint32>(DE_ARRAY_BEGIN(stencilOps), DE_ARRAY_END(stencilOps));
			params.stencil[ndx].writeMask		= rnd.getUint32();
		}
	}

	if (params.depthTestEnabled)
	{
		params.depthFunc		= rnd.choose<deUint32>(DE_ARRAY_BEGIN(compareFuncs), DE_ARRAY_END(compareFuncs));
		params.depth			= rnd.choose<float>(DE_ARRAY_BEGIN(depthValues), DE_ARRAY_END(depthValues));
		params.depthWriteMask	= rnd.getBool();
	}
}

void DepthStencilTests::init (void)
{
	static const struct
	{
		const char*	name;
		deUint32	func;
	} compareFuncs[] =
	{
		{ "never",		GL_NEVER	},
		{ "always",		GL_ALWAYS	},
		{ "less",		GL_LESS		},
		{ "lequal",		GL_LEQUAL	},
		{ "equal",		GL_EQUAL	},
		{ "gequal",		GL_GEQUAL	},
		{ "greater",	GL_GREATER	},
		{ "notequal",	GL_NOTEQUAL	}
	};

	static const struct
	{
		const char*	name;
		deUint32	op;
	} stencilOps[] =
	{
		{ "keep",		GL_KEEP			},
		{ "zero",		GL_ZERO			},
		{ "replace",	GL_REPLACE		},
		{ "incr",		GL_INCR			},
		{ "decr",		GL_DECR			},
		{ "invert",		GL_INVERT		},
		{ "incr_wrap",	GL_INCR_WRAP	},
		{ "decr_wrap",	GL_DECR_WRAP	}
	};

	static const struct
	{
		rr::FaceType	visibleFace;
		deUint32		sFail;
		deUint32		dFail;
		deUint32		dPass;
		int				stencilRef;
		deUint32		compareMask;
		deUint32		writeMask;
		float			depth;
	} functionCases[] =
	{
		{ rr::FACETYPE_BACK,	GL_DECR,		GL_INCR,	GL_INVERT,		4,	~0u, ~0u,	-0.7f },
		{ rr::FACETYPE_FRONT,	GL_DECR,		GL_INCR,	GL_INVERT,		2,	~0u, ~0u,	0.0f },
		{ rr::FACETYPE_BACK,	GL_DECR,		GL_INCR,	GL_INVERT,		1,	~0u, ~0u,	0.2f },
		{ rr::FACETYPE_FRONT,	GL_DECR_WRAP,	GL_INVERT,	GL_REPLACE,		4,	~0u, ~0u,	1.0f }
	};

	// All combinations of depth stencil functions.
	{
		tcu::TestCaseGroup* functionsGroup = new tcu::TestCaseGroup(m_testCtx, "stencil_depth_funcs", "Combinations of Depth and Stencil Functions");
		addChild(functionsGroup);

		for (int stencilFunc = 0; stencilFunc < DE_LENGTH_OF_ARRAY(compareFuncs)+1; stencilFunc++)
		{
			// One extra: depth test disabled.
			for (int depthFunc = 0; depthFunc < DE_LENGTH_OF_ARRAY(compareFuncs)+1; depthFunc++)
			{
				DepthStencilParams	params;
				ostringstream		name;
				bool				hasStencilFunc	= de::inBounds(stencilFunc, 0, DE_LENGTH_OF_ARRAY(compareFuncs));
				bool				hasDepthFunc	= de::inBounds(depthFunc, 0, DE_LENGTH_OF_ARRAY(compareFuncs));

				if (hasStencilFunc)
					name << "stencil_" << compareFuncs[stencilFunc].name << "_";
				else
					name << "no_stencil_";

				if (hasDepthFunc)
					name << "depth_" << compareFuncs[depthFunc].name;
				else
					name << "no_depth";

				params.depthFunc			= hasDepthFunc ? compareFuncs[depthFunc].func : 0;
				params.depthTestEnabled		= hasDepthFunc;
				params.depthWriteMask		= true;

				params.stencilTestEnabled	= hasStencilFunc;

				vector<DepthStencilParams> cases;
				for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(functionCases); ndx++)
				{
					rr::FaceType	visible		= functionCases[ndx].visibleFace;
					rr::FaceType	notVisible	= visible == rr::FACETYPE_FRONT ? rr::FACETYPE_BACK : rr::FACETYPE_FRONT;

					params.depth								= functionCases[ndx].depth;
					params.visibleFace							= visible;

					params.stencil[visible].function			= hasStencilFunc ? compareFuncs[stencilFunc].func : 0;
					params.stencil[visible].reference			= functionCases[ndx].stencilRef;
					params.stencil[visible].stencilFailOp		= functionCases[ndx].sFail;
					params.stencil[visible].depthFailOp			= functionCases[ndx].dFail;
					params.stencil[visible].depthPassOp			= functionCases[ndx].dPass;
					params.stencil[visible].compareMask			= functionCases[ndx].compareMask;
					params.stencil[visible].writeMask			= functionCases[ndx].writeMask;

					params.stencil[notVisible].function			= GL_ALWAYS;
					params.stencil[notVisible].reference		= 0;
					params.stencil[notVisible].stencilFailOp	= GL_REPLACE;
					params.stencil[notVisible].depthFailOp		= GL_REPLACE;
					params.stencil[notVisible].depthPassOp		= GL_REPLACE;
					params.stencil[notVisible].compareMask		= 0u;
					params.stencil[notVisible].writeMask		= ~0u;


					cases.push_back(params);
				}

				functionsGroup->addChild(new DepthStencilCase(m_context, name.str().c_str(), "", cases));
			}
		}
	}

	static const struct
	{
		rr::FaceType	visibleFace;
		deUint32		func;
		int				ref;
		deUint32		compareMask;
		deUint32		writeMask;
	} opCombinationCases[] =
	{
		{ rr::FACETYPE_BACK,	GL_LESS,		4,		~0u,	~0u	},
		{ rr::FACETYPE_FRONT,	GL_GREATER,		2,		~0u,	~0u	},
		{ rr::FACETYPE_BACK,	GL_EQUAL,		3,		~2u,	~0u	},
		{ rr::FACETYPE_FRONT,	GL_NOTEQUAL,	1,		~0u,	~1u	}
	};

	// All combinations of stencil ops.
	{
		tcu::TestCaseGroup* opCombinationGroup = new tcu::TestCaseGroup(m_testCtx, "stencil_ops", "Stencil Op Combinations");
		addChild(opCombinationGroup);

		for (int sFail = 0; sFail < DE_LENGTH_OF_ARRAY(stencilOps); sFail++)
		{
			for (int dFail = 0; dFail < DE_LENGTH_OF_ARRAY(stencilOps); dFail++)
			{
				for (int dPass = 0; dPass < DE_LENGTH_OF_ARRAY(stencilOps); dPass++)
				{
					DepthStencilParams	params;
					ostringstream		name;

					name << stencilOps[sFail].name << "_" << stencilOps[dFail].name << "_" << stencilOps[dPass].name;

					params.depthFunc			= GL_LEQUAL;
					params.depth				= 0.0f;
					params.depthTestEnabled		= true;
					params.depthWriteMask		= true;

					params.stencilTestEnabled	= true;

					vector<DepthStencilParams> cases;
					for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(opCombinationCases); ndx++)
					{
						rr::FaceType	visible		= opCombinationCases[ndx].visibleFace;
						rr::FaceType	notVisible	= visible == rr::FACETYPE_FRONT ? rr::FACETYPE_BACK : rr::FACETYPE_FRONT;

						params.visibleFace							= visible;

						params.stencil[visible].function			= opCombinationCases[ndx].func;
						params.stencil[visible].reference			= opCombinationCases[ndx].ref;
						params.stencil[visible].stencilFailOp		= stencilOps[sFail].op;
						params.stencil[visible].depthFailOp			= stencilOps[dFail].op;
						params.stencil[visible].depthPassOp			= stencilOps[dPass].op;
						params.stencil[visible].compareMask			= opCombinationCases[ndx].compareMask;
						params.stencil[visible].writeMask			= opCombinationCases[ndx].writeMask;

						params.stencil[notVisible].function			= GL_ALWAYS;
						params.stencil[notVisible].reference		= 0;
						params.stencil[notVisible].stencilFailOp	= GL_REPLACE;
						params.stencil[notVisible].depthFailOp		= GL_REPLACE;
						params.stencil[notVisible].depthPassOp		= GL_REPLACE;
						params.stencil[notVisible].compareMask		= 0u;
						params.stencil[notVisible].writeMask		= ~0u;


						cases.push_back(params);
					}

					opCombinationGroup->addChild(new DepthStencilCase(m_context, name.str().c_str(), "", cases));
				}
			}
		}
	}

	// Write masks
	{
		tcu::TestCaseGroup* writeMaskGroup = new tcu::TestCaseGroup(m_testCtx, "write_mask", "Depth and Stencil Write Masks");
		addChild(writeMaskGroup);

		// Depth mask
		{
			DepthStencilParams params;

			params.depthFunc			= GL_LEQUAL;
			params.depth				= 0.0f;
			params.depthTestEnabled		= true;
			params.stencilTestEnabled	= true;

			params.stencil[rr::FACETYPE_FRONT].function			= GL_NOTEQUAL;
			params.stencil[rr::FACETYPE_FRONT].reference		= 1;
			params.stencil[rr::FACETYPE_FRONT].stencilFailOp	= GL_INVERT;
			params.stencil[rr::FACETYPE_FRONT].depthFailOp		= GL_INCR;
			params.stencil[rr::FACETYPE_FRONT].depthPassOp		= GL_DECR;
			params.stencil[rr::FACETYPE_FRONT].compareMask		= ~0u;
			params.stencil[rr::FACETYPE_FRONT].writeMask		= ~0u;

			params.stencil[rr::FACETYPE_BACK].function			= GL_ALWAYS;
			params.stencil[rr::FACETYPE_BACK].reference			= 0;
			params.stencil[rr::FACETYPE_BACK].stencilFailOp		= GL_REPLACE;
			params.stencil[rr::FACETYPE_BACK].depthFailOp		= GL_INVERT;
			params.stencil[rr::FACETYPE_BACK].depthPassOp		= GL_INCR;
			params.stencil[rr::FACETYPE_BACK].compareMask		= ~0u;
			params.stencil[rr::FACETYPE_BACK].writeMask		= ~0u;

			vector<DepthStencilParams> cases;

			// Case 1: front, depth write enabled
			params.visibleFace		= rr::FACETYPE_FRONT;
			params.depthWriteMask	= true;
			cases.push_back(params);

			// Case 2: front, depth write disabled
			params.visibleFace		= rr::FACETYPE_FRONT;
			params.depthWriteMask	= false;
			cases.push_back(params);

			// Case 3: back, depth write enabled
			params.visibleFace		= rr::FACETYPE_BACK;
			params.depthWriteMask	= true;
			cases.push_back(params);

			// Case 4: back, depth write disabled
			params.visibleFace		= rr::FACETYPE_BACK;
			params.depthWriteMask	= false;
			cases.push_back(params);

			writeMaskGroup->addChild(new DepthStencilCase(m_context, "depth", "Depth Write Mask", cases));
		}

		// Stencil write masks.
		{
			static const struct
			{
				rr::FaceType	visibleFace;
				deUint32		frontWriteMask;
				deUint32		backWriteMask;
			} stencilWmaskCases[] =
			{
				{ rr::FACETYPE_FRONT,	~0u,	0u		},
				{ rr::FACETYPE_FRONT,	0u,		~0u		},
				{ rr::FACETYPE_FRONT,	0xfu,	0xf0u	},
				{ rr::FACETYPE_FRONT,	0x2u,	0x4u	},
				{ rr::FACETYPE_BACK,	0u,		~0u		},
				{ rr::FACETYPE_BACK,	~0u,	0u		},
				{ rr::FACETYPE_BACK,	0xf0u,	0xfu	},
				{ rr::FACETYPE_BACK,	0x4u,	0x2u	}
			};

			DepthStencilParams params;

			params.depthFunc			= GL_LEQUAL;
			params.depth				= 0.0f;
			params.depthTestEnabled		= true;
			params.depthWriteMask		= true;
			params.stencilTestEnabled	= true;

			params.stencil[rr::FACETYPE_FRONT].function			= GL_NOTEQUAL;
			params.stencil[rr::FACETYPE_FRONT].reference		= 1;
			params.stencil[rr::FACETYPE_FRONT].stencilFailOp	= GL_INVERT;
			params.stencil[rr::FACETYPE_FRONT].depthFailOp		= GL_INCR;
			params.stencil[rr::FACETYPE_FRONT].depthPassOp		= GL_DECR;
			params.stencil[rr::FACETYPE_FRONT].compareMask		= ~0u;

			params.stencil[rr::FACETYPE_BACK].function			= GL_ALWAYS;
			params.stencil[rr::FACETYPE_BACK].reference			= 0;
			params.stencil[rr::FACETYPE_BACK].stencilFailOp		= GL_REPLACE;
			params.stencil[rr::FACETYPE_BACK].depthFailOp		= GL_INVERT;
			params.stencil[rr::FACETYPE_BACK].depthPassOp		= GL_INCR;
			params.stencil[rr::FACETYPE_BACK].compareMask		= ~0u;

			vector<DepthStencilParams> cases;
			for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(stencilWmaskCases); ndx++)
			{
				params.visibleFace								= stencilWmaskCases[ndx].visibleFace;
				params.stencil[rr::FACETYPE_FRONT].writeMask	= stencilWmaskCases[ndx].frontWriteMask;
				params.stencil[rr::FACETYPE_BACK].writeMask	= stencilWmaskCases[ndx].backWriteMask;
				cases.push_back(params);
			}

			writeMaskGroup->addChild(new DepthStencilCase(m_context, "stencil", "Stencil Write Mask", cases));
		}

		// Depth & stencil write masks.
		{
			static const struct
			{
				bool			depthWriteMask;
				rr::FaceType	visibleFace;
				deUint32		frontWriteMask;
				deUint32		backWriteMask;
			} depthStencilWmaskCases[] =
			{
				{ false,	rr::FACETYPE_FRONT,		~0u,	0u		},
				{ false,	rr::FACETYPE_FRONT,		0u,		~0u		},
				{ false,	rr::FACETYPE_FRONT,		0xfu,	0xf0u	},
				{ true,		rr::FACETYPE_FRONT,		~0u,	0u		},
				{ true,		rr::FACETYPE_FRONT,		0u,		~0u		},
				{ true,		rr::FACETYPE_FRONT,		0xfu,	0xf0u	},
				{ false,	rr::FACETYPE_BACK,		0u,		~0u		},
				{ false,	rr::FACETYPE_BACK,		~0u,	0u		},
				{ false,	rr::FACETYPE_BACK,		0xf0u,	0xfu	},
				{ true,		rr::FACETYPE_BACK,		0u,		~0u		},
				{ true,		rr::FACETYPE_BACK,		~0u,	0u		},
				{ true,		rr::FACETYPE_BACK,		0xf0u,	0xfu	}
			};

			DepthStencilParams params;

			params.depthFunc			= GL_LEQUAL;
			params.depth				= 0.0f;
			params.depthTestEnabled		= true;
			params.depthWriteMask		= true;
			params.stencilTestEnabled	= true;

			params.stencil[rr::FACETYPE_FRONT].function			= GL_NOTEQUAL;
			params.stencil[rr::FACETYPE_FRONT].reference		= 1;
			params.stencil[rr::FACETYPE_FRONT].stencilFailOp	= GL_INVERT;
			params.stencil[rr::FACETYPE_FRONT].depthFailOp		= GL_INCR;
			params.stencil[rr::FACETYPE_FRONT].depthPassOp		= GL_DECR;
			params.stencil[rr::FACETYPE_FRONT].compareMask		= ~0u;

			params.stencil[rr::FACETYPE_BACK].function			= GL_ALWAYS;
			params.stencil[rr::FACETYPE_BACK].reference			= 0;
			params.stencil[rr::FACETYPE_BACK].stencilFailOp		= GL_REPLACE;
			params.stencil[rr::FACETYPE_BACK].depthFailOp		= GL_INVERT;
			params.stencil[rr::FACETYPE_BACK].depthPassOp		= GL_INCR;
			params.stencil[rr::FACETYPE_BACK].compareMask		= ~0u;

			vector<DepthStencilParams> cases;
			for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(depthStencilWmaskCases); ndx++)
			{
				params.depthWriteMask							= depthStencilWmaskCases[ndx].depthWriteMask;
				params.visibleFace								= depthStencilWmaskCases[ndx].visibleFace;
				params.stencil[rr::FACETYPE_FRONT].writeMask	= depthStencilWmaskCases[ndx].frontWriteMask;
				params.stencil[rr::FACETYPE_BACK].writeMask		= depthStencilWmaskCases[ndx].backWriteMask;
				cases.push_back(params);
			}

			writeMaskGroup->addChild(new DepthStencilCase(m_context, "both", "Depth and Stencil Write Masks", cases));
		}
	}

	// Randomized cases
	{
		tcu::TestCaseGroup* randomGroup = new tcu::TestCaseGroup(m_testCtx, "random", "Randomized Depth and Stencil Test Cases");
		addChild(randomGroup);

		for (int caseNdx = 0; caseNdx < NUM_RANDOM_CASES; caseNdx++)
		{
			vector<DepthStencilParams>	subCases	(NUM_RANDOM_SUB_CASES);
			de::Random					rnd			(deInt32Hash(caseNdx) ^ deInt32Hash(m_testCtx.getCommandLine().getBaseSeed()));

			for (vector<DepthStencilParams>::iterator iter = subCases.begin(); iter != subCases.end(); ++iter)
				randomDepthStencilState(rnd, *iter);

			randomGroup->addChild(new DepthStencilCase(m_context, de::toString(caseNdx).c_str(), "", subCases));
		}
	}
}

} // Functional
} // gles3
} // deqp