//
// Copyright 2020 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.
//

// FramebufferObjectTest.cpp: Tests basic usage of OES_framebuffer_object extension.

#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"

using namespace angle;

class FramebufferObjectTest : public ANGLETest<>
{
  protected:
    FramebufferObjectTest()
    {
        setWindowWidth(32);
        setWindowHeight(32);
        setConfigRedBits(8);
        setConfigGreenBits(8);
        setConfigBlueBits(8);
        setConfigAlphaBits(8);
        setConfigDepthBits(24);
    }

    void testSetUp() override
    {
        mTexture.reset(new GLTexture());
        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, mTexture->get());
    }

    void testTearDown() override { mTexture.reset(); }

    std::unique_ptr<GLTexture> mTexture;
};

// Checks that framebuffer object can be used without GL errors.
TEST_P(FramebufferObjectTest, FramebufferObject)
{
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_framebuffer_object"));

    GLuint fboId;
    GLint params;

    glGenFramebuffersOES(1, &fboId);
    EXPECT_GL_NO_ERROR();
    glIsFramebufferOES(fboId);
    EXPECT_GL_NO_ERROR();
    glBindFramebufferOES(GL_FRAMEBUFFER, fboId);
    EXPECT_GL_NO_ERROR();

    glCheckFramebufferStatusOES(GL_FRAMEBUFFER);
    EXPECT_GL_NO_ERROR();
    glGetFramebufferAttachmentParameterivOES(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                             GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &params);
    EXPECT_GL_NO_ERROR();

    glBindFramebufferOES(GL_FRAMEBUFFER, 0);
    glDeleteFramebuffersOES(1, &fboId);
    EXPECT_GL_NO_ERROR();
}

// Checks that texture object can be bound for framebuffer object.
TEST_P(FramebufferObjectTest, TextureObject)
{
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_framebuffer_object"));

    GLuint fboId;

    glGenFramebuffersOES(1, &fboId);
    glBindFramebufferOES(GL_FRAMEBUFFER, fboId);
    glFramebufferTexture2DOES(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture->get(),
                              0);
    EXPECT_GL_NO_ERROR();

    glBindFramebufferOES(GL_FRAMEBUFFER, 0);
    glDeleteFramebuffersOES(1, &fboId);
}

// Checks different formats for a texture object bound to a framebuffer object.
TEST_P(FramebufferObjectTest, TextureObjectDifferentFormats)
{
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_framebuffer_object"));

    GLuint fboId;

    glGenFramebuffersOES(1, &fboId);
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, fboId);

    using FormatInfo                                  = std::array<GLenum, 3>;
    constexpr std::array<FormatInfo, 5> kFormatArrays = {
        {{GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE},
         {GL_RGB, GL_RGB, GL_UNSIGNED_BYTE},
         {GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4},
         {GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1},
         {GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}}};

    for (const FormatInfo &formatInfo : kFormatArrays)
    {
        glTexImage2D(GL_TEXTURE_2D, 0, formatInfo[0], 1, 1, 0, formatInfo[1], formatInfo[2],
                     &GLColor::green);
        glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D,
                                  mTexture->get(), 0);
        ASSERT_EQ(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES),
                  (GLenum)GL_FRAMEBUFFER_COMPLETE_OES);
    }

    EXPECT_GL_NO_ERROR();

    glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
    glDeleteFramebuffersOES(1, &fboId);
}

// Checks that renderbuffer object can be used and can be bound for framebuffer object.
TEST_P(FramebufferObjectTest, RenderbufferObject)
{
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_framebuffer_object"));

    GLuint fboId;
    GLuint rboId;
    GLint params;

    glGenFramebuffersOES(1, &fboId);
    glBindFramebufferOES(GL_FRAMEBUFFER, fboId);

    glGenRenderbuffersOES(1, &rboId);
    EXPECT_GL_NO_ERROR();
    glIsRenderbufferOES(rboId);
    EXPECT_GL_NO_ERROR();
    glBindRenderbufferOES(GL_RENDERBUFFER, rboId);
    EXPECT_GL_NO_ERROR();
    glRenderbufferStorageOES(GL_RENDERBUFFER, GL_RGBA4, 32, 32);
    EXPECT_GL_NO_ERROR();
    glRenderbufferStorageOES(GL_RENDERBUFFER, GL_RGB5_A1, 32, 32);
    EXPECT_GL_NO_ERROR();
    glRenderbufferStorageOES(GL_RENDERBUFFER, GL_RGB565, 32, 32);
    EXPECT_GL_NO_ERROR();
    glRenderbufferStorageOES(GL_RENDERBUFFER, GL_RGBA8, 32, 32);
    EXPECT_GL_NO_ERROR();

    glFramebufferRenderbufferOES(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rboId);
    EXPECT_GL_NO_ERROR();
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &params);
    EXPECT_GL_NO_ERROR();

    glBindFramebufferOES(GL_FRAMEBUFFER, 0);
    glDeleteFramebuffersOES(1, &fboId);
    glDeleteRenderbuffersOES(1, &rboId);
    EXPECT_GL_NO_ERROR();
}

// Checks that an RGBA8 renderbuffer object can be used and can be bound for framebuffer object.
TEST_P(FramebufferObjectTest, RGBA8Renderbuffer)
{
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_framebuffer_object"));
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_rgba8"));

    GLuint fbo;
    GLuint rbo;

    glGenFramebuffersOES(1, &fbo);
    glBindFramebufferOES(GL_FRAMEBUFFER, fbo);

    glGenRenderbuffersOES(1, &rbo);
    EXPECT_GL_NO_ERROR();
    glIsRenderbufferOES(rbo);
    EXPECT_GL_NO_ERROR();
    glBindRenderbufferOES(GL_RENDERBUFFER, rbo);
    EXPECT_GL_NO_ERROR();
    glRenderbufferStorageOES(GL_RENDERBUFFER, GL_RGBA8, 16, 16);
    EXPECT_GL_NO_ERROR();

    glFramebufferRenderbufferOES(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo);
    EXPECT_GL_NO_ERROR();

    glClearColor(1.0, 1.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

    EXPECT_PIXEL_RECT_EQ(0, 0, 16, 16, GLColor::white);

    glBindFramebufferOES(GL_FRAMEBUFFER, 0);
    glDeleteFramebuffersOES(1, &fbo);
    glDeleteRenderbuffersOES(1, &rbo);
    EXPECT_GL_NO_ERROR();
}

// Checks that an RGB8 and an RGBA8 renderbuffer object can be used and can be bound for framebuffer
// object one after the other.
TEST_P(FramebufferObjectTest, RGB8AndRGBA8Renderbuffers)
{
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_framebuffer_object"));
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_rgb8_rgba8"));

    GLuint fbo;
    GLuint rbo[2];

    glGenFramebuffersOES(1, &fbo);
    glBindFramebufferOES(GL_FRAMEBUFFER, fbo);

    glGenRenderbuffersOES(2, rbo);
    EXPECT_GL_NO_ERROR();
    glBindRenderbufferOES(GL_RENDERBUFFER, rbo[0]);
    EXPECT_GL_NO_ERROR();
    glRenderbufferStorageOES(GL_RENDERBUFFER, GL_RGB8, 16, 16);
    EXPECT_GL_NO_ERROR();
    glBindRenderbufferOES(GL_RENDERBUFFER, rbo[1]);
    EXPECT_GL_NO_ERROR();
    glRenderbufferStorageOES(GL_RENDERBUFFER, GL_RGBA8, 16, 16);
    EXPECT_GL_NO_ERROR();

    glFramebufferRenderbufferOES(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo[0]);
    EXPECT_GL_NO_ERROR();

    glClearColor(0.0, 1.0, 0.0, 0.1);
    glClear(GL_COLOR_BUFFER_BIT);
    EXPECT_PIXEL_RECT_EQ(0, 0, 16, 16, GLColor::green);

    glFramebufferRenderbufferOES(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo[1]);
    EXPECT_GL_NO_ERROR();

    glClearColor(1.0, 0.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    EXPECT_PIXEL_RECT_EQ(0, 0, 16, 16, GLColor::magenta);

    glBindFramebufferOES(GL_FRAMEBUFFER, 0);
    glDeleteFramebuffersOES(1, &fbo);
    glDeleteRenderbuffersOES(2, rbo);
    EXPECT_GL_NO_ERROR();
}

// Checks that generateMipmap can be called without GL errors.
TEST_P(FramebufferObjectTest, GenerateMipmap)
{
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_framebuffer_object"));

    constexpr uint32_t kSize = 32;
    std::vector<unsigned char> pixelData(kSize * kSize * 4, 0);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixelData.data());

    glGenerateMipmapOES(GL_TEXTURE_2D);
    EXPECT_GL_NO_ERROR();
}

ANGLE_INSTANTIATE_TEST_ES1(FramebufferObjectTest);
