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