• 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::__anonfb80ce440111::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::__anonfb80ce440111::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(0, 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 		if ((subgroupProperties.supportedStages & VK_SHADER_STAGE_FRAGMENT_BIT) == 0)
705 			throw tcu::NotSupportedError("Derivative dynamic control flow tests require subgroup supported stage including VK_SHADER_STAGE_FRAGMENT_BIT");
706 	}
707 
708 	setup();
709 
710 	render(numVertices, numTriangles, indices);
711 
712 	{
713 		const tcu::TextureLevel&		renderedImage	= getResultImage();
714 
715 		if (m_definitions.surfaceType == SURFACETYPE_FLOAT_FBO)
716 		{
717 			const tcu::TextureFormat	dataFormat		(tcu::TextureFormat::RGBA, tcu::TextureFormat::FLOAT);
718 
719 			resultImage.setStorage(dataFormat, renderedImage.getWidth(), renderedImage.getHeight());
720 			tcu::copy(resultImage.getAccess(), tcu::ConstPixelBufferAccess(dataFormat, renderedImage.getSize(), renderedImage.getAccess().getDataPtr()));
721 		}
722 		else
723 		{
724 			resultImage = renderedImage;
725 		}
726 	}
727 
728 	// Verify
729 	{
730 		tcu::Surface errorMask(resultImage.getWidth(), resultImage.getHeight());
731 		tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toVec());
732 
733 		const bool isOk = verify(resultImage.getAccess(), errorMask.getAccess());
734 
735 		log << TestLog::ImageSet("Result", "Result images")
736 			<< TestLog::Image("Rendered", "Rendered image", resultImage);
737 
738 		if (!isOk)
739 			log << TestLog::Image("ErrorMask", "Error mask", errorMask);
740 
741 		log << TestLog::EndImageSet;
742 
743 		if (isOk)
744 			return tcu::TestStatus::pass("Pass");
745 		else
746 			return tcu::TestStatus::fail("Image comparison failed");
747 	}
748 }
749 
setup(ShaderRenderCaseInstance & instance,const tcu::Vec4 &) const750 void DerivateUniformSetup::setup (ShaderRenderCaseInstance& instance, const tcu::Vec4&) const
751 {
752 	DerivateCaseDefinition	definitions		= dynamic_cast<TriangleDerivateCaseInstance&>(instance).getDerivateCaseDefinition();
753 	DerivateCaseValues		values			= dynamic_cast<TriangleDerivateCaseInstance&>(instance).getDerivateCaseValues();
754 
755 	DE_ASSERT(glu::isDataTypeFloatOrVec(definitions.dataType));
756 
757 	instance.addUniform(0u, vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, glu::getDataTypeScalarSize(definitions.dataType) * sizeof(float), values.derivScale.getPtr());
758 	instance.addUniform(1u, vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, glu::getDataTypeScalarSize(definitions.dataType) * sizeof(float), values.derivBias.getPtr());
759 
760 	if (m_useSampler)
761 		instance.useSampler(2u, 0u); // To the uniform binding location 2 bind the texture 0
762 }
763 
764 // TriangleDerivateCase
765 
766 class TriangleDerivateCase : public ShaderRenderCase
767 {
768 public:
769 									TriangleDerivateCase	(tcu::TestContext&		testCtx,
770 															 const std::string&		name,
771 															 const std::string&		description,
772 															 const UniformSetup*	uniformSetup);
773 	virtual							~TriangleDerivateCase	(void);
774 
775 protected:
776 	mutable DerivateCaseDefinition	m_definitions;
777 	mutable DerivateCaseValues		m_values;
778 };
779 
TriangleDerivateCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const UniformSetup * uniformSetup)780 TriangleDerivateCase::TriangleDerivateCase (tcu::TestContext&		testCtx,
781 											const std::string&		name,
782 											const std::string&		description,
783 											const UniformSetup*		uniformSetup)
784 	: ShaderRenderCase		(testCtx, name, description, false, (ShaderEvaluator*)DE_NULL, uniformSetup, DE_NULL)
785 	, m_definitions			()
786 {
787 }
788 
~TriangleDerivateCase(void)789 TriangleDerivateCase::~TriangleDerivateCase (void)
790 {
791 }
792 
genVertexSource(glu::DataType coordType,glu::Precision precision)793 static std::string genVertexSource (glu::DataType coordType, glu::Precision precision)
794 {
795 	DE_ASSERT(coordType == glu::TYPE_LAST || glu::isDataTypeFloatOrVec(coordType));
796 
797 	const std::string vertexTmpl =
798 		"#version 450\n"
799 		"layout(location = 0) in highp vec4 a_position;\n"
800 		+ string(coordType != glu::TYPE_LAST ? "layout(location = 1) in ${PRECISION} ${DATATYPE} a_coord;\n"
801 											   "layout(location = 0) out ${PRECISION} ${DATATYPE} v_coord;\n" : "") +
802 		"out gl_PerVertex {\n"
803 		"	vec4 gl_Position;\n"
804 		"};\n"
805 		"void main (void)\n"
806 		"{\n"
807 		"	gl_Position = a_position;\n"
808 		+ string(coordType != glu::TYPE_LAST ? "	v_coord = a_coord;\n" : "") +
809 		"}\n";
810 
811 	map<string, string> vertexParams;
812 
813 	if (coordType != glu::TYPE_LAST)
814 	{
815 		vertexParams["PRECISION"]	= glu::getPrecisionName(precision);
816 		vertexParams["DATATYPE"]	= glu::getDataTypeName(coordType);
817 	}
818 
819 	return tcu::StringTemplate(vertexTmpl).specialize(vertexParams);
820 }
821 
822 // ConstantDerivateCaseInstance
823 
824 class ConstantDerivateCaseInstance : public TriangleDerivateCaseInstance
825 {
826 public:
827 								ConstantDerivateCaseInstance	(Context&						context,
828 																 const UniformSetup&			uniformSetup,
829 																 const DerivateCaseDefinition&	definitions,
830 																 const DerivateCaseValues&		values);
831 	virtual						~ConstantDerivateCaseInstance	(void);
832 
833 	virtual bool				verify							(const tcu::ConstPixelBufferAccess& result, const tcu::PixelBufferAccess& errorMask);
834 };
835 
ConstantDerivateCaseInstance(Context & context,const UniformSetup & uniformSetup,const DerivateCaseDefinition & definitions,const DerivateCaseValues & values)836 ConstantDerivateCaseInstance::ConstantDerivateCaseInstance (Context&							context,
837 															const UniformSetup&					uniformSetup,
838 															const DerivateCaseDefinition&		definitions,
839 															const DerivateCaseValues&			values)
840 	: TriangleDerivateCaseInstance	(context, uniformSetup, definitions, values)
841 {
842 }
843 
~ConstantDerivateCaseInstance(void)844 ConstantDerivateCaseInstance::~ConstantDerivateCaseInstance (void)
845 {
846 }
847 
verify(const tcu::ConstPixelBufferAccess & result,const tcu::PixelBufferAccess & errorMask)848 bool ConstantDerivateCaseInstance::verify (const tcu::ConstPixelBufferAccess& result, const tcu::PixelBufferAccess& errorMask)
849 {
850 	const tcu::Vec4 reference	(0.0f); // Derivate of constant argument should always be 0
851 	const tcu::Vec4	threshold	= getSurfaceThreshold() / abs(m_values.derivScale);
852 
853 	return verifyConstantDerivate(m_context.getTestContext().getLog(), result, errorMask, m_definitions.dataType,
854 								  reference, threshold, m_values.derivScale, m_values.derivBias);
855 }
856 
857 // ConstantDerivateCase
858 
859 class ConstantDerivateCase : public TriangleDerivateCase
860 {
861 public:
862 							ConstantDerivateCase		(tcu::TestContext&		testCtx,
863 														 const std::string&		name,
864 														 const std::string&		description,
865 														 DerivateFunc			func,
866 														 glu::DataType			type);
867 	virtual					~ConstantDerivateCase		(void);
868 
869 	virtual	void			initPrograms				(vk::SourceCollections& programCollection) const;
870 	virtual TestInstance*	createInstance				(Context& context) const;
871 };
872 
ConstantDerivateCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,DerivateFunc func,glu::DataType type)873 ConstantDerivateCase::ConstantDerivateCase (tcu::TestContext&		testCtx,
874 											const std::string&		name,
875 											const std::string&		description,
876 											DerivateFunc			func,
877 											glu::DataType			type)
878 	: TriangleDerivateCase	(testCtx, name, description, new DerivateUniformSetup(false))
879 {
880 	m_definitions.func				= func;
881 	m_definitions.dataType			= type;
882 	m_definitions.precision			= glu::PRECISION_HIGHP;
883 }
884 
~ConstantDerivateCase(void)885 ConstantDerivateCase::~ConstantDerivateCase (void)
886 {
887 }
888 
createInstance(Context & context) const889 TestInstance* ConstantDerivateCase::createInstance (Context& context) const
890 {
891 	DE_ASSERT(m_uniformSetup != DE_NULL);
892 	return new ConstantDerivateCaseInstance(context, *m_uniformSetup, m_definitions, m_values);
893 }
894 
initPrograms(vk::SourceCollections & programCollection) const895 void ConstantDerivateCase::initPrograms (vk::SourceCollections& programCollection) const
896 {
897 	const char* fragmentTmpl =
898 		"#version 450\n"
899 		"layout(location = 0) out mediump vec4 o_color;\n"
900 		"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
901 		"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; }; \n"
902 		"void main (void)\n"
903 		"{\n"
904 		"	${PRECISION} ${DATATYPE} res = ${FUNC}(${VALUE}) * u_scale + u_bias;\n"
905 		"	o_color = ${CAST_TO_OUTPUT};\n"
906 		"}\n";
907 
908 	map<string, string> fragmentParams;
909 	fragmentParams["PRECISION"]			= glu::getPrecisionName(m_definitions.precision);
910 	fragmentParams["DATATYPE"]			= glu::getDataTypeName(m_definitions.dataType);
911 	fragmentParams["FUNC"]				= getDerivateFuncName(m_definitions.func);
912 	fragmentParams["VALUE"]				= m_definitions.dataType == glu::TYPE_FLOAT_VEC4 ? "vec4(1.0, 7.2, -1e5, 0.0)" :
913 										  m_definitions.dataType == glu::TYPE_FLOAT_VEC3 ? "vec3(1e2, 8.0, 0.01)" :
914 										  m_definitions.dataType == glu::TYPE_FLOAT_VEC2 ? "vec2(-0.0, 2.7)" :
915 										  /* TYPE_FLOAT */								   "7.7";
916 	fragmentParams["CAST_TO_OUTPUT"]	= m_definitions.dataType == glu::TYPE_FLOAT_VEC4 ? "res" :
917 										  m_definitions.dataType == glu::TYPE_FLOAT_VEC3 ? "vec4(res, 1.0)" :
918 										  m_definitions.dataType == glu::TYPE_FLOAT_VEC2 ? "vec4(res, 0.0, 1.0)" :
919 										  /* TYPE_FLOAT */								   "vec4(res, 0.0, 0.0, 1.0)";
920 
921 	std::string fragmentSrc = tcu::StringTemplate(fragmentTmpl).specialize(fragmentParams);
922 	programCollection.glslSources.add("vert") << glu::VertexSource(genVertexSource(m_definitions.coordDataType, m_definitions.coordPrecision));
923 	programCollection.glslSources.add("frag") << glu::FragmentSource(fragmentSrc);
924 
925 	m_values.derivScale		= tcu::Vec4(1e3f, 1e3f, 1e3f, 1e3f);
926 	m_values.derivBias		= tcu::Vec4(0.5f, 0.5f, 0.5f, 0.5f);
927 }
928 
929 // Linear cases
930 
931 class LinearDerivateUniformSetup : public DerivateUniformSetup
932 {
933 public:
934 					LinearDerivateUniformSetup		(bool useSampler, BaseUniformType usedDefaultUniform);
935 	virtual			~LinearDerivateUniformSetup		(void);
936 
937 	virtual void	setup							(ShaderRenderCaseInstance& instance, const tcu::Vec4& constCoords) const;
938 
939 private:
940 	const BaseUniformType	m_usedDefaultUniform;
941 };
942 
LinearDerivateUniformSetup(bool useSampler,BaseUniformType usedDefaultUniform)943 LinearDerivateUniformSetup::LinearDerivateUniformSetup (bool useSampler, BaseUniformType usedDefaultUniform)
944 	: DerivateUniformSetup	(useSampler)
945 	, m_usedDefaultUniform	(usedDefaultUniform)
946 {
947 }
948 
~LinearDerivateUniformSetup(void)949 LinearDerivateUniformSetup::~LinearDerivateUniformSetup (void)
950 {
951 }
952 
setup(ShaderRenderCaseInstance & instance,const tcu::Vec4 & constCoords) const953 void LinearDerivateUniformSetup::setup (ShaderRenderCaseInstance& instance, const tcu::Vec4& constCoords) const
954 {
955 	DerivateUniformSetup::setup(instance, constCoords);
956 
957 	if (m_usedDefaultUniform != U_LAST)
958 		switch (m_usedDefaultUniform)
959 		{
960 			case UB_TRUE:
961 			case UI_ONE:
962 			case UI_TWO:
963 				instance.useUniform(2u, m_usedDefaultUniform);
964 				break;
965 			default:
966 				DE_ASSERT(false);
967 				break;
968 		}
969 }
970 
971 class LinearDerivateCaseInstance : public TriangleDerivateCaseInstance
972 {
973 public:
974 								LinearDerivateCaseInstance	(Context&						context,
975 															 const UniformSetup&			uniformSetup,
976 															 const DerivateCaseDefinition&	definitions,
977 															 const DerivateCaseValues&		values);
978 	virtual						~LinearDerivateCaseInstance	(void);
979 
980 	virtual bool				verify						(const tcu::ConstPixelBufferAccess& result, const tcu::PixelBufferAccess& errorMask);
981 };
982 
LinearDerivateCaseInstance(Context & context,const UniformSetup & uniformSetup,const DerivateCaseDefinition & definitions,const DerivateCaseValues & values)983 LinearDerivateCaseInstance::LinearDerivateCaseInstance (Context&						context,
984 														const UniformSetup&				uniformSetup,
985 														const DerivateCaseDefinition&	definitions,
986 														const DerivateCaseValues&		values)
987 	: TriangleDerivateCaseInstance	(context, uniformSetup, definitions, values)
988 {
989 }
990 
~LinearDerivateCaseInstance(void)991 LinearDerivateCaseInstance::~LinearDerivateCaseInstance (void)
992 {
993 }
994 
verify(const tcu::ConstPixelBufferAccess & result,const tcu::PixelBufferAccess & errorMask)995 bool LinearDerivateCaseInstance::verify (const tcu::ConstPixelBufferAccess& result, const tcu::PixelBufferAccess& errorMask)
996 {
997 	const tcu::Vec4		xScale				= tcu::Vec4(1.0f, 0.0f, 0.5f, -0.5f);
998 	const tcu::Vec4		yScale				= tcu::Vec4(0.0f, 1.0f, 0.5f, -0.5f);
999 	const tcu::Vec4		surfaceThreshold	= getSurfaceThreshold() / abs(m_values.derivScale);
1000 
1001 	if (isDfdxFunc(m_definitions.func) || isDfdyFunc(m_definitions.func))
1002 	{
1003 		const bool			isX			= isDfdxFunc(m_definitions.func);
1004 		const float			div			= isX ? float(result.getWidth()) : float(result.getHeight());
1005 		const tcu::Vec4		scale		= isX ? xScale : yScale;
1006 		tcu::Vec4			reference	= ((m_values.coordMax - m_values.coordMin) / div);
1007 		const tcu::Vec4		opThreshold	= getDerivateThreshold(m_definitions.precision, m_values.coordMin, m_values.coordMax, reference);
1008 		const tcu::Vec4		threshold	= max(surfaceThreshold, opThreshold);
1009 		const int			numComps	= glu::getDataTypeFloatScalars(m_definitions.dataType);
1010 
1011 		/* adjust the reference value for the correct dfdx or dfdy sample adjacency */
1012 		reference = reference * scale;
1013 
1014 		m_context.getTestContext().getLog()
1015 			<< tcu::TestLog::Message
1016 			<< "Verifying result image.\n"
1017 			<< "\tValid derivative is " << LogVecComps(reference, numComps) << " with threshold " << LogVecComps(threshold, numComps)
1018 			<< tcu::TestLog::EndMessage;
1019 
1020 		// short circuit if result is strictly within the normal value error bounds.
1021 		// This improves performance significantly.
1022 		if (verifyConstantDerivate(m_context.getTestContext().getLog(), result, errorMask, m_definitions.dataType,
1023 								   reference, threshold, m_values.derivScale, m_values.derivBias,
1024 								   LOG_NOTHING))
1025 		{
1026 			m_context.getTestContext().getLog()
1027 				<< tcu::TestLog::Message
1028 				<< "No incorrect derivatives found, result valid."
1029 				<< tcu::TestLog::EndMessage;
1030 
1031 			return true;
1032 		}
1033 
1034 		// some pixels exceed error bounds calculated for normal values. Verify that these
1035 		// potentially invalid pixels are in fact valid due to (for example) subnorm flushing.
1036 
1037 		m_context.getTestContext().getLog()
1038 			<< tcu::TestLog::Message
1039 			<< "Initial verification failed, verifying image by calculating accurate error bounds for each result pixel.\n"
1040 			<< "\tVerifying each result derivative is within its range of legal result values."
1041 			<< tcu::TestLog::EndMessage;
1042 
1043 		{
1044 			const tcu::UVec2			viewportSize	(VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
1045 			const float					w				= float(viewportSize.x());
1046 			const float					h				= float(viewportSize.y());
1047 			const tcu::Vec4				valueRamp		= (m_values.coordMax - m_values.coordMin);
1048 			Linear2DFunctionEvaluator	function;
1049 
1050 			function.matrix.setRow(0, tcu::Vec3(valueRamp.x() / w, 0.0f, m_values.coordMin.x()));
1051 			function.matrix.setRow(1, tcu::Vec3(0.0f, valueRamp.y() / h, m_values.coordMin.y()));
1052 			function.matrix.setRow(2, tcu::Vec3(valueRamp.z() / w, valueRamp.z() / h, m_values.coordMin.z() + m_values.coordMin.z()) / 2.0f);
1053 			function.matrix.setRow(3, tcu::Vec3(-valueRamp.w() / w, -valueRamp.w() / h, m_values.coordMax.w() + m_values.coordMax.w()) / 2.0f);
1054 
1055 			return reverifyConstantDerivateWithFlushRelaxations(m_context.getTestContext().getLog(), result, errorMask,
1056 																m_definitions.dataType, m_definitions.precision, m_values.derivScale,
1057 																m_values.derivBias, surfaceThreshold, m_definitions.func,
1058 																function);
1059 		}
1060 	}
1061 	else
1062 	{
1063 		DE_ASSERT(isFwidthFunc(m_definitions.func));
1064 		const float			w			= float(result.getWidth());
1065 		const float			h			= float(result.getHeight());
1066 
1067 		const tcu::Vec4		dx			= ((m_values.coordMax - m_values.coordMin) / w) * xScale;
1068 		const tcu::Vec4		dy			= ((m_values.coordMax - m_values.coordMin) / h) * yScale;
1069 		const tcu::Vec4		reference	= tcu::abs(dx) + tcu::abs(dy);
1070 		const tcu::Vec4		dxThreshold	= getDerivateThreshold(m_definitions.precision, m_values.coordMin*xScale, m_values.coordMax*xScale, dx);
1071 		const tcu::Vec4		dyThreshold	= getDerivateThreshold(m_definitions.precision, m_values.coordMin*yScale, m_values.coordMax*yScale, dy);
1072 		const tcu::Vec4		threshold	= max(surfaceThreshold, max(dxThreshold, dyThreshold));
1073 
1074 		return verifyConstantDerivate(m_context.getTestContext().getLog(), result, errorMask, m_definitions.dataType,
1075 									  reference, threshold, m_values.derivScale, m_values.derivBias);
1076 	}
1077 }
1078 
1079 // LinearDerivateCase
1080 
1081 class LinearDerivateCase : public TriangleDerivateCase
1082 {
1083 public:
1084 							LinearDerivateCase			(tcu::TestContext&		testCtx,
1085 														 const std::string&		name,
1086 														 const std::string&		description,
1087 														 DerivateFunc			func,
1088 														 glu::DataType			type,
1089 														 glu::Precision			precision,
1090 														 bool					inNonUniformControlFlow,
1091 														 SurfaceType			surfaceType,
1092 														 int					numSamples,
1093 														 const std::string&		fragmentSrcTmpl,
1094 														 BaseUniformType		usedDefaultUniform);
1095 	virtual					~LinearDerivateCase			(void);
1096 
1097 	virtual	void			initPrograms				(vk::SourceCollections& programCollection) const;
1098 	virtual TestInstance*	createInstance				(Context& context) const;
1099 
1100 private:
1101 	const std::string		m_fragmentTmpl;
1102 };
1103 
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)1104 LinearDerivateCase::LinearDerivateCase (tcu::TestContext&		testCtx,
1105 										const std::string&		name,
1106 										const std::string&		description,
1107 										DerivateFunc			func,
1108 										glu::DataType			type,
1109 										glu::Precision			precision,
1110 										bool					inNonUniformControlFlow,
1111 										SurfaceType				surfaceType,
1112 										int						numSamples,
1113 										const std::string&		fragmentSrcTmpl,
1114 										BaseUniformType			usedDefaultUniform)
1115 	: TriangleDerivateCase	(testCtx, name, description, new LinearDerivateUniformSetup(false, usedDefaultUniform))
1116 	, m_fragmentTmpl		(fragmentSrcTmpl)
1117 {
1118 	m_definitions.func						= func;
1119 	m_definitions.dataType					= type;
1120 	m_definitions.precision					= precision;
1121 	m_definitions.inNonUniformControlFlow	= inNonUniformControlFlow;
1122 	m_definitions.coordDataType				= m_definitions.dataType;
1123 	m_definitions.coordPrecision			= m_definitions.precision;
1124 	m_definitions.surfaceType				= surfaceType;
1125 	m_definitions.numSamples				= numSamples;
1126 }
1127 
~LinearDerivateCase(void)1128 LinearDerivateCase::~LinearDerivateCase (void)
1129 {
1130 }
1131 
createInstance(Context & context) const1132 TestInstance* LinearDerivateCase::createInstance (Context& context) const
1133 {
1134 	DE_ASSERT(m_uniformSetup != DE_NULL);
1135 	return new LinearDerivateCaseInstance(context, *m_uniformSetup, m_definitions, m_values);
1136 }
1137 
initPrograms(vk::SourceCollections & programCollection) const1138 void LinearDerivateCase::initPrograms (vk::SourceCollections& programCollection) const
1139 {
1140 	const SpirvVersion				spirvVersion = m_definitions.inNonUniformControlFlow ? vk::SPIRV_VERSION_1_3 : vk::SPIRV_VERSION_1_0;
1141 	const vk::ShaderBuildOptions	buildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u);
1142 
1143 	const tcu::UVec2	viewportSize	(VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
1144 	const float			w				= float(viewportSize.x());
1145 	const float			h				= float(viewportSize.y());
1146 	const bool			packToInt		= m_definitions.surfaceType == SURFACETYPE_FLOAT_FBO;
1147 	map<string, string>	fragmentParams;
1148 
1149 	fragmentParams["OUTPUT_TYPE"]		= glu::getDataTypeName(packToInt ? glu::TYPE_UINT_VEC4 : glu::TYPE_FLOAT_VEC4);
1150 	fragmentParams["OUTPUT_PREC"]		= glu::getPrecisionName(packToInt ? glu::PRECISION_HIGHP : m_definitions.precision);
1151 	fragmentParams["PRECISION"]			= glu::getPrecisionName(m_definitions.precision);
1152 	fragmentParams["DATATYPE"]			= glu::getDataTypeName(m_definitions.dataType);
1153 	fragmentParams["FUNC"]				= getDerivateFuncName(m_definitions.func);
1154 
1155 	if (packToInt)
1156 	{
1157 		fragmentParams["CAST_TO_OUTPUT"]	= m_definitions.dataType == glu::TYPE_FLOAT_VEC4 ? "floatBitsToUint(res)" :
1158 											  m_definitions.dataType == glu::TYPE_FLOAT_VEC3 ? "floatBitsToUint(vec4(res, 1.0))" :
1159 											  m_definitions.dataType == glu::TYPE_FLOAT_VEC2 ? "floatBitsToUint(vec4(res, 0.0, 1.0))" :
1160 											  /* TYPE_FLOAT */								   "floatBitsToUint(vec4(res, 0.0, 0.0, 1.0))";
1161 	}
1162 	else
1163 	{
1164 		fragmentParams["CAST_TO_OUTPUT"]	= m_definitions.dataType == glu::TYPE_FLOAT_VEC4 ? "res" :
1165 											  m_definitions.dataType == glu::TYPE_FLOAT_VEC3 ? "vec4(res, 1.0)" :
1166 											  m_definitions.dataType == glu::TYPE_FLOAT_VEC2 ? "vec4(res, 0.0, 1.0)" :
1167 											  /* TYPE_FLOAT */								   "vec4(res, 0.0, 0.0, 1.0)";
1168 	}
1169 
1170 	std::string fragmentSrc = tcu::StringTemplate(m_fragmentTmpl).specialize(fragmentParams);
1171 	programCollection.glslSources.add("vert") << glu::VertexSource(genVertexSource(m_definitions.coordDataType, m_definitions.coordPrecision));
1172 	programCollection.glslSources.add("frag") << glu::FragmentSource(fragmentSrc) << buildOptions;
1173 
1174 	switch (m_definitions.precision)
1175 	{
1176 		case glu::PRECISION_HIGHP:
1177 			m_values.coordMin = tcu::Vec4(-97.f, 0.2f, 71.f, 74.f);
1178 			m_values.coordMax = tcu::Vec4(-13.2f, -77.f, 44.f, 76.f);
1179 			break;
1180 
1181 		case glu::PRECISION_MEDIUMP:
1182 			m_values.coordMin = tcu::Vec4(-37.0f, 47.f, -7.f, 0.0f);
1183 			m_values.coordMax = tcu::Vec4(-1.0f, 12.f, 7.f, 19.f);
1184 			break;
1185 
1186 		case glu::PRECISION_LOWP:
1187 			m_values.coordMin = tcu::Vec4(0.0f, -1.0f, 0.0f, 1.0f);
1188 			m_values.coordMax = tcu::Vec4(1.0f, 1.0f, -1.0f, -1.0f);
1189 			break;
1190 
1191 		default:
1192 			DE_ASSERT(false);
1193 	}
1194 
1195 	if (m_definitions.surfaceType == SURFACETYPE_FLOAT_FBO)
1196 	{
1197 		// No scale or bias used for accuracy.
1198 		m_values.derivScale	= tcu::Vec4(1.0f);
1199 		m_values.derivBias		= tcu::Vec4(0.0f);
1200 	}
1201 	else
1202 	{
1203 		// Compute scale - bias that normalizes to 0..1 range.
1204 		const tcu::Vec4 dx = (m_values.coordMax - m_values.coordMin) / tcu::Vec4(w, w, w*0.5f, -w*0.5f);
1205 		const tcu::Vec4 dy = (m_values.coordMax - m_values.coordMin) / tcu::Vec4(h, h, h*0.5f, -h*0.5f);
1206 
1207 		if (isDfdxFunc(m_definitions.func))
1208 			m_values.derivScale = 0.5f / dx;
1209 		else if (isDfdyFunc(m_definitions.func))
1210 			m_values.derivScale = 0.5f / dy;
1211 		else if (isFwidthFunc(m_definitions.func))
1212 			m_values.derivScale = 0.5f / (tcu::abs(dx) + tcu::abs(dy));
1213 		else
1214 			DE_ASSERT(false);
1215 
1216 		m_values.derivBias = tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f);
1217 	}
1218 }
1219 
1220 // TextureDerivateCaseInstance
1221 
1222 class TextureDerivateCaseInstance : public TriangleDerivateCaseInstance
1223 {
1224 public:
1225 								TextureDerivateCaseInstance		(Context&							context,
1226 																 const UniformSetup&				uniformSetup,
1227 																 const DerivateCaseDefinition&		definitions,
1228 																 const DerivateCaseValues&			values,
1229 																 const TextureCaseValues&			textureValues);
1230 	virtual						~TextureDerivateCaseInstance	(void);
1231 
1232 	virtual bool				verify							(const tcu::ConstPixelBufferAccess& result, const tcu::PixelBufferAccess& errorMask);
1233 
1234 private:
1235 	const TextureCaseValues&	m_textureValues;
1236 };
1237 
TextureDerivateCaseInstance(Context & context,const UniformSetup & uniformSetup,const DerivateCaseDefinition & definitions,const DerivateCaseValues & values,const TextureCaseValues & textureValues)1238 TextureDerivateCaseInstance::TextureDerivateCaseInstance (Context&							context,
1239 														  const UniformSetup&				uniformSetup,
1240 														  const DerivateCaseDefinition&		definitions,
1241 														  const DerivateCaseValues&			values,
1242 														  const TextureCaseValues&			textureValues)
1243 	: TriangleDerivateCaseInstance	(context, uniformSetup, definitions, values)
1244 	, m_textureValues				(textureValues)
1245 {
1246 	de::MovePtr<tcu::Texture2D>		texture;
1247 
1248 	// Lowp and mediump cases use RGBA16F format, while highp uses RGBA32F.
1249 	{
1250 		const tcu::UVec2			viewportSize	(VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
1251 		const tcu::TextureFormat	format			= glu::mapGLInternalFormat(m_definitions.precision == glu::PRECISION_HIGHP ? GL_RGBA32F : GL_RGBA16F);
1252 
1253 		texture = de::MovePtr<tcu::Texture2D>(new tcu::Texture2D(format, viewportSize.x(), viewportSize.y()));
1254 		texture->allocLevel(0);
1255 	}
1256 
1257 	// Fill with gradients.
1258 	{
1259 		const tcu::PixelBufferAccess level0 = texture->getLevel(0);
1260 		for (int y = 0; y < level0.getHeight(); y++)
1261 		{
1262 			for (int x = 0; x < level0.getWidth(); x++)
1263 			{
1264 				const float		xf		= (float(x)+0.5f) / float(level0.getWidth());
1265 				const float		yf		= (float(y)+0.5f) / float(level0.getHeight());
1266 				const tcu::Vec4	s		= tcu::Vec4(xf, yf, (xf+yf)/2.0f, 1.0f - (xf+yf)/2.0f);
1267 
1268 				level0.setPixel(m_textureValues.texValueMin + (m_textureValues.texValueMax - m_textureValues.texValueMin)*s, x, y);
1269 			}
1270 		}
1271 	}
1272 
1273 	de::SharedPtr<TextureBinding>	testTexture		(new TextureBinding(texture.release(),
1274 																		tcu::Sampler(tcu::Sampler::CLAMP_TO_EDGE,
1275 																					 tcu::Sampler::CLAMP_TO_EDGE,
1276 																					 tcu::Sampler::CLAMP_TO_EDGE,
1277 																					 tcu::Sampler::NEAREST,
1278 																					 tcu::Sampler::NEAREST,
1279 																					 0.0f,
1280 																					 true,
1281 																					 tcu::Sampler::COMPAREMODE_NONE,
1282 																					 0,
1283 																					 tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f),
1284 																					 true)));
1285 	m_textures.push_back(testTexture);
1286 }
1287 
~TextureDerivateCaseInstance(void)1288 TextureDerivateCaseInstance::~TextureDerivateCaseInstance (void)
1289 {
1290 }
1291 
verify(const tcu::ConstPixelBufferAccess & result,const tcu::PixelBufferAccess & errorMask)1292 bool TextureDerivateCaseInstance::verify (const tcu::ConstPixelBufferAccess& result, const tcu::PixelBufferAccess& errorMask)
1293 {
1294 	// \note Edges are ignored in comparison
1295 	if (result.getWidth() < 2 || result.getHeight() < 2)
1296 		throw tcu::NotSupportedError("Too small viewport");
1297 
1298 	tcu::ConstPixelBufferAccess	compareArea			= tcu::getSubregion(result, 1, 1, result.getWidth()-2, result.getHeight()-2);
1299 	tcu::PixelBufferAccess		maskArea			= tcu::getSubregion(errorMask, 1, 1, errorMask.getWidth()-2, errorMask.getHeight()-2);
1300 	const tcu::Vec4				xScale				= tcu::Vec4(1.0f, 0.0f, 0.5f, -0.5f);
1301 	const tcu::Vec4				yScale				= tcu::Vec4(0.0f, 1.0f, 0.5f, -0.5f);
1302 	const float					w					= float(result.getWidth());
1303 	const float					h					= float(result.getHeight());
1304 
1305 	const tcu::Vec4				surfaceThreshold	= getSurfaceThreshold() / abs(m_values.derivScale);
1306 
1307 	if (isDfdxFunc(m_definitions.func) || isDfdyFunc(m_definitions.func))
1308 	{
1309 		const bool			isX			= isDfdxFunc(m_definitions.func);
1310 		const float			div			= isX ? w : h;
1311 		const tcu::Vec4		scale		= isX ? xScale : yScale;
1312 		tcu::Vec4			reference	= ((m_textureValues.texValueMax - m_textureValues.texValueMin) / div);
1313 		const tcu::Vec4		opThreshold	= getDerivateThreshold(m_definitions.precision, m_textureValues.texValueMin, m_textureValues.texValueMax, reference);
1314 		const tcu::Vec4		threshold	= max(surfaceThreshold, opThreshold);
1315 		const int			numComps	= glu::getDataTypeFloatScalars(m_definitions.dataType);
1316 
1317 		/* adjust the reference value for the correct dfdx or dfdy sample adjacency */
1318 		reference = reference * scale;
1319 
1320 		m_context.getTestContext().getLog()
1321 			<< tcu::TestLog::Message
1322 			<< "Verifying result image.\n"
1323 			<< "\tValid derivative is " << LogVecComps(reference, numComps) << " with threshold " << LogVecComps(threshold, numComps)
1324 			<< tcu::TestLog::EndMessage;
1325 
1326 		// short circuit if result is strictly within the normal value error bounds.
1327 		// This improves performance significantly.
1328 		if (verifyConstantDerivate(m_context.getTestContext().getLog(), compareArea, maskArea, m_definitions.dataType,
1329 								   reference, threshold, m_values.derivScale, m_values.derivBias,
1330 								   LOG_NOTHING))
1331 		{
1332 			m_context.getTestContext().getLog()
1333 				<< tcu::TestLog::Message
1334 				<< "No incorrect derivatives found, result valid."
1335 				<< tcu::TestLog::EndMessage;
1336 
1337 			return true;
1338 		}
1339 
1340 		// some pixels exceed error bounds calculated for normal values. Verify that these
1341 		// potentially invalid pixels are in fact valid due to (for example) subnorm flushing.
1342 
1343 		m_context.getTestContext().getLog()
1344 			<< tcu::TestLog::Message
1345 			<< "Initial verification failed, verifying image by calculating accurate error bounds for each result pixel.\n"
1346 			<< "\tVerifying each result derivative is within its range of legal result values."
1347 			<< tcu::TestLog::EndMessage;
1348 
1349 		{
1350 			const tcu::Vec4				valueRamp		= (m_textureValues.texValueMax - m_textureValues.texValueMin);
1351 			Linear2DFunctionEvaluator	function;
1352 
1353 			function.matrix.setRow(0, tcu::Vec3(valueRamp.x() / w, 0.0f, m_textureValues.texValueMin.x()));
1354 			function.matrix.setRow(1, tcu::Vec3(0.0f, valueRamp.y() / h, m_textureValues.texValueMin.y()));
1355 			function.matrix.setRow(2, tcu::Vec3(valueRamp.z() / w, valueRamp.z() / h, m_textureValues.texValueMin.z() + m_textureValues.texValueMin.z()) / 2.0f);
1356 			function.matrix.setRow(3, tcu::Vec3(-valueRamp.w() / w, -valueRamp.w() / h, m_textureValues.texValueMax.w() + m_textureValues.texValueMax.w()) / 2.0f);
1357 
1358 			return reverifyConstantDerivateWithFlushRelaxations(m_context.getTestContext().getLog(), compareArea, maskArea,
1359 																m_definitions.dataType, m_definitions.precision, m_values.derivScale,
1360 																m_values.derivBias, surfaceThreshold, m_definitions.func,
1361 																function);
1362 		}
1363 	}
1364 	else
1365 	{
1366 		DE_ASSERT(isFwidthFunc(m_definitions.func));
1367 		const tcu::Vec4	dx			= ((m_textureValues.texValueMax - m_textureValues.texValueMin) / w) * xScale;
1368 		const tcu::Vec4	dy			= ((m_textureValues.texValueMax - m_textureValues.texValueMin) / h) * yScale;
1369 		const tcu::Vec4	reference	= tcu::abs(dx) + tcu::abs(dy);
1370 		const tcu::Vec4	dxThreshold	= getDerivateThreshold(m_definitions.precision, m_textureValues.texValueMin*xScale, m_textureValues.texValueMax*xScale, dx);
1371 		const tcu::Vec4	dyThreshold	= getDerivateThreshold(m_definitions.precision, m_textureValues.texValueMin*yScale, m_textureValues.texValueMax*yScale, dy);
1372 		const tcu::Vec4	threshold	= max(surfaceThreshold, max(dxThreshold, dyThreshold));
1373 
1374 		return verifyConstantDerivate(m_context.getTestContext().getLog(), compareArea, maskArea, m_definitions.dataType,
1375 									  reference, threshold, m_values.derivScale, m_values.derivBias);
1376 	}
1377 }
1378 
1379 // TextureDerivateCase
1380 
1381 class TextureDerivateCase : public TriangleDerivateCase
1382 {
1383 public:
1384 							TextureDerivateCase			(tcu::TestContext&		testCtx,
1385 														 const std::string&		name,
1386 														 const std::string&		description,
1387 														 DerivateFunc			func,
1388 														 glu::DataType			type,
1389 														 glu::Precision			precision,
1390 														 SurfaceType			surfaceType,
1391 														 int					numSamples);
1392 	virtual					~TextureDerivateCase		(void);
1393 
1394 	virtual	void			initPrograms				(vk::SourceCollections& programCollection) const;
1395 	virtual TestInstance*	createInstance				(Context& context) const;
1396 
1397 private:
1398 	mutable TextureCaseValues	m_textureValues;
1399 };
1400 
TextureDerivateCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,DerivateFunc func,glu::DataType type,glu::Precision precision,SurfaceType surfaceType,int numSamples)1401 TextureDerivateCase::TextureDerivateCase (tcu::TestContext&		testCtx,
1402 										  const std::string&	name,
1403 										  const std::string&	description,
1404 										  DerivateFunc			func,
1405 										  glu::DataType			type,
1406 										  glu::Precision		precision,
1407 										  SurfaceType			surfaceType,
1408 										  int					numSamples)
1409 	: TriangleDerivateCase	(testCtx, name, description, new DerivateUniformSetup(true))
1410 {
1411 	m_definitions.dataType			= type;
1412 	m_definitions.func				= func;
1413 	m_definitions.precision			= precision;
1414 	m_definitions.coordDataType		= glu::TYPE_FLOAT_VEC2;
1415 	m_definitions.coordPrecision	= glu::PRECISION_HIGHP;
1416 	m_definitions.surfaceType		= surfaceType;
1417 	m_definitions.numSamples		= numSamples;
1418 }
1419 
~TextureDerivateCase(void)1420 TextureDerivateCase::~TextureDerivateCase (void)
1421 {
1422 }
1423 
createInstance(Context & context) const1424 TestInstance* TextureDerivateCase::createInstance (Context& context) const
1425 {
1426 	DE_ASSERT(m_uniformSetup != DE_NULL);
1427 	return new TextureDerivateCaseInstance(context, *m_uniformSetup, m_definitions, m_values, m_textureValues);
1428 }
1429 
initPrograms(vk::SourceCollections & programCollection) const1430 void TextureDerivateCase::initPrograms (vk::SourceCollections& programCollection) const
1431 {
1432 	// Generate shader
1433 	{
1434 		const char* fragmentTmpl =
1435 			"#version 450\n"
1436 			"layout(location = 0) in highp vec2 v_coord;\n"
1437 			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1438 			"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
1439 			"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; };\n"
1440 			"layout(binding = 2) uniform ${PRECISION} sampler2D u_sampler;\n"
1441 			"void main (void)\n"
1442 			"{\n"
1443 			"	${PRECISION} vec4 tex = texture(u_sampler, v_coord);\n"
1444 			"	${PRECISION} ${DATATYPE} res = ${FUNC}(tex${SWIZZLE}) * u_scale + u_bias;\n"
1445 			"	o_color = ${CAST_TO_OUTPUT};\n"
1446 			"}\n";
1447 
1448 		const bool			packToInt		= m_definitions.surfaceType == SURFACETYPE_FLOAT_FBO;
1449 		map<string, string> fragmentParams;
1450 
1451 		fragmentParams["OUTPUT_TYPE"]		= glu::getDataTypeName(packToInt ? glu::TYPE_UINT_VEC4 : glu::TYPE_FLOAT_VEC4);
1452 		fragmentParams["OUTPUT_PREC"]		= glu::getPrecisionName(packToInt ? glu::PRECISION_HIGHP : m_definitions.precision);
1453 		fragmentParams["PRECISION"]			= glu::getPrecisionName(m_definitions.precision);
1454 		fragmentParams["DATATYPE"]			= glu::getDataTypeName(m_definitions.dataType);
1455 		fragmentParams["FUNC"]				= getDerivateFuncName(m_definitions.func);
1456 		fragmentParams["SWIZZLE"]			= m_definitions.dataType == glu::TYPE_FLOAT_VEC4 ? "" :
1457 											  m_definitions.dataType == glu::TYPE_FLOAT_VEC3 ? ".xyz" :
1458 											  m_definitions.dataType == glu::TYPE_FLOAT_VEC2 ? ".xy" :
1459 											  /* TYPE_FLOAT */								   ".x";
1460 
1461 		if (packToInt)
1462 		{
1463 			fragmentParams["CAST_TO_OUTPUT"]	= m_definitions.dataType == glu::TYPE_FLOAT_VEC4 ? "floatBitsToUint(res)" :
1464 												  m_definitions.dataType == glu::TYPE_FLOAT_VEC3 ? "floatBitsToUint(vec4(res, 1.0))" :
1465 												  m_definitions.dataType == glu::TYPE_FLOAT_VEC2 ? "floatBitsToUint(vec4(res, 0.0, 1.0))" :
1466 												  /* TYPE_FLOAT */								   "floatBitsToUint(vec4(res, 0.0, 0.0, 1.0))";
1467 		}
1468 		else
1469 		{
1470 			fragmentParams["CAST_TO_OUTPUT"]	= m_definitions.dataType == glu::TYPE_FLOAT_VEC4 ? "res" :
1471 												  m_definitions.dataType == glu::TYPE_FLOAT_VEC3 ? "vec4(res, 1.0)" :
1472 												  m_definitions.dataType == glu::TYPE_FLOAT_VEC2 ? "vec4(res, 0.0, 1.0)" :
1473 												  /* TYPE_FLOAT */								   "vec4(res, 0.0, 0.0, 1.0)";
1474 		}
1475 
1476 		std::string fragmentSrc = tcu::StringTemplate(fragmentTmpl).specialize(fragmentParams);
1477 		programCollection.glslSources.add("vert") << glu::VertexSource(genVertexSource(m_definitions.coordDataType, m_definitions.coordPrecision));
1478 		programCollection.glslSources.add("frag") << glu::FragmentSource(fragmentSrc);
1479 	}
1480 
1481 	// Texture size matches viewport and nearest sampling is used. Thus texture sampling
1482 	// is equal to just interpolating the texture value range.
1483 
1484 	// Determine value range for texture.
1485 
1486 	switch (m_definitions.precision)
1487 	{
1488 		case glu::PRECISION_HIGHP:
1489 			m_textureValues.texValueMin = tcu::Vec4(-97.f, 0.2f, 71.f, 74.f);
1490 			m_textureValues.texValueMax = tcu::Vec4(-13.2f, -77.f, 44.f, 76.f);
1491 			break;
1492 
1493 		case glu::PRECISION_MEDIUMP:
1494 			m_textureValues.texValueMin = tcu::Vec4(-37.0f, 47.f, -7.f, 0.0f);
1495 			m_textureValues.texValueMax = tcu::Vec4(-1.0f, 12.f, 7.f, 19.f);
1496 			break;
1497 
1498 		case glu::PRECISION_LOWP:
1499 			m_textureValues.texValueMin = tcu::Vec4(0.0f, -1.0f, 0.0f, 1.0f);
1500 			m_textureValues.texValueMax = tcu::Vec4(1.0f, 1.0f, -1.0f, -1.0f);
1501 			break;
1502 
1503 		default:
1504 			DE_ASSERT(false);
1505 	}
1506 
1507 	// Texture coordinates
1508 	m_values.coordMin = tcu::Vec4(0.0f);
1509 	m_values.coordMax = tcu::Vec4(1.0f);
1510 
1511 	if (m_definitions.surfaceType == SURFACETYPE_FLOAT_FBO)
1512 	{
1513 		// No scale or bias used for accuracy.
1514 		m_values.derivScale		= tcu::Vec4(1.0f);
1515 		m_values.derivBias		= tcu::Vec4(0.0f);
1516 	}
1517 	else
1518 	{
1519 		// Compute scale - bias that normalizes to 0..1 range.
1520 		const tcu::UVec2	viewportSize	(VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
1521 		const float			w				= float(viewportSize.x());
1522 		const float			h				= float(viewportSize.y());
1523 		const tcu::Vec4		dx				= (m_textureValues.texValueMax - m_textureValues.texValueMin) / tcu::Vec4(w, w, w*0.5f, -w*0.5f);
1524 		const tcu::Vec4		dy				= (m_textureValues.texValueMax - m_textureValues.texValueMin) / tcu::Vec4(h, h, h*0.5f, -h*0.5f);
1525 
1526 		if (isDfdxFunc(m_definitions.func))
1527 			m_values.derivScale = 0.5f / dx;
1528 		else if (isDfdyFunc(m_definitions.func))
1529 			m_values.derivScale = 0.5f / dy;
1530 		else if (isFwidthFunc(m_definitions.func))
1531 			m_values.derivScale = 0.5f / (tcu::abs(dx) + tcu::abs(dy));
1532 		else
1533 			DE_ASSERT(false);
1534 
1535 		m_values.derivBias = tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f);
1536 	}
1537 }
1538 
1539 // ShaderDerivateTests
1540 
1541 class ShaderDerivateTests : public tcu::TestCaseGroup
1542 {
1543 public:
1544 							ShaderDerivateTests		(tcu::TestContext& testCtx);
1545 	virtual					~ShaderDerivateTests	(void);
1546 
1547 	virtual void			init					(void);
1548 
1549 private:
1550 							ShaderDerivateTests		(const ShaderDerivateTests&);		// not allowed!
1551 	ShaderDerivateTests&	operator=				(const ShaderDerivateTests&);		// not allowed!
1552 };
1553 
ShaderDerivateTests(tcu::TestContext & testCtx)1554 ShaderDerivateTests::ShaderDerivateTests (tcu::TestContext& testCtx)
1555 	: TestCaseGroup(testCtx, "derivate", "Derivate Function Tests")
1556 {
1557 }
1558 
~ShaderDerivateTests(void)1559 ShaderDerivateTests::~ShaderDerivateTests (void)
1560 {
1561 }
1562 
1563 struct FunctionSpec
1564 {
1565 	std::string		name;
1566 	DerivateFunc	function;
1567 	glu::DataType	dataType;
1568 	glu::Precision	precision;
1569 
FunctionSpecvkt::sr::__anonfb80ce440111::FunctionSpec1570 	FunctionSpec (const std::string& name_, DerivateFunc function_, glu::DataType dataType_, glu::Precision precision_)
1571 		: name		(name_)
1572 		, function	(function_)
1573 		, dataType	(dataType_)
1574 		, precision	(precision_)
1575 	{
1576 	}
1577 };
1578 
init(void)1579 void ShaderDerivateTests::init (void)
1580 {
1581 	static const struct
1582 	{
1583 		const char*			name;
1584 		const char*			description;
1585 		const char*			source;
1586 		BaseUniformType		usedDefaultUniform;
1587 		bool				inNonUniformControlFlow;
1588 	} s_linearDerivateCases[] =
1589 	{
1590 		{
1591 			"linear",
1592 			"Basic derivate of linearly interpolated argument",
1593 
1594 			"#version 450\n"
1595 			"layout(location = 0) in ${PRECISION} ${DATATYPE} v_coord;\n"
1596 			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1597 			"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
1598 			"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; };\n"
1599 			"void main (void)\n"
1600 			"{\n"
1601 			"	${PRECISION} ${DATATYPE} res = ${FUNC}(v_coord) * u_scale + u_bias;\n"
1602 			"	o_color = ${CAST_TO_OUTPUT};\n"
1603 			"}\n",
1604 
1605 			U_LAST,
1606 			false
1607 		},
1608 		{
1609 			"in_function",
1610 			"Derivate of linear function argument",
1611 
1612 			"#version 450\n"
1613 			"layout(location = 0) in ${PRECISION} ${DATATYPE} v_coord;\n"
1614 			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1615 			"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
1616 			"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; };\n"
1617 			"\n"
1618 			"${PRECISION} ${DATATYPE} computeRes (${PRECISION} ${DATATYPE} value)\n"
1619 			"{\n"
1620 			"	return ${FUNC}(v_coord) * u_scale + u_bias;\n"
1621 			"}\n"
1622 			"\n"
1623 			"void main (void)\n"
1624 			"{\n"
1625 			"	${PRECISION} ${DATATYPE} res = computeRes(v_coord);\n"
1626 			"	o_color = ${CAST_TO_OUTPUT};\n"
1627 			"}\n",
1628 
1629 			U_LAST,
1630 			false
1631 		},
1632 		{
1633 			"static_if",
1634 			"Derivate of linearly interpolated value in static if",
1635 
1636 			"#version 450\n"
1637 			"layout(location = 0) in ${PRECISION} ${DATATYPE} v_coord;\n"
1638 			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1639 			"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
1640 			"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; };\n"
1641 			"void main (void)\n"
1642 			"{\n"
1643 			"	${PRECISION} ${DATATYPE} res;\n"
1644 			"	if (false)\n"
1645 			"		res = ${FUNC}(-v_coord) * u_scale + u_bias;\n"
1646 			"	else\n"
1647 			"		res = ${FUNC}(v_coord) * u_scale + u_bias;\n"
1648 			"	o_color = ${CAST_TO_OUTPUT};\n"
1649 			"}\n",
1650 
1651 			U_LAST,
1652 			false
1653 		},
1654 		{
1655 			"static_loop",
1656 			"Derivate of linearly interpolated value in static loop",
1657 
1658 			"#version 450\n"
1659 			"layout(location = 0) in ${PRECISION} ${DATATYPE} v_coord;\n"
1660 			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1661 			"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
1662 			"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; };\n"
1663 			"void main (void)\n"
1664 			"{\n"
1665 			"	${PRECISION} ${DATATYPE} res = ${DATATYPE}(0.0);\n"
1666 			"	for (int i = 0; i < 2; i++)\n"
1667 			"		res += ${FUNC}(v_coord * float(i));\n"
1668 			"	res = res * u_scale + u_bias;\n"
1669 			"	o_color = ${CAST_TO_OUTPUT};\n"
1670 			"}\n",
1671 
1672 			U_LAST,
1673 			false
1674 		},
1675 		{
1676 			"static_switch",
1677 			"Derivate of linearly interpolated value in static switch",
1678 
1679 			"#version 450\n"
1680 			"layout(location = 0) in ${PRECISION} ${DATATYPE} v_coord;\n"
1681 			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1682 			"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
1683 			"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; };\n"
1684 			"void main (void)\n"
1685 			"{\n"
1686 			"	${PRECISION} ${DATATYPE} res;\n"
1687 			"	switch (1)\n"
1688 			"	{\n"
1689 			"		case 0:	res = ${FUNC}(-v_coord) * u_scale + u_bias;	break;\n"
1690 			"		case 1:	res = ${FUNC}(v_coord) * u_scale + u_bias;	break;\n"
1691 			"	}\n"
1692 			"	o_color = ${CAST_TO_OUTPUT};\n"
1693 			"}\n",
1694 
1695 			U_LAST,
1696 			false
1697 		},
1698 		{
1699 			"uniform_if",
1700 			"Derivate of linearly interpolated value in uniform if",
1701 
1702 			"#version 450\n"
1703 			"layout(location = 0) in ${PRECISION} ${DATATYPE} v_coord;\n"
1704 			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1705 			"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
1706 			"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; };\n"
1707 			"layout(binding = 2, std140) uniform Ui_true { bool ub_true; };\n"
1708 			"void main (void)\n"
1709 			"{\n"
1710 			"	${PRECISION} ${DATATYPE} res;\n"
1711 			"	if (ub_true)"
1712 			"		res = ${FUNC}(v_coord) * u_scale + u_bias;\n"
1713 			"	else\n"
1714 			"		res = ${FUNC}(-v_coord) * u_scale + u_bias;\n"
1715 			"	o_color = ${CAST_TO_OUTPUT};\n"
1716 			"}\n",
1717 
1718 			UB_TRUE,
1719 			false
1720 		},
1721 		{
1722 			"uniform_loop",
1723 			"Derivate of linearly interpolated value in uniform loop",
1724 
1725 			"#version 450\n"
1726 			"layout(location = 0) in ${PRECISION} ${DATATYPE} v_coord;\n"
1727 			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1728 			"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
1729 			"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; };\n"
1730 			"layout(binding = 2, std140) uniform Ui_two { int ui_two; };\n"
1731 			"void main (void)\n"
1732 			"{\n"
1733 			"	${PRECISION} ${DATATYPE} res = ${DATATYPE}(0.0);\n"
1734 			"	for (int i = 0; i < ui_two; i++)\n"
1735 			"		res += ${FUNC}(v_coord * float(i));\n"
1736 			"	res = res * u_scale + u_bias;\n"
1737 			"	o_color = ${CAST_TO_OUTPUT};\n"
1738 			"}\n",
1739 
1740 			UI_TWO,
1741 			false
1742 		},
1743 		{
1744 			"uniform_switch",
1745 			"Derivate of linearly interpolated value in uniform switch",
1746 
1747 			"#version 450\n"
1748 			"layout(location = 0) in ${PRECISION} ${DATATYPE} v_coord;\n"
1749 			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1750 			"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
1751 			"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; };\n"
1752 			"layout(binding = 2, std140) uniform Ui_one { int ui_one; };\n"
1753 			"void main (void)\n"
1754 			"{\n"
1755 			"	${PRECISION} ${DATATYPE} res;\n"
1756 			"	switch (ui_one)\n"
1757 			"	{\n"
1758 			"		case 0:	res = ${FUNC}(-v_coord) * u_scale + u_bias;	break;\n"
1759 			"		case 1:	res = ${FUNC}(v_coord) * u_scale + u_bias;	break;\n"
1760 			"	}\n"
1761 			"	o_color = ${CAST_TO_OUTPUT};\n"
1762 			"}\n",
1763 
1764 			UI_ONE,
1765 			false
1766 		},
1767 		{
1768 			"dynamic_if",
1769 			"Derivate of linearly interpolated value in static if",
1770 
1771 			"#version 450\n"
1772 			"#extension GL_KHR_shader_subgroup_ballot : require\n"
1773 			"layout(location = 0) in ${PRECISION} ${DATATYPE} v_coord;\n"
1774 			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1775 			"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
1776 			"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; };\n"
1777 			"layout(binding = 2, std140) uniform Ui_one { int ui_one; };\n"
1778 			"void main (void)\n"
1779 			"{\n"
1780 			"	${PRECISION} ${DATATYPE} res;\n"
1781 			"	bool non_uniform = ((uint(gl_FragCoord.x * 0.4) + uint(gl_FragCoord.y * 0.3)) & 2) != 0;\n"
1782 			"	uvec4 quad_ballot = uvec4(0);\n"
1783 			"	quad_ballot[gl_SubgroupInvocationID >> 5] = 0xf << (gl_SubgroupInvocationID & 0x1c);\n"
1784 			"	bool quad_uniform = (subgroupBallot(non_uniform) & quad_ballot) == quad_ballot;\n"
1785 			"	if (quad_uniform)\n"
1786 			"		res = ${FUNC}(v_coord) * u_scale + u_bias;\n"
1787 			"	else\n"
1788 			"		res = ${FUNC}(v_coord * float(ui_one)) * u_scale + u_bias;\n"
1789 			"	o_color = ${CAST_TO_OUTPUT};\n"
1790 			"}\n",
1791 
1792 			UI_ONE,
1793 			true
1794 		},
1795 		{
1796 			"dynamic_loop",
1797 			"Derivate of linearly interpolated value in uniform loop",
1798 
1799 			"#version 450\n"
1800 			"#extension GL_KHR_shader_subgroup_ballot : require\n"
1801 			"layout(location = 0) in ${PRECISION} ${DATATYPE} v_coord;\n"
1802 			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1803 			"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
1804 			"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; };\n"
1805 			"layout(binding = 2, std140) uniform Ui_one { int ui_one; };\n"
1806 			"void main (void)\n"
1807 			"{\n"
1808 			"	${PRECISION} ${DATATYPE} res = ${DATATYPE}(0.0);\n"
1809 			"	bool non_uniform = ((uint(gl_FragCoord.x * 0.4) + uint(gl_FragCoord.y * 0.3)) & 2) != 0;\n"
1810 			"	uvec4 quad_ballot = uvec4(0);\n"
1811 			"	quad_ballot[gl_SubgroupInvocationID >> 5] = 0xf << (gl_SubgroupInvocationID & 0x1c);\n"
1812 			"	bool quad_uniform = (subgroupBallot(non_uniform) & quad_ballot) == quad_ballot;\n"
1813 			"	for (int i = 0; i < ui_one + int(quad_uniform); i++)\n"
1814 			"		res = ${FUNC}(v_coord * float(i - int(quad_uniform) + 1));\n"
1815 			"	res = res * u_scale + u_bias;\n"
1816 			"	o_color = ${CAST_TO_OUTPUT};\n"
1817 			"}\n",
1818 
1819 			UI_ONE,
1820 			true
1821 		},
1822 		{
1823 			"dynamic_switch",
1824 			"Derivate of linearly interpolated value in uniform switch",
1825 
1826 			"#version 450\n"
1827 			"#extension GL_KHR_shader_subgroup_ballot : require\n"
1828 			"layout(location = 0) in ${PRECISION} ${DATATYPE} v_coord;\n"
1829 			"layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1830 			"layout(binding = 0, std140) uniform Scale { ${PRECISION} ${DATATYPE} u_scale; };\n"
1831 			"layout(binding = 1, std140) uniform Bias { ${PRECISION} ${DATATYPE} u_bias; };\n"
1832 			"layout(binding = 2, std140) uniform Ui_one { int ui_one; };\n"
1833 			"void main (void)\n"
1834 			"{\n"
1835 			"	${PRECISION} ${DATATYPE} res;\n"
1836 			"	bool non_uniform = ((uint(gl_FragCoord.x * 0.4) + uint(gl_FragCoord.y * 0.3)) & 2) != 0;\n"
1837 			"	uvec4 quad_ballot = uvec4(0);\n"
1838 			"	quad_ballot[gl_SubgroupInvocationID >> 5] = 0xf << (gl_SubgroupInvocationID & 0x1c);\n"
1839 			"	bool quad_uniform = (subgroupBallot(non_uniform) & quad_ballot) == quad_ballot;\n"
1840 			"	switch (int(quad_uniform))\n"
1841 			"	{\n"
1842 			"		case 0:	res = ${FUNC}(v_coord) * u_scale + u_bias;	break;\n"
1843 			"		case 1:	res = ${FUNC}(v_coord * float(ui_one)) * u_scale + u_bias;	break;\n"
1844 			"	}\n"
1845 			"	o_color = ${CAST_TO_OUTPUT};\n"
1846 			"}\n",
1847 
1848 			UI_ONE,
1849 			true
1850 		},
1851 	};
1852 
1853 	static const struct
1854 	{
1855 		const char*		name;
1856 		SurfaceType		surfaceType;
1857 		int				numSamples;
1858 	} s_fboConfigs[] =
1859 	{
1860 		{ "fbo",			SURFACETYPE_UNORM_FBO,		0 },
1861 		{ "fbo_msaa2",		SURFACETYPE_UNORM_FBO,		2 },
1862 		{ "fbo_msaa4",		SURFACETYPE_UNORM_FBO,		4 },
1863 		{ "fbo_float",		SURFACETYPE_FLOAT_FBO,		0 },
1864 	};
1865 
1866 	static const struct
1867 	{
1868 		const char*		name;
1869 		SurfaceType		surfaceType;
1870 		int				numSamples;
1871 	} s_textureConfigs[] =
1872 	{
1873 		{ "basic",			SURFACETYPE_UNORM_FBO,		0 },
1874 		{ "msaa4",			SURFACETYPE_UNORM_FBO,		4 },
1875 		{ "float",			SURFACETYPE_FLOAT_FBO,		0 },
1876 	};
1877 
1878 	// .dfdx[fine|coarse], .dfdy[fine|coarse], .fwidth[fine|coarse]
1879 	for (int funcNdx = 0; funcNdx < DERIVATE_LAST; funcNdx++)
1880 	{
1881 		const DerivateFunc					function		= DerivateFunc(funcNdx);
1882 		de::MovePtr<tcu::TestCaseGroup>		functionGroup	(new tcu::TestCaseGroup(m_testCtx, getDerivateFuncCaseName(function), getDerivateFuncName(function)));
1883 
1884 		// .constant - no precision variants, checks that derivate of constant arguments is 0
1885 		{
1886 			de::MovePtr<tcu::TestCaseGroup>	constantGroup	(new tcu::TestCaseGroup(m_testCtx, "constant", "Derivate of constant argument"));
1887 
1888 			for (int vecSize = 1; vecSize <= 4; vecSize++)
1889 			{
1890 				const glu::DataType			dataType		= vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1891 				constantGroup->addChild(new ConstantDerivateCase(m_testCtx, glu::getDataTypeName(dataType), "", function, dataType));
1892 			}
1893 
1894 			functionGroup->addChild(constantGroup.release());
1895 		}
1896 
1897 		// Cases based on LinearDerivateCase
1898 		for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(s_linearDerivateCases); caseNdx++)
1899 		{
1900 			de::MovePtr<tcu::TestCaseGroup>	linearCaseGroup	(new tcu::TestCaseGroup(m_testCtx, s_linearDerivateCases[caseNdx].name, s_linearDerivateCases[caseNdx].description));
1901 			const char*						source			= s_linearDerivateCases[caseNdx].source;
1902 
1903 			for (int vecSize = 1; vecSize <= 4; vecSize++)
1904 			{
1905 				for (int precNdx = 0; precNdx < glu::PRECISION_LAST; precNdx++)
1906 				{
1907 					const glu::DataType		dataType		= vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1908 					const glu::Precision	precision		= glu::Precision(precNdx);
1909 					const SurfaceType		surfaceType		= SURFACETYPE_UNORM_FBO;
1910 					const int				numSamples		= 0;
1911 					std::ostringstream		caseName;
1912 
1913 					if (caseNdx != 0 && precision == glu::PRECISION_LOWP)
1914 						continue; // Skip as lowp doesn't actually produce any bits when rendered to default FB.
1915 
1916 					caseName << glu::getDataTypeName(dataType) << "_" << glu::getPrecisionName(precision);
1917 
1918 					linearCaseGroup->addChild(new LinearDerivateCase(m_testCtx, caseName.str(), "", function, dataType, precision, s_linearDerivateCases[caseNdx].inNonUniformControlFlow, surfaceType, numSamples, source, s_linearDerivateCases[caseNdx].usedDefaultUniform));
1919 				}
1920 			}
1921 
1922 			functionGroup->addChild(linearCaseGroup.release());
1923 		}
1924 
1925 		// Fbo cases
1926 		for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(s_fboConfigs); caseNdx++)
1927 		{
1928 			de::MovePtr<tcu::TestCaseGroup>	fboGroup		(new tcu::TestCaseGroup(m_testCtx, s_fboConfigs[caseNdx].name, "Derivate usage when rendering into FBO"));
1929 			const char*						source			= s_linearDerivateCases[0].source; // use source from .linear group
1930 			const SurfaceType				surfaceType		= s_fboConfigs[caseNdx].surfaceType;
1931 			const int						numSamples		= s_fboConfigs[caseNdx].numSamples;
1932 
1933 			for (int vecSize = 1; vecSize <= 4; vecSize++)
1934 			{
1935 				for (int precNdx = 0; precNdx < glu::PRECISION_LAST; precNdx++)
1936 				{
1937 					const glu::DataType		dataType		= vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1938 					const glu::Precision	precision		= glu::Precision(precNdx);
1939 					std::ostringstream		caseName;
1940 
1941 					if (surfaceType != SURFACETYPE_FLOAT_FBO && precision == glu::PRECISION_LOWP)
1942 						continue; // Skip as lowp doesn't actually produce any bits when rendered to U8 RT.
1943 
1944 					caseName << glu::getDataTypeName(dataType) << "_" << glu::getPrecisionName(precision);
1945 
1946 					fboGroup->addChild(new LinearDerivateCase(m_testCtx, caseName.str(), "", function, dataType, precision, false, surfaceType, numSamples, source, U_LAST));
1947 				}
1948 			}
1949 
1950 			functionGroup->addChild(fboGroup.release());
1951 		}
1952 
1953 		// .texture
1954 		{
1955 			de::MovePtr<tcu::TestCaseGroup>		textureGroup	(new tcu::TestCaseGroup(m_testCtx, "texture", "Derivate of texture lookup result"));
1956 
1957 			for (int texCaseNdx = 0; texCaseNdx < DE_LENGTH_OF_ARRAY(s_textureConfigs); texCaseNdx++)
1958 			{
1959 				de::MovePtr<tcu::TestCaseGroup>	caseGroup		(new tcu::TestCaseGroup(m_testCtx, s_textureConfigs[texCaseNdx].name, ""));
1960 				const SurfaceType				surfaceType		= s_textureConfigs[texCaseNdx].surfaceType;
1961 				const int						numSamples		= s_textureConfigs[texCaseNdx].numSamples;
1962 
1963 				for (int vecSize = 1; vecSize <= 4; vecSize++)
1964 				{
1965 					for (int precNdx = 0; precNdx < glu::PRECISION_LAST; precNdx++)
1966 					{
1967 						const glu::DataType		dataType		= vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1968 						const glu::Precision	precision		= glu::Precision(precNdx);
1969 						std::ostringstream		caseName;
1970 
1971 						if (surfaceType != SURFACETYPE_FLOAT_FBO && precision == glu::PRECISION_LOWP)
1972 							continue; // Skip as lowp doesn't actually produce any bits when rendered to U8 RT.
1973 
1974 						caseName << glu::getDataTypeName(dataType) << "_" << glu::getPrecisionName(precision);
1975 
1976 						caseGroup->addChild(new TextureDerivateCase(m_testCtx, caseName.str(), "", function, dataType, precision, surfaceType, numSamples));
1977 					}
1978 				}
1979 
1980 				textureGroup->addChild(caseGroup.release());
1981 			}
1982 
1983 			functionGroup->addChild(textureGroup.release());
1984 		}
1985 
1986 		addChild(functionGroup.release());
1987 	}
1988 }
1989 
1990 } // anonymous
1991 
createDerivateTests(tcu::TestContext & testCtx)1992 tcu::TestCaseGroup* createDerivateTests (tcu::TestContext& testCtx)
1993 {
1994 	return new ShaderDerivateTests(testCtx);
1995 }
1996 
1997 } // sr
1998 } // vkt
1999