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 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 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 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 122 InterpolationCase::~InterpolationCase (void) 123 { 124 } 125 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> 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 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 277 VaryingInterpolationTests::VaryingInterpolationTests (Context& context) 278 : TestCaseGroup(context, "interpolation", "Varying Interpolation Accuracy Tests") 279 { 280 } 281 282 VaryingInterpolationTests::~VaryingInterpolationTests (void) 283 { 284 } 285 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