Edit

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

Branch :

  • Show log

    Commit

  • Author : Luc Ferron
    Date : 2018-06-08 15:57:31
    Hash : af883628
    Message : Vulkan: enable as many end2end tests as possible Bug: angleproject:2615 Change-Id: I918cc18984b2e5b22b5e13398355a2fd60e4eb00 Reviewed-on: https://chromium-review.googlesource.com/1093564 Commit-Queue: Luc Ferron <lucferron@chromium.org> Reviewed-by: 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);
        }
    
        static constexpr float Decode(const uint32_t sVal, const uint32_t eVal, const uint32_t mVal)
        {
            constexpr int eBias = (1 << (kEBits - 1)) - 1;
            constexpr int mDiv  = 1 << kMBits;
            float ret = powf(-1, sVal) * powf(2, int(eVal) - eBias) * (1.0f + float(mVal) / mDiv);
            return ret;
        }
    };
    using Float16  = SizedFloat<1, 5, 10>;
    using UFloat11 = SizedFloat<0, 5, 6>;
    using UFloat10 = SizedFloat<0, 5, 5>;
    
    struct RGB9_E5 final
    {
        // GLES 3.0.5 p129
        static constexpr int N = 9;   // number of mantissa bits per component
        static constexpr int B = 15;  // exponent bias
    
        static uint32_t Encode(const float red, const float green, const float blue)
        {
            const auto floori = [](const float x) { return static_cast<int>(floor(x)); };
            // GLES 3.0.5 p129
            constexpr int eMax = 31;  // max allowed biased exponent value
    
            const float twoToN       = powf(2, N);
            const float sharedExpMax = (twoToN - 1) / twoToN * powf(2, eMax - B);
    
            const auto fnClampColor = [&](const float color) {
                return std::max(0.0f, std::min(color, sharedExpMax));
            };
            const float redC   = fnClampColor(red);
            const float greenC = fnClampColor(green);
            const float blueC  = fnClampColor(blue);
    
            const float maxC = std::max({redC, greenC, blueC});
            const int expP   = std::max(-B - 1, floori(log2f(maxC))) + 1 + B;
    
            const auto fnColorS = [&](const float colorC, const float exp) {
                return floori(colorC / powf(2, exp - B - N) + 0.5f);
            };
            const int maxS = fnColorS(maxC, expP);
            const int expS = expP + ((maxS == (1 << N)) ? 1 : 0);
    
            const int redS   = fnColorS(redC, expS);
            const int greenS = fnColorS(greenC, expS);
            const int blueS  = fnColorS(blueC, expS);
    
            // Pack as u32 EGBR.
            uint32_t ret = expS & 0x1f;
            ret <<= 9;
            ret |= blueS & 0x1ff;
            ret <<= 9;
            ret |= greenS & 0x1ff;
            ret <<= 9;
            ret |= redS & 0x1ff;
            return ret;
        }
    
        static void Decode(uint32_t packed,
                           float *const out_red,
                           float *const out_green,
                           float *const out_blue)
        {
            const auto redS = packed & 0x1ff;
            packed >>= 9;
            const auto greenS = packed & 0x1ff;
            packed >>= 9;
            const auto blueS = packed & 0x1ff;
            packed >>= 9;
            const auto expS = packed & 0x1f;
    
            // These are *not* IEEE-like UFloat14s.
            // GLES 3.0.5 p165:
            // red = redS*pow(2,expS-B-N)
            const auto fnToFloat = [&](const uint32_t x) { return x * powf(2, int(expS) - B - N); };
            *out_red             = fnToFloat(redS);
            *out_green           = fnToFloat(greenS);
            *out_blue            = fnToFloat(blueS);
        }
    };
    
    }  // 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::Decode(0, 0x0f, 0), 1.0f);
        EXPECT_EQ(Float16::Decode(0, 0x0f - 1, 0), 0.5f);
    
        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));
    }
    
    // Ensure our RGB9_E5 encoding is reasonable, at least for our testcase.
    TEST(TextureUploadFormatTestInternals, RGB9E5Encoding)
    {
        const auto fnTest = [](const float refR, const float refG, const float refB) {
            const auto packed = RGB9_E5::Encode(refR, refG, refB);
            float testR, testG, testB;
            RGB9_E5::Decode(packed, &testR, &testG, &testB);
            EXPECT_EQ(testR, refR);
            EXPECT_EQ(testG, refG);
            EXPECT_EQ(testB, refB);
        };
        fnTest(0.125f, 0.250f, 0.625f);
    }
    
    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)
    {
        // TODO(lucferron): Diagnose and fix http://anglebug.com/2657
        ANGLE_SKIP_TEST_IF(IsVulkan() && IsAndroid());
    
        ANGLE_SKIP_TEST_IF(IsD3D9() || IsD3D11_FL93());
    
        constexpr char kVertShaderES2[]     = 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));
            })";
        ANGLE_GL_PROGRAM(floatsProg, kVertShaderES2, kFragShader_Floats);
    
        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);
        // Must be nearest because some texture formats aren't filterable!
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        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)  // Format might not be supported. (e.g. on ES2)
                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});
        }
    
        if (getClientMajorVersion() < 3)
            return;
    
        constexpr char kVertShaderES3[]    = R"(#version 300 es
            void main()
            {
                gl_PointSize = 1.0;
                gl_Position = vec4(0, 0, 0, 1);
            })";
        constexpr char kFragShader_Ints[]  = R"(#version 300 es
            precision mediump float;
            uniform highp isampler2D uTex;
            out vec4 oFragColor;
    
            void main()
            {
                oFragColor = vec4(texture(uTex, vec2(0,0))) / 8.0;
            })";
        constexpr char kFragShader_Uints[] = R"(#version 300 es
            precision mediump float;
            uniform highp usampler2D uTex;
            out vec4 oFragColor;
    
            void main()
            {
                oFragColor = vec4(texture(uTex, vec2(0,0))) / 8.0;
            })";
        ANGLE_GL_PROGRAM(intsProg, kVertShaderES3, kFragShader_Ints);
        ANGLE_GL_PROGRAM(uintsProg, kVertShaderES3, kFragShader_Uints);
    
        // 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_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_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_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});
        }
    
        // Non-normalized uints
        glUseProgram(uintsProg);
    
        // 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});
        }
    
        // 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});
        }
    
        // 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});
        }
    
        // 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[] = {RGB9_E5::Encode(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,
                           ES3_D3D11(),
                           ES2_D3D11_FL9_3(),
                           ES2_D3D9(),
                           ES2_OPENGL(),
                           ES3_OPENGL(),
                           ES2_OPENGLES(),
                           ES3_OPENGLES(),
                           ES2_VULKAN());