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
46 using std::map;
47 using std::string;
48 using std::vector;
49 using tcu::SurfaceAccess;
50 using tcu::TestLog;
51 using tcu::Vec3;
52 using tcu::Vec4;
53
54 namespace deqp
55 {
56 namespace gles3
57 {
58 namespace Accuracy
59 {
60
projectedTriInterpolate(const tcu::Vec3 & s,const tcu::Vec3 & w,float nx,float ny)61 static 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]) /
64 ((1.0f - nx - ny) / w[0] + ny / w[1] + nx / w[2]);
65 }
66
renderReference(const SurfaceAccess & dst,const float coords[4* 3],const Vec4 & wCoord,const Vec3 & scale,const Vec3 & bias)67 static void renderReference(const SurfaceAccess &dst, const float coords[4 * 3], const Vec4 &wCoord, const Vec3 &scale,
68 const Vec3 &bias)
69 {
70 float dstW = (float)dst.getWidth();
71 float dstH = (float)dst.getHeight();
72
73 Vec3 triR[2] = {Vec3(coords[0 * 3 + 0], coords[1 * 3 + 0], coords[2 * 3 + 0]),
74 Vec3(coords[3 * 3 + 0], coords[2 * 3 + 0], coords[1 * 3 + 0])};
75 Vec3 triG[2] = {Vec3(coords[0 * 3 + 1], coords[1 * 3 + 1], coords[2 * 3 + 1]),
76 Vec3(coords[3 * 3 + 1], coords[2 * 3 + 1], coords[1 * 3 + 1])};
77 Vec3 triB[2] = {Vec3(coords[0 * 3 + 2], coords[1 * 3 + 2], coords[2 * 3 + 2]),
78 Vec3(coords[3 * 3 + 2], coords[2 * 3 + 2], coords[1 * 3 + 2])};
79 tcu::Vec3 triW[2] = {wCoord.swizzle(0, 1, 2), wCoord.swizzle(3, 2, 1)};
80
81 for (int py = 0; py < dst.getHeight(); py++)
82 {
83 for (int px = 0; px < dst.getWidth(); px++)
84 {
85 float wx = (float)px + 0.5f;
86 float wy = (float)py + 0.5f;
87 float nx = wx / dstW;
88 float ny = wy / dstH;
89
90 int triNdx = nx + ny >= 1.0f ? 1 : 0;
91 float triNx = triNdx ? 1.0f - nx : nx;
92 float triNy = triNdx ? 1.0f - ny : ny;
93
94 float r = projectedTriInterpolate(triR[triNdx], triW[triNdx], triNx, triNy) * scale[0] + bias[0];
95 float g = projectedTriInterpolate(triG[triNdx], triW[triNdx], triNx, triNy) * scale[1] + bias[1];
96 float b = projectedTriInterpolate(triB[triNdx], triW[triNdx], triNx, triNy) * scale[2] + bias[2];
97
98 Vec4 color = Vec4(r, g, b, 1.0f);
99
100 dst.setPixel(color, px, py);
101 }
102 }
103 }
104
105 class InterpolationCase : public TestCase
106 {
107 public:
108 InterpolationCase(Context &context, const char *name, const char *desc, glu::Precision precision,
109 const tcu::Vec3 &minVal, const tcu::Vec3 &maxVal, bool projective);
110 ~InterpolationCase(void);
111
112 IterateResult iterate(void);
113
114 private:
115 glu::Precision m_precision;
116 tcu::Vec3 m_min;
117 tcu::Vec3 m_max;
118 bool m_projective;
119 };
120
InterpolationCase(Context & context,const char * name,const char * desc,glu::Precision precision,const tcu::Vec3 & minVal,const tcu::Vec3 & maxVal,bool projective)121 InterpolationCase::InterpolationCase(Context &context, const char *name, const char *desc, glu::Precision precision,
122 const tcu::Vec3 &minVal, const tcu::Vec3 &maxVal, bool projective)
123 : TestCase(context, tcu::NODETYPE_ACCURACY, name, desc)
124 , m_precision(precision)
125 , m_min(minVal)
126 , m_max(maxVal)
127 , m_projective(projective)
128 {
129 }
130
~InterpolationCase(void)131 InterpolationCase::~InterpolationCase(void)
132 {
133 }
134
isValidFloat(glu::Precision precision,float val)135 static bool isValidFloat(glu::Precision precision, float val)
136 {
137 if (precision == glu::PRECISION_MEDIUMP)
138 {
139 tcu::Float16 fp16(val);
140 return !fp16.isDenorm() && !fp16.isInf() && !fp16.isNaN();
141 }
142 else
143 {
144 tcu::Float32 fp32(val);
145 return !fp32.isDenorm() && !fp32.isInf() && !fp32.isNaN();
146 }
147 }
148
149 template <int Size>
isValidFloatVec(glu::Precision precision,const tcu::Vector<float,Size> & vec)150 static bool isValidFloatVec(glu::Precision precision, const tcu::Vector<float, Size> &vec)
151 {
152 for (int ndx = 0; ndx < Size; ndx++)
153 {
154 if (!isValidFloat(precision, vec[ndx]))
155 return false;
156 }
157 return true;
158 }
159
iterate(void)160 InterpolationCase::IterateResult InterpolationCase::iterate(void)
161 {
162 TestLog &log = m_testCtx.getLog();
163 de::Random rnd(deStringHash(getName()));
164 const tcu::RenderTarget &renderTarget = m_context.getRenderTarget();
165 int viewportWidth = 128;
166 int viewportHeight = 128;
167
168 if (renderTarget.getWidth() < viewportWidth || renderTarget.getHeight() < viewportHeight)
169 throw tcu::NotSupportedError("Too small viewport", "", __FILE__, __LINE__);
170
171 int viewportX = rnd.getInt(0, renderTarget.getWidth() - viewportWidth);
172 int viewportY = rnd.getInt(0, renderTarget.getHeight() - viewportHeight);
173
174 static const char *s_vertShaderTemplate = "#version 300 es\n"
175 "in highp vec4 a_position;\n"
176 "in ${PRECISION} vec3 a_coords;\n"
177 "out ${PRECISION} vec3 v_coords;\n"
178 "\n"
179 "void main (void)\n"
180 "{\n"
181 " gl_Position = a_position;\n"
182 " v_coords = a_coords;\n"
183 "}\n";
184 static const char *s_fragShaderTemplate = "#version 300 es\n"
185 "in ${PRECISION} vec3 v_coords;\n"
186 "uniform ${PRECISION} vec3 u_scale;\n"
187 "uniform ${PRECISION} vec3 u_bias;\n"
188 "layout(location = 0) out ${PRECISION} vec4 o_color;\n"
189 "\n"
190 "void main (void)\n"
191 "{\n"
192 " o_color = vec4(v_coords * u_scale + u_bias, 1.0);\n"
193 "}\n";
194
195 map<string, string> templateParams;
196 templateParams["PRECISION"] = glu::getPrecisionName(m_precision);
197
198 glu::ShaderProgram program(
199 m_context.getRenderContext(),
200 glu::makeVtxFragSources(tcu::StringTemplate(s_vertShaderTemplate).specialize(templateParams),
201 tcu::StringTemplate(s_fragShaderTemplate).specialize(templateParams)));
202 log << program;
203 if (!program.isOk())
204 {
205 if (m_precision == glu::PRECISION_HIGHP && !m_context.getContextInfo().isFragmentHighPrecisionSupported())
206 m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Fragment highp not supported");
207 else
208 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compile failed");
209 return STOP;
210 }
211
212 // Position coordinates.
213 Vec4 wCoord = m_projective ? Vec4(1.3f, 0.8f, 0.6f, 2.0f) : Vec4(1.0f, 1.0f, 1.0f, 1.0f);
214 float positions[] = {-1.0f * wCoord.x(), -1.0f * wCoord.x(), 0.0f, wCoord.x(),
215 -1.0f * wCoord.y(), +1.0f * wCoord.y(), 0.0f, wCoord.y(),
216 +1.0f * wCoord.z(), -1.0f * wCoord.z(), 0.0f, wCoord.z(),
217 +1.0f * wCoord.w(), +1.0f * wCoord.w(), 0.0f, wCoord.w()};
218
219 // Coordinates for interpolation.
220 tcu::Vec3 scale = 1.0f / (m_max - m_min);
221 tcu::Vec3 bias = -1.0f * m_min * scale;
222 float coords[] = {(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 log << TestLog::Message << "a_coords = " << ((tcu::Vec3(0.0f) - bias) / scale) << " -> "
228 << ((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 uint16_t 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,
271 bias);
272
273 glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, rendered.getAccess());
274
275 // Compute difference.
276 const int bestScoreDiff = 16;
277 const int worstScoreDiff = 300;
278 int score = tcu::measurePixelDiffAccuracy(log, "Result", "Image comparison result", reference, rendered,
279 bestScoreDiff, worstScoreDiff, tcu::COMPARE_LOG_EVERYTHING);
280
281 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, de::toString(score).c_str());
282 return STOP;
283 }
284
VaryingInterpolationTests(Context & context)285 VaryingInterpolationTests::VaryingInterpolationTests(Context &context)
286 : TestCaseGroup(context, "interpolation", "Varying Interpolation Accuracy Tests")
287 {
288 }
289
~VaryingInterpolationTests(void)290 VaryingInterpolationTests::~VaryingInterpolationTests(void)
291 {
292 }
293
init(void)294 void VaryingInterpolationTests::init(void)
295 {
296 DE_STATIC_ASSERT(glu::PRECISION_LOWP + 1 == glu::PRECISION_MEDIUMP);
297 DE_STATIC_ASSERT(glu::PRECISION_MEDIUMP + 1 == glu::PRECISION_HIGHP);
298
299 // Exp = Emax-3, Mantissa = 0
300 float minF32 = tcu::Float32((0u << 31) | (0xfcu << 23) | 0x0u).asFloat();
301 float maxF32 = tcu::Float32((1u << 31) | (0xfcu << 23) | 0x0u).asFloat();
302 float minF16 = tcu::Float16((uint16_t)((0u << 15) | (0x1cu << 10) | 0x0u)).asFloat();
303 float maxF16 = tcu::Float16((uint16_t)((1u << 15) | (0x1cu << 10) | 0x0u)).asFloat();
304
305 static const struct
306 {
307 const char *name;
308 Vec3 minVal;
309 Vec3 maxVal;
310 glu::Precision minPrecision;
311 } coordRanges[] = {
312 {"zero_to_one", Vec3(0.0f, 0.0f, 0.0f), Vec3(1.0f, 1.0f, 1.0f), glu::PRECISION_LOWP},
313 {"zero_to_minus_one", Vec3(0.0f, 0.0f, 0.0f), Vec3(-1.0f, -1.0f, -1.0f), glu::PRECISION_LOWP},
314 {"minus_one_to_one", Vec3(-1.0f, -1.0f, -1.0f), Vec3(1.0f, 1.0f, 1.0f), glu::PRECISION_LOWP},
315 {"minus_ten_to_ten", Vec3(-10.0f, -10.0f, -10.0f), Vec3(10.0f, 10.0f, 10.0f), glu::PRECISION_MEDIUMP},
316 {"thousands", Vec3(-5e3f, 1e3f, 1e3f), Vec3(3e3f, -1e3f, 7e3f), glu::PRECISION_MEDIUMP},
317 {"full_mediump", Vec3(minF16, minF16, minF16), Vec3(maxF16, maxF16, maxF16), glu::PRECISION_MEDIUMP},
318 {"full_highp", Vec3(minF32, minF32, minF32), Vec3(maxF32, maxF32, maxF32), glu::PRECISION_HIGHP},
319 };
320
321 for (int precision = glu::PRECISION_LOWP; precision <= glu::PRECISION_HIGHP; precision++)
322 {
323 for (int coordNdx = 0; coordNdx < DE_LENGTH_OF_ARRAY(coordRanges); coordNdx++)
324 {
325 if (precision < (int)coordRanges[coordNdx].minPrecision)
326 continue;
327
328 string baseName =
329 string(glu::getPrecisionName((glu::Precision)precision)) + "_" + coordRanges[coordNdx].name;
330
331 addChild(new InterpolationCase(m_context, baseName.c_str(), "", (glu::Precision)precision,
332 coordRanges[coordNdx].minVal, coordRanges[coordNdx].maxVal, false));
333 addChild(new InterpolationCase(m_context, (baseName + "_proj").c_str(), "", (glu::Precision)precision,
334 coordRanges[coordNdx].minVal, coordRanges[coordNdx].maxVal, true));
335 }
336 }
337 }
338
339 } // namespace Accuracy
340 } // namespace gles3
341 } // namespace deqp
342