| /*------------------------------------------------------------------------- |
| * drawElements Quality Program OpenGL ES 3.0 Module |
| * ------------------------------------------------- |
| * |
| * Copyright 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| *//*! |
| * \file |
| * \brief Varying interpolation accuracy tests. |
| * |
| * \todo [2012-07-03 pyry] On GLES3 we could use floating-point render target |
| * for better accuracy evaluation. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "es3aVaryingInterpolationTests.hpp" |
| #include "gluPixelTransfer.hpp" |
| #include "gluShaderProgram.hpp" |
| #include "gluShaderUtil.hpp" |
| #include "tcuStringTemplate.hpp" |
| #include "gluContextInfo.hpp" |
| #include "glsTextureTestUtil.hpp" |
| #include "tcuVector.hpp" |
| #include "tcuVectorUtil.hpp" |
| #include "tcuTestLog.hpp" |
| #include "tcuFloat.hpp" |
| #include "tcuImageCompare.hpp" |
| #include "tcuRenderTarget.hpp" |
| #include "deRandom.hpp" |
| #include "deStringUtil.hpp" |
| #include "deString.h" |
| |
| #include "glw.h" |
| |
| using tcu::TestLog; |
| using tcu::Vec3; |
| using tcu::Vec4; |
| using std::string; |
| using std::vector; |
| using std::map; |
| using tcu::SurfaceAccess; |
| |
| namespace deqp |
| { |
| namespace gles3 |
| { |
| namespace Accuracy |
| { |
| |
| static inline float projectedTriInterpolate (const tcu::Vec3& s, const tcu::Vec3& w, float nx, float ny) |
| { |
| 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]); |
| } |
| |
| static void renderReference (const SurfaceAccess& dst, const float coords[4*3], const Vec4& wCoord, const Vec3& scale, const Vec3& bias) |
| { |
| float dstW = (float)dst.getWidth(); |
| float dstH = (float)dst.getHeight(); |
| |
| 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]) }; |
| 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]) }; |
| 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]) }; |
| tcu::Vec3 triW[2] = { wCoord.swizzle(0, 1, 2), wCoord.swizzle(3, 2, 1) }; |
| |
| for (int py = 0; py < dst.getHeight(); py++) |
| { |
| for (int px = 0; px < dst.getWidth(); px++) |
| { |
| float wx = (float)px + 0.5f; |
| float wy = (float)py + 0.5f; |
| float nx = wx / dstW; |
| float ny = wy / dstH; |
| |
| int triNdx = nx + ny >= 1.0f ? 1 : 0; |
| float triNx = triNdx ? 1.0f - nx : nx; |
| float triNy = triNdx ? 1.0f - ny : ny; |
| |
| float r = projectedTriInterpolate(triR[triNdx], triW[triNdx], triNx, triNy) * scale[0] + bias[0]; |
| float g = projectedTriInterpolate(triG[triNdx], triW[triNdx], triNx, triNy) * scale[1] + bias[1]; |
| float b = projectedTriInterpolate(triB[triNdx], triW[triNdx], triNx, triNy) * scale[2] + bias[2]; |
| |
| Vec4 color = Vec4(r, g, b, 1.0f); |
| |
| dst.setPixel(color, px, py); |
| } |
| } |
| } |
| |
| class InterpolationCase : public TestCase |
| { |
| public: |
| InterpolationCase (Context& context, const char* name, const char* desc, glu::Precision precision, const tcu::Vec3& minVal, const tcu::Vec3& maxVal, bool projective); |
| ~InterpolationCase (void); |
| |
| IterateResult iterate (void); |
| |
| private: |
| glu::Precision m_precision; |
| tcu::Vec3 m_min; |
| tcu::Vec3 m_max; |
| bool m_projective; |
| }; |
| |
| InterpolationCase::InterpolationCase (Context& context, const char* name, const char* desc, glu::Precision precision, const tcu::Vec3& minVal, const tcu::Vec3& maxVal, bool projective) |
| : TestCase (context, tcu::NODETYPE_ACCURACY, name, desc) |
| , m_precision (precision) |
| , m_min (minVal) |
| , m_max (maxVal) |
| , m_projective (projective) |
| { |
| } |
| |
| InterpolationCase::~InterpolationCase (void) |
| { |
| } |
| |
| static bool isValidFloat (glu::Precision precision, float val) |
| { |
| if (precision == glu::PRECISION_MEDIUMP) |
| { |
| tcu::Float16 fp16(val); |
| return !fp16.isDenorm() && !fp16.isInf() && !fp16.isNaN(); |
| } |
| else |
| { |
| tcu::Float32 fp32(val); |
| return !fp32.isDenorm() && !fp32.isInf() && !fp32.isNaN(); |
| } |
| } |
| |
| template <int Size> |
| static bool isValidFloatVec (glu::Precision precision, const tcu::Vector<float, Size>& vec) |
| { |
| for (int ndx = 0; ndx < Size; ndx++) |
| { |
| if (!isValidFloat(precision, vec[ndx])) |
| return false; |
| } |
| return true; |
| } |
| |
| InterpolationCase::IterateResult InterpolationCase::iterate (void) |
| { |
| TestLog& log = m_testCtx.getLog(); |
| de::Random rnd (deStringHash(getName())); |
| const tcu::RenderTarget& renderTarget = m_context.getRenderTarget(); |
| int viewportWidth = 128; |
| int viewportHeight = 128; |
| |
| if (renderTarget.getWidth() < viewportWidth || |
| renderTarget.getHeight() < viewportHeight) |
| throw tcu::NotSupportedError("Too small viewport", "", __FILE__, __LINE__); |
| |
| int viewportX = rnd.getInt(0, renderTarget.getWidth() - viewportWidth); |
| int viewportY = rnd.getInt(0, renderTarget.getHeight() - viewportHeight); |
| |
| static const char* s_vertShaderTemplate = |
| "#version 300 es\n" |
| "in highp vec4 a_position;\n" |
| "in ${PRECISION} vec3 a_coords;\n" |
| "out ${PRECISION} vec3 v_coords;\n" |
| "\n" |
| "void main (void)\n" |
| "{\n" |
| " gl_Position = a_position;\n" |
| " v_coords = a_coords;\n" |
| "}\n"; |
| static const char* s_fragShaderTemplate = |
| "#version 300 es\n" |
| "in ${PRECISION} vec3 v_coords;\n" |
| "uniform ${PRECISION} vec3 u_scale;\n" |
| "uniform ${PRECISION} vec3 u_bias;\n" |
| "layout(location = 0) out ${PRECISION} vec4 o_color;\n" |
| "\n" |
| "void main (void)\n" |
| "{\n" |
| " o_color = vec4(v_coords * u_scale + u_bias, 1.0);\n" |
| "}\n"; |
| |
| map<string, string> templateParams; |
| templateParams["PRECISION"] = glu::getPrecisionName(m_precision); |
| |
| glu::ShaderProgram program(m_context.getRenderContext(), |
| glu::makeVtxFragSources(tcu::StringTemplate(s_vertShaderTemplate).specialize(templateParams), |
| tcu::StringTemplate(s_fragShaderTemplate).specialize(templateParams))); |
| log << program; |
| if (!program.isOk()) |
| { |
| if (m_precision == glu::PRECISION_HIGHP && !m_context.getContextInfo().isFragmentHighPrecisionSupported()) |
| m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Fragment highp not supported"); |
| else |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compile failed"); |
| return STOP; |
| } |
| |
| // Position coordinates. |
| Vec4 wCoord = m_projective ? Vec4(1.3f, 0.8f, 0.6f, 2.0f) : Vec4(1.0f, 1.0f, 1.0f, 1.0f); |
| float positions[] = |
| { |
| -1.0f*wCoord.x(), -1.0f*wCoord.x(), 0.0f, wCoord.x(), |
| -1.0f*wCoord.y(), +1.0f*wCoord.y(), 0.0f, wCoord.y(), |
| +1.0f*wCoord.z(), -1.0f*wCoord.z(), 0.0f, wCoord.z(), |
| +1.0f*wCoord.w(), +1.0f*wCoord.w(), 0.0f, wCoord.w() |
| }; |
| |
| // Coordinates for interpolation. |
| tcu::Vec3 scale = 1.0f / (m_max - m_min); |
| tcu::Vec3 bias = -1.0f*m_min*scale; |
| float coords[] = |
| { |
| (0.0f - bias[0])/scale[0], (0.5f - bias[1])/scale[1], (1.0f - bias[2])/scale[2], |
| (0.5f - bias[0])/scale[0], (1.0f - bias[1])/scale[1], (0.5f - bias[2])/scale[2], |
| (0.5f - bias[0])/scale[0], (0.0f - bias[1])/scale[1], (0.5f - bias[2])/scale[2], |
| (1.0f - bias[0])/scale[0], (0.5f - bias[1])/scale[1], (0.0f - bias[2])/scale[2] |
| }; |
| |
| log << TestLog::Message << "a_coords = " << ((tcu::Vec3(0.0f) - bias)/scale) << " -> " << ((tcu::Vec3(1.0f) - bias)/scale) << TestLog::EndMessage; |
| log << TestLog::Message << "u_scale = " << scale << TestLog::EndMessage; |
| log << TestLog::Message << "u_bias = " << bias << TestLog::EndMessage; |
| |
| // Verify that none of the inputs are denormalized / inf / nan. |
| TCU_CHECK(isValidFloatVec(m_precision, scale)); |
| TCU_CHECK(isValidFloatVec(m_precision, bias)); |
| for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(coords); ndx++) |
| { |
| TCU_CHECK(isValidFloat(m_precision, coords[ndx])); |
| TCU_CHECK(isValidFloat(m_precision, coords[ndx] * scale[ndx % 3] + bias[ndx % 3])); |
| } |
| |
| // Indices. |
| static const deUint16 indices[] = { 0, 1, 2, 2, 1, 3 }; |
| |
| { |
| const int posLoc = glGetAttribLocation(program.getProgram(), "a_position"); |
| const int coordLoc = glGetAttribLocation(program.getProgram(), "a_coords"); |
| |
| glEnableVertexAttribArray(posLoc); |
| glVertexAttribPointer(posLoc, 4, GL_FLOAT, GL_FALSE, 0, &positions[0]); |
| |
| glEnableVertexAttribArray(coordLoc); |
| glVertexAttribPointer(coordLoc, 3, GL_FLOAT, GL_FALSE, 0, &coords[0]); |
| } |
| |
| glUseProgram(program.getProgram()); |
| glUniform3f(glGetUniformLocation(program.getProgram(), "u_scale"), scale.x(), scale.y(), scale.z()); |
| glUniform3f(glGetUniformLocation(program.getProgram(), "u_bias"), bias.x(), bias.y(), bias.z()); |
| |
| GLU_CHECK_MSG("After program setup"); |
| |
| // Frames. |
| tcu::Surface rendered (viewportWidth, viewportHeight); |
| tcu::Surface reference (viewportWidth, viewportHeight); |
| |
| // Render with GL. |
| glViewport(viewportX, viewportY, viewportWidth, viewportHeight); |
| glDrawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(indices), GL_UNSIGNED_SHORT, &indices[0]); |
| |
| // Render reference \note While GPU is hopefully doing our draw call. |
| renderReference(SurfaceAccess(reference, m_context.getRenderTarget().getPixelFormat()), coords, wCoord, scale, bias); |
| |
| glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, rendered.getAccess()); |
| |
| // Compute difference. |
| const int bestScoreDiff = 16; |
| const int worstScoreDiff = 300; |
| int score = tcu::measurePixelDiffAccuracy(log, "Result", "Image comparison result", reference, rendered, bestScoreDiff, worstScoreDiff, tcu::COMPARE_LOG_EVERYTHING); |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, de::toString(score).c_str()); |
| return STOP; |
| } |
| |
| VaryingInterpolationTests::VaryingInterpolationTests (Context& context) |
| : TestCaseGroup(context, "interpolation", "Varying Interpolation Accuracy Tests") |
| { |
| } |
| |
| VaryingInterpolationTests::~VaryingInterpolationTests (void) |
| { |
| } |
| |
| void VaryingInterpolationTests::init (void) |
| { |
| DE_STATIC_ASSERT(glu::PRECISION_LOWP+1 == glu::PRECISION_MEDIUMP); |
| DE_STATIC_ASSERT(glu::PRECISION_MEDIUMP+1 == glu::PRECISION_HIGHP); |
| |
| // Exp = Emax-3, Mantissa = 0 |
| float minF32 = tcu::Float32((0u<<31) | (0xfcu<<23) | 0x0u).asFloat(); |
| float maxF32 = tcu::Float32((1u<<31) | (0xfcu<<23) | 0x0u).asFloat(); |
| float minF16 = tcu::Float16((deUint16)((0u<<15) | (0x1cu<<10) | 0x0u)).asFloat(); |
| float maxF16 = tcu::Float16((deUint16)((1u<<15) | (0x1cu<<10) | 0x0u)).asFloat(); |
| |
| static const struct |
| { |
| const char* name; |
| Vec3 minVal; |
| Vec3 maxVal; |
| glu::Precision minPrecision; |
| } coordRanges[] = |
| { |
| { "zero_to_one", Vec3( 0.0f, 0.0f, 0.0f), Vec3( 1.0f, 1.0f, 1.0f), glu::PRECISION_LOWP }, |
| { "zero_to_minus_one", Vec3( 0.0f, 0.0f, 0.0f), Vec3( -1.0f, -1.0f, -1.0f), glu::PRECISION_LOWP }, |
| { "minus_one_to_one", Vec3( -1.0f, -1.0f, -1.0f), Vec3( 1.0f, 1.0f, 1.0f), glu::PRECISION_LOWP }, |
| { "minus_ten_to_ten", Vec3(-10.0f, -10.0f, -10.0f), Vec3( 10.0f, 10.0f, 10.0f), glu::PRECISION_MEDIUMP }, |
| { "thousands", Vec3( -5e3f, 1e3f, 1e3f), Vec3( 3e3f, -1e3f, 7e3f), glu::PRECISION_MEDIUMP }, |
| { "full_mediump", Vec3(minF16, minF16, minF16), Vec3(maxF16, maxF16, maxF16), glu::PRECISION_MEDIUMP }, |
| { "full_highp", Vec3(minF32, minF32, minF32), Vec3(maxF32, maxF32, maxF32), glu::PRECISION_HIGHP }, |
| }; |
| |
| for (int precision = glu::PRECISION_LOWP; precision <= glu::PRECISION_HIGHP; precision++) |
| { |
| for (int coordNdx = 0; coordNdx < DE_LENGTH_OF_ARRAY(coordRanges); coordNdx++) |
| { |
| if (precision < (int)coordRanges[coordNdx].minPrecision) |
| continue; |
| |
| string baseName = string(glu::getPrecisionName((glu::Precision)precision)) + "_" + coordRanges[coordNdx].name; |
| |
| addChild(new InterpolationCase(m_context, baseName.c_str(), "", (glu::Precision)precision, coordRanges[coordNdx].minVal, coordRanges[coordNdx].maxVal, false)); |
| addChild(new InterpolationCase(m_context, (baseName + "_proj").c_str(), "", (glu::Precision)precision, coordRanges[coordNdx].minVal, coordRanges[coordNdx].maxVal, true)); |
| } |
| } |
| } |
| |
| } // Accuracy |
| } // gles3 |
| } // deqp |