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