Edit

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

Branch :

  • Show log

    Commit

  • Author : Le Hoang Quyen
    Date : 2019-11-21 01:19:40
    Hash : 6fcc0bb8
    Message : Metal: Re-add end2end test configs (running test is still disabled) angle_test_instantiate.cpp & angle_test_instantiate_apple.mm: - Disabled metal platform selection on pre-10.13 mac devices for Bug: angleproject:4153 Explicitly disabled tests on metal: - DifferentStencilMasksTest.DrawWithDifferentMask - PointSpritesTest.PointSizeAboveMaxIsClamped - WebGL2ReadOutsideFramebufferTest.CopyTexSubImage3D This requires the crash fix in http://crrev.com/c/1924101 Bug: angleproject:4153 Bug: angleproject:2634 Change-Id: I95046d731a8ba7414cf1a1f4b6f2940282725872 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1926389 Commit-Queue: Jonah Ryan-Davis <jonahr@google.com> Reviewed-by: Jamie Madill <jmadill@chromium.org> Reviewed-by: Jonah Ryan-Davis <jonahr@google.com>

  • src/tests/gl_tests/WebGLReadOutsideFramebufferTest.cpp
  • //
    // Copyright 2017 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.
    //
    
    // WebGLReadOutsideFramebufferTest.cpp : Test functions which read the framebuffer (readPixels,
    // copyTexSubImage2D, copyTexImage2D) on areas outside the framebuffer.
    
    #include "test_utils/ANGLETest.h"
    
    #include "test_utils/gl_raii.h"
    
    namespace
    {
    
    class PixelRect
    {
      public:
        PixelRect(int width, int height) : mWidth(width), mHeight(height), mData(width * height) {}
    
        // Set each pixel to a different color consisting of the x,y position and a given tag.
        // Making each pixel a different means any misplaced pixel will cause a failure.
        // Encoding the position proved valuable in debugging.
        void fill(unsigned tag)
        {
            for (int x = 0; x < mWidth; ++x)
            {
                for (int y = 0; y < mHeight; ++y)
                {
                    mData[x + y * mWidth] = angle::GLColor(x + (y << 8) + (tag << 16));
                }
            }
        }
    
        void toTexture2D(GLuint target, GLuint texid) const
        {
            glBindTexture(target, texid);
            if (target == GL_TEXTURE_CUBE_MAP)
            {
                glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, mWidth, mHeight, 0, GL_RGBA,
                             GL_UNSIGNED_BYTE, mData.data());
                glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA, mWidth, mHeight, 0, GL_RGBA,
                             GL_UNSIGNED_BYTE, mData.data());
                glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA, mWidth, mHeight, 0, GL_RGBA,
                             GL_UNSIGNED_BYTE, mData.data());
                glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA, mWidth, mHeight, 0, GL_RGBA,
                             GL_UNSIGNED_BYTE, mData.data());
                glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA, mWidth, mHeight, 0, GL_RGBA,
                             GL_UNSIGNED_BYTE, mData.data());
                glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA, mWidth, mHeight, 0, GL_RGBA,
                             GL_UNSIGNED_BYTE, mData.data());
            }
            else
            {
                ASSERT_GLENUM_EQ(GL_TEXTURE_2D, target);
                glTexImage2D(target, 0, GL_RGBA, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                             mData.data());
            }
            glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        }
    
        void toTexture3D(GLuint texid, GLint depth) const
        {
            glBindTexture(GL_TEXTURE_3D, texid);
    
            glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, mWidth, mHeight, depth, 0, GL_RGBA,
                         GL_UNSIGNED_BYTE, nullptr);
            for (GLint z = 0; z < depth; z++)
            {
                glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, z, mWidth, mHeight, 1, GL_RGBA,
                                GL_UNSIGNED_BYTE, mData.data());
            }
            glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
        }
    
        void readFB(int x, int y)
        {
            glReadPixels(x, y, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, mData.data());
        }
    
        // Read pixels from 'other' into 'this' from position (x,y).
        // Pixels outside 'other' are untouched or zeroed according to 'zeroOutside.'
        void readPixelRect(const PixelRect &other, int x, int y, bool zeroOutside)
        {
            for (int i = 0; i < mWidth; ++i)
            {
                for (int j = 0; j < mHeight; ++j)
                {
                    angle::GLColor *dest = &mData[i + j * mWidth];
                    if (!other.getPixel(x + i, y + j, dest) && zeroOutside)
                    {
                        *dest = angle::GLColor(0);
                    }
                }
            }
        }
    
        bool getPixel(int x, int y, angle::GLColor *colorOut) const
        {
            if (0 <= x && x < mWidth && 0 <= y && y < mHeight)
            {
                *colorOut = mData[x + y * mWidth];
                return true;
            }
            return false;
        }
    
        void compare(const PixelRect &expected) const
        {
            ASSERT_EQ(mWidth, expected.mWidth);
            ASSERT_EQ(mHeight, expected.mHeight);
    
            for (int x = 0; x < mWidth; ++x)
            {
                for (int y = 0; y < mHeight; ++y)
                {
                    ASSERT_EQ(expected.mData[x + y * mWidth], mData[x + y * mWidth])
                        << "at (" << x << ", " << y << ")";
                }
            }
        }
    
      private:
        int mWidth, mHeight;
        std::vector<angle::GLColor> mData;
    };
    
    }  // namespace
    
    namespace angle
    {
    
    class WebGLReadOutsideFramebufferTest : public ANGLETest
    {
      public:
        // Read framebuffer to 'pixelsOut' via glReadPixels.
        void TestReadPixels(int x, int y, int, PixelRect *pixelsOut) { pixelsOut->readFB(x, y); }
    
        // Read framebuffer to 'pixelsOut' via glCopyTexSubImage2D and GL_TEXTURE_2D.
        void TestCopyTexSubImage2D(int x, int y, int, PixelRect *pixelsOut)
        {
            // Init texture with given pixels.
            GLTexture destTexture;
            pixelsOut->toTexture2D(GL_TEXTURE_2D, destTexture.get());
    
            // Read framebuffer -> texture -> 'pixelsOut'
            glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, x, y, kReadWidth, kReadHeight);
            readTexture2D(GL_TEXTURE_2D, destTexture.get(), kReadWidth, kReadHeight, pixelsOut);
        }
    
        // Read framebuffer to 'pixelsOut' via glCopyTexSubImage2D and cube map.
        void TestCopyTexSubImageCube(int x, int y, int, PixelRect *pixelsOut)
        {
            // Init texture with given pixels.
            GLTexture destTexture;
            pixelsOut->toTexture2D(GL_TEXTURE_CUBE_MAP, destTexture.get());
    
            // Read framebuffer -> texture -> 'pixelsOut'
            glCopyTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, 0, 0, x, y, kReadWidth, kReadHeight);
            readTexture2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, destTexture.get(), kReadWidth, kReadHeight,
                          pixelsOut);
        }
    
        // Read framebuffer to 'pixelsOut' via glCopyTexSubImage3D.
        void TestCopyTexSubImage3D(int x, int y, int z, PixelRect *pixelsOut)
        {
            // Init texture with given pixels.
            GLTexture destTexture;
            pixelsOut->toTexture3D(destTexture.get(), kTextureDepth);
    
            // Read framebuffer -> texture -> 'pixelsOut'
            glCopyTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, z, x, y, kReadWidth, kReadHeight);
            readTexture3D(destTexture, kReadWidth, kReadHeight, z, pixelsOut);
        }
    
        // Read framebuffer to 'pixelsOut' via glCopyTexImage2D and GL_TEXTURE_2D.
        void TestCopyTexImage2D(int x, int y, int, PixelRect *pixelsOut)
        {
            // Init texture with given pixels.
            GLTexture destTexture;
            pixelsOut->toTexture2D(GL_TEXTURE_2D, destTexture.get());
    
            // Read framebuffer -> texture -> 'pixelsOut'
            glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, kReadWidth, kReadHeight, 0);
            readTexture2D(GL_TEXTURE_2D, destTexture.get(), kReadWidth, kReadHeight, pixelsOut);
        }
    
        // Read framebuffer to 'pixelsOut' via glCopyTexImage2D and cube map.
        void TestCopyTexImageCube(int x, int y, int, PixelRect *pixelsOut)
        {
            // Init texture with given pixels.
            GLTexture destTexture;
            pixelsOut->toTexture2D(GL_TEXTURE_CUBE_MAP, destTexture.get());
    
            // Read framebuffer -> texture -> 'pixelsOut'
            glCopyTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, x, y, kReadWidth, kReadHeight,
                             0);
            readTexture2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, destTexture, kReadWidth, kReadHeight,
                          pixelsOut);
        }
    
      protected:
        static constexpr int kFbWidth      = 128;
        static constexpr int kFbHeight     = 128;
        static constexpr int kTextureDepth = 16;
        static constexpr int kReadWidth    = 4;
        static constexpr int kReadHeight   = 4;
        static constexpr int kReadLayer    = 2;
    
        // Tag the framebuffer pixels differently than the initial read buffer pixels, so we know for
        // sure which pixels are changed by reading.
        static constexpr GLuint fbTag   = 0x1122;
        static constexpr GLuint readTag = 0xaabb;
    
        WebGLReadOutsideFramebufferTest() : mFBData(kFbWidth, kFbHeight)
        {
            setWindowWidth(kFbWidth);
            setWindowHeight(kFbHeight);
            setConfigRedBits(8);
            setConfigGreenBits(8);
            setConfigBlueBits(8);
            setConfigAlphaBits(8);
            setWebGLCompatibilityEnabled(true);
        }
    
        void testSetUp() override
        {
            constexpr char kVS[] = R"(
    attribute vec3 a_position;
    varying vec2 v_texCoord;
    void main() {
        v_texCoord = a_position.xy * 0.5 + 0.5;
        gl_Position = vec4(a_position, 1);
    })";
            constexpr char kFS[] = R"(
    precision mediump float;
    varying vec2 v_texCoord;
    uniform sampler2D u_texture;
    void main() {
        gl_FragColor = texture2D(u_texture, v_texCoord);
    })";
    
            mProgram = CompileProgram(kVS, kFS);
            glUseProgram(mProgram);
            GLint uniformLoc = glGetUniformLocation(mProgram, "u_texture");
            ASSERT_NE(-1, uniformLoc);
            glUniform1i(uniformLoc, 0);
    
            glDisable(GL_BLEND);
            glDisable(GL_DEPTH_TEST);
    
            // fill framebuffer with unique pixels
            mFBData.fill(fbTag);
            GLTexture fbTexture;
            mFBData.toTexture2D(GL_TEXTURE_2D, fbTexture);
            drawQuad(mProgram, "a_position", 0.0f, 1.0f, true);
        }
    
        void testTearDown() override { glDeleteProgram(mProgram); }
    
        using TestFunc = void (WebGLReadOutsideFramebufferTest::*)(int x,
                                                                   int y,
                                                                   int z,
                                                                   PixelRect *dest);
    
        void Main2D(TestFunc testFunc, bool zeroOutside) { mainImpl(testFunc, zeroOutside, 0); }
    
        void Main3D(TestFunc testFunc, bool zeroOutside)
        {
            mainImpl(testFunc, zeroOutside, kReadLayer);
        }
    
        void mainImpl(TestFunc testFunc, bool zeroOutside, int readLayer)
        {
            PixelRect actual(kReadWidth, kReadHeight);
            PixelRect expected(kReadWidth, kReadHeight);
    
            // Read a kReadWidth*kReadHeight rectangle of pixels from places that include:
            // - completely outside framebuffer, on all sides of it (i,j < 0 or > 2)
            // - completely inside framebuffer (i,j == 1)
            // - straddling framebuffer boundary, at each corner and side
            for (int i = -1; i < 4; ++i)
            {
                for (int j = -1; j < 4; ++j)
                {
                    int x = i * kFbWidth / 2 - kReadWidth / 2;
                    int y = j * kFbHeight / 2 - kReadHeight / 2;
    
                    // Put unique pixel values into the read destinations.
                    actual.fill(readTag);
                    expected.readPixelRect(actual, 0, 0, false);
    
                    // Read from framebuffer into 'actual.'
                    glBindFramebuffer(GL_FRAMEBUFFER, 0);
                    (this->*testFunc)(x, y, readLayer, &actual);
                    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    
                    // Simulate framebuffer read, into 'expected.'
                    expected.readPixelRect(mFBData, x, y, zeroOutside);
    
                    // See if they are the same.
                    actual.compare(expected);
                }
            }
        }
    
        // Get contents of given texture by drawing it into a framebuffer then reading with
        // glReadPixels().
        void readTexture2D(GLuint target, GLuint texture, GLsizei width, GLsizei height, PixelRect *out)
        {
            GLFramebuffer fbo;
            glBindFramebuffer(GL_FRAMEBUFFER, fbo);
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, texture, 0);
            out->readFB(0, 0);
        }
    
        // Get contents of current texture by drawing it into a framebuffer then reading with
        // glReadPixels().
        void readTexture3D(GLuint texture, GLsizei width, GLsizei height, int zSlice, PixelRect *out)
        {
            GLFramebuffer fbo;
            glBindFramebuffer(GL_FRAMEBUFFER, fbo);
            glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0, zSlice);
            out->readFB(0, 0);
        }
    
        PixelRect mFBData;
        GLuint mProgram;
    };
    
    class WebGL2ReadOutsideFramebufferTest : public WebGLReadOutsideFramebufferTest
    {};
    
    // Check that readPixels does not set a destination pixel when
    // the corresponding source pixel is outside the framebuffer.
    TEST_P(WebGLReadOutsideFramebufferTest, ReadPixels)
    {
        Main2D(&WebGLReadOutsideFramebufferTest::TestReadPixels, false);
    }
    
    // Check that copyTexSubImage2D does not set a destination pixel when
    // the corresponding source pixel is outside the framebuffer.
    TEST_P(WebGLReadOutsideFramebufferTest, CopyTexSubImage2D)
    {
        Main2D(&WebGLReadOutsideFramebufferTest::TestCopyTexSubImage2D, false);
    
        // TODO(fjhenigman): Figure out this failure.
        // Cube map skipped on 64-bit Windows with D3D FL 9.3
        ANGLE_SKIP_TEST_IF(GetParam() == ES2_D3D11_FL9_3());
    
        Main2D(&WebGLReadOutsideFramebufferTest::TestCopyTexSubImageCube, false);
    }
    
    // Check that copyTexImage2D sets (0,0,0,0) for pixels outside the framebuffer.
    TEST_P(WebGLReadOutsideFramebufferTest, CopyTexImage2D)
    {
        // http://anglebug.com/4092
        ANGLE_SKIP_TEST_IF(IsVulkan() || IsD3D9() || IsD3D11());
        Main2D(&WebGLReadOutsideFramebufferTest::TestCopyTexImage2D, true);
    
        // TODO(fjhenigman): Figure out this failure.
        // Cube map skipped on 64-bit Windows with D3D FL 9.3
        ANGLE_SKIP_TEST_IF(GetParam() == ES2_D3D11_FL9_3());
    
        Main2D(&WebGLReadOutsideFramebufferTest::TestCopyTexImageCube, true);
    }
    
    // Check that copyTexSubImage3D does not set a destination pixel when
    // the corresponding source pixel is outside the framebuffer.
    TEST_P(WebGL2ReadOutsideFramebufferTest, CopyTexSubImage3D)
    {
        // TODO(hqle): Metal doesn't implement 3D texture yet.
        // http://anglebug.com/4136 (ES2 renderer is mistakenly included in this test)
        ANGLE_SKIP_TEST_IF(IsMetal());
        // http://anglebug.com/4092
        ANGLE_SKIP_TEST_IF(IsVulkan() || IsD3D9() || IsD3D11());
        // Robust CopyTexSubImage3D behaviour is not implemented on OpenGL.
        ANGLE_SKIP_TEST_IF(IsDesktopOpenGL() || IsOpenGLES());
    
        Main3D(&WebGLReadOutsideFramebufferTest::TestCopyTexSubImage3D, false);
    }
    
    ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(WebGLReadOutsideFramebufferTest);
    
    ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(WebGL2ReadOutsideFramebufferTest);
    
    }  // namespace angle