Edit

kc3-lang/angle/src/tests/gl_tests/TextureUploadFormatTest.cpp

Branch :

  • Show log

    Commit

  • Author : Geoff Lang
    Date : 2017-11-28 11:36:28
    Hash : 5201ce12
    Message : Fix MSVC 2015 warnings. BUG=angleproject:2258 Change-Id: I280eb0e461fd749b01594bb1bb9a417f2cbc23fa Reviewed-on: https://chromium-review.googlesource.com/793975 Reviewed-by: Geoff Lang <geofflang@chromium.org> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Geoff Lang <geofflang@chromium.org>

  • src/tests/gl_tests/TextureUploadFormatTest.cpp
  • //
    // Copyright 2015 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    // Texture upload format tests:
    //   Test all texture unpack/upload formats for sampling correctness.
    //
    
    #include "common/mathutil.h"
    #include "test_utils/ANGLETest.h"
    #include "test_utils/gl_raii.h"
    
    using namespace angle;
    
    namespace
    {
    
    class TextureUploadFormatTest : public ANGLETest
    {
    };
    
    struct TexFormat final
    {
        GLenum internalFormat;
        GLenum unpackFormat;
        GLenum unpackType;
    
        TexFormat() = delete;
    
        uint8_t bytesPerPixel() const
        {
            uint8_t bytesPerChannel;
            switch (unpackType)
            {
                case GL_UNSIGNED_SHORT_5_6_5:
                case GL_UNSIGNED_SHORT_4_4_4_4:
                case GL_UNSIGNED_SHORT_5_5_5_1:
                    return 2;
    
                case GL_UNSIGNED_INT_2_10_10_10_REV:
                case GL_UNSIGNED_INT_24_8:
                case GL_UNSIGNED_INT_10F_11F_11F_REV:
                case GL_UNSIGNED_INT_5_9_9_9_REV:
                    return 4;
    
                case GL_FLOAT_32_UNSIGNED_INT_24_8_REV:
                    return 8;
    
                case GL_UNSIGNED_BYTE:
                case GL_BYTE:
                    bytesPerChannel = 1;
                    break;
    
                case GL_UNSIGNED_SHORT:
                case GL_SHORT:
                case GL_HALF_FLOAT:
                case GL_HALF_FLOAT_OES:
                    bytesPerChannel = 2;
                    break;
    
                case GL_UNSIGNED_INT:
                case GL_INT:
                case GL_FLOAT:
                    bytesPerChannel = 4;
                    break;
    
                default:
                    assert(false);
                    return 0;
            }
    
            switch (unpackFormat)
            {
                case GL_RGBA:
                case GL_RGBA_INTEGER:
                    return bytesPerChannel * 4;
    
                case GL_RGB:
                case GL_RGB_INTEGER:
                    return bytesPerChannel * 3;
    
                case GL_RG:
                case GL_RG_INTEGER:
                case GL_LUMINANCE_ALPHA:
                    return bytesPerChannel * 2;
    
                case GL_RED:
                case GL_RED_INTEGER:
                case GL_LUMINANCE:
                case GL_ALPHA:
                case GL_DEPTH_COMPONENT:
                    return bytesPerChannel * 1;
    
                default:
                    assert(false);
                    return 0;
            }
        }
    };
    
    template <const uint8_t bits>
    constexpr uint32_t EncodeNormUint(const float val)
    {
        return static_cast<uint32_t>(val * (UINT32_MAX >> (32 - bits)) + 0.5);  // round-half-up
    }
    
    template <const int signBit, const int eBits, const int mBits>
    struct SizedFloat
    {
        static constexpr int kSignBit = signBit;
        static constexpr int kEBits   = eBits;
        static constexpr int kMBits   = mBits;
    
        static constexpr uint32_t Assemble(const uint32_t sVal,
                                           const uint32_t eVal,
                                           const uint32_t mVal)
        {
            return (signBit ? (sVal << (eBits + mBits)) : 0) | (eVal << mBits) | mVal;
        }
    
        static uint32_t Encode(const float signedV)
        {
            const float v = signBit ? fabsf(signedV) : std::max(0.0f, signedV);
    
            const int eBias   = (1 << (eBits - 1)) - 1;
            const int eValMax = (1 << eBits) - 1;
    
            const float eApprox = log2f(v);
            const auto eActual  = static_cast<int>(floorf(eApprox));
    
            int eVal      = eBias + eActual;
            uint32_t mVal = 0;
            if (v != v)
            {  // NaN
                eVal = eValMax;
                mVal = 1;
            }
            else if (eVal < 0)
            {  // underflow to zero
                eVal = 0;
                mVal = 0;
            }
            else if (eVal >= eValMax)
            {  // overfloat to Inf
                eVal = eValMax;
                mVal = 0;
            }
            else
            {
                float mFloat = 0.0;
                if (eVal == 0)
                {  // denormal
                    mFloat = v * powf(2, 1 - eBias);
                }
                else
                {  // standard range
                    mFloat = v * powf(2, -static_cast<float>(eActual)) - 1.0f;
                }
                mVal = static_cast<uint32_t>(mFloat * (1 << mBits) + 0.5);
            }
    
            const auto sVal = static_cast<uint32_t>(v < 0.0f);
            return Assemble(sVal, eVal, mVal);
        }
    };
    using Float16  = SizedFloat<1, 5, 10>;
    using UFloat11 = SizedFloat<0, 5, 6>;
    using UFloat10 = SizedFloat<0, 5, 5>;
    
    uint32_t EncodeRGB9_E5_Rev(const float signedR, const float signedG, const float signedB)
    {
        const float r       = std::max(0.0f, signedR);
        const float g       = std::max(0.0f, signedG);
        const float b       = std::max(0.0f, signedB);
        const int eBits     = 5;
        const int eBias     = (1 << (eBits - 1)) - 1;  // 15
        const int eMax      = (1 << eBits) - 1;
        const int mBits     = 9;
        const uint32_t mMax = (1 << mBits) - 1;
        // Maximize mVal for one channel
        // => Find the lowest viable exponent
        int minViableActualExp                    = 1 << eBits;
        const auto fnMinimizeViableActualExponent = [&](const float v) {
            const auto cur = static_cast<int>(ceil(log2f(v / mMax)));
            if (cur < minViableActualExp)
            {
                minViableActualExp = cur;
            }
        };
        fnMinimizeViableActualExponent(r);
        fnMinimizeViableActualExponent(g);
        fnMinimizeViableActualExponent(b);
        const int eVal = std::max(0, std::min(minViableActualExp + eBias + mBits, eMax));
    
        const auto fnM = [&](const float v) {
            const auto m = static_cast<uint32_t>(v * powf(2, static_cast<float>(mBits + eBias - eVal)));
            return std::min(m, mMax);
        };
    
        const auto mR = fnM(r);
        const auto mG = fnM(g);
        const auto mB = fnM(b);
        return (mR << 0) | (mG << 9) | (mB << 18) | (eVal << 27);
    }
    
    }  // anonymous namespace
    
    // Test our encoding code to ensure we get the values out that we expect.
    // We could alternatively hardcode our inputs for these couple cases, but it's nice to do this
    // programatically, since it should make it easier to write any further tests without having to
    // re-encode by hand.
    TEST(TextureUploadFormatTestInternals, Float16Encoding)
    {
        EXPECT_EQ(Float16::Assemble(0, 0x0f, 0), Float16::Encode(1.0));
        EXPECT_EQ(Float16::Assemble(0, 0x0f - 1, 0), Float16::Encode(1.0 / 2));
    
        EXPECT_EQ(Float16::Assemble(0, 0x0f - 3, 0), Float16::Encode(1.0 / 8));
        EXPECT_EQ(Float16::Assemble(0, 0x0f - 2, 0), Float16::Encode(2.0 / 8));
        EXPECT_EQ(Float16::Assemble(0, 0x0f - 2, 1 << (Float16::kMBits - 1)), Float16::Encode(3.0 / 8));
        EXPECT_EQ(Float16::Assemble(0, 0x0f - 1, 1 << (Float16::kMBits - 2)), Float16::Encode(5.0 / 8));
    }
    
    namespace
    {
    
    template <typename DestT, typename SrcT, size_t SrcN>
    void ZeroAndCopy(DestT &dest, const SrcT (&src)[SrcN])
    {
        dest.fill(0);
        memcpy(dest.data(), src, sizeof(SrcT) * SrcN);
    }
    
    std::string EnumStr(const GLenum v)
    {
        std::stringstream ret;
        ret << "0x" << std::hex << v;
        return ret.str();
    }
    
    }  // anonymous namespace
    
    // Upload (1,2,5,3) to integer formats, and (1,2,5,3)/8.0 to float formats.
    // Draw a point into a 1x1 renderbuffer and readback the result for comparison with expectations.
    // Test all internalFormat/unpackFormat/unpackType combinations from ES3.0.
    TEST_P(TextureUploadFormatTest, All)
    {
        ANGLE_SKIP_TEST_IF(IsD3D9() || IsD3D11_FL93());
    
        constexpr char kVertShader[] = R"(
            void main()
            {
                gl_PointSize = 1.0;
                gl_Position = vec4(0, 0, 0, 1);
            })";
    
        constexpr char kFragShader_Floats[] = R"(
            precision mediump float;
            uniform sampler2D uTex;
    
            void main()
            {
                gl_FragColor = texture2D(uTex, vec2(0,0));
            })";
        constexpr char kFragShader_Ints[]   = R"(
            precision mediump float;
            uniform sampler2D uTex;
    
            void main()
            {
                gl_FragColor = texture2D(uTex, vec2(0,0)) / 8.0;
            })";
        ANGLE_GL_PROGRAM(floatsProg, kVertShader, kFragShader_Floats);
        ANGLE_GL_PROGRAM(intsProg, kVertShader, kFragShader_Ints);
    
        GLint uTex = glGetUniformLocation(floatsProg, "uTex");
        ASSERT_NE(uTex, -1);
        glUseProgram(floatsProg);
        glUniform1i(uTex, 0);
    
        uTex = glGetUniformLocation(intsProg, "uTex");
        ASSERT_NE(uTex, -1);
        glUseProgram(intsProg);
        glUniform1i(uTex, 0);
    
        glDisable(GL_DITHER);
    
        ASSERT_GL_NO_ERROR();
    
        // Create the 1x1 framebuffer
    
        GLRenderbuffer backbufferRB;
        glBindRenderbuffer(GL_RENDERBUFFER, backbufferRB);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 1, 1);
        glBindRenderbuffer(GL_RENDERBUFFER, 0);
    
        GLFramebuffer backbufferFB;
        glBindFramebuffer(GL_FRAMEBUFFER, backbufferFB);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, backbufferRB);
        ASSERT_GL_NO_ERROR();
        ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
    
        glViewport(0, 0, 1, 1);
    
        // Create and bind our test texture
    
        GLTexture testTex;
        glBindTexture(GL_TEXTURE_2D, testTex);
    
        ASSERT_GL_NO_ERROR();
    
        // Initialize our test variables
    
        glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
        const bool hasSubrectUploads = !glGetError();
    
        constexpr uint8_t srcIntVals[4] = {1u, 2u, 5u, 3u};
        constexpr float srcVals[4] = {srcIntVals[0] / 8.0f, srcIntVals[1] / 8.0f, srcIntVals[2] / 8.0f,
                                      srcIntVals[3] / 8.0f};
        constexpr uint8_t refVals[4] = {static_cast<uint8_t>(EncodeNormUint<8>(srcVals[0])),
                                        static_cast<uint8_t>(EncodeNormUint<8>(srcVals[1])),
                                        static_cast<uint8_t>(EncodeNormUint<8>(srcVals[2])),
                                        static_cast<uint8_t>(EncodeNormUint<8>(srcVals[3]))};
    
        // Test a format with the specified data
    
        const auto fnTestData = [&](const TexFormat &format, const void *const data, const GLColor &err,
                                    const char *const info) {
            ASSERT_GL_NO_ERROR();
            glTexImage2D(GL_TEXTURE_2D, 0, format.internalFormat, 1, 1, 0, format.unpackFormat,
                         format.unpackType, data);
            const auto uploadErr = glGetError();
            if (uploadErr)
                return;
    
            glClearColor(1, 0, 1, 1);
            glClear(GL_COLOR_BUFFER_BIT);
            glDrawArrays(GL_POINTS, 0, 1);
    
            const auto actual = ReadColor(0, 0);
    
            GLColor expected;
            switch (format.unpackFormat)
            {
                case GL_RGBA:
                case GL_RGBA_INTEGER:
                    expected = {refVals[0], refVals[1], refVals[2], refVals[3]};
                    break;
                case GL_RGB:
                    expected = {refVals[0], refVals[1], refVals[2], 255};
                    break;
                case GL_RG:
                    expected = {refVals[0], refVals[1], 0, 255};
                    break;
                case GL_RED:
                case GL_DEPTH_COMPONENT:
                case GL_DEPTH_STENCIL:
                    expected = {refVals[0], 0, 0, 255};
                    break;
    
                case GL_RGB_INTEGER:
                    expected = {refVals[0], refVals[1], refVals[2], refVals[0]};
                    break;
                case GL_RG_INTEGER:
                    expected = {refVals[0], refVals[1], 0, refVals[0]};
                    break;
                case GL_RED_INTEGER:
                    expected = {refVals[0], 0, 0, refVals[0]};
                    break;
    
                case GL_LUMINANCE_ALPHA:
                    expected = {refVals[0], refVals[0], refVals[0], refVals[1]};
                    break;
                case GL_LUMINANCE:
                    expected = {refVals[0], refVals[0], refVals[0], 255};
                    break;
                case GL_ALPHA:
                    expected = {0, 0, 0, refVals[0]};
                    break;
    
                default:
                    assert(false);
            }
    
            ASSERT_GL_NO_ERROR();
            auto result = actual.ExpectNear(expected, err);
            if (!result)
            {
                result << " [" << EnumStr(format.internalFormat) << "/" << EnumStr(format.unpackFormat)
                       << "/" << EnumStr(format.unpackType) << " " << info << "]";
            }
            EXPECT_TRUE(result);
        };
    
        // Provide buffers for test data, and a func to run the test on both the data directly, and on
        // a basic subrect selection to ensure pixel byte size is calculated correctly.
        // Possible todo here is to add tests to ensure stride calculation.
    
        std::array<uint8_t, sizeof(float) * 4> srcBuffer;
    
        std::array<uint8_t, srcBuffer.size() * 2> subrectBuffer;
        const auto fnTest = [&](const TexFormat &format, const GLColor &err) {
            fnTestData(format, srcBuffer.data(), err, "simple");
    
            if (!hasSubrectUploads)
                return;
    
            const auto bytesPerPixel = format.bytesPerPixel();
    
            glPixelStorei(GL_UNPACK_SKIP_PIXELS, 1);
    
            subrectBuffer.fill(0);
            memcpy(subrectBuffer.data() + bytesPerPixel, srcBuffer.data(), bytesPerPixel);
            fnTestData(format, subrectBuffer.data(), err, "subrect");
    
            glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
        };
    
        // Test All The Formats, organized by unpack format and type.
        // (Combos from GLES 3.0.5 p111-112: Table 3.2: "Valid combinations of format, type, and sized
        // internalformat.")
    
        // Start with normalized ints
        glUseProgram(floatsProg);
    
        // RGBA+UNSIGNED_BYTE
        {
            constexpr uint8_t src[] = {static_cast<uint8_t>(EncodeNormUint<8>(srcVals[0])),
                                       static_cast<uint8_t>(EncodeNormUint<8>(srcVals[1])),
                                       static_cast<uint8_t>(EncodeNormUint<8>(srcVals[2])),
                                       static_cast<uint8_t>(EncodeNormUint<8>(srcVals[3]))};
            ZeroAndCopy(srcBuffer, src);
    
            fnTest({GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, {1, 1, 1, 1});
            fnTest({GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_BYTE}, {8, 8, 8, 255});
            fnTest({GL_RGBA4, GL_RGBA, GL_UNSIGNED_BYTE}, {16, 16, 16, 16});
    
            fnTest({GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE}, {1, 1, 1, 0});
            fnTest({GL_RGB565, GL_RGB, GL_UNSIGNED_BYTE}, {8, 4, 8, 0});
    
            fnTest({GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, {1, 1, 0, 0});
    
            fnTest({GL_R8, GL_RED, GL_UNSIGNED_BYTE}, {1, 0, 0, 0});
    
            fnTest({GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE}, {1, 1, 1, 1});
            fnTest({GL_RGB, GL_RGB, GL_UNSIGNED_BYTE}, {1, 1, 1, 0});
            fnTest({GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE}, {1, 1, 1, 1});
            fnTest({GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE}, {1, 1, 1, 0});
            fnTest({GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE}, {0, 0, 0, 1});
        }
    
        // RGBA+BYTE
        {
            constexpr uint8_t src[] = {static_cast<uint8_t>(EncodeNormUint<7>(srcVals[0])),
                                       static_cast<uint8_t>(EncodeNormUint<7>(srcVals[1])),
                                       static_cast<uint8_t>(EncodeNormUint<7>(srcVals[2])),
                                       static_cast<uint8_t>(EncodeNormUint<7>(srcVals[3]))};
            ZeroAndCopy(srcBuffer, src);
    
            fnTest({GL_RGBA8_SNORM, GL_RGBA, GL_BYTE}, {2, 2, 2, 2});
            fnTest({GL_RGB8_SNORM, GL_RGB, GL_BYTE}, {2, 2, 2, 0});
            fnTest({GL_RG8_SNORM, GL_RG, GL_BYTE}, {2, 2, 0, 0});
            fnTest({GL_R8_SNORM, GL_RED, GL_BYTE}, {2, 0, 0, 0});
        }
    
        // RGB+UNSIGNED_SHORT_5_6_5
        {
            constexpr uint16_t src[] = {static_cast<uint16_t>((EncodeNormUint<5>(srcVals[0]) << 11) |
                                                              (EncodeNormUint<6>(srcVals[1]) << 5) |
                                                              (EncodeNormUint<5>(srcVals[2]) << 0))};
            ZeroAndCopy(srcBuffer, src);
    
            fnTest({GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, {8, 4, 8, 0});
            fnTest({GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, {8, 4, 8, 0});
        }
    
        // RGBA+UNSIGNED_SHORT_4_4_4_4
        {
            constexpr uint16_t src[] = {static_cast<uint16_t>(
                (EncodeNormUint<4>(srcVals[0]) << 12) | (EncodeNormUint<4>(srcVals[1]) << 8) |
                (EncodeNormUint<4>(srcVals[2]) << 4) | (EncodeNormUint<4>(srcVals[3]) << 0))};
            ZeroAndCopy(srcBuffer, src);
    
            // fnTest({GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4}, {16,16,16,16});
            fnTest({GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4}, {16, 16, 16, 16});
        }
    
        // RGBA+UNSIGNED_SHORT_5_5_5_1
        {
            constexpr uint16_t src[] = {static_cast<uint16_t>(
                (EncodeNormUint<5>(srcVals[0]) << 11) | (EncodeNormUint<5>(srcVals[1]) << 6) |
                (EncodeNormUint<5>(srcVals[2]) << 1) | (EncodeNormUint<1>(srcVals[3]) << 0))};
            ZeroAndCopy(srcBuffer, src);
    
            fnTest({GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, {8, 8, 8, 255});
            fnTest({GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, {8, 8, 8, 255});
        }
    
        // RGBA+UNSIGNED_INT_2_10_10_10_REV
        {
            constexpr uint32_t src[] = {
                (EncodeNormUint<10>(srcVals[0]) << 0) | (EncodeNormUint<10>(srcVals[1]) << 10) |
                (EncodeNormUint<10>(srcVals[2]) << 20) | (EncodeNormUint<2>(srcVals[3]) << 30)};
            ZeroAndCopy(srcBuffer, src);
    
            fnTest({GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, {1, 1, 1, 128});
            fnTest({GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, {8, 8, 8, 255});
        }
    
        // DEPTH_COMPONENT+UNSIGNED_SHORT
        {
            const uint16_t src[] = {static_cast<uint16_t>(EncodeNormUint<16>(srcVals[0]))};
            ZeroAndCopy(srcBuffer, src);
    
            fnTest({GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, {1, 0, 0, 0});
        }
    
        // DEPTH_COMPONENT+UNSIGNED_INT
        {
            constexpr uint32_t src[] = {EncodeNormUint<32>(srcVals[0])};
            ZeroAndCopy(srcBuffer, src);
    
            fnTest({GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT}, {1, 0, 0, 0});
            fnTest({GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT}, {1, 0, 0, 0});
        }
    
        // DEPTH_STENCIL+UNSIGNED_INT_24_8
        {
            // Drop stencil.
            constexpr uint32_t src[] = {EncodeNormUint<24>(srcVals[0]) << 8};
            ZeroAndCopy(srcBuffer, src);
    
            fnTest({GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, {1, 0, 0, 0});
        }
    
        // Non-normalized ints
        glUseProgram(intsProg);
    
        // RGBA_INTEGER+UNSIGNED_BYTE
        {
            constexpr uint8_t src[4] = {srcIntVals[0], srcIntVals[1], srcIntVals[2], srcIntVals[3]};
            ZeroAndCopy(srcBuffer, src);
    
            fnTest({GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE}, {1, 1, 1, 1});
            fnTest({GL_RGB8UI, GL_RGB_INTEGER, GL_UNSIGNED_BYTE}, {1, 1, 1, 1});
            fnTest({GL_RG8UI, GL_RG_INTEGER, GL_UNSIGNED_BYTE}, {1, 1, 1, 1});
            fnTest({GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE}, {1, 1, 1, 1});
    
            fnTest({GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE}, {1, 1, 1, 1});
            fnTest({GL_RGB8I, GL_RGB_INTEGER, GL_BYTE}, {1, 1, 1, 1});
            fnTest({GL_RG8I, GL_RG_INTEGER, GL_BYTE}, {1, 1, 1, 1});
            fnTest({GL_R8I, GL_RED_INTEGER, GL_BYTE}, {1, 1, 1, 1});
        }
    
        // RGBA_INTEGER+UNSIGNED_SHORT
        {
            constexpr uint16_t src[4] = {srcIntVals[0], srcIntVals[1], srcIntVals[2], srcIntVals[3]};
            ZeroAndCopy(srcBuffer, src);
    
            fnTest({GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT}, {1, 1, 1, 1});
            fnTest({GL_RGB16UI, GL_RGB_INTEGER, GL_UNSIGNED_SHORT}, {1, 1, 1, 1});
            fnTest({GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT}, {1, 1, 1, 1});
            fnTest({GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT}, {1, 1, 1, 1});
    
            fnTest({GL_RGBA16I, GL_RGBA_INTEGER, GL_SHORT}, {1, 1, 1, 1});
            fnTest({GL_RGB16I, GL_RGB_INTEGER, GL_SHORT}, {1, 1, 1, 1});
            fnTest({GL_RG16I, GL_RG_INTEGER, GL_SHORT}, {1, 1, 1, 1});
            fnTest({GL_R16I, GL_RED_INTEGER, GL_SHORT}, {1, 1, 1, 1});
        }
    
        // RGBA_INTEGER+UNSIGNED_INT
        {
            constexpr uint32_t src[4] = {srcIntVals[0], srcIntVals[1], srcIntVals[2], srcIntVals[3]};
            ZeroAndCopy(srcBuffer, src);
    
            fnTest({GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT}, {1, 1, 1, 1});
            fnTest({GL_RGB32UI, GL_RGB_INTEGER, GL_UNSIGNED_INT}, {1, 1, 1, 1});
            fnTest({GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT}, {1, 1, 1, 1});
            fnTest({GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT}, {1, 1, 1, 1});
    
            fnTest({GL_RGBA32I, GL_RGBA_INTEGER, GL_INT}, {1, 1, 1, 1});
            fnTest({GL_RGB32I, GL_RGB_INTEGER, GL_INT}, {1, 1, 1, 1});
            fnTest({GL_RG32I, GL_RG_INTEGER, GL_INT}, {1, 1, 1, 1});
            fnTest({GL_R32I, GL_RED_INTEGER, GL_INT}, {1, 1, 1, 1});
        }
    
        // RGBA_INTEGER+UNSIGNED_INT_2_10_10_10_REV
        {
            constexpr uint32_t src[] = {static_cast<uint32_t>(srcIntVals[0] << 0) |
                                        static_cast<uint32_t>(srcIntVals[1] << 10) |
                                        static_cast<uint32_t>(srcIntVals[2] << 20) |
                                        static_cast<uint32_t>(srcIntVals[3] << 30)};
            ZeroAndCopy(srcBuffer, src);
    
            fnTest({GL_RGB10_A2UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV}, {1, 1, 1, 1});
        }
    
        // True floats
        glUseProgram(floatsProg);
    
        // RGBA+HALF_FLOAT
        {
            const uint16_t src[] = {static_cast<uint16_t>(Float16::Encode(srcVals[0])),
                                    static_cast<uint16_t>(Float16::Encode(srcVals[1])),
                                    static_cast<uint16_t>(Float16::Encode(srcVals[2])),
                                    static_cast<uint16_t>(Float16::Encode(srcVals[3]))};
            ZeroAndCopy(srcBuffer, src);
    
            fnTest({GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT}, {1, 1, 1, 1});
    
            fnTest({GL_RGB16F, GL_RGB, GL_HALF_FLOAT}, {1, 1, 1, 0});
            fnTest({GL_R11F_G11F_B10F, GL_RGB, GL_HALF_FLOAT}, {1, 1, 1, 0});
            fnTest({GL_RGB9_E5, GL_RGB, GL_HALF_FLOAT}, {1, 1, 1, 0});
    
            fnTest({GL_RG16F, GL_RG, GL_HALF_FLOAT}, {1, 1, 0, 0});
    
            fnTest({GL_R16F, GL_RED, GL_HALF_FLOAT}, {1, 0, 0, 0});
    
            fnTest({GL_RGBA, GL_RGBA, GL_HALF_FLOAT_OES}, {1, 1, 1, 1});
            fnTest({GL_RGB, GL_RGB, GL_HALF_FLOAT_OES}, {1, 1, 1, 0});
            fnTest({GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_HALF_FLOAT_OES}, {1, 1, 1, 1});
            fnTest({GL_LUMINANCE, GL_LUMINANCE, GL_HALF_FLOAT_OES}, {1, 1, 1, 0});
            fnTest({GL_ALPHA, GL_ALPHA, GL_HALF_FLOAT_OES}, {0, 0, 0, 1});
        }
    
        // RGBA+FLOAT
        {
            ZeroAndCopy(srcBuffer, srcVals);
    
            fnTest({GL_RGBA32F, GL_RGBA, GL_FLOAT}, {1, 1, 1, 1});
            fnTest({GL_RGBA16F, GL_RGBA, GL_FLOAT}, {1, 1, 1, 1});
    
            fnTest({GL_RGB32F, GL_RGB, GL_FLOAT}, {1, 1, 1, 0});
            fnTest({GL_RGB16F, GL_RGB, GL_FLOAT}, {1, 1, 1, 0});
            fnTest({GL_R11F_G11F_B10F, GL_RGB, GL_FLOAT}, {1, 1, 1, 0});
            fnTest({GL_RGB9_E5, GL_RGB, GL_FLOAT}, {1, 1, 1, 0});
    
            fnTest({GL_RG32F, GL_RG, GL_FLOAT}, {1, 1, 0, 0});
            fnTest({GL_RG16F, GL_RG, GL_FLOAT}, {1, 1, 0, 0});
    
            fnTest({GL_R32F, GL_RED, GL_FLOAT}, {1, 0, 0, 0});
            fnTest({GL_R16F, GL_RED, GL_FLOAT}, {1, 0, 0, 0});
        }
    
        // UNSIGNED_INT_10F_11F_11F_REV
        {
            const uint32_t src[] = {(UFloat11::Encode(srcVals[0]) << 0) |
                                    (UFloat11::Encode(srcVals[1]) << 11) |
                                    (UFloat10::Encode(srcVals[2]) << 22)};
            ZeroAndCopy(srcBuffer, src);
    
            fnTest({GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV}, {1, 1, 1, 0});
        }
    
        // UNSIGNED_INT_5_9_9_9_REV
        {
            const uint32_t src[] = {EncodeRGB9_E5_Rev(srcVals[0], srcVals[1], srcVals[2])};
            ZeroAndCopy(srcBuffer, src);
    
            fnTest({GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV}, {1, 1, 1, 0});
        }
    
        // DEPTH_COMPONENT+FLOAT
        {
            // Skip stencil.
            constexpr float src[] = {srcVals[0], 0};
            ZeroAndCopy(srcBuffer, src);
    
            fnTest({GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT}, {1, 0, 0, 0});
            fnTest({GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV},
                   {1, 0, 0, 0});
        }
    
        EXPECT_GL_NO_ERROR();
    }
    
    ANGLE_INSTANTIATE_TEST(TextureUploadFormatTest,
                           ES2_D3D11(),
                           ES2_D3D11_FL9_3(),
                           ES2_D3D9(),
                           ES2_OPENGL(),
                           ES2_OPENGLES());