Home | History | Annotate | Download | only in functional
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program OpenGL ES 3.0 Module
      3  * -------------------------------------------------
      4  *
      5  * Copyright 2014 The Android Open Source Project
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief Shader derivate function tests.
     22  *
     23  * \todo [2013-06-25 pyry] Missing features:
     24  *  - lines and points
     25  *  - projected coordinates
     26  *  - continous non-trivial functions (sin, exp)
     27  *  - non-continous functions (step)
     28  *//*--------------------------------------------------------------------*/
     29 
     30 #include "es3fShaderDerivateTests.hpp"
     31 #include "gluShaderProgram.hpp"
     32 #include "gluRenderContext.hpp"
     33 #include "gluDrawUtil.hpp"
     34 #include "gluPixelTransfer.hpp"
     35 #include "gluShaderUtil.hpp"
     36 #include "gluStrUtil.hpp"
     37 #include "gluTextureUtil.hpp"
     38 #include "gluTexture.hpp"
     39 #include "tcuStringTemplate.hpp"
     40 #include "tcuRenderTarget.hpp"
     41 #include "tcuSurface.hpp"
     42 #include "tcuTestLog.hpp"
     43 #include "tcuVectorUtil.hpp"
     44 #include "tcuTextureUtil.hpp"
     45 #include "tcuRGBA.hpp"
     46 #include "tcuFloat.hpp"
     47 #include "deRandom.hpp"
     48 #include "deUniquePtr.hpp"
     49 #include "deString.h"
     50 #include "glwEnums.hpp"
     51 #include "glwFunctions.hpp"
     52 #include "glsShaderRenderCase.hpp" // gls::setupDefaultUniforms()
     53 
     54 #include <sstream>
     55 
     56 namespace deqp
     57 {
     58 namespace gles3
     59 {
     60 namespace Functional
     61 {
     62 
     63 using std::vector;
     64 using std::string;
     65 using std::map;
     66 using tcu::TestLog;
     67 using std::ostringstream;
     68 
     69 enum
     70 {
     71 	VIEWPORT_WIDTH		= 167,
     72 	VIEWPORT_HEIGHT		= 103,
     73 	FBO_WIDTH			= 99,
     74 	FBO_HEIGHT			= 133,
     75 	MAX_FAILED_MESSAGES	= 10
     76 };
     77 
     78 enum DerivateFunc
     79 {
     80 	DERIVATE_DFDX	= 0,
     81 	DERIVATE_DFDY,
     82 	DERIVATE_FWIDTH,
     83 
     84 	DERIVATE_LAST
     85 };
     86 
     87 enum SurfaceType
     88 {
     89 	SURFACETYPE_DEFAULT_FRAMEBUFFER = 0,
     90 	SURFACETYPE_UNORM_FBO,
     91 	SURFACETYPE_FLOAT_FBO,	// \note Uses RGBA32UI fbo actually, since FP rendertargets are not in core spec.
     92 
     93 	SURFACETYPE_LAST
     94 };
     95 
     96 // Utilities
     97 
     98 namespace
     99 {
    100 
    101 class AutoFbo
    102 {
    103 public:
    104 	AutoFbo (const glw::Functions& gl)
    105 		: m_gl	(gl)
    106 		, m_fbo	(0)
    107 	{
    108 	}
    109 
    110 	~AutoFbo (void)
    111 	{
    112 		if (m_fbo)
    113 			m_gl.deleteFramebuffers(1, &m_fbo);
    114 	}
    115 
    116 	void gen (void)
    117 	{
    118 		DE_ASSERT(!m_fbo);
    119 		m_gl.genFramebuffers(1, &m_fbo);
    120 	}
    121 
    122 	deUint32 operator* (void) const { return m_fbo; }
    123 
    124 private:
    125 	const glw::Functions&	m_gl;
    126 	deUint32				m_fbo;
    127 };
    128 
    129 class AutoRbo
    130 {
    131 public:
    132 	AutoRbo (const glw::Functions& gl)
    133 		: m_gl	(gl)
    134 		, m_rbo	(0)
    135 	{
    136 	}
    137 
    138 	~AutoRbo (void)
    139 	{
    140 		if (m_rbo)
    141 			m_gl.deleteRenderbuffers(1, &m_rbo);
    142 	}
    143 
    144 	void gen (void)
    145 	{
    146 		DE_ASSERT(!m_rbo);
    147 		m_gl.genRenderbuffers(1, &m_rbo);
    148 	}
    149 
    150 	deUint32 operator* (void) const { return m_rbo; }
    151 
    152 private:
    153 	const glw::Functions&	m_gl;
    154 	deUint32				m_rbo;
    155 };
    156 
    157 } // anonymous
    158 
    159 static const char* getDerivateFuncName (DerivateFunc func)
    160 {
    161 	switch (func)
    162 	{
    163 		case DERIVATE_DFDX:		return "dFdx";
    164 		case DERIVATE_DFDY:		return "dFdy";
    165 		case DERIVATE_FWIDTH:	return "fwidth";
    166 		default:
    167 			DE_ASSERT(false);
    168 			return DE_NULL;
    169 	}
    170 }
    171 
    172 static const char* getDerivateFuncCaseName (DerivateFunc func)
    173 {
    174 	switch (func)
    175 	{
    176 		case DERIVATE_DFDX:		return "dfdx";
    177 		case DERIVATE_DFDY:		return "dfdy";
    178 		case DERIVATE_FWIDTH:	return "fwidth";
    179 		default:
    180 			DE_ASSERT(false);
    181 			return DE_NULL;
    182 	}
    183 }
    184 
    185 static inline tcu::BVec4 getDerivateMask (glu::DataType type)
    186 {
    187 	switch (type)
    188 	{
    189 		case glu::TYPE_FLOAT:		return tcu::BVec4(true, false, false, false);
    190 		case glu::TYPE_FLOAT_VEC2:	return tcu::BVec4(true, true, false, false);
    191 		case glu::TYPE_FLOAT_VEC3:	return tcu::BVec4(true, true, true, false);
    192 		case glu::TYPE_FLOAT_VEC4:	return tcu::BVec4(true, true, true, true);
    193 		default:
    194 			DE_ASSERT(false);
    195 			return tcu::BVec4(true);
    196 	}
    197 }
    198 
    199 static inline tcu::Vec4 readDerivate (const tcu::ConstPixelBufferAccess& surface, const tcu::Vec4& derivScale, const tcu::Vec4& derivBias, int x, int y)
    200 {
    201 	return (surface.getPixel(x, y) - derivBias) / derivScale;
    202 }
    203 
    204 static inline tcu::UVec4 getCompExpBits (const tcu::Vec4& v)
    205 {
    206 	return tcu::UVec4(tcu::Float32(v[0]).exponentBits(),
    207 					  tcu::Float32(v[1]).exponentBits(),
    208 					  tcu::Float32(v[2]).exponentBits(),
    209 					  tcu::Float32(v[3]).exponentBits());
    210 }
    211 
    212 float computeFloatingPointError (const float value, const int numAccurateBits)
    213 {
    214 	const int		numGarbageBits	= 23-numAccurateBits;
    215 	const deUint32	mask			= (1u<<numGarbageBits)-1u;
    216 	const int		exp				= tcu::Float32(value).exponent();
    217 
    218 	return tcu::Float32::construct(+1, exp, (1u<<23) | mask).asFloat() - tcu::Float32::construct(+1, exp, 1u<<23).asFloat();
    219 }
    220 
    221 static inline tcu::Vec4 getDerivateThreshold (const glu::Precision precision, const tcu::Vec4& valueMin, const tcu::Vec4& valueMax, const tcu::Vec4& expectedDerivate)
    222 {
    223 	const int			baseBits		= precision == glu::PRECISION_HIGHP		? 23	:
    224 										  precision == glu::PRECISION_MEDIUMP	? 10	:
    225 										  precision == glu::PRECISION_LOWP		? 6		: 0;
    226 	const tcu::UVec4	derivExp		= getCompExpBits(expectedDerivate);
    227 	const tcu::UVec4	maxValueExp		= max(getCompExpBits(valueMin), getCompExpBits(valueMax));
    228 	const tcu::UVec4	numBitsLost		= maxValueExp - min(maxValueExp, derivExp);
    229 	const tcu::IVec4	numAccurateBits	= max(baseBits - numBitsLost.asInt() - 3, tcu::IVec4(0));
    230 
    231 	return tcu::Vec4(computeFloatingPointError(expectedDerivate[0], numAccurateBits[0]),
    232 					 computeFloatingPointError(expectedDerivate[1], numAccurateBits[1]),
    233 					 computeFloatingPointError(expectedDerivate[2], numAccurateBits[2]),
    234 					 computeFloatingPointError(expectedDerivate[3], numAccurateBits[3]));
    235 }
    236 
    237 namespace
    238 {
    239 
    240 struct LogVecComps
    241 {
    242 	const tcu::Vec4&	v;
    243 	int					numComps;
    244 
    245 	LogVecComps (const tcu::Vec4& v_, int numComps_)
    246 		: v			(v_)
    247 		, numComps	(numComps_)
    248 	{
    249 	}
    250 };
    251 
    252 std::ostream& operator<< (std::ostream& str, const LogVecComps& v)
    253 {
    254 	DE_ASSERT(de::inRange(v.numComps, 1, 4));
    255 	if (v.numComps == 1)		return str << v.v[0];
    256 	else if (v.numComps == 2)	return str << v.v.toWidth<2>();
    257 	else if (v.numComps == 3)	return str << v.v.toWidth<3>();
    258 	else						return str << v.v;
    259 }
    260 
    261 } // anonymous
    262 
    263 static bool verifyConstantDerivate (tcu::TestLog&						log,
    264 									const tcu::ConstPixelBufferAccess&	result,
    265 									const tcu::PixelBufferAccess&		errorMask,
    266 									glu::DataType						dataType,
    267 									const tcu::Vec4&					reference,
    268 									const tcu::Vec4&					threshold,
    269 									const tcu::Vec4&					scale,
    270 									const tcu::Vec4&					bias)
    271 {
    272 	const int			numComps		= glu::getDataTypeFloatScalars(dataType);
    273 	const tcu::BVec4	mask			= tcu::logicalNot(getDerivateMask(dataType));
    274 	int					numFailedPixels	= 0;
    275 
    276 	log << TestLog::Message << "Expecting " << LogVecComps(reference, numComps) << " with threshold " << LogVecComps(threshold, numComps) << TestLog::EndMessage;
    277 
    278 	for (int y = 0; y < result.getHeight(); y++)
    279 	{
    280 		for (int x = 0; x < result.getWidth(); x++)
    281 		{
    282 			const tcu::Vec4		resDerivate		= readDerivate(result, scale, bias, x, y);
    283 			const bool			isOk			= tcu::allEqual(tcu::logicalOr(tcu::lessThanEqual(tcu::abs(reference - resDerivate), threshold), mask), tcu::BVec4(true));
    284 
    285 			if (!isOk)
    286 			{
    287 				if (numFailedPixels < MAX_FAILED_MESSAGES)
    288 					log << TestLog::Message << "FAIL: got " << LogVecComps(resDerivate, numComps)
    289 											<< ", diff = " << LogVecComps(tcu::abs(reference - resDerivate), numComps)
    290 											<< ", at x = " << x << ", y = " << y
    291 						<< TestLog::EndMessage;
    292 				numFailedPixels += 1;
    293 				errorMask.setPixel(tcu::RGBA::red.toVec(), x, y);
    294 			}
    295 		}
    296 	}
    297 
    298 	if (numFailedPixels >= MAX_FAILED_MESSAGES)
    299 		log << TestLog::Message << "..." << TestLog::EndMessage;
    300 
    301 	if (numFailedPixels > 0)
    302 		log << TestLog::Message << "FAIL: found " << numFailedPixels << " failed pixels" << TestLog::EndMessage;
    303 
    304 	return numFailedPixels == 0;
    305 }
    306 
    307 // TriangleDerivateCase
    308 
    309 class TriangleDerivateCase : public TestCase
    310 {
    311 public:
    312 						TriangleDerivateCase	(Context& context, const char* name, const char* description);
    313 						~TriangleDerivateCase	(void);
    314 
    315 	IterateResult		iterate					(void);
    316 
    317 protected:
    318 	virtual void		setupRenderState		(deUint32 program) { DE_UNREF(program); }
    319 	virtual bool		verify					(const tcu::ConstPixelBufferAccess& result, const tcu::PixelBufferAccess& errorMask) = DE_NULL;
    320 
    321 	tcu::IVec2			getViewportSize			(void) const;
    322 	tcu::Vec4			getSurfaceThreshold		(void) const;
    323 
    324 	glu::DataType		m_dataType;
    325 	glu::Precision		m_precision;
    326 
    327 	glu::DataType		m_coordDataType;
    328 	glu::Precision		m_coordPrecision;
    329 
    330 	std::string			m_fragmentSrc;
    331 
    332 	tcu::Vec4			m_coordMin;
    333 	tcu::Vec4			m_coordMax;
    334 	tcu::Vec4			m_derivScale;
    335 	tcu::Vec4			m_derivBias;
    336 
    337 	SurfaceType			m_surfaceType;
    338 	int					m_numSamples;
    339 	deUint32			m_hint;
    340 };
    341 
    342 TriangleDerivateCase::TriangleDerivateCase (Context& context, const char* name, const char* description)
    343 	: TestCase			(context, name, description)
    344 	, m_dataType		(glu::TYPE_LAST)
    345 	, m_precision		(glu::PRECISION_LAST)
    346 	, m_coordDataType	(glu::TYPE_LAST)
    347 	, m_coordPrecision	(glu::PRECISION_LAST)
    348 	, m_surfaceType		(SURFACETYPE_DEFAULT_FRAMEBUFFER)
    349 	, m_numSamples		(0)
    350 	, m_hint			(GL_DONT_CARE)
    351 {
    352 	DE_ASSERT(m_surfaceType != SURFACETYPE_DEFAULT_FRAMEBUFFER || m_numSamples == 0);
    353 }
    354 
    355 TriangleDerivateCase::~TriangleDerivateCase (void)
    356 {
    357 	TriangleDerivateCase::deinit();
    358 }
    359 
    360 static std::string genVertexSource (glu::DataType coordType, glu::Precision precision)
    361 {
    362 	DE_ASSERT(glu::isDataTypeFloatOrVec(coordType));
    363 
    364 	const char* vertexTmpl =
    365 		"#version 300 es\n"
    366 		"in highp vec4 a_position;\n"
    367 		"in ${PRECISION} ${DATATYPE} a_coord;\n"
    368 		"out ${PRECISION} ${DATATYPE} v_coord;\n"
    369 		"void main (void)\n"
    370 		"{\n"
    371 		"	gl_Position = a_position;\n"
    372 		"	v_coord = a_coord;\n"
    373 		"}\n";
    374 
    375 	map<string, string> vertexParams;
    376 
    377 	vertexParams["PRECISION"]	= glu::getPrecisionName(precision);
    378 	vertexParams["DATATYPE"]	= glu::getDataTypeName(coordType);
    379 
    380 	return tcu::StringTemplate(vertexTmpl).specialize(vertexParams);
    381 }
    382 
    383 inline tcu::IVec2 TriangleDerivateCase::getViewportSize (void) const
    384 {
    385 	if (m_surfaceType == SURFACETYPE_DEFAULT_FRAMEBUFFER)
    386 	{
    387 		const int	width	= de::min<int>(m_context.getRenderTarget().getWidth(),	VIEWPORT_WIDTH);
    388 		const int	height	= de::min<int>(m_context.getRenderTarget().getHeight(),	VIEWPORT_HEIGHT);
    389 		return tcu::IVec2(width, height);
    390 	}
    391 	else
    392 		return tcu::IVec2(FBO_WIDTH, FBO_HEIGHT);
    393 }
    394 
    395 TriangleDerivateCase::IterateResult TriangleDerivateCase::iterate (void)
    396 {
    397 	const glw::Functions&		gl				= m_context.getRenderContext().getFunctions();
    398 	const glu::ShaderProgram	program			(m_context.getRenderContext(), glu::makeVtxFragSources(genVertexSource(m_coordDataType, m_coordPrecision), m_fragmentSrc));
    399 	de::Random					rnd				(deStringHash(getName()) ^ 0xbbc24);
    400 	const bool					useFbo			= m_surfaceType != SURFACETYPE_DEFAULT_FRAMEBUFFER;
    401 	const deUint32				fboFormat		= m_surfaceType == SURFACETYPE_FLOAT_FBO ? GL_RGBA32UI : GL_RGBA8;
    402 	const tcu::IVec2			viewportSize	= getViewportSize();
    403 	const int					viewportX		= useFbo ? 0 : rnd.getInt(0, m_context.getRenderTarget().getWidth()		- viewportSize.x());
    404 	const int					viewportY		= useFbo ? 0 : rnd.getInt(0, m_context.getRenderTarget().getHeight()	- viewportSize.y());
    405 	AutoFbo						fbo				(gl);
    406 	AutoRbo						rbo				(gl);
    407 	tcu::TextureLevel			result;
    408 
    409 	m_testCtx.getLog() << program;
    410 
    411 	if (!program.isOk())
    412 		TCU_FAIL("Compile failed");
    413 
    414 	if (useFbo)
    415 	{
    416 		m_testCtx.getLog() << TestLog::Message
    417 						   << "Rendering to FBO, format = " << glu::getPixelFormatStr(fboFormat)
    418 						   << ", samples = " << m_numSamples
    419 						   << TestLog::EndMessage;
    420 
    421 		fbo.gen();
    422 		rbo.gen();
    423 
    424 		gl.bindRenderbuffer(GL_RENDERBUFFER, *rbo);
    425 		gl.renderbufferStorageMultisample(GL_RENDERBUFFER, m_numSamples, fboFormat, viewportSize.x(), viewportSize.y());
    426 		gl.bindFramebuffer(GL_FRAMEBUFFER, *fbo);
    427 		gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *rbo);
    428 		TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
    429 	}
    430 	else
    431 		m_testCtx.getLog() << TestLog::Message << "Rendering to default framebuffer" << TestLog::EndMessage;
    432 
    433 	m_testCtx.getLog() << TestLog::Message << "in: " << m_coordMin << " -> " << m_coordMax << "\n"
    434 										   << "v_coord.x = in.x * x\n"
    435 										   << "v_coord.y = in.y * y\n"
    436 										   << "v_coord.z = in.z * (x+y)/2\n"
    437 										   << "v_coord.w = in.w * (1 - (x+y)/2)\n"
    438 					   << TestLog::EndMessage
    439 					   << TestLog::Message << "u_scale: " << m_derivScale << ", u_bias: " << m_derivBias << " (displayed values have scale/bias removed)" << TestLog::EndMessage
    440 					   << TestLog::Message << "Viewport: " << viewportSize.x() << "x" << viewportSize.y() << TestLog::EndMessage
    441 					   << TestLog::Message << "GL_FRAGMENT_SHADER_DERIVATE_HINT: " << glu::getHintModeStr(m_hint) << TestLog::EndMessage;
    442 
    443 	// Draw
    444 	{
    445 		const float positions[] =
    446 		{
    447 			-1.0f, -1.0f, 0.0f, 1.0f,
    448 			-1.0f,  1.0f, 0.0f, 1.0f,
    449 			 1.0f, -1.0f, 0.0f, 1.0f,
    450 			 1.0f,  1.0f, 0.0f, 1.0f
    451 		};
    452 		const float coords[] =
    453 		{
    454 			m_coordMin.x(), m_coordMin.y(), m_coordMin.z(),							m_coordMax.w(),
    455 			m_coordMin.x(), m_coordMax.y(), (m_coordMin.z()+m_coordMax.z())*0.5f,	(m_coordMin.w()+m_coordMax.w())*0.5f,
    456 			m_coordMax.x(), m_coordMin.y(), (m_coordMin.z()+m_coordMax.z())*0.5f,	(m_coordMin.w()+m_coordMax.w())*0.5f,
    457 			m_coordMax.x(), m_coordMax.y(), m_coordMax.z(),							m_coordMin.w()
    458 		};
    459 		const glu::VertexArrayBinding vertexArrays[] =
    460 		{
    461 			glu::va::Float("a_position",	4, 4, 0, &positions[0]),
    462 			glu::va::Float("a_coord",		4, 4, 0, &coords[0])
    463 		};
    464 		const deUint16 indices[] = { 0, 2, 1, 2, 3, 1 };
    465 
    466 		gl.clearColor(0.125f, 0.25f, 0.5f, 1.0f);
    467 		gl.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
    468 
    469 		gl.useProgram(program.getProgram());
    470 
    471 		{
    472 			const int	scaleLoc	= gl.getUniformLocation(program.getProgram(), "u_scale");
    473 			const int	biasLoc		= gl.getUniformLocation(program.getProgram(), "u_bias");
    474 
    475 			switch (m_dataType)
    476 			{
    477 				case glu::TYPE_FLOAT:
    478 					gl.uniform1f(scaleLoc, m_derivScale.x());
    479 					gl.uniform1f(biasLoc, m_derivBias.x());
    480 					break;
    481 
    482 				case glu::TYPE_FLOAT_VEC2:
    483 					gl.uniform2fv(scaleLoc, 1, m_derivScale.getPtr());
    484 					gl.uniform2fv(biasLoc, 1, m_derivBias.getPtr());
    485 					break;
    486 
    487 				case glu::TYPE_FLOAT_VEC3:
    488 					gl.uniform3fv(scaleLoc, 1, m_derivScale.getPtr());
    489 					gl.uniform3fv(biasLoc, 1, m_derivBias.getPtr());
    490 					break;
    491 
    492 				case glu::TYPE_FLOAT_VEC4:
    493 					gl.uniform4fv(scaleLoc, 1, m_derivScale.getPtr());
    494 					gl.uniform4fv(biasLoc, 1, m_derivBias.getPtr());
    495 					break;
    496 
    497 				default:
    498 					DE_ASSERT(false);
    499 			}
    500 		}
    501 
    502 		gls::setupDefaultUniforms(m_context.getRenderContext(), program.getProgram());
    503 		setupRenderState(program.getProgram());
    504 
    505 		gl.hint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, m_hint);
    506 		GLU_EXPECT_NO_ERROR(gl.getError(), "Setup program state");
    507 
    508 		gl.viewport(viewportX, viewportY, viewportSize.x(), viewportSize.y());
    509 		glu::draw(m_context.getRenderContext(), program.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), &vertexArrays[0],
    510 				  glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
    511 		GLU_EXPECT_NO_ERROR(gl.getError(), "Draw");
    512 	}
    513 
    514 	// Read back results
    515 	{
    516 		const bool		isMSAA		= useFbo && m_numSamples > 0;
    517 		AutoFbo			resFbo		(gl);
    518 		AutoRbo			resRbo		(gl);
    519 
    520 		// Resolve if necessary
    521 		if (isMSAA)
    522 		{
    523 			resFbo.gen();
    524 			resRbo.gen();
    525 
    526 			gl.bindRenderbuffer(GL_RENDERBUFFER, *resRbo);
    527 			gl.renderbufferStorageMultisample(GL_RENDERBUFFER, 0, fboFormat, viewportSize.x(), viewportSize.y());
    528 			gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, *resFbo);
    529 			gl.framebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *resRbo);
    530 			TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
    531 
    532 			gl.blitFramebuffer(0, 0, viewportSize.x(), viewportSize.y(), 0, 0, viewportSize.x(), viewportSize.y(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
    533 			GLU_EXPECT_NO_ERROR(gl.getError(), "Resolve blit");
    534 
    535 			gl.bindFramebuffer(GL_READ_FRAMEBUFFER, *resFbo);
    536 		}
    537 
    538 		switch (m_surfaceType)
    539 		{
    540 			case SURFACETYPE_DEFAULT_FRAMEBUFFER:
    541 			case SURFACETYPE_UNORM_FBO:
    542 				result.setStorage(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), viewportSize.x(), viewportSize.y());
    543 				glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, result);
    544 				break;
    545 
    546 			case SURFACETYPE_FLOAT_FBO:
    547 			{
    548 				const tcu::TextureFormat	dataFormat		(tcu::TextureFormat::RGBA, tcu::TextureFormat::FLOAT);
    549 				const tcu::TextureFormat	transferFormat	(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNSIGNED_INT32);
    550 
    551 				result.setStorage(dataFormat, viewportSize.x(), viewportSize.y());
    552 				glu::readPixels(m_context.getRenderContext(), viewportX, viewportY,
    553 								tcu::PixelBufferAccess(transferFormat, result.getWidth(), result.getHeight(), result.getDepth(), result.getAccess().getDataPtr()));
    554 				break;
    555 			}
    556 
    557 			default:
    558 				DE_ASSERT(false);
    559 		}
    560 
    561 		GLU_EXPECT_NO_ERROR(gl.getError(), "Read pixels");
    562 	}
    563 
    564 	// Verify
    565 	{
    566 		tcu::Surface errorMask(result.getWidth(), result.getHeight());
    567 		tcu::clear(errorMask.getAccess(), tcu::RGBA::green.toVec());
    568 
    569 		const bool isOk = verify(result.getAccess(), errorMask.getAccess());
    570 
    571 		m_testCtx.getLog() << TestLog::ImageSet("Result", "Result images")
    572 						   << TestLog::Image("Rendered", "Rendered image", result);
    573 
    574 		if (!isOk)
    575 			m_testCtx.getLog() << TestLog::Image("ErrorMask", "Error mask", errorMask);
    576 
    577 		m_testCtx.getLog() << TestLog::EndImageSet;
    578 
    579 		m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
    580 								isOk ? "Pass"				: "Image comparison failed");
    581 	}
    582 
    583 	return STOP;
    584 }
    585 
    586 tcu::Vec4 TriangleDerivateCase::getSurfaceThreshold (void) const
    587 {
    588 	switch (m_surfaceType)
    589 	{
    590 		case SURFACETYPE_DEFAULT_FRAMEBUFFER:
    591 		{
    592 			const tcu::PixelFormat	pixelFormat		= m_context.getRenderTarget().getPixelFormat();
    593 			const tcu::IVec4		channelBits		(pixelFormat.redBits, pixelFormat.greenBits, pixelFormat.blueBits, pixelFormat.alphaBits);
    594 			const tcu::IVec4		intThreshold	= tcu::IVec4(1) << (8 - channelBits);
    595 			const tcu::Vec4			normThreshold	= intThreshold.asFloat() / 255.0f;
    596 
    597 			return normThreshold;
    598 		}
    599 
    600 		case SURFACETYPE_UNORM_FBO:				return tcu::IVec4(1).asFloat() / 255.0f;
    601 		case SURFACETYPE_FLOAT_FBO:				return tcu::Vec4(0.0f);
    602 		default:
    603 			DE_ASSERT(false);
    604 			return tcu::Vec4(0.0f);
    605 	}
    606 }
    607 
    608 // ConstantDerivateCase
    609 
    610 class ConstantDerivateCase : public TriangleDerivateCase
    611 {
    612 public:
    613 						ConstantDerivateCase		(Context& context, const char* name, const char* description, DerivateFunc func, glu::DataType type);
    614 						~ConstantDerivateCase		(void) {}
    615 
    616 	void				init						(void);
    617 
    618 protected:
    619 	bool				verify						(const tcu::ConstPixelBufferAccess& result, const tcu::PixelBufferAccess& errorMask);
    620 
    621 private:
    622 	DerivateFunc		m_func;
    623 };
    624 
    625 ConstantDerivateCase::ConstantDerivateCase (Context& context, const char* name, const char* description, DerivateFunc func, glu::DataType type)
    626 	: TriangleDerivateCase	(context, name, description)
    627 	, m_func				(func)
    628 {
    629 	m_dataType			= type;
    630 	m_precision			= glu::PRECISION_HIGHP;
    631 	m_coordDataType		= m_dataType;
    632 	m_coordPrecision	= m_precision;
    633 }
    634 
    635 void ConstantDerivateCase::init (void)
    636 {
    637 	const char* fragmentTmpl =
    638 		"#version 300 es\n"
    639 		"layout(location = 0) out mediump vec4 o_color;\n"
    640 		"uniform ${PRECISION} ${DATATYPE} u_scale;\n"
    641 		"uniform ${PRECISION} ${DATATYPE} u_bias;\n"
    642 		"void main (void)\n"
    643 		"{\n"
    644 		"	${PRECISION} ${DATATYPE} res = ${FUNC}(${VALUE}) * u_scale + u_bias;\n"
    645 		"	o_color = ${CAST_TO_OUTPUT};\n"
    646 		"}\n";
    647 	map<string, string> fragmentParams;
    648 	fragmentParams["PRECISION"]			= glu::getPrecisionName(m_precision);
    649 	fragmentParams["DATATYPE"]			= glu::getDataTypeName(m_dataType);
    650 	fragmentParams["FUNC"]				= getDerivateFuncName(m_func);
    651 	fragmentParams["VALUE"]				= m_dataType == glu::TYPE_FLOAT_VEC4 ? "vec4(1.0, 7.2, -1e5, 0.0)" :
    652 										  m_dataType == glu::TYPE_FLOAT_VEC3 ? "vec3(1e2, 8.0, 0.01)" :
    653 										  m_dataType == glu::TYPE_FLOAT_VEC2 ? "vec2(-0.0, 2.7)" :
    654 										  /* TYPE_FLOAT */					   "7.7";
    655 	fragmentParams["CAST_TO_OUTPUT"]	= m_dataType == glu::TYPE_FLOAT_VEC4 ? "res" :
    656 										  m_dataType == glu::TYPE_FLOAT_VEC3 ? "vec4(res, 1.0)" :
    657 										  m_dataType == glu::TYPE_FLOAT_VEC2 ? "vec4(res, 0.0, 1.0)" :
    658 										  /* TYPE_FLOAT */					   "vec4(res, 0.0, 0.0, 1.0)";
    659 
    660 	m_fragmentSrc = tcu::StringTemplate(fragmentTmpl).specialize(fragmentParams);
    661 
    662 	m_derivScale	= tcu::Vec4(1e3f, 1e3f, 1e3f, 1e3f);
    663 	m_derivBias		= tcu::Vec4(0.5f, 0.5f, 0.5f, 0.5f);
    664 }
    665 
    666 bool ConstantDerivateCase::verify (const tcu::ConstPixelBufferAccess& result, const tcu::PixelBufferAccess& errorMask)
    667 {
    668 	const tcu::Vec4 reference	(0.0f); // Derivate of constant argument should always be 0
    669 	const tcu::Vec4	threshold	= getSurfaceThreshold() / abs(m_derivScale);
    670 
    671 	return verifyConstantDerivate(m_testCtx.getLog(), result, errorMask, m_dataType,
    672 								  reference, threshold, m_derivScale, m_derivBias);
    673 }
    674 
    675 // LinearDerivateCase
    676 
    677 class LinearDerivateCase : public TriangleDerivateCase
    678 {
    679 public:
    680 						LinearDerivateCase		(Context& context, const char* name, const char* description, DerivateFunc func, glu::DataType type, glu::Precision precision, deUint32 hint, SurfaceType surfaceType, int numSamples, const char* fragmentSrcTmpl);
    681 						~LinearDerivateCase		(void) {}
    682 
    683 	void				init					(void);
    684 
    685 protected:
    686 	bool				verify					(const tcu::ConstPixelBufferAccess& result, const tcu::PixelBufferAccess& errorMask);
    687 
    688 private:
    689 	DerivateFunc		m_func;
    690 	std::string			m_fragmentTmpl;
    691 };
    692 
    693 LinearDerivateCase::LinearDerivateCase (Context& context, const char* name, const char* description, DerivateFunc func, glu::DataType type, glu::Precision precision, deUint32 hint, SurfaceType surfaceType, int numSamples, const char* fragmentSrcTmpl)
    694 	: TriangleDerivateCase	(context, name, description)
    695 	, m_func				(func)
    696 	, m_fragmentTmpl		(fragmentSrcTmpl)
    697 {
    698 	m_dataType			= type;
    699 	m_precision			= precision;
    700 	m_coordDataType		= m_dataType;
    701 	m_coordPrecision	= m_precision;
    702 	m_hint				= hint;
    703 	m_surfaceType		= surfaceType;
    704 	m_numSamples		= numSamples;
    705 }
    706 
    707 void LinearDerivateCase::init (void)
    708 {
    709 	const tcu::IVec2	viewportSize	= getViewportSize();
    710 	const float			w				= float(viewportSize.x());
    711 	const float			h				= float(viewportSize.y());
    712 	const bool			packToInt		= m_surfaceType == SURFACETYPE_FLOAT_FBO;
    713 	map<string, string>	fragmentParams;
    714 
    715 	fragmentParams["OUTPUT_TYPE"]		= glu::getDataTypeName(packToInt ? glu::TYPE_UINT_VEC4 : glu::TYPE_FLOAT_VEC4);
    716 	fragmentParams["OUTPUT_PREC"]		= glu::getPrecisionName(packToInt ? glu::PRECISION_HIGHP : m_precision);
    717 	fragmentParams["PRECISION"]			= glu::getPrecisionName(m_precision);
    718 	fragmentParams["DATATYPE"]			= glu::getDataTypeName(m_dataType);
    719 	fragmentParams["FUNC"]				= getDerivateFuncName(m_func);
    720 
    721 	if (packToInt)
    722 	{
    723 		fragmentParams["CAST_TO_OUTPUT"]	= m_dataType == glu::TYPE_FLOAT_VEC4 ? "floatBitsToUint(res)" :
    724 											  m_dataType == glu::TYPE_FLOAT_VEC3 ? "floatBitsToUint(vec4(res, 1.0))" :
    725 											  m_dataType == glu::TYPE_FLOAT_VEC2 ? "floatBitsToUint(vec4(res, 0.0, 1.0))" :
    726 											  /* TYPE_FLOAT */					   "floatBitsToUint(vec4(res, 0.0, 0.0, 1.0))";
    727 	}
    728 	else
    729 	{
    730 		fragmentParams["CAST_TO_OUTPUT"]	= m_dataType == glu::TYPE_FLOAT_VEC4 ? "res" :
    731 											  m_dataType == glu::TYPE_FLOAT_VEC3 ? "vec4(res, 1.0)" :
    732 											  m_dataType == glu::TYPE_FLOAT_VEC2 ? "vec4(res, 0.0, 1.0)" :
    733 											  /* TYPE_FLOAT */					   "vec4(res, 0.0, 0.0, 1.0)";
    734 	}
    735 
    736 	m_fragmentSrc = tcu::StringTemplate(m_fragmentTmpl.c_str()).specialize(fragmentParams);
    737 
    738 	switch (m_precision)
    739 	{
    740 		case glu::PRECISION_HIGHP:
    741 			m_coordMin = tcu::Vec4(-97.f, 0.2f, 71.f, 74.f);
    742 			m_coordMax = tcu::Vec4(-13.2f, -77.f, 44.f, 76.f);
    743 			break;
    744 
    745 		case glu::PRECISION_MEDIUMP:
    746 			m_coordMin = tcu::Vec4(-37.0f, 47.f, -7.f, 0.0f);
    747 			m_coordMax = tcu::Vec4(-1.0f, 12.f, 7.f, 19.f);
    748 			break;
    749 
    750 		case glu::PRECISION_LOWP:
    751 			m_coordMin = tcu::Vec4(0.0f, -1.0f, 0.0f, 1.0f);
    752 			m_coordMax = tcu::Vec4(1.0f, 1.0f, -1.0f, -1.0f);
    753 			break;
    754 
    755 		default:
    756 			DE_ASSERT(false);
    757 	}
    758 
    759 	if (m_surfaceType == SURFACETYPE_FLOAT_FBO)
    760 	{
    761 		// No scale or bias used for accuracy.
    762 		m_derivScale	= tcu::Vec4(1.0f);
    763 		m_derivBias		= tcu::Vec4(0.0f);
    764 	}
    765 	else
    766 	{
    767 		// Compute scale - bias that normalizes to 0..1 range.
    768 		const tcu::Vec4 dx = (m_coordMax - m_coordMin) / tcu::Vec4(w, w, w*0.5f, -w*0.5f);
    769 		const tcu::Vec4 dy = (m_coordMax - m_coordMin) / tcu::Vec4(h, h, h*0.5f, -h*0.5f);
    770 
    771 		switch (m_func)
    772 		{
    773 			case DERIVATE_DFDX:
    774 				m_derivScale = 0.5f / dx;
    775 				break;
    776 
    777 			case DERIVATE_DFDY:
    778 				m_derivScale = 0.5f / dy;
    779 				break;
    780 
    781 			case DERIVATE_FWIDTH:
    782 				m_derivScale = 0.5f / (tcu::abs(dx) + tcu::abs(dy));
    783 				break;
    784 
    785 			default:
    786 				DE_ASSERT(false);
    787 		}
    788 
    789 		m_derivBias = tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f);
    790 	}
    791 }
    792 
    793 bool LinearDerivateCase::verify (const tcu::ConstPixelBufferAccess& result, const tcu::PixelBufferAccess& errorMask)
    794 {
    795 	const tcu::Vec4		xScale				= tcu::Vec4(1.0f, 0.0f, 0.5f, -0.5f);
    796 	const tcu::Vec4		yScale				= tcu::Vec4(0.0f, 1.0f, 0.5f, -0.5f);
    797 	const tcu::Vec4		surfaceThreshold	= getSurfaceThreshold() / abs(m_derivScale);
    798 
    799 	if (m_func == DERIVATE_DFDX || m_func == DERIVATE_DFDY)
    800 	{
    801 		const bool			isX			= m_func == DERIVATE_DFDX;
    802 		const float			div			= isX ? float(result.getWidth()) : float(result.getHeight());
    803 		const tcu::Vec4		scale		= isX ? xScale : yScale;
    804 		const tcu::Vec4		reference	= ((m_coordMax - m_coordMin) / div) * scale;
    805 		const tcu::Vec4		opThreshold	= getDerivateThreshold(m_precision, m_coordMin*scale, m_coordMax*scale, reference);
    806 		const tcu::Vec4		threshold	= max(surfaceThreshold, opThreshold);
    807 
    808 		return verifyConstantDerivate(m_testCtx.getLog(), result, errorMask, m_dataType,
    809 									  reference, threshold, m_derivScale, m_derivBias);
    810 	}
    811 	else
    812 	{
    813 		DE_ASSERT(m_func == DERIVATE_FWIDTH);
    814 		const float			w			= float(result.getWidth());
    815 		const float			h			= float(result.getHeight());
    816 
    817 		const tcu::Vec4		dx			= ((m_coordMax - m_coordMin) / w) * xScale;
    818 		const tcu::Vec4		dy			= ((m_coordMax - m_coordMin) / h) * yScale;
    819 		const tcu::Vec4		reference	= tcu::abs(dx) + tcu::abs(dy);
    820 		const tcu::Vec4		dxThreshold	= getDerivateThreshold(m_precision, m_coordMin*xScale, m_coordMax*xScale, dx);
    821 		const tcu::Vec4		dyThreshold	= getDerivateThreshold(m_precision, m_coordMin*yScale, m_coordMax*yScale, dy);
    822 		const tcu::Vec4		threshold	= max(surfaceThreshold, max(dxThreshold, dyThreshold));
    823 
    824 		return verifyConstantDerivate(m_testCtx.getLog(), result, errorMask, m_dataType,
    825 									  reference, threshold, m_derivScale, m_derivBias);
    826 	}
    827 }
    828 
    829 // TextureDerivateCase
    830 
    831 class TextureDerivateCase : public TriangleDerivateCase
    832 {
    833 public:
    834 						TextureDerivateCase		(Context& context, const char* name, const char* description, DerivateFunc func, glu::DataType type, glu::Precision precision, deUint32 hint, SurfaceType surfaceType, int numSamples);
    835 						~TextureDerivateCase	(void);
    836 
    837 	void				init					(void);
    838 	void				deinit					(void);
    839 
    840 protected:
    841 	void				setupRenderState		(deUint32 program);
    842 	bool				verify					(const tcu::ConstPixelBufferAccess& result, const tcu::PixelBufferAccess& errorMask);
    843 
    844 private:
    845 	DerivateFunc		m_func;
    846 
    847 	tcu::Vec4			m_texValueMin;
    848 	tcu::Vec4			m_texValueMax;
    849 	glu::Texture2D*		m_texture;
    850 };
    851 
    852 TextureDerivateCase::TextureDerivateCase (Context& context, const char* name, const char* description, DerivateFunc func, glu::DataType type, glu::Precision precision, deUint32 hint, SurfaceType surfaceType, int numSamples)
    853 	: TriangleDerivateCase	(context, name, description)
    854 	, m_func				(func)
    855 	, m_texture				(DE_NULL)
    856 {
    857 	m_dataType			= type;
    858 	m_precision			= precision;
    859 	m_coordDataType		= glu::TYPE_FLOAT_VEC2;
    860 	m_coordPrecision	= glu::PRECISION_HIGHP;
    861 	m_hint				= hint;
    862 	m_surfaceType		= surfaceType;
    863 	m_numSamples		= numSamples;
    864 }
    865 
    866 TextureDerivateCase::~TextureDerivateCase (void)
    867 {
    868 	delete m_texture;
    869 }
    870 
    871 void TextureDerivateCase::init (void)
    872 {
    873 	// Generate shader
    874 	{
    875 		const char* fragmentTmpl =
    876 			"#version 300 es\n"
    877 			"in highp vec2 v_coord;\n"
    878 			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
    879 			"uniform ${PRECISION} sampler2D u_sampler;\n"
    880 			"uniform ${PRECISION} ${DATATYPE} u_scale;\n"
    881 			"uniform ${PRECISION} ${DATATYPE} u_bias;\n"
    882 			"void main (void)\n"
    883 			"{\n"
    884 			"	${PRECISION} vec4 tex = texture(u_sampler, v_coord);\n"
    885 			"	${PRECISION} ${DATATYPE} res = ${FUNC}(tex${SWIZZLE}) * u_scale + u_bias;\n"
    886 			"	o_color = ${CAST_TO_OUTPUT};\n"
    887 			"}\n";
    888 
    889 		const bool			packToInt		= m_surfaceType == SURFACETYPE_FLOAT_FBO;
    890 		map<string, string> fragmentParams;
    891 
    892 		fragmentParams["OUTPUT_TYPE"]		= glu::getDataTypeName(packToInt ? glu::TYPE_UINT_VEC4 : glu::TYPE_FLOAT_VEC4);
    893 		fragmentParams["OUTPUT_PREC"]		= glu::getPrecisionName(packToInt ? glu::PRECISION_HIGHP : m_precision);
    894 		fragmentParams["PRECISION"]			= glu::getPrecisionName(m_precision);
    895 		fragmentParams["DATATYPE"]			= glu::getDataTypeName(m_dataType);
    896 		fragmentParams["FUNC"]				= getDerivateFuncName(m_func);
    897 		fragmentParams["SWIZZLE"]			= m_dataType == glu::TYPE_FLOAT_VEC4 ? "" :
    898 											  m_dataType == glu::TYPE_FLOAT_VEC3 ? ".xyz" :
    899 											  m_dataType == glu::TYPE_FLOAT_VEC2 ? ".xy" :
    900 											  /* TYPE_FLOAT */					   ".x";
    901 
    902 		if (packToInt)
    903 		{
    904 			fragmentParams["CAST_TO_OUTPUT"]	= m_dataType == glu::TYPE_FLOAT_VEC4 ? "floatBitsToUint(res)" :
    905 												  m_dataType == glu::TYPE_FLOAT_VEC3 ? "floatBitsToUint(vec4(res, 1.0))" :
    906 												  m_dataType == glu::TYPE_FLOAT_VEC2 ? "floatBitsToUint(vec4(res, 0.0, 1.0))" :
    907 												  /* TYPE_FLOAT */					   "floatBitsToUint(vec4(res, 0.0, 0.0, 1.0))";
    908 		}
    909 		else
    910 		{
    911 			fragmentParams["CAST_TO_OUTPUT"]	= m_dataType == glu::TYPE_FLOAT_VEC4 ? "res" :
    912 												  m_dataType == glu::TYPE_FLOAT_VEC3 ? "vec4(res, 1.0)" :
    913 												  m_dataType == glu::TYPE_FLOAT_VEC2 ? "vec4(res, 0.0, 1.0)" :
    914 												  /* TYPE_FLOAT */					   "vec4(res, 0.0, 0.0, 1.0)";
    915 		}
    916 
    917 		m_fragmentSrc = tcu::StringTemplate(fragmentTmpl).specialize(fragmentParams);
    918 	}
    919 
    920 	// Texture size matches viewport and nearest sampling is used. Thus texture sampling
    921 	// is equal to just interpolating the texture value range.
    922 
    923 	// Determine value range for texture.
    924 
    925 	switch (m_precision)
    926 	{
    927 		case glu::PRECISION_HIGHP:
    928 			m_texValueMin = tcu::Vec4(-97.f, 0.2f, 71.f, 74.f);
    929 			m_texValueMax = tcu::Vec4(-13.2f, -77.f, 44.f, 76.f);
    930 			break;
    931 
    932 		case glu::PRECISION_MEDIUMP:
    933 			m_texValueMin = tcu::Vec4(-37.0f, 47.f, -7.f, 0.0f);
    934 			m_texValueMax = tcu::Vec4(-1.0f, 12.f, 7.f, 19.f);
    935 			break;
    936 
    937 		case glu::PRECISION_LOWP:
    938 			m_texValueMin = tcu::Vec4(0.0f, -1.0f, 0.0f, 1.0f);
    939 			m_texValueMax = tcu::Vec4(1.0f, 1.0f, -1.0f, -1.0f);
    940 			break;
    941 
    942 		default:
    943 			DE_ASSERT(false);
    944 	}
    945 
    946 	// Lowp and mediump cases use RGBA16F format, while highp uses RGBA32F.
    947 	{
    948 		const tcu::IVec2 viewportSize = getViewportSize();
    949 		DE_ASSERT(!m_texture);
    950 		m_texture = new glu::Texture2D(m_context.getRenderContext(), m_precision == glu::PRECISION_HIGHP ? GL_RGBA32F : GL_RGBA16F, viewportSize.x(), viewportSize.y());
    951 		m_texture->getRefTexture().allocLevel(0);
    952 	}
    953 
    954 	// Texture coordinates
    955 	m_coordMin = tcu::Vec4(0.0f);
    956 	m_coordMax = tcu::Vec4(1.0f);
    957 
    958 	// Fill with gradients.
    959 	{
    960 		const tcu::PixelBufferAccess level0 = m_texture->getRefTexture().getLevel(0);
    961 		for (int y = 0; y < level0.getHeight(); y++)
    962 		{
    963 			for (int x = 0; x < level0.getWidth(); x++)
    964 			{
    965 				const float		xf		= (float(x)+0.5f) / float(level0.getWidth());
    966 				const float		yf		= (float(y)+0.5f) / float(level0.getHeight());
    967 				const tcu::Vec4	s		= tcu::Vec4(xf, yf, (xf+yf)/2.0f, 1.0f - (xf+yf)/2.0f);
    968 
    969 				level0.setPixel(m_texValueMin + (m_texValueMax - m_texValueMin)*s, x, y);
    970 			}
    971 		}
    972 	}
    973 
    974 	m_texture->upload();
    975 
    976 	if (m_surfaceType == SURFACETYPE_FLOAT_FBO)
    977 	{
    978 		// No scale or bias used for accuracy.
    979 		m_derivScale	= tcu::Vec4(1.0f);
    980 		m_derivBias		= tcu::Vec4(0.0f);
    981 	}
    982 	else
    983 	{
    984 		// Compute scale - bias that normalizes to 0..1 range.
    985 		const tcu::IVec2	viewportSize	= getViewportSize();
    986 		const float			w				= float(viewportSize.x());
    987 		const float			h				= float(viewportSize.y());
    988 		const tcu::Vec4		dx				= (m_texValueMax - m_texValueMin) / tcu::Vec4(w, w, w*0.5f, -w*0.5f);
    989 		const tcu::Vec4		dy				= (m_texValueMax - m_texValueMin) / tcu::Vec4(h, h, h*0.5f, -h*0.5f);
    990 
    991 		switch (m_func)
    992 		{
    993 			case DERIVATE_DFDX:
    994 				m_derivScale = 0.5f / dx;
    995 				break;
    996 
    997 			case DERIVATE_DFDY:
    998 				m_derivScale = 0.5f / dy;
    999 				break;
   1000 
   1001 			case DERIVATE_FWIDTH:
   1002 				m_derivScale = 0.5f / (tcu::abs(dx) + tcu::abs(dy));
   1003 				break;
   1004 
   1005 			default:
   1006 				DE_ASSERT(false);
   1007 		}
   1008 
   1009 		m_derivBias = tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f);
   1010 	}
   1011 }
   1012 
   1013 void TextureDerivateCase::deinit (void)
   1014 {
   1015 	delete m_texture;
   1016 	m_texture = DE_NULL;
   1017 }
   1018 
   1019 void TextureDerivateCase::setupRenderState (deUint32 program)
   1020 {
   1021 	const glw::Functions&	gl			= m_context.getRenderContext().getFunctions();
   1022 	const int				texUnit		= 1;
   1023 
   1024 	gl.activeTexture	(GL_TEXTURE0+texUnit);
   1025 	gl.bindTexture		(GL_TEXTURE_2D, m_texture->getGLTexture());
   1026 	gl.texParameteri	(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,	GL_NEAREST);
   1027 	gl.texParameteri	(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,	GL_NEAREST);
   1028 	gl.texParameteri	(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,		GL_CLAMP_TO_EDGE);
   1029 	gl.texParameteri	(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,		GL_CLAMP_TO_EDGE);
   1030 
   1031 	gl.uniform1i		(gl.getUniformLocation(program, "u_sampler"), texUnit);
   1032 }
   1033 
   1034 bool TextureDerivateCase::verify (const tcu::ConstPixelBufferAccess& result, const tcu::PixelBufferAccess& errorMask)
   1035 {
   1036 	// \note Edges are ignored in comparison
   1037 	if (result.getWidth() < 2 || result.getHeight() < 2)
   1038 		throw tcu::NotSupportedError("Too small viewport");
   1039 
   1040 	tcu::ConstPixelBufferAccess	compareArea			= tcu::getSubregion(result, 1, 1, result.getWidth()-2, result.getHeight()-2);
   1041 	tcu::PixelBufferAccess		maskArea			= tcu::getSubregion(errorMask, 1, 1, errorMask.getWidth()-2, errorMask.getHeight()-2);
   1042 	const tcu::Vec4				xScale				= tcu::Vec4(1.0f, 0.0f, 0.5f, -0.5f);
   1043 	const tcu::Vec4				yScale				= tcu::Vec4(0.0f, 1.0f, 0.5f, -0.5f);
   1044 	const float					w					= float(result.getWidth());
   1045 	const float					h					= float(result.getHeight());
   1046 
   1047 	const tcu::Vec4				surfaceThreshold	= getSurfaceThreshold() / abs(m_derivScale);
   1048 
   1049 	if (m_func == DERIVATE_DFDX || m_func == DERIVATE_DFDY)
   1050 	{
   1051 		const bool			isX			= m_func == DERIVATE_DFDX;
   1052 		const float			div			= isX ? w : h;
   1053 		const tcu::Vec4		scale		= isX ? xScale : yScale;
   1054 		const tcu::Vec4		reference	= ((m_texValueMax - m_texValueMin) / div) * scale;
   1055 		const tcu::Vec4		opThreshold	= getDerivateThreshold(m_precision, m_texValueMin*scale, m_texValueMax*scale, reference);
   1056 		const tcu::Vec4		threshold	= max(surfaceThreshold, opThreshold);
   1057 
   1058 		return verifyConstantDerivate(m_testCtx.getLog(), compareArea, maskArea, m_dataType,
   1059 									  reference, threshold, m_derivScale, m_derivBias);
   1060 	}
   1061 	else
   1062 	{
   1063 		DE_ASSERT(m_func == DERIVATE_FWIDTH);
   1064 		const tcu::Vec4	dx			= ((m_texValueMax - m_texValueMin) / w) * xScale;
   1065 		const tcu::Vec4	dy			= ((m_texValueMax - m_texValueMin) / h) * yScale;
   1066 		const tcu::Vec4	reference	= tcu::abs(dx) + tcu::abs(dy);
   1067 		const tcu::Vec4	dxThreshold	= getDerivateThreshold(m_precision, m_texValueMin*xScale, m_texValueMax*xScale, dx);
   1068 		const tcu::Vec4	dyThreshold	= getDerivateThreshold(m_precision, m_texValueMin*yScale, m_texValueMax*yScale, dy);
   1069 		const tcu::Vec4	threshold	= max(surfaceThreshold, max(dxThreshold, dyThreshold));
   1070 
   1071 		return verifyConstantDerivate(m_testCtx.getLog(), compareArea, maskArea, m_dataType,
   1072 									  reference, threshold, m_derivScale, m_derivBias);
   1073 	}
   1074 }
   1075 
   1076 ShaderDerivateTests::ShaderDerivateTests (Context& context)
   1077 	: TestCaseGroup(context, "derivate", "Derivate Function Tests")
   1078 {
   1079 }
   1080 
   1081 ShaderDerivateTests::~ShaderDerivateTests (void)
   1082 {
   1083 }
   1084 
   1085 struct FunctionSpec
   1086 {
   1087 	std::string		name;
   1088 	DerivateFunc	function;
   1089 	glu::DataType	dataType;
   1090 	glu::Precision	precision;
   1091 
   1092 	FunctionSpec (const std::string& name_, DerivateFunc function_, glu::DataType dataType_, glu::Precision precision_)
   1093 		: name		(name_)
   1094 		, function	(function_)
   1095 		, dataType	(dataType_)
   1096 		, precision	(precision_)
   1097 	{
   1098 	}
   1099 };
   1100 
   1101 void ShaderDerivateTests::init (void)
   1102 {
   1103 	static const struct
   1104 	{
   1105 		const char*		name;
   1106 		const char*		description;
   1107 		const char*		source;
   1108 	} s_linearDerivateCases[] =
   1109 	{
   1110 		{
   1111 			"linear",
   1112 			"Basic derivate of linearly interpolated argument",
   1113 
   1114 			"#version 300 es\n"
   1115 			"in ${PRECISION} ${DATATYPE} v_coord;\n"
   1116 			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
   1117 			"uniform ${PRECISION} ${DATATYPE} u_scale;\n"
   1118 			"uniform ${PRECISION} ${DATATYPE} u_bias;\n"
   1119 			"void main (void)\n"
   1120 			"{\n"
   1121 			"	${PRECISION} ${DATATYPE} res = ${FUNC}(v_coord) * u_scale + u_bias;\n"
   1122 			"	o_color = ${CAST_TO_OUTPUT};\n"
   1123 			"}\n"
   1124 		},
   1125 		{
   1126 			"in_function",
   1127 			"Derivate of linear function argument",
   1128 
   1129 			"#version 300 es\n"
   1130 			"in ${PRECISION} ${DATATYPE} v_coord;\n"
   1131 			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
   1132 			"uniform ${PRECISION} ${DATATYPE} u_scale;\n"
   1133 			"uniform ${PRECISION} ${DATATYPE} u_bias;\n"
   1134 			"\n"
   1135 			"${PRECISION} ${DATATYPE} computeRes (${PRECISION} ${DATATYPE} value)\n"
   1136 			"{\n"
   1137 			"	return ${FUNC}(v_coord) * u_scale + u_bias;\n"
   1138 			"}\n"
   1139 			"\n"
   1140 			"void main (void)\n"
   1141 			"{\n"
   1142 			"	${PRECISION} ${DATATYPE} res = computeRes(v_coord);\n"
   1143 			"	o_color = ${CAST_TO_OUTPUT};\n"
   1144 			"}\n"
   1145 		},
   1146 		{
   1147 			"static_if",
   1148 			"Derivate of linearly interpolated value in static if",
   1149 
   1150 			"#version 300 es\n"
   1151 			"in ${PRECISION} ${DATATYPE} v_coord;\n"
   1152 			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
   1153 			"uniform ${PRECISION} ${DATATYPE} u_scale;\n"
   1154 			"uniform ${PRECISION} ${DATATYPE} u_bias;\n"
   1155 			"void main (void)\n"
   1156 			"{\n"
   1157 			"	${PRECISION} ${DATATYPE} res;\n"
   1158 			"	if (false)\n"
   1159 			"		res = ${FUNC}(-v_coord) * u_scale + u_bias;\n"
   1160 			"	else\n"
   1161 			"		res = ${FUNC}(v_coord) * u_scale + u_bias;\n"
   1162 			"	o_color = ${CAST_TO_OUTPUT};\n"
   1163 			"}\n"
   1164 		},
   1165 		{
   1166 			"static_loop",
   1167 			"Derivate of linearly interpolated value in static loop",
   1168 
   1169 			"#version 300 es\n"
   1170 			"in ${PRECISION} ${DATATYPE} v_coord;\n"
   1171 			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
   1172 			"uniform ${PRECISION} ${DATATYPE} u_scale;\n"
   1173 			"uniform ${PRECISION} ${DATATYPE} u_bias;\n"
   1174 			"void main (void)\n"
   1175 			"{\n"
   1176 			"	${PRECISION} ${DATATYPE} res = ${DATATYPE}(0.0);\n"
   1177 			"	for (int i = 0; i < 2; i++)\n"
   1178 			"		res += ${FUNC}(v_coord * float(i));\n"
   1179 			"	res = res * u_scale + u_bias;\n"
   1180 			"	o_color = ${CAST_TO_OUTPUT};\n"
   1181 			"}\n"
   1182 		},
   1183 		{
   1184 			"static_switch",
   1185 			"Derivate of linearly interpolated value in static switch",
   1186 
   1187 			"#version 300 es\n"
   1188 			"in ${PRECISION} ${DATATYPE} v_coord;\n"
   1189 			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
   1190 			"uniform ${PRECISION} ${DATATYPE} u_scale;\n"
   1191 			"uniform ${PRECISION} ${DATATYPE} u_bias;\n"
   1192 			"void main (void)\n"
   1193 			"{\n"
   1194 			"	${PRECISION} ${DATATYPE} res;\n"
   1195 			"	switch (1)\n"
   1196 			"	{\n"
   1197 			"		case 0:	res = ${FUNC}(-v_coord) * u_scale + u_bias;	break;\n"
   1198 			"		case 1:	res = ${FUNC}(v_coord) * u_scale + u_bias;	break;\n"
   1199 			"	}\n"
   1200 			"	o_color = ${CAST_TO_OUTPUT};\n"
   1201 			"}\n"
   1202 		},
   1203 		{
   1204 			"uniform_if",
   1205 			"Derivate of linearly interpolated value in uniform if",
   1206 
   1207 			"#version 300 es\n"
   1208 			"in ${PRECISION} ${DATATYPE} v_coord;\n"
   1209 			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
   1210 			"uniform ${PRECISION} ${DATATYPE} u_scale;\n"
   1211 			"uniform ${PRECISION} ${DATATYPE} u_bias;\n"
   1212 			"uniform bool ub_true;\n"
   1213 			"void main (void)\n"
   1214 			"{\n"
   1215 			"	${PRECISION} ${DATATYPE} res;\n"
   1216 			"	if (ub_true)"
   1217 			"		res = ${FUNC}(v_coord) * u_scale + u_bias;\n"
   1218 			"	else\n"
   1219 			"		res = ${FUNC}(-v_coord) * u_scale + u_bias;\n"
   1220 			"	o_color = ${CAST_TO_OUTPUT};\n"
   1221 			"}\n"
   1222 		},
   1223 		{
   1224 			"uniform_loop",
   1225 			"Derivate of linearly interpolated value in uniform loop",
   1226 
   1227 			"#version 300 es\n"
   1228 			"in ${PRECISION} ${DATATYPE} v_coord;\n"
   1229 			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
   1230 			"uniform ${PRECISION} ${DATATYPE} u_scale;\n"
   1231 			"uniform ${PRECISION} ${DATATYPE} u_bias;\n"
   1232 			"uniform int ui_two;\n"
   1233 			"void main (void)\n"
   1234 			"{\n"
   1235 			"	${PRECISION} ${DATATYPE} res = ${DATATYPE}(0.0);\n"
   1236 			"	for (int i = 0; i < ui_two; i++)\n"
   1237 			"		res += ${FUNC}(v_coord * float(i));\n"
   1238 			"	res = res * u_scale + u_bias;\n"
   1239 			"	o_color = ${CAST_TO_OUTPUT};\n"
   1240 			"}\n"
   1241 		},
   1242 		{
   1243 			"uniform_switch",
   1244 			"Derivate of linearly interpolated value in uniform switch",
   1245 
   1246 			"#version 300 es\n"
   1247 			"in ${PRECISION} ${DATATYPE} v_coord;\n"
   1248 			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
   1249 			"uniform ${PRECISION} ${DATATYPE} u_scale;\n"
   1250 			"uniform ${PRECISION} ${DATATYPE} u_bias;\n"
   1251 			"uniform int ui_one;\n"
   1252 			"void main (void)\n"
   1253 			"{\n"
   1254 			"	${PRECISION} ${DATATYPE} res;\n"
   1255 			"	switch (ui_one)\n"
   1256 			"	{\n"
   1257 			"		case 0:	res = ${FUNC}(-v_coord) * u_scale + u_bias;	break;\n"
   1258 			"		case 1:	res = ${FUNC}(v_coord) * u_scale + u_bias;	break;\n"
   1259 			"	}\n"
   1260 			"	o_color = ${CAST_TO_OUTPUT};\n"
   1261 			"}\n"
   1262 		},
   1263 	};
   1264 
   1265 	static const struct
   1266 	{
   1267 		const char*		name;
   1268 		SurfaceType		surfaceType;
   1269 		int				numSamples;
   1270 	} s_fboConfigs[] =
   1271 	{
   1272 		{ "fbo",		SURFACETYPE_DEFAULT_FRAMEBUFFER,	0 },
   1273 		{ "fbo_msaa2",	SURFACETYPE_UNORM_FBO,				2 },
   1274 		{ "fbo_msaa4",	SURFACETYPE_UNORM_FBO,				4 },
   1275 		{ "fbo_float",	SURFACETYPE_FLOAT_FBO,				0 },
   1276 	};
   1277 
   1278 	static const struct
   1279 	{
   1280 		const char*		name;
   1281 		deUint32		hint;
   1282 	} s_hints[] =
   1283 	{
   1284 		{ "fastest",	GL_FASTEST	},
   1285 		{ "nicest",		GL_NICEST	},
   1286 	};
   1287 
   1288 	static const struct
   1289 	{
   1290 		const char*		name;
   1291 		SurfaceType		surfaceType;
   1292 		int				numSamples;
   1293 	} s_hintFboConfigs[] =
   1294 	{
   1295 		{ "default",		SURFACETYPE_DEFAULT_FRAMEBUFFER,	0 },
   1296 		{ "fbo_msaa4",		SURFACETYPE_UNORM_FBO,				4 },
   1297 		{ "fbo_float",		SURFACETYPE_FLOAT_FBO,				0 }
   1298 	};
   1299 
   1300 	static const struct
   1301 	{
   1302 		const char*		name;
   1303 		SurfaceType		surfaceType;
   1304 		int				numSamples;
   1305 		deUint32		hint;
   1306 	} s_textureConfigs[] =
   1307 	{
   1308 		{ "basic",			SURFACETYPE_DEFAULT_FRAMEBUFFER,	0,	GL_DONT_CARE	},
   1309 		{ "msaa4",			SURFACETYPE_UNORM_FBO,				4,	GL_DONT_CARE	},
   1310 		{ "float_fastest",	SURFACETYPE_FLOAT_FBO,				0,	GL_FASTEST		},
   1311 		{ "float_nicest",	SURFACETYPE_FLOAT_FBO,				0,	GL_NICEST		},
   1312 	};
   1313 
   1314 	// .dfdx, .dfdy, .fwidth
   1315 	for (int funcNdx = 0; funcNdx < DERIVATE_LAST; funcNdx++)
   1316 	{
   1317 		const DerivateFunc			function		= DerivateFunc(funcNdx);
   1318 		tcu::TestCaseGroup* const	functionGroup	= new tcu::TestCaseGroup(m_testCtx, getDerivateFuncCaseName(function), getDerivateFuncName(function));
   1319 		addChild(functionGroup);
   1320 
   1321 		// .constant - no precision variants, checks that derivate of constant arguments is 0
   1322 		{
   1323 			tcu::TestCaseGroup* const constantGroup = new tcu::TestCaseGroup(m_testCtx, "constant", "Derivate of constant argument");
   1324 			functionGroup->addChild(constantGroup);
   1325 
   1326 			for (int vecSize = 1; vecSize <= 4; vecSize++)
   1327 			{
   1328 				const glu::DataType dataType = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
   1329 				constantGroup->addChild(new ConstantDerivateCase(m_context, glu::getDataTypeName(dataType), "", function, dataType));
   1330 			}
   1331 		}
   1332 
   1333 		// Cases based on LinearDerivateCase
   1334 		for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(s_linearDerivateCases); caseNdx++)
   1335 		{
   1336 			tcu::TestCaseGroup* const linearCaseGroup	= new tcu::TestCaseGroup(m_testCtx, s_linearDerivateCases[caseNdx].name, s_linearDerivateCases[caseNdx].description);
   1337 			const char*			source					= s_linearDerivateCases[caseNdx].source;
   1338 			functionGroup->addChild(linearCaseGroup);
   1339 
   1340 			for (int vecSize = 1; vecSize <= 4; vecSize++)
   1341 			{
   1342 				for (int precNdx = 0; precNdx < glu::PRECISION_LAST; precNdx++)
   1343 				{
   1344 					const glu::DataType		dataType		= vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
   1345 					const glu::Precision	precision		= glu::Precision(precNdx);
   1346 					const SurfaceType		surfaceType		= SURFACETYPE_DEFAULT_FRAMEBUFFER;
   1347 					const int				numSamples		= 0;
   1348 					const deUint32			hint			= GL_DONT_CARE;
   1349 					ostringstream			caseName;
   1350 
   1351 					if (caseNdx != 0 && precision == glu::PRECISION_LOWP)
   1352 						continue; // Skip as lowp doesn't actually produce any bits when rendered to default FB.
   1353 
   1354 					caseName << glu::getDataTypeName(dataType) << "_" << glu::getPrecisionName(precision);
   1355 
   1356 					linearCaseGroup->addChild(new LinearDerivateCase(m_context, caseName.str().c_str(), "", function, dataType, precision, hint, surfaceType, numSamples, source));
   1357 				}
   1358 			}
   1359 		}
   1360 
   1361 		// Fbo cases
   1362 		for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(s_fboConfigs); caseNdx++)
   1363 		{
   1364 			tcu::TestCaseGroup*	const	fboGroup		= new tcu::TestCaseGroup(m_testCtx, s_fboConfigs[caseNdx].name, "Derivate usage when rendering into FBO");
   1365 			const char*					source			= s_linearDerivateCases[0].source; // use source from .linear group
   1366 			const SurfaceType			surfaceType		= s_fboConfigs[caseNdx].surfaceType;
   1367 			const int					numSamples		= s_fboConfigs[caseNdx].numSamples;
   1368 			functionGroup->addChild(fboGroup);
   1369 
   1370 			for (int vecSize = 1; vecSize <= 4; vecSize++)
   1371 			{
   1372 				for (int precNdx = 0; precNdx < glu::PRECISION_LAST; precNdx++)
   1373 				{
   1374 					const glu::DataType		dataType		= vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
   1375 					const glu::Precision	precision		= glu::Precision(precNdx);
   1376 					const deUint32			hint			= GL_DONT_CARE;
   1377 					ostringstream			caseName;
   1378 
   1379 					if (surfaceType != SURFACETYPE_FLOAT_FBO && precision == glu::PRECISION_LOWP)
   1380 						continue; // Skip as lowp doesn't actually produce any bits when rendered to U8 RT.
   1381 
   1382 					caseName << glu::getDataTypeName(dataType) << "_" << glu::getPrecisionName(precision);
   1383 
   1384 					fboGroup->addChild(new LinearDerivateCase(m_context, caseName.str().c_str(), "", function, dataType, precision, hint, surfaceType, numSamples, source));
   1385 				}
   1386 			}
   1387 		}
   1388 
   1389 		// .fastest, .nicest
   1390 		for (int hintCaseNdx = 0; hintCaseNdx < DE_LENGTH_OF_ARRAY(s_hints); hintCaseNdx++)
   1391 		{
   1392 			tcu::TestCaseGroup* const	hintGroup		= new tcu::TestCaseGroup(m_testCtx, s_hints[hintCaseNdx].name, "Shader derivate hints");
   1393 			const char*					source			= s_linearDerivateCases[0].source; // use source from .linear group
   1394 			const deUint32				hint			= s_hints[hintCaseNdx].hint;
   1395 			functionGroup->addChild(hintGroup);
   1396 
   1397 			for (int fboCaseNdx = 0; fboCaseNdx < DE_LENGTH_OF_ARRAY(s_hintFboConfigs); fboCaseNdx++)
   1398 			{
   1399 				tcu::TestCaseGroup*	const	fboGroup		= new tcu::TestCaseGroup(m_testCtx, s_hintFboConfigs[fboCaseNdx].name, "");
   1400 				const SurfaceType			surfaceType		= s_hintFboConfigs[fboCaseNdx].surfaceType;
   1401 				const int					numSamples		= s_hintFboConfigs[fboCaseNdx].numSamples;
   1402 				hintGroup->addChild(fboGroup);
   1403 
   1404 				for (int vecSize = 1; vecSize <= 4; vecSize++)
   1405 				{
   1406 					for (int precNdx = 0; precNdx < glu::PRECISION_LAST; precNdx++)
   1407 					{
   1408 						const glu::DataType		dataType		= vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
   1409 						const glu::Precision	precision		= glu::Precision(precNdx);
   1410 						ostringstream			caseName;
   1411 
   1412 						if (surfaceType != SURFACETYPE_FLOAT_FBO && precision == glu::PRECISION_LOWP)
   1413 							continue; // Skip as lowp doesn't actually produce any bits when rendered to U8 RT.
   1414 
   1415 						caseName << glu::getDataTypeName(dataType) << "_" << glu::getPrecisionName(precision);
   1416 
   1417 						fboGroup->addChild(new LinearDerivateCase(m_context, caseName.str().c_str(), "", function, dataType, precision, hint, surfaceType, numSamples, source));
   1418 					}
   1419 				}
   1420 			}
   1421 		}
   1422 
   1423 		// .texture
   1424 		{
   1425 			tcu::TestCaseGroup* const textureGroup = new tcu::TestCaseGroup(m_testCtx, "texture", "Derivate of texture lookup result");
   1426 			functionGroup->addChild(textureGroup);
   1427 
   1428 			for (int texCaseNdx = 0; texCaseNdx < DE_LENGTH_OF_ARRAY(s_textureConfigs); texCaseNdx++)
   1429 			{
   1430 				tcu::TestCaseGroup*	const	caseGroup		= new tcu::TestCaseGroup(m_testCtx, s_textureConfigs[texCaseNdx].name, "");
   1431 				const SurfaceType			surfaceType		= s_textureConfigs[texCaseNdx].surfaceType;
   1432 				const int					numSamples		= s_textureConfigs[texCaseNdx].numSamples;
   1433 				const deUint32				hint			= s_textureConfigs[texCaseNdx].hint;
   1434 				textureGroup->addChild(caseGroup);
   1435 
   1436 				for (int vecSize = 1; vecSize <= 4; vecSize++)
   1437 				{
   1438 					for (int precNdx = 0; precNdx < glu::PRECISION_LAST; precNdx++)
   1439 					{
   1440 						const glu::DataType		dataType		= vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
   1441 						const glu::Precision	precision		= glu::Precision(precNdx);
   1442 						ostringstream			caseName;
   1443 
   1444 						if (surfaceType != SURFACETYPE_FLOAT_FBO && precision == glu::PRECISION_LOWP)
   1445 							continue; // Skip as lowp doesn't actually produce any bits when rendered to U8 RT.
   1446 
   1447 						caseName << glu::getDataTypeName(dataType) << "_" << glu::getPrecisionName(precision);
   1448 
   1449 						caseGroup->addChild(new TextureDerivateCase(m_context, caseName.str().c_str(), "", function, dataType, precision, hint, surfaceType, numSamples));
   1450 					}
   1451 				}
   1452 			}
   1453 		}
   1454 	}
   1455 }
   1456 
   1457 } // Functional
   1458 } // gles3
   1459 } // deqp
   1460