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 Varying interpolation accuracy tests.
22 *
23 * \todo [2012-07-03 pyry] On GLES3 we could use floating-point render target
24 *						   for better accuracy evaluation.
25 *//*--------------------------------------------------------------------*/
26
27#include "es3aVaryingInterpolationTests.hpp"
28#include "gluPixelTransfer.hpp"
29#include "gluShaderProgram.hpp"
30#include "gluShaderUtil.hpp"
31#include "tcuStringTemplate.hpp"
32#include "gluContextInfo.hpp"
33#include "glsTextureTestUtil.hpp"
34#include "tcuVector.hpp"
35#include "tcuVectorUtil.hpp"
36#include "tcuTestLog.hpp"
37#include "tcuFloat.hpp"
38#include "tcuImageCompare.hpp"
39#include "tcuRenderTarget.hpp"
40#include "deRandom.hpp"
41#include "deStringUtil.hpp"
42#include "deString.h"
43
44#include "glw.h"
45
46using tcu::TestLog;
47using tcu::Vec3;
48using tcu::Vec4;
49using std::string;
50using std::vector;
51using std::map;
52using deqp::gls::TextureTestUtil::SurfaceAccess;
53
54namespace deqp
55{
56namespace gles3
57{
58namespace Accuracy
59{
60
61static inline float projectedTriInterpolate (const tcu::Vec3& s, const tcu::Vec3& w, float nx, float ny)
62{
63	return (s[0]*(1.0f-nx-ny)/w[0] + s[1]*ny/w[1] + s[2]*nx/w[2]) / ((1.0f-nx-ny)/w[0] + ny/w[1] + nx/w[2]);
64}
65
66static void renderReference (const SurfaceAccess& dst, const float coords[4*3], const Vec4& wCoord, const Vec3& scale, const Vec3& bias)
67{
68	float		dstW		= (float)dst.getWidth();
69	float		dstH		= (float)dst.getHeight();
70
71	Vec3		triR[2]		= { Vec3(coords[0*3+0], coords[1*3+0], coords[2*3+0]), Vec3(coords[3*3+0], coords[2*3+0], coords[1*3+0]) };
72	Vec3		triG[2]		= { Vec3(coords[0*3+1], coords[1*3+1], coords[2*3+1]), Vec3(coords[3*3+1], coords[2*3+1], coords[1*3+1]) };
73	Vec3		triB[2]		= { Vec3(coords[0*3+2], coords[1*3+2], coords[2*3+2]), Vec3(coords[3*3+2], coords[2*3+2], coords[1*3+2]) };
74	tcu::Vec3	triW[2]		= { wCoord.swizzle(0, 1, 2), wCoord.swizzle(3, 2, 1) };
75
76	for (int py = 0; py < dst.getHeight(); py++)
77	{
78		for (int px = 0; px < dst.getWidth(); px++)
79		{
80			float	wx		= (float)px + 0.5f;
81			float	wy		= (float)py + 0.5f;
82			float	nx		= wx / dstW;
83			float	ny		= wy / dstH;
84
85			int		triNdx	= nx + ny >= 1.0f ? 1 : 0;
86			float	triNx	= triNdx ? 1.0f - nx : nx;
87			float	triNy	= triNdx ? 1.0f - ny : ny;
88
89			float	r		= projectedTriInterpolate(triR[triNdx], triW[triNdx], triNx, triNy) * scale[0] + bias[0];
90			float	g		= projectedTriInterpolate(triG[triNdx], triW[triNdx], triNx, triNy) * scale[1] + bias[1];
91			float	b		= projectedTriInterpolate(triB[triNdx], triW[triNdx], triNx, triNy) * scale[2] + bias[2];
92
93			Vec4	color	= Vec4(r, g, b, 1.0f);
94
95			dst.setPixel(color, px, py);
96		}
97	}
98}
99
100class InterpolationCase : public TestCase
101{
102public:
103					InterpolationCase			(Context& context, const char* name, const char* desc, glu::Precision precision, const tcu::Vec3& minVal, const tcu::Vec3& maxVal, bool projective);
104					~InterpolationCase			(void);
105
106	IterateResult	iterate						(void);
107
108private:
109	glu::Precision	m_precision;
110	tcu::Vec3		m_min;
111	tcu::Vec3		m_max;
112	bool			m_projective;
113};
114
115InterpolationCase::InterpolationCase (Context& context, const char* name, const char* desc, glu::Precision precision, const tcu::Vec3& minVal, const tcu::Vec3& maxVal, bool projective)
116	: TestCase		(context, tcu::NODETYPE_ACCURACY, name, desc)
117	, m_precision	(precision)
118	, m_min			(minVal)
119	, m_max			(maxVal)
120	, m_projective	(projective)
121{
122}
123
124InterpolationCase::~InterpolationCase (void)
125{
126}
127
128static bool isValidFloat (glu::Precision precision, float val)
129{
130	if (precision == glu::PRECISION_MEDIUMP)
131	{
132		tcu::Float16 fp16(val);
133		return !fp16.isDenorm() && !fp16.isInf() && !fp16.isNaN();
134	}
135	else
136	{
137		tcu::Float32 fp32(val);
138		return !fp32.isDenorm() && !fp32.isInf() && !fp32.isNaN();
139	}
140}
141
142template <int Size>
143static bool isValidFloatVec (glu::Precision precision, const tcu::Vector<float, Size>& vec)
144{
145	for (int ndx = 0; ndx < Size; ndx++)
146	{
147		if (!isValidFloat(precision, vec[ndx]))
148			return false;
149	}
150	return true;
151}
152
153InterpolationCase::IterateResult InterpolationCase::iterate (void)
154{
155	TestLog&					log				= m_testCtx.getLog();
156	de::Random					rnd				(deStringHash(getName()));
157	const tcu::RenderTarget&	renderTarget	= m_context.getRenderTarget();
158	int							viewportWidth	= 128;
159	int							viewportHeight	= 128;
160
161	if (renderTarget.getWidth() < viewportWidth ||
162		renderTarget.getHeight() < viewportHeight)
163		throw tcu::NotSupportedError("Too small viewport", "", __FILE__, __LINE__);
164
165	int							viewportX		= rnd.getInt(0, renderTarget.getWidth()		- viewportWidth);
166	int							viewportY		= rnd.getInt(0, renderTarget.getHeight()	- viewportHeight);
167
168	static const char* s_vertShaderTemplate =
169		"#version 300 es\n"
170		"in highp vec4 a_position;\n"
171		"in ${PRECISION} vec3 a_coords;\n"
172		"out ${PRECISION} vec3 v_coords;\n"
173		"\n"
174		"void main (void)\n"
175		"{\n"
176		"	gl_Position = a_position;\n"
177		"	v_coords = a_coords;\n"
178		"}\n";
179	static const char* s_fragShaderTemplate =
180		"#version 300 es\n"
181		"in ${PRECISION} vec3 v_coords;\n"
182		"uniform ${PRECISION} vec3 u_scale;\n"
183		"uniform ${PRECISION} vec3 u_bias;\n"
184		"layout(location = 0) out ${PRECISION} vec4 o_color;\n"
185		"\n"
186		"void main (void)\n"
187		"{\n"
188		"	o_color = vec4(v_coords * u_scale + u_bias, 1.0);\n"
189		"}\n";
190
191	map<string, string> templateParams;
192	templateParams["PRECISION"] = glu::getPrecisionName(m_precision);
193
194	glu::ShaderProgram program(m_context.getRenderContext(),
195							   glu::makeVtxFragSources(tcu::StringTemplate(s_vertShaderTemplate).specialize(templateParams),
196													   tcu::StringTemplate(s_fragShaderTemplate).specialize(templateParams)));
197	log << program;
198	if (!program.isOk())
199	{
200		if (m_precision == glu::PRECISION_HIGHP && !m_context.getContextInfo().isFragmentHighPrecisionSupported())
201			m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Fragment highp not supported");
202		else
203			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compile failed");
204		return STOP;
205	}
206
207	// Position coordinates.
208	Vec4 wCoord = m_projective ? Vec4(1.3f, 0.8f, 0.6f, 2.0f) : Vec4(1.0f, 1.0f, 1.0f, 1.0f);
209	float positions[] =
210	{
211		-1.0f*wCoord.x(), -1.0f*wCoord.x(), 0.0f, wCoord.x(),
212		-1.0f*wCoord.y(), +1.0f*wCoord.y(), 0.0f, wCoord.y(),
213		+1.0f*wCoord.z(), -1.0f*wCoord.z(), 0.0f, wCoord.z(),
214		+1.0f*wCoord.w(), +1.0f*wCoord.w(), 0.0f, wCoord.w()
215	};
216
217	// Coordinates for interpolation.
218	tcu::Vec3 scale	= 1.0f / (m_max - m_min);
219	tcu::Vec3 bias	= -1.0f*m_min*scale;
220	float coords[] =
221	{
222		(0.0f - bias[0])/scale[0], (0.5f - bias[1])/scale[1], (1.0f - bias[2])/scale[2],
223		(0.5f - bias[0])/scale[0], (1.0f - bias[1])/scale[1], (0.5f - bias[2])/scale[2],
224		(0.5f - bias[0])/scale[0], (0.0f - bias[1])/scale[1], (0.5f - bias[2])/scale[2],
225		(1.0f - bias[0])/scale[0], (0.5f - bias[1])/scale[1], (0.0f - bias[2])/scale[2]
226	};
227
228	log << TestLog::Message << "a_coords = " << ((tcu::Vec3(0.0f) - bias)/scale) << " -> " << ((tcu::Vec3(1.0f) - bias)/scale) << TestLog::EndMessage;
229	log << TestLog::Message << "u_scale = " << scale << TestLog::EndMessage;
230	log << TestLog::Message << "u_bias = " << bias << TestLog::EndMessage;
231
232	// Verify that none of the inputs are denormalized / inf / nan.
233	TCU_CHECK(isValidFloatVec(m_precision, scale));
234	TCU_CHECK(isValidFloatVec(m_precision, bias));
235	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(coords); ndx++)
236	{
237		TCU_CHECK(isValidFloat(m_precision, coords[ndx]));
238		TCU_CHECK(isValidFloat(m_precision, coords[ndx] * scale[ndx % 3] + bias[ndx % 3]));
239	}
240
241	// Indices.
242	static const deUint16 indices[] = { 0, 1, 2, 2, 1, 3 };
243
244	{
245		const int	posLoc		= glGetAttribLocation(program.getProgram(), "a_position");
246		const int	coordLoc	= glGetAttribLocation(program.getProgram(), "a_coords");
247
248		glEnableVertexAttribArray(posLoc);
249		glVertexAttribPointer(posLoc, 4, GL_FLOAT, GL_FALSE, 0, &positions[0]);
250
251		glEnableVertexAttribArray(coordLoc);
252		glVertexAttribPointer(coordLoc, 3, GL_FLOAT, GL_FALSE, 0, &coords[0]);
253	}
254
255	glUseProgram(program.getProgram());
256	glUniform3f(glGetUniformLocation(program.getProgram(), "u_scale"), scale.x(), scale.y(), scale.z());
257	glUniform3f(glGetUniformLocation(program.getProgram(), "u_bias"), bias.x(), bias.y(), bias.z());
258
259	GLU_CHECK_MSG("After program setup");
260
261	// Frames.
262	tcu::Surface	rendered		(viewportWidth, viewportHeight);
263	tcu::Surface	reference		(viewportWidth, viewportHeight);
264
265	// Render with GL.
266	glViewport(viewportX, viewportY, viewportWidth, viewportHeight);
267	glDrawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(indices), GL_UNSIGNED_SHORT, &indices[0]);
268
269	// Render reference \note While GPU is hopefully doing our draw call.
270	renderReference(SurfaceAccess(reference, m_context.getRenderTarget().getPixelFormat()), coords, wCoord, scale, bias);
271
272	glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, rendered.getAccess());
273
274	// Compute difference.
275	const int		bestScoreDiff	= 16;
276	const int		worstScoreDiff	= 300;
277	int				score			= tcu::measurePixelDiffAccuracy(log, "Result", "Image comparison result", reference, rendered, bestScoreDiff, worstScoreDiff, tcu::COMPARE_LOG_EVERYTHING);
278
279	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, de::toString(score).c_str());
280	return STOP;
281}
282
283VaryingInterpolationTests::VaryingInterpolationTests (Context& context)
284	: TestCaseGroup(context, "interpolation", "Varying Interpolation Accuracy Tests")
285{
286}
287
288VaryingInterpolationTests::~VaryingInterpolationTests (void)
289{
290}
291
292void VaryingInterpolationTests::init (void)
293{
294	DE_STATIC_ASSERT(glu::PRECISION_LOWP+1		== glu::PRECISION_MEDIUMP);
295	DE_STATIC_ASSERT(glu::PRECISION_MEDIUMP+1	== glu::PRECISION_HIGHP);
296
297	// Exp = Emax-3, Mantissa = 0
298	float minF32 = tcu::Float32((0u<<31) | (0xfcu<<23) | 0x0u).asFloat();
299	float maxF32 = tcu::Float32((1u<<31) | (0xfcu<<23) | 0x0u).asFloat();
300	float minF16 = tcu::Float16((deUint16)((0u<<15) | (0x1cu<<10) | 0x0u)).asFloat();
301	float maxF16 = tcu::Float16((deUint16)((1u<<15) | (0x1cu<<10) | 0x0u)).asFloat();
302
303	static const struct
304	{
305		const char*		name;
306		Vec3			minVal;
307		Vec3			maxVal;
308		glu::Precision	minPrecision;
309	} coordRanges[] =
310	{
311		{ "zero_to_one",		Vec3(  0.0f,   0.0f,   0.0f), Vec3(  1.0f,   1.0f,   1.0f), glu::PRECISION_LOWP		},
312		{ "zero_to_minus_one",	Vec3(  0.0f,   0.0f,   0.0f), Vec3( -1.0f,  -1.0f,  -1.0f), glu::PRECISION_LOWP		},
313		{ "minus_one_to_one",	Vec3( -1.0f,  -1.0f,  -1.0f), Vec3(  1.0f,   1.0f,   1.0f), glu::PRECISION_LOWP		},
314		{ "minus_ten_to_ten",	Vec3(-10.0f, -10.0f, -10.0f), Vec3( 10.0f,  10.0f,  10.0f), glu::PRECISION_MEDIUMP	},
315		{ "thousands",			Vec3( -5e3f,   1e3f,   1e3f), Vec3(  3e3f,  -1e3f,   7e3f), glu::PRECISION_MEDIUMP	},
316		{ "full_mediump",		Vec3(minF16, minF16, minF16), Vec3(maxF16, maxF16, maxF16), glu::PRECISION_MEDIUMP	},
317		{ "full_highp",			Vec3(minF32, minF32, minF32), Vec3(maxF32, maxF32, maxF32), glu::PRECISION_HIGHP	},
318	};
319
320	for (int precision = glu::PRECISION_LOWP; precision <= glu::PRECISION_HIGHP; precision++)
321	{
322		for (int coordNdx = 0; coordNdx < DE_LENGTH_OF_ARRAY(coordRanges); coordNdx++)
323		{
324			if (precision < (int)coordRanges[coordNdx].minPrecision)
325				continue;
326
327			string baseName = string(glu::getPrecisionName((glu::Precision)precision)) + "_" + coordRanges[coordNdx].name;
328
329			addChild(new InterpolationCase(m_context, baseName.c_str(),				"",	(glu::Precision)precision, coordRanges[coordNdx].minVal, coordRanges[coordNdx].maxVal, false));
330			addChild(new InterpolationCase(m_context, (baseName + "_proj").c_str(),	"",	(glu::Precision)precision, coordRanges[coordNdx].minVal, coordRanges[coordNdx].maxVal, true));
331		}
332	}
333}
334
335} // Accuracy
336} // gles3
337} // deqp
338