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