1/*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2016 The Khronos Group Inc.
6 * Copyright (c) 2016 Samsung Electronics Co., Ltd.
7 * Copyright (c) 2016 The Android Open Source Project
8 *
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 *
21 *//*!
22 * \file
23 * \brief Shader derivate function tests.
24 *
25 * \todo [2013-06-25 pyry] Missing features:
26 *  - lines and points
27 *  - projected coordinates
28 *  - continous non-trivial functions (sin, exp)
29 *  - non-continous functions (step)
30 *//*--------------------------------------------------------------------*/
31
32#include "vktShaderRenderDerivateTests.hpp"
33#include "vktShaderRender.hpp"
34#include "vkImageUtil.hpp"
35
36#include "gluTextureUtil.hpp"
37
38#include "tcuStringTemplate.hpp"
39#include "tcuSurface.hpp"
40#include "tcuTestLog.hpp"
41#include "tcuVectorUtil.hpp"
42#include "tcuTextureUtil.hpp"
43#include "tcuRGBA.hpp"
44#include "tcuFloat.hpp"
45#include "tcuInterval.hpp"
46
47#include "deUniquePtr.hpp"
48#include "glwEnums.hpp"
49
50#include <sstream>
51#include <string>
52
53namespace vkt
54{
55namespace sr
56{
57namespace
58{
59
60using namespace vk;
61
62using std::vector;
63using std::string;
64using std::map;
65using tcu::TestLog;
66using std::ostringstream;
67
68enum
69{
70	VIEWPORT_WIDTH			= 99,
71	VIEWPORT_HEIGHT			= 133,
72	MAX_FAILED_MESSAGES		= 10
73};
74
75enum DerivateFunc
76{
77	DERIVATE_DFDX			= 0,
78	DERIVATE_DFDXFINE,
79	DERIVATE_DFDXCOARSE,
80
81	DERIVATE_DFDY,
82	DERIVATE_DFDYFINE,
83	DERIVATE_DFDYCOARSE,
84
85	DERIVATE_FWIDTH,
86	DERIVATE_FWIDTHFINE,
87	DERIVATE_FWIDTHCOARSE,
88
89	DERIVATE_LAST
90};
91
92enum SurfaceType
93{
94	SURFACETYPE_UNORM_FBO	= 0,
95	SURFACETYPE_FLOAT_FBO,	// \note Uses RGBA32UI fbo actually, since FP rendertargets are not in core spec.
96
97	SURFACETYPE_LAST
98};
99
100// Utilities
101
102static const char* getDerivateFuncName (DerivateFunc func)
103{
104	switch (func)
105	{
106		case DERIVATE_DFDX:				return "dFdx";
107		case DERIVATE_DFDXFINE:			return "dFdxFine";
108		case DERIVATE_DFDXCOARSE:		return "dFdxCoarse";
109		case DERIVATE_DFDY:				return "dFdy";
110		case DERIVATE_DFDYFINE:			return "dFdyFine";
111		case DERIVATE_DFDYCOARSE:		return "dFdyCoarse";
112		case DERIVATE_FWIDTH:			return "fwidth";
113		case DERIVATE_FWIDTHFINE:		return "fwidthFine";
114		case DERIVATE_FWIDTHCOARSE:		return "fwidthCoarse";
115		default:
116			DE_ASSERT(false);
117			return DE_NULL;
118	}
119}
120
121static const char* getDerivateFuncCaseName (DerivateFunc func)
122{
123	switch (func)
124	{
125		case DERIVATE_DFDX:				return "dfdx";
126		case DERIVATE_DFDXFINE:			return "dfdxfine";
127		case DERIVATE_DFDXCOARSE:		return "dfdxcoarse";
128		case DERIVATE_DFDY:				return "dfdy";
129		case DERIVATE_DFDYFINE:			return "dfdyfine";
130		case DERIVATE_DFDYCOARSE:		return "dfdycoarse";
131		case DERIVATE_FWIDTH:			return "fwidth";
132		case DERIVATE_FWIDTHFINE:		return "fwidthfine";
133		case DERIVATE_FWIDTHCOARSE:		return "fwidthcoarse";
134		default:
135			DE_ASSERT(false);
136			return DE_NULL;
137	}
138}
139
140static inline bool isDfdxFunc (DerivateFunc func)
141{
142	return func == DERIVATE_DFDX || func == DERIVATE_DFDXFINE || func == DERIVATE_DFDXCOARSE;
143}
144
145static inline bool isDfdyFunc (DerivateFunc func)
146{
147	return func == DERIVATE_DFDY || func == DERIVATE_DFDYFINE || func == DERIVATE_DFDYCOARSE;
148}
149
150static inline bool isFwidthFunc (DerivateFunc func)
151{
152	return func == DERIVATE_FWIDTH || func == DERIVATE_FWIDTHFINE || func == DERIVATE_FWIDTHCOARSE;
153}
154
155static inline tcu::BVec4 getDerivateMask (glu::DataType type)
156{
157	switch (type)
158	{
159		case glu::TYPE_FLOAT:		return tcu::BVec4(true, false, false, false);
160		case glu::TYPE_FLOAT_VEC2:	return tcu::BVec4(true, true, false, false);
161		case glu::TYPE_FLOAT_VEC3:	return tcu::BVec4(true, true, true, false);
162		case glu::TYPE_FLOAT_VEC4:	return tcu::BVec4(true, true, true, true);
163		default:
164			DE_ASSERT(false);
165			return tcu::BVec4(true);
166	}
167}
168
169static inline tcu::Vec4 readDerivate (const tcu::ConstPixelBufferAccess& surface, const tcu::Vec4& derivScale, const tcu::Vec4& derivBias, int x, int y)
170{
171	return (surface.getPixel(x, y) - derivBias) / derivScale;
172}
173
174static inline tcu::UVec4 getCompExpBits (const tcu::Vec4& v)
175{
176	return tcu::UVec4(tcu::Float32(v[0]).exponentBits(),
177					  tcu::Float32(v[1]).exponentBits(),
178					  tcu::Float32(v[2]).exponentBits(),
179					  tcu::Float32(v[3]).exponentBits());
180}
181
182float computeFloatingPointError (const float value, const int numAccurateBits)
183{
184	const int		numGarbageBits	= 23-numAccurateBits;
185	const deUint32	mask			= (1u<<numGarbageBits)-1u;
186	const int		exp				= tcu::Float32(value).exponent();
187
188	return tcu::Float32::construct(+1, exp, (1u<<23) | mask).asFloat() - tcu::Float32::construct(+1, exp, 1u<<23).asFloat();
189}
190
191static int getNumMantissaBits (const glu::Precision precision)
192{
193	switch (precision)
194	{
195		case glu::PRECISION_HIGHP:		return 23;
196		case glu::PRECISION_MEDIUMP:	return 10;
197		case glu::PRECISION_LOWP:		return 6;
198		default:
199			DE_ASSERT(false);
200			return 0;
201	}
202}
203
204static int getMinExponent (const glu::Precision precision)
205{
206	switch (precision)
207	{
208		case glu::PRECISION_HIGHP:		return -126;
209		case glu::PRECISION_MEDIUMP:	return -14;
210		case glu::PRECISION_LOWP:		return -8;
211		default:
212			DE_ASSERT(false);
213			return 0;
214	}
215}
216
217static float getSingleULPForExponent (int exp, int numMantissaBits)
218{
219	if (numMantissaBits > 0)
220	{
221		DE_ASSERT(numMantissaBits <= 23);
222
223		const int ulpBitNdx = 23-numMantissaBits;
224		return tcu::Float32::construct(+1, exp, (1<<23) | (1 << ulpBitNdx)).asFloat() - tcu::Float32::construct(+1, exp, (1<<23)).asFloat();
225	}
226	else
227	{
228		DE_ASSERT(numMantissaBits == 0);
229		return tcu::Float32::construct(+1, exp, (1<<23)).asFloat();
230	}
231}
232
233static float getSingleULPForValue (float value, int numMantissaBits)
234{
235	const int exp = tcu::Float32(value).exponent();
236	return getSingleULPForExponent(exp, numMantissaBits);
237}
238
239static float convertFloatFlushToZeroRtn (float value, int minExponent, int numAccurateBits)
240{
241	if (value == 0.0f)
242	{
243		return 0.0f;
244	}
245	else
246	{
247		const tcu::Float32	inputFloat			= tcu::Float32(value);
248		const int			numTruncatedBits	= 23-numAccurateBits;
249		const deUint32		truncMask			= (1u<<numTruncatedBits)-1u;
250
251		if (value > 0.0f)
252		{
253			if (value > 0.0f && tcu::Float32(value).exponent() < minExponent)
254			{
255				// flush to zero if possible
256				return 0.0f;
257			}
258			else
259			{
260				// just mask away non-representable bits
261				return tcu::Float32::construct(+1, inputFloat.exponent(), inputFloat.mantissa() & ~truncMask).asFloat();
262			}
263		}
264		else
265		{
266			if (inputFloat.mantissa() & truncMask)
267			{
268				// decrement one ulp if truncated bits are non-zero (i.e. if value is not representable)
269				return tcu::Float32::construct(-1, inputFloat.exponent(), inputFloat.mantissa() & ~truncMask).asFloat() - getSingleULPForExponent(inputFloat.exponent(), numAccurateBits);
270			}
271			else
272			{
273				// value is representable, no need to do anything
274				return value;
275			}
276		}
277	}
278}
279
280static float convertFloatFlushToZeroRtp (float value, int minExponent, int numAccurateBits)
281{
282	return -convertFloatFlushToZeroRtn(-value, minExponent, numAccurateBits);
283}
284
285static float addErrorUlp (float value, float numUlps, int numMantissaBits)
286{
287	return value + numUlps * getSingleULPForValue(value, numMantissaBits);
288}
289
290enum
291{
292	INTERPOLATION_LOST_BITS = 3, // number mantissa of bits allowed to be lost in varying interpolation
293};
294
295static inline tcu::Vec4 getDerivateThreshold (const glu::Precision precision, const tcu::Vec4& valueMin, const tcu::Vec4& valueMax, const tcu::Vec4& expectedDerivate)
296{
297	const int			baseBits		= getNumMantissaBits(precision);
298	const tcu::UVec4	derivExp		= getCompExpBits(expectedDerivate);
299	const tcu::UVec4	maxValueExp		= max(getCompExpBits(valueMin), getCompExpBits(valueMax));
300	const tcu::UVec4	numBitsLost		= maxValueExp - min(maxValueExp, derivExp);
301	const tcu::IVec4	numAccurateBits	= max(baseBits - numBitsLost.asInt() - (int)INTERPOLATION_LOST_BITS, tcu::IVec4(0));
302
303	return tcu::Vec4(computeFloatingPointError(expectedDerivate[0], numAccurateBits[0]),
304					 computeFloatingPointError(expectedDerivate[1], numAccurateBits[1]),
305					 computeFloatingPointError(expectedDerivate[2], numAccurateBits[2]),
306					 computeFloatingPointError(expectedDerivate[3], numAccurateBits[3]));
307}
308
309struct LogVecComps
310{
311	const tcu::Vec4&	v;
312	int					numComps;
313
314	LogVecComps (const tcu::Vec4& v_, int numComps_)
315		: v			(v_)
316		, numComps	(numComps_)
317	{
318	}
319};
320
321std::ostream& operator<< (std::ostream& str, const LogVecComps& v)
322{
323	DE_ASSERT(de::inRange(v.numComps, 1, 4));
324	if (v.numComps == 1)		return str << v.v[0];
325	else if (v.numComps == 2)	return str << v.v.toWidth<2>();
326	else if (v.numComps == 3)	return str << v.v.toWidth<3>();
327	else						return str << v.v;
328}
329
330enum VerificationLogging
331{
332	LOG_ALL = 0,
333	LOG_NOTHING
334};
335
336static bool verifyConstantDerivate (tcu::TestLog&						log,
337									const tcu::ConstPixelBufferAccess&	result,
338									const tcu::PixelBufferAccess&		errorMask,
339									glu::DataType						dataType,
340									const tcu::Vec4&					reference,
341									const tcu::Vec4&					threshold,
342									const tcu::Vec4&					scale,
343									const tcu::Vec4&					bias,
344									VerificationLogging					logPolicy = LOG_ALL)
345{
346	const int			numComps		= glu::getDataTypeFloatScalars(dataType);
347	const tcu::BVec4	mask			= tcu::logicalNot(getDerivateMask(dataType));
348	int					numFailedPixels	= 0;
349
350	if (logPolicy == LOG_ALL)
351		log << TestLog::Message << "Expecting " << LogVecComps(reference, numComps) << " with threshold " << LogVecComps(threshold, numComps) << TestLog::EndMessage;
352
353	for (int y = 0; y < result.getHeight(); y++)
354	{
355		for (int x = 0; x < result.getWidth(); x++)
356		{
357			const tcu::Vec4		resDerivate		= readDerivate(result, scale, bias, x, y);
358			const bool			isOk			= tcu::allEqual(tcu::logicalOr(tcu::lessThanEqual(tcu::abs(reference - resDerivate), threshold), mask), tcu::BVec4(true));
359
360			if (!isOk)
361			{
362				if (numFailedPixels < MAX_FAILED_MESSAGES && logPolicy == LOG_ALL)
363					log << TestLog::Message << "FAIL: got " << LogVecComps(resDerivate, numComps)
364											<< ", diff = " << LogVecComps(tcu::abs(reference - resDerivate), numComps)
365											<< ", at x = " << x << ", y = " << y
366						<< TestLog::EndMessage;
367				numFailedPixels += 1;
368				errorMask.setPixel(tcu::RGBA::red().toVec(), x, y);
369			}
370		}
371	}
372
373	if (numFailedPixels >= MAX_FAILED_MESSAGES && logPolicy == LOG_ALL)
374		log << TestLog::Message << "..." << TestLog::EndMessage;
375
376	if (numFailedPixels > 0 && logPolicy == LOG_ALL)
377		log << TestLog::Message << "FAIL: found " << numFailedPixels << " failed pixels" << TestLog::EndMessage;
378
379	return numFailedPixels == 0;
380}
381
382struct Linear2DFunctionEvaluator
383{
384	tcu::Matrix<float, 4, 3> matrix;
385
386	//      .-----.
387	//      | s_x |
388	//  M x | s_y |
389	//      | 1.0 |
390	//      '-----'
391	tcu::Vec4 evaluateAt (float screenX, float screenY) const;
392};
393
394tcu::Vec4 Linear2DFunctionEvaluator::evaluateAt (float screenX, float screenY) const
395{
396	const tcu::Vec3 position(screenX, screenY, 1.0f);
397	return matrix * position;
398}
399
400static bool reverifyConstantDerivateWithFlushRelaxations (tcu::TestLog&							log,
401														  const tcu::ConstPixelBufferAccess&	result,
402														  const tcu::PixelBufferAccess&			errorMask,
403														  glu::DataType							dataType,
404														  glu::Precision						precision,
405														  const tcu::Vec4&						derivScale,
406														  const tcu::Vec4&						derivBias,
407														  const tcu::Vec4&						surfaceThreshold,
408														  DerivateFunc							derivateFunc,
409														  const Linear2DFunctionEvaluator&		function)
410{
411	DE_ASSERT(result.getWidth() == errorMask.getWidth());
412	DE_ASSERT(result.getHeight() == errorMask.getHeight());
413	DE_ASSERT(isDfdxFunc(derivateFunc) || isDfdyFunc(derivateFunc));
414
415	const tcu::IVec4	red						(255, 0, 0, 255);
416	const tcu::IVec4	green					(0, 255, 0, 255);
417	const float			divisionErrorUlps		= 2.5f;
418
419	const int			numComponents			= glu::getDataTypeFloatScalars(dataType);
420	const int			numBits					= getNumMantissaBits(precision);
421	const int			minExponent				= getMinExponent(precision);
422
423	const int			numVaryingSampleBits	= numBits - INTERPOLATION_LOST_BITS;
424	int					numFailedPixels			= 0;
425
426	tcu::clear(errorMask, green);
427
428	// search for failed pixels
429	for (int y = 0; y < result.getHeight(); ++y)
430	for (int x = 0; x < result.getWidth(); ++x)
431	{
432		//                 flushToZero?(f2z?(functionValueCurrent) - f2z?(functionValueBefore))
433		// flushToZero? ( ------------------------------------------------------------------------ +- 2.5 ULP )
434		//                                                  dx
435
436		const tcu::Vec4	resultDerivative		= readDerivate(result, derivScale, derivBias, x, y);
437
438		// sample at the front of the back pixel and the back of the front pixel to cover the whole area of
439		// legal sample positions. In general case this is NOT OK, but we know that the target funtion is
440		// (mostly*) linear which allows us to take the sample points at arbitrary points. This gets us the
441		// maximum difference possible in exponents which are used in error bound calculations.
442		// * non-linearity may happen around zero or with very high function values due to subnorms not
443		//   behaving well.
444		const tcu::Vec4	functionValueForward	= (isDfdxFunc(derivateFunc))
445													? (function.evaluateAt((float)x + 2.0f, (float)y + 0.5f))
446													: (function.evaluateAt((float)x + 0.5f, (float)y + 2.0f));
447		const tcu::Vec4	functionValueBackward	= (isDfdyFunc(derivateFunc))
448													? (function.evaluateAt((float)x - 1.0f, (float)y + 0.5f))
449													: (function.evaluateAt((float)x + 0.5f, (float)y - 1.0f));
450
451		bool	anyComponentFailed				= false;
452
453		// check components separately
454		for (int c = 0; c < numComponents; ++c)
455		{
456			// Simulate interpolation. Add allowed interpolation error and round to target precision. Allow one half ULP (i.e. correct rounding)
457			const tcu::Interval	forwardComponent		(convertFloatFlushToZeroRtn(addErrorUlp((float)functionValueForward[c],  -0.5f, numVaryingSampleBits), minExponent, numBits),
458														 convertFloatFlushToZeroRtp(addErrorUlp((float)functionValueForward[c],  +0.5f, numVaryingSampleBits), minExponent, numBits));
459			const tcu::Interval	backwardComponent		(convertFloatFlushToZeroRtn(addErrorUlp((float)functionValueBackward[c], -0.5f, numVaryingSampleBits), minExponent, numBits),
460														 convertFloatFlushToZeroRtp(addErrorUlp((float)functionValueBackward[c], +0.5f, numVaryingSampleBits), minExponent, numBits));
461			const int			maxValueExp				= de::max(de::max(tcu::Float32(forwardComponent.lo()).exponent(),   tcu::Float32(forwardComponent.hi()).exponent()),
462																  de::max(tcu::Float32(backwardComponent.lo()).exponent(),  tcu::Float32(backwardComponent.hi()).exponent()));
463
464			// subtraction in numerator will likely cause a cancellation of the most
465			// significant bits. Apply error bounds.
466
467			const tcu::Interval	numerator				(forwardComponent - backwardComponent);
468			const int			numeratorLoExp			= tcu::Float32(numerator.lo()).exponent();
469			const int			numeratorHiExp			= tcu::Float32(numerator.hi()).exponent();
470			const int			numeratorLoBitsLost		= de::max(0, maxValueExp - numeratorLoExp); //!< must clamp to zero since if forward and backward components have different
471			const int			numeratorHiBitsLost		= de::max(0, maxValueExp - numeratorHiExp); //!< sign, numerator might have larger exponent than its operands.
472			const int			numeratorLoBits			= de::max(0, numBits - numeratorLoBitsLost);
473			const int			numeratorHiBits			= de::max(0, numBits - numeratorHiBitsLost);
474
475			const tcu::Interval	numeratorRange			(convertFloatFlushToZeroRtn((float)numerator.lo(), minExponent, numeratorLoBits),
476														 convertFloatFlushToZeroRtp((float)numerator.hi(), minExponent, numeratorHiBits));
477
478			const tcu::Interval	divisionRange			= numeratorRange / 3.0f; // legal sample area is anywhere within this and neighboring pixels (i.e. size = 3)
479			const tcu::Interval	divisionResultRange		(convertFloatFlushToZeroRtn(addErrorUlp((float)divisionRange.lo(), -divisionErrorUlps, numBits), minExponent, numBits),
480														 convertFloatFlushToZeroRtp(addErrorUlp((float)divisionRange.hi(), +divisionErrorUlps, numBits), minExponent, numBits));
481			const tcu::Interval	finalResultRange		(divisionResultRange.lo() - surfaceThreshold[c], divisionResultRange.hi() + surfaceThreshold[c]);
482
483			if (resultDerivative[c] >= finalResultRange.lo() && resultDerivative[c] <= finalResultRange.hi())
484			{
485				// value ok
486			}
487			else
488			{
489				if (numFailedPixels < MAX_FAILED_MESSAGES)
490					log << tcu::TestLog::Message
491						<< "Error in pixel at " << x << ", " << y << " with component " << c << " (channel " << ("rgba"[c]) << ")\n"
492						<< "\tGot pixel value " << result.getPixelInt(x, y) << "\n"
493						<< "\t\tdFd" << ((isDfdxFunc(derivateFunc)) ? ('x') : ('y')) << " ~= " << resultDerivative[c] << "\n"
494						<< "\t\tdifference to a valid range: "
495							<< ((resultDerivative[c] < finalResultRange.lo()) ? ("-") : ("+"))
496							<< ((resultDerivative[c] < finalResultRange.lo()) ? (finalResultRange.lo() - resultDerivative[c]) : (resultDerivative[c] - finalResultRange.hi()))
497							<< "\n"
498						<< "\tDerivative value range:\n"
499						<< "\t\tMin: " << finalResultRange.lo() << "\n"
500						<< "\t\tMax: " << finalResultRange.hi() << "\n"
501						<< tcu::TestLog::EndMessage;
502
503				++numFailedPixels;
504				anyComponentFailed = true;
505			}
506		}
507
508		if (anyComponentFailed)
509			errorMask.setPixel(red, x, y);
510	}
511
512	if (numFailedPixels >= MAX_FAILED_MESSAGES)
513		log << TestLog::Message << "..." << TestLog::EndMessage;
514
515	if (numFailedPixels > 0)
516		log << TestLog::Message << "FAIL: found " << numFailedPixels << " failed pixels" << TestLog::EndMessage;
517
518	return numFailedPixels == 0;
519}
520
521// TestCase utils
522
523struct DerivateCaseDefinition
524{
525	DerivateCaseDefinition (void)
526	{
527		func				= DERIVATE_LAST;
528		dataType			= glu::TYPE_LAST;
529		precision			= glu::PRECISION_LAST;
530		coordDataType		= glu::TYPE_LAST;
531		coordPrecision		= glu::PRECISION_LAST;
532		surfaceType			= SURFACETYPE_UNORM_FBO;
533		numSamples			= 0;
534	}
535
536	DerivateFunc			func;
537	glu::DataType			dataType;
538	glu::Precision			precision;
539
540	glu::DataType			coordDataType;
541	glu::Precision			coordPrecision;
542
543	SurfaceType				surfaceType;
544	int						numSamples;
545};
546
547struct DerivateCaseValues
548{
549	tcu::Vec4	coordMin;
550	tcu::Vec4	coordMax;
551	tcu::Vec4	derivScale;
552	tcu::Vec4	derivBias;
553};
554
555struct TextureCaseValues
556{
557	tcu::Vec4	texValueMin;
558	tcu::Vec4	texValueMax;
559};
560
561class DerivateUniformSetup : public UniformSetup
562{
563public:
564						DerivateUniformSetup		(bool useSampler);
565	virtual				~DerivateUniformSetup		(void);
566
567	virtual void		setup						(ShaderRenderCaseInstance& instance, const tcu::Vec4&) const;
568
569private:
570	const bool			m_useSampler;
571};
572
573DerivateUniformSetup::DerivateUniformSetup (bool useSampler)
574	: m_useSampler(useSampler)
575{
576}
577
578DerivateUniformSetup::~DerivateUniformSetup (void)
579{
580}
581
582// TriangleDerivateCaseInstance
583
584class TriangleDerivateCaseInstance : public ShaderRenderCaseInstance
585{
586public:
587									TriangleDerivateCaseInstance	(Context&						context,
588																	 const UniformSetup&			uniformSetup,
589																	 const DerivateCaseDefinition&	definitions,
590																	 const DerivateCaseValues&		values);
591	virtual							~TriangleDerivateCaseInstance	(void);
592	virtual tcu::TestStatus			iterate							(void);
593	DerivateCaseDefinition			getDerivateCaseDefinition		(void) { return m_definitions; }
594	DerivateCaseValues				getDerivateCaseValues			(void) { return m_values; }
595
596protected:
597	virtual bool					verify							(const tcu::ConstPixelBufferAccess& result, const tcu::PixelBufferAccess& errorMask) = 0;
598	tcu::Vec4						getSurfaceThreshold				(void) const;
599	virtual void					setupDefaultInputs				(void);
600
601	const DerivateCaseDefinition&	m_definitions;
602	const DerivateCaseValues&		m_values;
603};
604
605static VkSampleCountFlagBits getVkSampleCount (int numSamples)
606{
607	switch (numSamples)
608	{
609		case 0:		return VK_SAMPLE_COUNT_1_BIT;
610		case 2:		return VK_SAMPLE_COUNT_2_BIT;
611		case 4:		return VK_SAMPLE_COUNT_4_BIT;
612		default:
613			DE_ASSERT(false);
614			return (VkSampleCountFlagBits)0;
615	}
616}
617
618TriangleDerivateCaseInstance::TriangleDerivateCaseInstance (Context&						context,
619															const UniformSetup&				uniformSetup,
620															const DerivateCaseDefinition&	definitions,
621															const DerivateCaseValues&		values)
622	: ShaderRenderCaseInstance	(context, true, DE_NULL, uniformSetup, DE_NULL)
623	, m_definitions				(definitions)
624	, m_values					(values)
625{
626	m_renderSize	= tcu::UVec2(VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
627	m_colorFormat	= vk::mapTextureFormat(glu::mapGLInternalFormat(m_definitions.surfaceType == SURFACETYPE_FLOAT_FBO ? GL_RGBA32UI : GL_RGBA8));
628
629	setSampleCount(getVkSampleCount(definitions.numSamples));
630}
631
632TriangleDerivateCaseInstance::~TriangleDerivateCaseInstance (void)
633{
634}
635
636tcu::Vec4 TriangleDerivateCaseInstance::getSurfaceThreshold (void) const
637{
638	switch (m_definitions.surfaceType)
639	{
640		case SURFACETYPE_UNORM_FBO:				return tcu::IVec4(1).asFloat() / 255.0f;
641		case SURFACETYPE_FLOAT_FBO:				return tcu::Vec4(0.0f);
642		default:
643			DE_ASSERT(false);
644			return tcu::Vec4(0.0f);
645	}
646}
647
648void TriangleDerivateCaseInstance::setupDefaultInputs (void)
649{
650	const int		numVertices			= 4;
651	const float		positions[]			=
652	{
653		-1.0f, -1.0f, 0.0f, 1.0f,
654		-1.0f,  1.0f, 0.0f, 1.0f,
655		1.0f, -1.0f, 0.0f, 1.0f,
656		1.0f,  1.0f, 0.0f, 1.0f
657	};
658	const float		coords[]			=
659	{
660		m_values.coordMin.x(), m_values.coordMin.y(), m_values.coordMin.z(),								m_values.coordMax.w(),
661		m_values.coordMin.x(), m_values.coordMax.y(), (m_values.coordMin.z()+m_values.coordMax.z())*0.5f,	(m_values.coordMin.w()+m_values.coordMax.w())*0.5f,
662		m_values.coordMax.x(), m_values.coordMin.y(), (m_values.coordMin.z()+m_values.coordMax.z())*0.5f,	(m_values.coordMin.w()+m_values.coordMax.w())*0.5f,
663		m_values.coordMax.x(), m_values.coordMax.y(), m_values.coordMax.z(),								m_values.coordMin.w()
664	};
665
666	addAttribute(0u, vk::VK_FORMAT_R32G32B32A32_SFLOAT, 4 * (deUint32)sizeof(float), numVertices, positions);
667	if (m_definitions.coordDataType != glu::TYPE_LAST)
668		addAttribute(1u, vk::VK_FORMAT_R32G32B32A32_SFLOAT, 4 * (deUint32)sizeof(float), numVertices, coords);
669}
670
671tcu::TestStatus TriangleDerivateCaseInstance::iterate (void)
672{
673	tcu::TestLog&				log				= m_context.getTestContext().getLog();
674	const deUint32				numVertices		= 4;
675	const deUint32				numTriangles	= 2;
676	const deUint16				indices[]		= { 0, 2, 1, 2, 3, 1 };
677	tcu::TextureLevel			resultImage;
678
679	setup();
680
681	render(numVertices, numTriangles, indices);
682
683	{
684		const tcu::TextureLevel&		renderedImage	= getResultImage();
685
686		if (m_definitions.surfaceType == SURFACETYPE_FLOAT_FBO)
687		{
688			const tcu::TextureFormat	dataFormat		(tcu::TextureFormat::RGBA, tcu::TextureFormat::FLOAT);
689
690			resultImage.setStorage(dataFormat, renderedImage.getWidth(), renderedImage.getHeight());
691			tcu::copy(resultImage.getAccess(), tcu::ConstPixelBufferAccess(dataFormat, renderedImage.getSize(), renderedImage.getAccess().getDataPtr()));
692		}
693		else
694		{
695			resultImage = renderedImage;
696		}
697	}
698
699	// Verify
700	{
701		tcu::Surface errorMask(resultImage.getWidth(), resultImage.getHeight());
702		tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toVec());
703
704		const bool isOk = verify(resultImage.getAccess(), errorMask.getAccess());
705
706		log << TestLog::ImageSet("Result", "Result images")
707			<< TestLog::Image("Rendered", "Rendered image", resultImage);
708
709		if (!isOk)
710			log << TestLog::Image("ErrorMask", "Error mask", errorMask);
711
712		log << TestLog::EndImageSet;
713
714		if (isOk)
715			return tcu::TestStatus::pass("Pass");
716		else
717			return tcu::TestStatus::fail("Image comparison failed");
718	}
719}
720
721void DerivateUniformSetup::setup (ShaderRenderCaseInstance& instance, const tcu::Vec4&) const
722{
723	DerivateCaseDefinition	definitions		= dynamic_cast<TriangleDerivateCaseInstance&>(instance).getDerivateCaseDefinition();
724	DerivateCaseValues		values			= dynamic_cast<TriangleDerivateCaseInstance&>(instance).getDerivateCaseValues();
725
726	DE_ASSERT(glu::isDataTypeFloatOrVec(definitions.dataType));
727
728	instance.addUniform(0u, vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, glu::getDataTypeScalarSize(definitions.dataType) * sizeof(float), values.derivScale.getPtr());
729	instance.addUniform(1u, vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, glu::getDataTypeScalarSize(definitions.dataType) * sizeof(float), values.derivBias.getPtr());
730
731	if (m_useSampler)
732		instance.useSampler(2u, 0u); // To the uniform binding location 2 bind the texture 0
733}
734
735// TriangleDerivateCase
736
737class TriangleDerivateCase : public ShaderRenderCase
738{
739public:
740									TriangleDerivateCase	(tcu::TestContext&		testCtx,
741															 const std::string&		name,
742															 const std::string&		description,
743															 const UniformSetup*	uniformSetup);
744	virtual							~TriangleDerivateCase	(void);
745
746protected:
747	mutable DerivateCaseDefinition	m_definitions;
748	mutable DerivateCaseValues		m_values;
749};
750
751TriangleDerivateCase::TriangleDerivateCase (tcu::TestContext&		testCtx,
752											const std::string&		name,
753											const std::string&		description,
754											const UniformSetup*		uniformSetup)
755	: ShaderRenderCase		(testCtx, name, description, false, (ShaderEvaluator*)DE_NULL, uniformSetup, DE_NULL)
756	, m_definitions			()
757{
758}
759
760TriangleDerivateCase::~TriangleDerivateCase (void)
761{
762}
763
764static std::string genVertexSource (glu::DataType coordType, glu::Precision precision)
765{
766	DE_ASSERT(coordType == glu::TYPE_LAST || glu::isDataTypeFloatOrVec(coordType));
767
768	const std::string vertexTmpl =
769		"#version 450\n"
770		"layout(location = 0) in highp vec4 a_position;\n"
771		+ string(coordType != glu::TYPE_LAST ? "layout(location = 1) in ${PRECISION} ${DATATYPE} a_coord;\n"
772											   "layout(location = 0) out ${PRECISION} ${DATATYPE} v_coord;\n" : "") +
773		"out gl_PerVertex {\n"
774		"	vec4 gl_Position;\n"
775		"};\n"
776		"void main (void)\n"
777		"{\n"
778		"	gl_Position = a_position;\n"
779		+ string(coordType != glu::TYPE_LAST ? "	v_coord = a_coord;\n" : "") +
780		"}\n";
781
782	map<string, string> vertexParams;
783
784	if (coordType != glu::TYPE_LAST)
785	{
786		vertexParams["PRECISION"]	= glu::getPrecisionName(precision);
787		vertexParams["DATATYPE"]	= glu::getDataTypeName(coordType);
788	}
789
790	return tcu::StringTemplate(vertexTmpl).specialize(vertexParams);
791}
792
793// ConstantDerivateCaseInstance
794
795class ConstantDerivateCaseInstance : public TriangleDerivateCaseInstance
796{
797public:
798								ConstantDerivateCaseInstance	(Context&						context,
799																 const UniformSetup&			uniformSetup,
800																 const DerivateCaseDefinition&	definitions,
801																 const DerivateCaseValues&		values);
802	virtual						~ConstantDerivateCaseInstance	(void);
803
804	virtual bool				verify							(const tcu::ConstPixelBufferAccess& result, const tcu::PixelBufferAccess& errorMask);
805};
806
807ConstantDerivateCaseInstance::ConstantDerivateCaseInstance (Context&							context,
808															const UniformSetup&					uniformSetup,
809															const DerivateCaseDefinition&		definitions,
810															const DerivateCaseValues&			values)
811	: TriangleDerivateCaseInstance	(context, uniformSetup, definitions, values)
812{
813}
814
815ConstantDerivateCaseInstance::~ConstantDerivateCaseInstance (void)
816{
817}
818
819bool ConstantDerivateCaseInstance::verify (const tcu::ConstPixelBufferAccess& result, const tcu::PixelBufferAccess& errorMask)
820{
821	const tcu::Vec4 reference	(0.0f); // Derivate of constant argument should always be 0
822	const tcu::Vec4	threshold	= getSurfaceThreshold() / abs(m_values.derivScale);
823
824	return verifyConstantDerivate(m_context.getTestContext().getLog(), result, errorMask, m_definitions.dataType,
825								  reference, threshold, m_values.derivScale, m_values.derivBias);
826}
827
828// ConstantDerivateCase
829
830class ConstantDerivateCase : public TriangleDerivateCase
831{
832public:
833							ConstantDerivateCase		(tcu::TestContext&		testCtx,
834														 const std::string&		name,
835														 const std::string&		description,
836														 DerivateFunc			func,
837														 glu::DataType			type);
838	virtual					~ConstantDerivateCase		(void);
839
840	virtual	void			initPrograms				(vk::SourceCollections& programCollection) const;
841	virtual TestInstance*	createInstance				(Context& context) const;
842};
843
844ConstantDerivateCase::ConstantDerivateCase (tcu::TestContext&		testCtx,
845											const std::string&		name,
846											const std::string&		description,
847											DerivateFunc			func,
848											glu::DataType			type)
849	: TriangleDerivateCase	(testCtx, name, description, new DerivateUniformSetup(false))
850{
851	m_definitions.func				= func;
852	m_definitions.dataType			= type;
853	m_definitions.precision			= glu::PRECISION_HIGHP;
854}
855
856ConstantDerivateCase::~ConstantDerivateCase (void)
857{
858}
859
860TestInstance* ConstantDerivateCase::createInstance (Context& context) const
861{
862	DE_ASSERT(m_uniformSetup != DE_NULL);
863	return new ConstantDerivateCaseInstance(context, *m_uniformSetup, m_definitions, m_values);
864}
865
866void ConstantDerivateCase::initPrograms (vk::SourceCollections& programCollection) const
867{
868	const char* fragmentTmpl =
869		"#version 450\n"
870		"layout(location = 0) out mediump vec4 o_color;\n"
871		"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
872		"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; }; \n"
873		"void main (void)\n"
874		"{\n"
875		"	${PRECISION} ${DATATYPE} res = ${FUNC}(${VALUE}) * u_scale + u_bias;\n"
876		"	o_color = ${CAST_TO_OUTPUT};\n"
877		"}\n";
878
879	map<string, string> fragmentParams;
880	fragmentParams["PRECISION"]			= glu::getPrecisionName(m_definitions.precision);
881	fragmentParams["DATATYPE"]			= glu::getDataTypeName(m_definitions.dataType);
882	fragmentParams["FUNC"]				= getDerivateFuncName(m_definitions.func);
883	fragmentParams["VALUE"]				= m_definitions.dataType == glu::TYPE_FLOAT_VEC4 ? "vec4(1.0, 7.2, -1e5, 0.0)" :
884										  m_definitions.dataType == glu::TYPE_FLOAT_VEC3 ? "vec3(1e2, 8.0, 0.01)" :
885										  m_definitions.dataType == glu::TYPE_FLOAT_VEC2 ? "vec2(-0.0, 2.7)" :
886										  /* TYPE_FLOAT */								   "7.7";
887	fragmentParams["CAST_TO_OUTPUT"]	= m_definitions.dataType == glu::TYPE_FLOAT_VEC4 ? "res" :
888										  m_definitions.dataType == glu::TYPE_FLOAT_VEC3 ? "vec4(res, 1.0)" :
889										  m_definitions.dataType == glu::TYPE_FLOAT_VEC2 ? "vec4(res, 0.0, 1.0)" :
890										  /* TYPE_FLOAT */								   "vec4(res, 0.0, 0.0, 1.0)";
891
892	std::string fragmentSrc = tcu::StringTemplate(fragmentTmpl).specialize(fragmentParams);
893	programCollection.glslSources.add("vert") << glu::VertexSource(genVertexSource(m_definitions.coordDataType, m_definitions.coordPrecision));
894	programCollection.glslSources.add("frag") << glu::FragmentSource(fragmentSrc);
895
896	m_values.derivScale		= tcu::Vec4(1e3f, 1e3f, 1e3f, 1e3f);
897	m_values.derivBias		= tcu::Vec4(0.5f, 0.5f, 0.5f, 0.5f);
898}
899
900// Linear cases
901
902class LinearDerivateUniformSetup : public DerivateUniformSetup
903{
904public:
905					LinearDerivateUniformSetup		(bool useSampler, BaseUniformType usedDefaultUniform);
906	virtual			~LinearDerivateUniformSetup		(void);
907
908	virtual void	setup							(ShaderRenderCaseInstance& instance, const tcu::Vec4& constCoords) const;
909
910private:
911	const BaseUniformType	m_usedDefaultUniform;
912};
913
914LinearDerivateUniformSetup::LinearDerivateUniformSetup (bool useSampler, BaseUniformType usedDefaultUniform)
915	: DerivateUniformSetup	(useSampler)
916	, m_usedDefaultUniform	(usedDefaultUniform)
917{
918}
919
920LinearDerivateUniformSetup::~LinearDerivateUniformSetup (void)
921{
922}
923
924void LinearDerivateUniformSetup::setup (ShaderRenderCaseInstance& instance, const tcu::Vec4& constCoords) const
925{
926	DerivateUniformSetup::setup(instance, constCoords);
927
928	if (m_usedDefaultUniform != U_LAST)
929		switch (m_usedDefaultUniform)
930		{
931			case UB_TRUE:
932			case UI_ONE:
933			case UI_TWO:
934				instance.useUniform(2u, m_usedDefaultUniform);
935				break;
936			default:
937				DE_ASSERT(false);
938				break;
939		}
940}
941
942class LinearDerivateCaseInstance : public TriangleDerivateCaseInstance
943{
944public:
945								LinearDerivateCaseInstance	(Context&						context,
946															 const UniformSetup&			uniformSetup,
947															 const DerivateCaseDefinition&	definitions,
948															 const DerivateCaseValues&		values);
949	virtual						~LinearDerivateCaseInstance	(void);
950
951	virtual bool				verify						(const tcu::ConstPixelBufferAccess& result, const tcu::PixelBufferAccess& errorMask);
952};
953
954LinearDerivateCaseInstance::LinearDerivateCaseInstance (Context&						context,
955														const UniformSetup&				uniformSetup,
956														const DerivateCaseDefinition&	definitions,
957														const DerivateCaseValues&		values)
958	: TriangleDerivateCaseInstance	(context, uniformSetup, definitions, values)
959{
960}
961
962LinearDerivateCaseInstance::~LinearDerivateCaseInstance (void)
963{
964}
965
966bool LinearDerivateCaseInstance::verify (const tcu::ConstPixelBufferAccess& result, const tcu::PixelBufferAccess& errorMask)
967{
968	const tcu::Vec4		xScale				= tcu::Vec4(1.0f, 0.0f, 0.5f, -0.5f);
969	const tcu::Vec4		yScale				= tcu::Vec4(0.0f, 1.0f, 0.5f, -0.5f);
970	const tcu::Vec4		surfaceThreshold	= getSurfaceThreshold() / abs(m_values.derivScale);
971
972	if (isDfdxFunc(m_definitions.func) || isDfdyFunc(m_definitions.func))
973	{
974		const bool			isX			= isDfdxFunc(m_definitions.func);
975		const float			div			= isX ? float(result.getWidth()) : float(result.getHeight());
976		const tcu::Vec4		scale		= isX ? xScale : yScale;
977		tcu::Vec4			reference	= ((m_values.coordMax - m_values.coordMin) / div);
978		const tcu::Vec4		opThreshold	= getDerivateThreshold(m_definitions.precision, m_values.coordMin, m_values.coordMax, reference);
979		const tcu::Vec4		threshold	= max(surfaceThreshold, opThreshold);
980		const int			numComps	= glu::getDataTypeFloatScalars(m_definitions.dataType);
981
982		/* adjust the reference value for the correct dfdx or dfdy sample adjacency */
983		reference = reference * scale;
984
985		m_context.getTestContext().getLog()
986			<< tcu::TestLog::Message
987			<< "Verifying result image.\n"
988			<< "\tValid derivative is " << LogVecComps(reference, numComps) << " with threshold " << LogVecComps(threshold, numComps)
989			<< tcu::TestLog::EndMessage;
990
991		// short circuit if result is strictly within the normal value error bounds.
992		// This improves performance significantly.
993		if (verifyConstantDerivate(m_context.getTestContext().getLog(), result, errorMask, m_definitions.dataType,
994								   reference, threshold, m_values.derivScale, m_values.derivBias,
995								   LOG_NOTHING))
996		{
997			m_context.getTestContext().getLog()
998				<< tcu::TestLog::Message
999				<< "No incorrect derivatives found, result valid."
1000				<< tcu::TestLog::EndMessage;
1001
1002			return true;
1003		}
1004
1005		// some pixels exceed error bounds calculated for normal values. Verify that these
1006		// potentially invalid pixels are in fact valid due to (for example) subnorm flushing.
1007
1008		m_context.getTestContext().getLog()
1009			<< tcu::TestLog::Message
1010			<< "Initial verification failed, verifying image by calculating accurate error bounds for each result pixel.\n"
1011			<< "\tVerifying each result derivative is within its range of legal result values."
1012			<< tcu::TestLog::EndMessage;
1013
1014		{
1015			const tcu::UVec2			viewportSize	(VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
1016			const float					w				= float(viewportSize.x());
1017			const float					h				= float(viewportSize.y());
1018			const tcu::Vec4				valueRamp		= (m_values.coordMax - m_values.coordMin);
1019			Linear2DFunctionEvaluator	function;
1020
1021			function.matrix.setRow(0, tcu::Vec3(valueRamp.x() / w, 0.0f, m_values.coordMin.x()));
1022			function.matrix.setRow(1, tcu::Vec3(0.0f, valueRamp.y() / h, m_values.coordMin.y()));
1023			function.matrix.setRow(2, tcu::Vec3(valueRamp.z() / w, valueRamp.z() / h, m_values.coordMin.z() + m_values.coordMin.z()) / 2.0f);
1024			function.matrix.setRow(3, tcu::Vec3(-valueRamp.w() / w, -valueRamp.w() / h, m_values.coordMax.w() + m_values.coordMax.w()) / 2.0f);
1025
1026			return reverifyConstantDerivateWithFlushRelaxations(m_context.getTestContext().getLog(), result, errorMask,
1027																m_definitions.dataType, m_definitions.precision, m_values.derivScale,
1028																m_values.derivBias, surfaceThreshold, m_definitions.func,
1029																function);
1030		}
1031	}
1032	else
1033	{
1034		DE_ASSERT(isFwidthFunc(m_definitions.func));
1035		const float			w			= float(result.getWidth());
1036		const float			h			= float(result.getHeight());
1037
1038		const tcu::Vec4		dx			= ((m_values.coordMax - m_values.coordMin) / w) * xScale;
1039		const tcu::Vec4		dy			= ((m_values.coordMax - m_values.coordMin) / h) * yScale;
1040		const tcu::Vec4		reference	= tcu::abs(dx) + tcu::abs(dy);
1041		const tcu::Vec4		dxThreshold	= getDerivateThreshold(m_definitions.precision, m_values.coordMin*xScale, m_values.coordMax*xScale, dx);
1042		const tcu::Vec4		dyThreshold	= getDerivateThreshold(m_definitions.precision, m_values.coordMin*yScale, m_values.coordMax*yScale, dy);
1043		const tcu::Vec4		threshold	= max(surfaceThreshold, max(dxThreshold, dyThreshold));
1044
1045		return verifyConstantDerivate(m_context.getTestContext().getLog(), result, errorMask, m_definitions.dataType,
1046									  reference, threshold, m_values.derivScale, m_values.derivBias);
1047	}
1048}
1049
1050// LinearDerivateCase
1051
1052class LinearDerivateCase : public TriangleDerivateCase
1053{
1054public:
1055							LinearDerivateCase			(tcu::TestContext&		testCtx,
1056														 const std::string&		name,
1057														 const std::string&		description,
1058														 DerivateFunc			func,
1059														 glu::DataType			type,
1060														 glu::Precision			precision,
1061														 SurfaceType			surfaceType,
1062														 int					numSamples,
1063														 const std::string&		fragmentSrcTmpl,
1064														 BaseUniformType		usedDefaultUniform);
1065	virtual					~LinearDerivateCase			(void);
1066
1067	virtual	void			initPrograms				(vk::SourceCollections& programCollection) const;
1068	virtual TestInstance*	createInstance				(Context& context) const;
1069
1070private:
1071	const std::string		m_fragmentTmpl;
1072};
1073
1074LinearDerivateCase::LinearDerivateCase (tcu::TestContext&		testCtx,
1075										const std::string&		name,
1076										const std::string&		description,
1077										DerivateFunc			func,
1078										glu::DataType			type,
1079										glu::Precision			precision,
1080										SurfaceType				surfaceType,
1081										int						numSamples,
1082										const std::string&		fragmentSrcTmpl,
1083										BaseUniformType			usedDefaultUniform)
1084	: TriangleDerivateCase	(testCtx, name, description, new LinearDerivateUniformSetup(false, usedDefaultUniform))
1085	, m_fragmentTmpl		(fragmentSrcTmpl)
1086{
1087	m_definitions.func				= func;
1088	m_definitions.dataType			= type;
1089	m_definitions.precision			= precision;
1090	m_definitions.coordDataType		= m_definitions.dataType;
1091	m_definitions.coordPrecision	= m_definitions.precision;
1092	m_definitions.surfaceType		= surfaceType;
1093	m_definitions.numSamples		= numSamples;
1094}
1095
1096LinearDerivateCase::~LinearDerivateCase (void)
1097{
1098}
1099
1100TestInstance* LinearDerivateCase::createInstance (Context& context) const
1101{
1102	DE_ASSERT(m_uniformSetup != DE_NULL);
1103	return new LinearDerivateCaseInstance(context, *m_uniformSetup, m_definitions, m_values);
1104}
1105
1106void LinearDerivateCase::initPrograms (vk::SourceCollections& programCollection) const
1107{
1108	const tcu::UVec2	viewportSize	(VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
1109	const float			w				= float(viewportSize.x());
1110	const float			h				= float(viewportSize.y());
1111	const bool			packToInt		= m_definitions.surfaceType == SURFACETYPE_FLOAT_FBO;
1112	map<string, string>	fragmentParams;
1113
1114	fragmentParams["OUTPUT_TYPE"]		= glu::getDataTypeName(packToInt ? glu::TYPE_UINT_VEC4 : glu::TYPE_FLOAT_VEC4);
1115	fragmentParams["OUTPUT_PREC"]		= glu::getPrecisionName(packToInt ? glu::PRECISION_HIGHP : m_definitions.precision);
1116	fragmentParams["PRECISION"]			= glu::getPrecisionName(m_definitions.precision);
1117	fragmentParams["DATATYPE"]			= glu::getDataTypeName(m_definitions.dataType);
1118	fragmentParams["FUNC"]				= getDerivateFuncName(m_definitions.func);
1119
1120	if (packToInt)
1121	{
1122		fragmentParams["CAST_TO_OUTPUT"]	= m_definitions.dataType == glu::TYPE_FLOAT_VEC4 ? "floatBitsToUint(res)" :
1123											  m_definitions.dataType == glu::TYPE_FLOAT_VEC3 ? "floatBitsToUint(vec4(res, 1.0))" :
1124											  m_definitions.dataType == glu::TYPE_FLOAT_VEC2 ? "floatBitsToUint(vec4(res, 0.0, 1.0))" :
1125											  /* TYPE_FLOAT */								   "floatBitsToUint(vec4(res, 0.0, 0.0, 1.0))";
1126	}
1127	else
1128	{
1129		fragmentParams["CAST_TO_OUTPUT"]	= m_definitions.dataType == glu::TYPE_FLOAT_VEC4 ? "res" :
1130											  m_definitions.dataType == glu::TYPE_FLOAT_VEC3 ? "vec4(res, 1.0)" :
1131											  m_definitions.dataType == glu::TYPE_FLOAT_VEC2 ? "vec4(res, 0.0, 1.0)" :
1132											  /* TYPE_FLOAT */								   "vec4(res, 0.0, 0.0, 1.0)";
1133	}
1134
1135	std::string fragmentSrc = tcu::StringTemplate(m_fragmentTmpl).specialize(fragmentParams);
1136	programCollection.glslSources.add("vert") << glu::VertexSource(genVertexSource(m_definitions.coordDataType, m_definitions.coordPrecision));
1137	programCollection.glslSources.add("frag") << glu::FragmentSource(fragmentSrc);
1138
1139	switch (m_definitions.precision)
1140	{
1141		case glu::PRECISION_HIGHP:
1142			m_values.coordMin = tcu::Vec4(-97.f, 0.2f, 71.f, 74.f);
1143			m_values.coordMax = tcu::Vec4(-13.2f, -77.f, 44.f, 76.f);
1144			break;
1145
1146		case glu::PRECISION_MEDIUMP:
1147			m_values.coordMin = tcu::Vec4(-37.0f, 47.f, -7.f, 0.0f);
1148			m_values.coordMax = tcu::Vec4(-1.0f, 12.f, 7.f, 19.f);
1149			break;
1150
1151		case glu::PRECISION_LOWP:
1152			m_values.coordMin = tcu::Vec4(0.0f, -1.0f, 0.0f, 1.0f);
1153			m_values.coordMax = tcu::Vec4(1.0f, 1.0f, -1.0f, -1.0f);
1154			break;
1155
1156		default:
1157			DE_ASSERT(false);
1158	}
1159
1160	if (m_definitions.surfaceType == SURFACETYPE_FLOAT_FBO)
1161	{
1162		// No scale or bias used for accuracy.
1163		m_values.derivScale	= tcu::Vec4(1.0f);
1164		m_values.derivBias		= tcu::Vec4(0.0f);
1165	}
1166	else
1167	{
1168		// Compute scale - bias that normalizes to 0..1 range.
1169		const tcu::Vec4 dx = (m_values.coordMax - m_values.coordMin) / tcu::Vec4(w, w, w*0.5f, -w*0.5f);
1170		const tcu::Vec4 dy = (m_values.coordMax - m_values.coordMin) / tcu::Vec4(h, h, h*0.5f, -h*0.5f);
1171
1172		if (isDfdxFunc(m_definitions.func))
1173			m_values.derivScale = 0.5f / dx;
1174		else if (isDfdyFunc(m_definitions.func))
1175			m_values.derivScale = 0.5f / dy;
1176		else if (isFwidthFunc(m_definitions.func))
1177			m_values.derivScale = 0.5f / (tcu::abs(dx) + tcu::abs(dy));
1178		else
1179			DE_ASSERT(false);
1180
1181		m_values.derivBias = tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f);
1182	}
1183}
1184
1185// TextureDerivateCaseInstance
1186
1187class TextureDerivateCaseInstance : public TriangleDerivateCaseInstance
1188{
1189public:
1190								TextureDerivateCaseInstance		(Context&							context,
1191																 const UniformSetup&				uniformSetup,
1192																 const DerivateCaseDefinition&		definitions,
1193																 const DerivateCaseValues&			values,
1194																 const TextureCaseValues&			textureValues);
1195	virtual						~TextureDerivateCaseInstance	(void);
1196
1197	virtual bool				verify							(const tcu::ConstPixelBufferAccess& result, const tcu::PixelBufferAccess& errorMask);
1198
1199private:
1200	const TextureCaseValues&	m_textureValues;
1201};
1202
1203TextureDerivateCaseInstance::TextureDerivateCaseInstance (Context&							context,
1204														  const UniformSetup&				uniformSetup,
1205														  const DerivateCaseDefinition&		definitions,
1206														  const DerivateCaseValues&			values,
1207														  const TextureCaseValues&			textureValues)
1208	: TriangleDerivateCaseInstance	(context, uniformSetup, definitions, values)
1209	, m_textureValues				(textureValues)
1210{
1211	de::MovePtr<tcu::Texture2D>		texture;
1212
1213	// Lowp and mediump cases use RGBA16F format, while highp uses RGBA32F.
1214	{
1215		const tcu::UVec2			viewportSize	(VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
1216		const tcu::TextureFormat	format			= glu::mapGLInternalFormat(m_definitions.precision == glu::PRECISION_HIGHP ? GL_RGBA32F : GL_RGBA16F);
1217
1218		texture = de::MovePtr<tcu::Texture2D>(new tcu::Texture2D(format, viewportSize.x(), viewportSize.y()));
1219		texture->allocLevel(0);
1220	}
1221
1222	// Fill with gradients.
1223	{
1224		const tcu::PixelBufferAccess level0 = texture->getLevel(0);
1225		for (int y = 0; y < level0.getHeight(); y++)
1226		{
1227			for (int x = 0; x < level0.getWidth(); x++)
1228			{
1229				const float		xf		= (float(x)+0.5f) / float(level0.getWidth());
1230				const float		yf		= (float(y)+0.5f) / float(level0.getHeight());
1231				const tcu::Vec4	s		= tcu::Vec4(xf, yf, (xf+yf)/2.0f, 1.0f - (xf+yf)/2.0f);
1232
1233				level0.setPixel(m_textureValues.texValueMin + (m_textureValues.texValueMax - m_textureValues.texValueMin)*s, x, y);
1234			}
1235		}
1236	}
1237
1238	de::SharedPtr<TextureBinding>	testTexture		(new TextureBinding(texture.release(),
1239																		tcu::Sampler(tcu::Sampler::CLAMP_TO_EDGE,
1240																					 tcu::Sampler::CLAMP_TO_EDGE,
1241																					 tcu::Sampler::CLAMP_TO_EDGE,
1242																					 tcu::Sampler::NEAREST,
1243																					 tcu::Sampler::NEAREST)));
1244	m_textures.push_back(testTexture);
1245}
1246
1247TextureDerivateCaseInstance::~TextureDerivateCaseInstance (void)
1248{
1249}
1250
1251bool TextureDerivateCaseInstance::verify (const tcu::ConstPixelBufferAccess& result, const tcu::PixelBufferAccess& errorMask)
1252{
1253	// \note Edges are ignored in comparison
1254	if (result.getWidth() < 2 || result.getHeight() < 2)
1255		throw tcu::NotSupportedError("Too small viewport");
1256
1257	tcu::ConstPixelBufferAccess	compareArea			= tcu::getSubregion(result, 1, 1, result.getWidth()-2, result.getHeight()-2);
1258	tcu::PixelBufferAccess		maskArea			= tcu::getSubregion(errorMask, 1, 1, errorMask.getWidth()-2, errorMask.getHeight()-2);
1259	const tcu::Vec4				xScale				= tcu::Vec4(1.0f, 0.0f, 0.5f, -0.5f);
1260	const tcu::Vec4				yScale				= tcu::Vec4(0.0f, 1.0f, 0.5f, -0.5f);
1261	const float					w					= float(result.getWidth());
1262	const float					h					= float(result.getHeight());
1263
1264	const tcu::Vec4				surfaceThreshold	= getSurfaceThreshold() / abs(m_values.derivScale);
1265
1266	if (isDfdxFunc(m_definitions.func) || isDfdyFunc(m_definitions.func))
1267	{
1268		const bool			isX			= isDfdxFunc(m_definitions.func);
1269		const float			div			= isX ? w : h;
1270		const tcu::Vec4		scale		= isX ? xScale : yScale;
1271		tcu::Vec4			reference	= ((m_textureValues.texValueMax - m_textureValues.texValueMin) / div);
1272		const tcu::Vec4		opThreshold	= getDerivateThreshold(m_definitions.precision, m_textureValues.texValueMin, m_textureValues.texValueMax, reference);
1273		const tcu::Vec4		threshold	= max(surfaceThreshold, opThreshold);
1274		const int			numComps	= glu::getDataTypeFloatScalars(m_definitions.dataType);
1275
1276		/* adjust the reference value for the correct dfdx or dfdy sample adjacency */
1277		reference = reference * scale;
1278
1279		m_context.getTestContext().getLog()
1280			<< tcu::TestLog::Message
1281			<< "Verifying result image.\n"
1282			<< "\tValid derivative is " << LogVecComps(reference, numComps) << " with threshold " << LogVecComps(threshold, numComps)
1283			<< tcu::TestLog::EndMessage;
1284
1285		// short circuit if result is strictly within the normal value error bounds.
1286		// This improves performance significantly.
1287		if (verifyConstantDerivate(m_context.getTestContext().getLog(), compareArea, maskArea, m_definitions.dataType,
1288								   reference, threshold, m_values.derivScale, m_values.derivBias,
1289								   LOG_NOTHING))
1290		{
1291			m_context.getTestContext().getLog()
1292				<< tcu::TestLog::Message
1293				<< "No incorrect derivatives found, result valid."
1294				<< tcu::TestLog::EndMessage;
1295
1296			return true;
1297		}
1298
1299		// some pixels exceed error bounds calculated for normal values. Verify that these
1300		// potentially invalid pixels are in fact valid due to (for example) subnorm flushing.
1301
1302		m_context.getTestContext().getLog()
1303			<< tcu::TestLog::Message
1304			<< "Initial verification failed, verifying image by calculating accurate error bounds for each result pixel.\n"
1305			<< "\tVerifying each result derivative is within its range of legal result values."
1306			<< tcu::TestLog::EndMessage;
1307
1308		{
1309			const tcu::Vec4				valueRamp		= (m_textureValues.texValueMax - m_textureValues.texValueMin);
1310			Linear2DFunctionEvaluator	function;
1311
1312			function.matrix.setRow(0, tcu::Vec3(valueRamp.x() / w, 0.0f, m_textureValues.texValueMin.x()));
1313			function.matrix.setRow(1, tcu::Vec3(0.0f, valueRamp.y() / h, m_textureValues.texValueMin.y()));
1314			function.matrix.setRow(2, tcu::Vec3(valueRamp.z() / w, valueRamp.z() / h, m_textureValues.texValueMin.z() + m_textureValues.texValueMin.z()) / 2.0f);
1315			function.matrix.setRow(3, tcu::Vec3(-valueRamp.w() / w, -valueRamp.w() / h, m_textureValues.texValueMax.w() + m_textureValues.texValueMax.w()) / 2.0f);
1316
1317			return reverifyConstantDerivateWithFlushRelaxations(m_context.getTestContext().getLog(), compareArea, maskArea,
1318																m_definitions.dataType, m_definitions.precision, m_values.derivScale,
1319																m_values.derivBias, surfaceThreshold, m_definitions.func,
1320																function);
1321		}
1322	}
1323	else
1324	{
1325		DE_ASSERT(isFwidthFunc(m_definitions.func));
1326		const tcu::Vec4	dx			= ((m_textureValues.texValueMax - m_textureValues.texValueMin) / w) * xScale;
1327		const tcu::Vec4	dy			= ((m_textureValues.texValueMax - m_textureValues.texValueMin) / h) * yScale;
1328		const tcu::Vec4	reference	= tcu::abs(dx) + tcu::abs(dy);
1329		const tcu::Vec4	dxThreshold	= getDerivateThreshold(m_definitions.precision, m_textureValues.texValueMin*xScale, m_textureValues.texValueMax*xScale, dx);
1330		const tcu::Vec4	dyThreshold	= getDerivateThreshold(m_definitions.precision, m_textureValues.texValueMin*yScale, m_textureValues.texValueMax*yScale, dy);
1331		const tcu::Vec4	threshold	= max(surfaceThreshold, max(dxThreshold, dyThreshold));
1332
1333		return verifyConstantDerivate(m_context.getTestContext().getLog(), compareArea, maskArea, m_definitions.dataType,
1334									  reference, threshold, m_values.derivScale, m_values.derivBias);
1335	}
1336}
1337
1338// TextureDerivateCase
1339
1340class TextureDerivateCase : public TriangleDerivateCase
1341{
1342public:
1343							TextureDerivateCase			(tcu::TestContext&		testCtx,
1344														 const std::string&		name,
1345														 const std::string&		description,
1346														 DerivateFunc			func,
1347														 glu::DataType			type,
1348														 glu::Precision			precision,
1349														 SurfaceType			surfaceType,
1350														 int					numSamples);
1351	virtual					~TextureDerivateCase		(void);
1352
1353	virtual	void			initPrograms				(vk::SourceCollections& programCollection) const;
1354	virtual TestInstance*	createInstance				(Context& context) const;
1355
1356private:
1357	mutable TextureCaseValues	m_textureValues;
1358};
1359
1360TextureDerivateCase::TextureDerivateCase (tcu::TestContext&		testCtx,
1361										  const std::string&	name,
1362										  const std::string&	description,
1363										  DerivateFunc			func,
1364										  glu::DataType			type,
1365										  glu::Precision		precision,
1366										  SurfaceType			surfaceType,
1367										  int					numSamples)
1368	: TriangleDerivateCase	(testCtx, name, description, new DerivateUniformSetup(true))
1369{
1370	m_definitions.dataType			= type;
1371	m_definitions.func				= func;
1372	m_definitions.precision			= precision;
1373	m_definitions.coordDataType		= glu::TYPE_FLOAT_VEC2;
1374	m_definitions.coordPrecision	= glu::PRECISION_HIGHP;
1375	m_definitions.surfaceType		= surfaceType;
1376	m_definitions.numSamples		= numSamples;
1377}
1378
1379TextureDerivateCase::~TextureDerivateCase (void)
1380{
1381}
1382
1383TestInstance* TextureDerivateCase::createInstance (Context& context) const
1384{
1385	DE_ASSERT(m_uniformSetup != DE_NULL);
1386	return new TextureDerivateCaseInstance(context, *m_uniformSetup, m_definitions, m_values, m_textureValues);
1387}
1388
1389void TextureDerivateCase::initPrograms (vk::SourceCollections& programCollection) const
1390{
1391	// Generate shader
1392	{
1393		const char* fragmentTmpl =
1394			"#version 450\n"
1395			"layout(location = 0) in highp vec2 v_coord;\n"
1396			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1397			"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
1398			"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; };\n"
1399			"layout(binding = 2) uniform ${PRECISION} sampler2D u_sampler;\n"
1400			"void main (void)\n"
1401			"{\n"
1402			"	${PRECISION} vec4 tex = texture(u_sampler, v_coord);\n"
1403			"	${PRECISION} ${DATATYPE} res = ${FUNC}(tex${SWIZZLE}) * u_scale + u_bias;\n"
1404			"	o_color = ${CAST_TO_OUTPUT};\n"
1405			"}\n";
1406
1407		const bool			packToInt		= m_definitions.surfaceType == SURFACETYPE_FLOAT_FBO;
1408		map<string, string> fragmentParams;
1409
1410		fragmentParams["OUTPUT_TYPE"]		= glu::getDataTypeName(packToInt ? glu::TYPE_UINT_VEC4 : glu::TYPE_FLOAT_VEC4);
1411		fragmentParams["OUTPUT_PREC"]		= glu::getPrecisionName(packToInt ? glu::PRECISION_HIGHP : m_definitions.precision);
1412		fragmentParams["PRECISION"]			= glu::getPrecisionName(m_definitions.precision);
1413		fragmentParams["DATATYPE"]			= glu::getDataTypeName(m_definitions.dataType);
1414		fragmentParams["FUNC"]				= getDerivateFuncName(m_definitions.func);
1415		fragmentParams["SWIZZLE"]			= m_definitions.dataType == glu::TYPE_FLOAT_VEC4 ? "" :
1416											  m_definitions.dataType == glu::TYPE_FLOAT_VEC3 ? ".xyz" :
1417											  m_definitions.dataType == glu::TYPE_FLOAT_VEC2 ? ".xy" :
1418											  /* TYPE_FLOAT */								   ".x";
1419
1420		if (packToInt)
1421		{
1422			fragmentParams["CAST_TO_OUTPUT"]	= m_definitions.dataType == glu::TYPE_FLOAT_VEC4 ? "floatBitsToUint(res)" :
1423												  m_definitions.dataType == glu::TYPE_FLOAT_VEC3 ? "floatBitsToUint(vec4(res, 1.0))" :
1424												  m_definitions.dataType == glu::TYPE_FLOAT_VEC2 ? "floatBitsToUint(vec4(res, 0.0, 1.0))" :
1425												  /* TYPE_FLOAT */								   "floatBitsToUint(vec4(res, 0.0, 0.0, 1.0))";
1426		}
1427		else
1428		{
1429			fragmentParams["CAST_TO_OUTPUT"]	= m_definitions.dataType == glu::TYPE_FLOAT_VEC4 ? "res" :
1430												  m_definitions.dataType == glu::TYPE_FLOAT_VEC3 ? "vec4(res, 1.0)" :
1431												  m_definitions.dataType == glu::TYPE_FLOAT_VEC2 ? "vec4(res, 0.0, 1.0)" :
1432												  /* TYPE_FLOAT */								   "vec4(res, 0.0, 0.0, 1.0)";
1433		}
1434
1435		std::string fragmentSrc = tcu::StringTemplate(fragmentTmpl).specialize(fragmentParams);
1436		programCollection.glslSources.add("vert") << glu::VertexSource(genVertexSource(m_definitions.coordDataType, m_definitions.coordPrecision));
1437		programCollection.glslSources.add("frag") << glu::FragmentSource(fragmentSrc);
1438	}
1439
1440	// Texture size matches viewport and nearest sampling is used. Thus texture sampling
1441	// is equal to just interpolating the texture value range.
1442
1443	// Determine value range for texture.
1444
1445	switch (m_definitions.precision)
1446	{
1447		case glu::PRECISION_HIGHP:
1448			m_textureValues.texValueMin = tcu::Vec4(-97.f, 0.2f, 71.f, 74.f);
1449			m_textureValues.texValueMax = tcu::Vec4(-13.2f, -77.f, 44.f, 76.f);
1450			break;
1451
1452		case glu::PRECISION_MEDIUMP:
1453			m_textureValues.texValueMin = tcu::Vec4(-37.0f, 47.f, -7.f, 0.0f);
1454			m_textureValues.texValueMax = tcu::Vec4(-1.0f, 12.f, 7.f, 19.f);
1455			break;
1456
1457		case glu::PRECISION_LOWP:
1458			m_textureValues.texValueMin = tcu::Vec4(0.0f, -1.0f, 0.0f, 1.0f);
1459			m_textureValues.texValueMax = tcu::Vec4(1.0f, 1.0f, -1.0f, -1.0f);
1460			break;
1461
1462		default:
1463			DE_ASSERT(false);
1464	}
1465
1466	// Texture coordinates
1467	m_values.coordMin = tcu::Vec4(0.0f);
1468	m_values.coordMax = tcu::Vec4(1.0f);
1469
1470	if (m_definitions.surfaceType == SURFACETYPE_FLOAT_FBO)
1471	{
1472		// No scale or bias used for accuracy.
1473		m_values.derivScale		= tcu::Vec4(1.0f);
1474		m_values.derivBias		= tcu::Vec4(0.0f);
1475	}
1476	else
1477	{
1478		// Compute scale - bias that normalizes to 0..1 range.
1479		const tcu::UVec2	viewportSize	(VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
1480		const float			w				= float(viewportSize.x());
1481		const float			h				= float(viewportSize.y());
1482		const tcu::Vec4		dx				= (m_textureValues.texValueMax - m_textureValues.texValueMin) / tcu::Vec4(w, w, w*0.5f, -w*0.5f);
1483		const tcu::Vec4		dy				= (m_textureValues.texValueMax - m_textureValues.texValueMin) / tcu::Vec4(h, h, h*0.5f, -h*0.5f);
1484
1485		if (isDfdxFunc(m_definitions.func))
1486			m_values.derivScale = 0.5f / dx;
1487		else if (isDfdyFunc(m_definitions.func))
1488			m_values.derivScale = 0.5f / dy;
1489		else if (isFwidthFunc(m_definitions.func))
1490			m_values.derivScale = 0.5f / (tcu::abs(dx) + tcu::abs(dy));
1491		else
1492			DE_ASSERT(false);
1493
1494		m_values.derivBias = tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f);
1495	}
1496}
1497
1498// ShaderDerivateTests
1499
1500class ShaderDerivateTests : public tcu::TestCaseGroup
1501{
1502public:
1503							ShaderDerivateTests		(tcu::TestContext& testCtx);
1504	virtual					~ShaderDerivateTests	(void);
1505
1506	virtual void			init					(void);
1507
1508private:
1509							ShaderDerivateTests		(const ShaderDerivateTests&);		// not allowed!
1510	ShaderDerivateTests&	operator=				(const ShaderDerivateTests&);		// not allowed!
1511};
1512
1513ShaderDerivateTests::ShaderDerivateTests (tcu::TestContext& testCtx)
1514	: TestCaseGroup(testCtx, "derivate", "Derivate Function Tests")
1515{
1516}
1517
1518ShaderDerivateTests::~ShaderDerivateTests (void)
1519{
1520}
1521
1522struct FunctionSpec
1523{
1524	std::string		name;
1525	DerivateFunc	function;
1526	glu::DataType	dataType;
1527	glu::Precision	precision;
1528
1529	FunctionSpec (const std::string& name_, DerivateFunc function_, glu::DataType dataType_, glu::Precision precision_)
1530		: name		(name_)
1531		, function	(function_)
1532		, dataType	(dataType_)
1533		, precision	(precision_)
1534	{
1535	}
1536};
1537
1538void ShaderDerivateTests::init (void)
1539{
1540	static const struct
1541	{
1542		const char*			name;
1543		const char*			description;
1544		const char*			source;
1545		BaseUniformType		usedDefaultUniform;
1546	} s_linearDerivateCases[] =
1547	{
1548		{
1549			"linear",
1550			"Basic derivate of linearly interpolated argument",
1551
1552			"#version 450\n"
1553			"layout(location = 0) in ${PRECISION} ${DATATYPE} v_coord;\n"
1554			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1555			"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
1556			"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; };\n"
1557			"void main (void)\n"
1558			"{\n"
1559			"	${PRECISION} ${DATATYPE} res = ${FUNC}(v_coord) * u_scale + u_bias;\n"
1560			"	o_color = ${CAST_TO_OUTPUT};\n"
1561			"}\n",
1562
1563			U_LAST
1564		},
1565		{
1566			"in_function",
1567			"Derivate of linear function argument",
1568
1569			"#version 450\n"
1570			"layout(location = 0) in ${PRECISION} ${DATATYPE} v_coord;\n"
1571			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1572			"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
1573			"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; };\n"
1574			"\n"
1575			"${PRECISION} ${DATATYPE} computeRes (${PRECISION} ${DATATYPE} value)\n"
1576			"{\n"
1577			"	return ${FUNC}(v_coord) * u_scale + u_bias;\n"
1578			"}\n"
1579			"\n"
1580			"void main (void)\n"
1581			"{\n"
1582			"	${PRECISION} ${DATATYPE} res = computeRes(v_coord);\n"
1583			"	o_color = ${CAST_TO_OUTPUT};\n"
1584			"}\n",
1585
1586			U_LAST
1587		},
1588		{
1589			"static_if",
1590			"Derivate of linearly interpolated value in static if",
1591
1592			"#version 450\n"
1593			"layout(location = 0) in ${PRECISION} ${DATATYPE} v_coord;\n"
1594			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1595			"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
1596			"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; };\n"
1597			"void main (void)\n"
1598			"{\n"
1599			"	${PRECISION} ${DATATYPE} res;\n"
1600			"	if (false)\n"
1601			"		res = ${FUNC}(-v_coord) * u_scale + u_bias;\n"
1602			"	else\n"
1603			"		res = ${FUNC}(v_coord) * u_scale + u_bias;\n"
1604			"	o_color = ${CAST_TO_OUTPUT};\n"
1605			"}\n",
1606
1607			U_LAST
1608		},
1609		{
1610			"static_loop",
1611			"Derivate of linearly interpolated value in static loop",
1612
1613			"#version 450\n"
1614			"layout(location = 0) in ${PRECISION} ${DATATYPE} v_coord;\n"
1615			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1616			"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
1617			"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; };\n"
1618			"void main (void)\n"
1619			"{\n"
1620			"	${PRECISION} ${DATATYPE} res = ${DATATYPE}(0.0);\n"
1621			"	for (int i = 0; i < 2; i++)\n"
1622			"		res += ${FUNC}(v_coord * float(i));\n"
1623			"	res = res * u_scale + u_bias;\n"
1624			"	o_color = ${CAST_TO_OUTPUT};\n"
1625			"}\n",
1626
1627			U_LAST
1628		},
1629		{
1630			"static_switch",
1631			"Derivate of linearly interpolated value in static switch",
1632
1633			"#version 450\n"
1634			"layout(location = 0) in ${PRECISION} ${DATATYPE} v_coord;\n"
1635			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1636			"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
1637			"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; };\n"
1638			"void main (void)\n"
1639			"{\n"
1640			"	${PRECISION} ${DATATYPE} res;\n"
1641			"	switch (1)\n"
1642			"	{\n"
1643			"		case 0:	res = ${FUNC}(-v_coord) * u_scale + u_bias;	break;\n"
1644			"		case 1:	res = ${FUNC}(v_coord) * u_scale + u_bias;	break;\n"
1645			"	}\n"
1646			"	o_color = ${CAST_TO_OUTPUT};\n"
1647			"}\n",
1648
1649			U_LAST
1650		},
1651		{
1652			"uniform_if",
1653			"Derivate of linearly interpolated value in uniform if",
1654
1655			"#version 450\n"
1656			"layout(location = 0) in ${PRECISION} ${DATATYPE} v_coord;\n"
1657			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1658			"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
1659			"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; };\n"
1660			"layout(binding = 2, std140) uniform Ui_true { bool ub_true; };\n"
1661			"void main (void)\n"
1662			"{\n"
1663			"	${PRECISION} ${DATATYPE} res;\n"
1664			"	if (ub_true)"
1665			"		res = ${FUNC}(v_coord) * u_scale + u_bias;\n"
1666			"	else\n"
1667			"		res = ${FUNC}(-v_coord) * u_scale + u_bias;\n"
1668			"	o_color = ${CAST_TO_OUTPUT};\n"
1669			"}\n",
1670
1671			UB_TRUE
1672		},
1673		{
1674			"uniform_loop",
1675			"Derivate of linearly interpolated value in uniform loop",
1676
1677			"#version 450\n"
1678			"layout(location = 0) in ${PRECISION} ${DATATYPE} v_coord;\n"
1679			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1680			"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
1681			"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; };\n"
1682			"layout(binding = 2, std140) uniform Ui_two { int ui_two; };\n"
1683			"void main (void)\n"
1684			"{\n"
1685			"	${PRECISION} ${DATATYPE} res = ${DATATYPE}(0.0);\n"
1686			"	for (int i = 0; i < ui_two; i++)\n"
1687			"		res += ${FUNC}(v_coord * float(i));\n"
1688			"	res = res * u_scale + u_bias;\n"
1689			"	o_color = ${CAST_TO_OUTPUT};\n"
1690			"}\n",
1691
1692			UI_TWO
1693		},
1694		{
1695			"uniform_switch",
1696			"Derivate of linearly interpolated value in uniform switch",
1697
1698			"#version 450\n"
1699			"layout(location = 0) in ${PRECISION} ${DATATYPE} v_coord;\n"
1700			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1701			"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
1702			"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; };\n"
1703			"layout(binding = 2, std140) uniform Ui_one { int ui_one; };\n"
1704			"void main (void)\n"
1705			"{\n"
1706			"	${PRECISION} ${DATATYPE} res;\n"
1707			"	switch (ui_one)\n"
1708			"	{\n"
1709			"		case 0:	res = ${FUNC}(-v_coord) * u_scale + u_bias;	break;\n"
1710			"		case 1:	res = ${FUNC}(v_coord) * u_scale + u_bias;	break;\n"
1711			"	}\n"
1712			"	o_color = ${CAST_TO_OUTPUT};\n"
1713			"}\n",
1714
1715			UI_ONE
1716		},
1717	};
1718
1719	static const struct
1720	{
1721		const char*		name;
1722		SurfaceType		surfaceType;
1723		int				numSamples;
1724	} s_fboConfigs[] =
1725	{
1726		{ "fbo",			SURFACETYPE_UNORM_FBO,		0 },
1727		{ "fbo_msaa2",		SURFACETYPE_UNORM_FBO,		2 },
1728		{ "fbo_msaa4",		SURFACETYPE_UNORM_FBO,		4 },
1729		{ "fbo_float",		SURFACETYPE_FLOAT_FBO,		0 },
1730	};
1731
1732	static const struct
1733	{
1734		const char*		name;
1735		SurfaceType		surfaceType;
1736		int				numSamples;
1737	} s_textureConfigs[] =
1738	{
1739		{ "basic",			SURFACETYPE_UNORM_FBO,		0 },
1740		{ "msaa4",			SURFACETYPE_UNORM_FBO,		4 },
1741		{ "float",			SURFACETYPE_FLOAT_FBO,		0 },
1742	};
1743
1744	// .dfdx[fine|coarse], .dfdy[fine|coarse], .fwidth[fine|coarse]
1745	for (int funcNdx = 0; funcNdx < DERIVATE_LAST; funcNdx++)
1746	{
1747		const DerivateFunc					function		= DerivateFunc(funcNdx);
1748		de::MovePtr<tcu::TestCaseGroup>		functionGroup	(new tcu::TestCaseGroup(m_testCtx, getDerivateFuncCaseName(function), getDerivateFuncName(function)));
1749
1750		// .constant - no precision variants, checks that derivate of constant arguments is 0
1751		{
1752			de::MovePtr<tcu::TestCaseGroup>	constantGroup	(new tcu::TestCaseGroup(m_testCtx, "constant", "Derivate of constant argument"));
1753
1754			for (int vecSize = 1; vecSize <= 4; vecSize++)
1755			{
1756				const glu::DataType			dataType		= vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1757				constantGroup->addChild(new ConstantDerivateCase(m_testCtx, glu::getDataTypeName(dataType), "", function, dataType));
1758			}
1759
1760			functionGroup->addChild(constantGroup.release());
1761		}
1762
1763		// Cases based on LinearDerivateCase
1764		for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(s_linearDerivateCases); caseNdx++)
1765		{
1766			de::MovePtr<tcu::TestCaseGroup>	linearCaseGroup	(new tcu::TestCaseGroup(m_testCtx, s_linearDerivateCases[caseNdx].name, s_linearDerivateCases[caseNdx].description));
1767			const char*						source			= s_linearDerivateCases[caseNdx].source;
1768
1769			for (int vecSize = 1; vecSize <= 4; vecSize++)
1770			{
1771				for (int precNdx = 0; precNdx < glu::PRECISION_LAST; precNdx++)
1772				{
1773					const glu::DataType		dataType		= vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1774					const glu::Precision	precision		= glu::Precision(precNdx);
1775					const SurfaceType		surfaceType		= SURFACETYPE_UNORM_FBO;
1776					const int				numSamples		= 0;
1777					std::ostringstream		caseName;
1778
1779					if (caseNdx != 0 && precision == glu::PRECISION_LOWP)
1780						continue; // Skip as lowp doesn't actually produce any bits when rendered to default FB.
1781
1782					caseName << glu::getDataTypeName(dataType) << "_" << glu::getPrecisionName(precision);
1783
1784					linearCaseGroup->addChild(new LinearDerivateCase(m_testCtx, caseName.str(), "", function, dataType, precision, surfaceType, numSamples, source, s_linearDerivateCases[caseNdx].usedDefaultUniform));
1785				}
1786			}
1787
1788			functionGroup->addChild(linearCaseGroup.release());
1789		}
1790
1791		// Fbo cases
1792		for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(s_fboConfigs); caseNdx++)
1793		{
1794			de::MovePtr<tcu::TestCaseGroup>	fboGroup		(new tcu::TestCaseGroup(m_testCtx, s_fboConfigs[caseNdx].name, "Derivate usage when rendering into FBO"));
1795			const char*						source			= s_linearDerivateCases[0].source; // use source from .linear group
1796			const SurfaceType				surfaceType		= s_fboConfigs[caseNdx].surfaceType;
1797			const int						numSamples		= s_fboConfigs[caseNdx].numSamples;
1798
1799			for (int vecSize = 1; vecSize <= 4; vecSize++)
1800			{
1801				for (int precNdx = 0; precNdx < glu::PRECISION_LAST; precNdx++)
1802				{
1803					const glu::DataType		dataType		= vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1804					const glu::Precision	precision		= glu::Precision(precNdx);
1805					std::ostringstream		caseName;
1806
1807					if (surfaceType != SURFACETYPE_FLOAT_FBO && precision == glu::PRECISION_LOWP)
1808						continue; // Skip as lowp doesn't actually produce any bits when rendered to U8 RT.
1809
1810					caseName << glu::getDataTypeName(dataType) << "_" << glu::getPrecisionName(precision);
1811
1812					fboGroup->addChild(new LinearDerivateCase(m_testCtx, caseName.str(), "", function, dataType, precision, surfaceType, numSamples, source, U_LAST));
1813				}
1814			}
1815
1816			functionGroup->addChild(fboGroup.release());
1817		}
1818
1819		// .texture
1820		{
1821			de::MovePtr<tcu::TestCaseGroup>		textureGroup	(new tcu::TestCaseGroup(m_testCtx, "texture", "Derivate of texture lookup result"));
1822
1823			for (int texCaseNdx = 0; texCaseNdx < DE_LENGTH_OF_ARRAY(s_textureConfigs); texCaseNdx++)
1824			{
1825				de::MovePtr<tcu::TestCaseGroup>	caseGroup		(new tcu::TestCaseGroup(m_testCtx, s_textureConfigs[texCaseNdx].name, ""));
1826				const SurfaceType				surfaceType		= s_textureConfigs[texCaseNdx].surfaceType;
1827				const int						numSamples		= s_textureConfigs[texCaseNdx].numSamples;
1828
1829				for (int vecSize = 1; vecSize <= 4; vecSize++)
1830				{
1831					for (int precNdx = 0; precNdx < glu::PRECISION_LAST; precNdx++)
1832					{
1833						const glu::DataType		dataType		= vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1834						const glu::Precision	precision		= glu::Precision(precNdx);
1835						std::ostringstream		caseName;
1836
1837						if (surfaceType != SURFACETYPE_FLOAT_FBO && precision == glu::PRECISION_LOWP)
1838							continue; // Skip as lowp doesn't actually produce any bits when rendered to U8 RT.
1839
1840						caseName << glu::getDataTypeName(dataType) << "_" << glu::getPrecisionName(precision);
1841
1842						caseGroup->addChild(new TextureDerivateCase(m_testCtx, caseName.str(), "", function, dataType, precision, surfaceType, numSamples));
1843					}
1844				}
1845
1846				textureGroup->addChild(caseGroup.release());
1847			}
1848
1849			functionGroup->addChild(textureGroup.release());
1850		}
1851
1852		addChild(functionGroup.release());
1853	}
1854}
1855
1856} // anonymous
1857
1858tcu::TestCaseGroup* createDerivateTests (tcu::TestContext& testCtx)
1859{
1860	return new ShaderDerivateTests(testCtx);
1861}
1862
1863} // sr
1864} // vkt
1865