// Copyright (C) 2018 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.

#include "GLSnapshotTesting.h"

#include <gtest/gtest.h>

namespace gfxstream {
namespace gl {
namespace {

struct GlRenderbufferFormat {
    GLenum name;
    GLsizei red;
    GLsizei green;
    GLsizei blue;
    GLsizei alpha;
    GLsizei depth;
    GLsizei stencil;
};

struct GlRenderbufferState {
    GLuint width;
    GLuint height;
    GlRenderbufferFormat format;
};

static const GlRenderbufferState kGLES2DefaultRenderbufferState = {
        0,
        0,
        {.name = GL_RGBA4}};

static const GlRenderbufferFormat kGLES2RenderbufferFormatFieldSizes[] = {
        {GL_DEPTH_COMPONENT16, 0, 0, 0, 0, 16, 0},
        {GL_RGBA4, 4, 4, 4, 4, 0, 0},
        {GL_RGB5_A1, 5, 5, 5, 1, 0, 0},
        {GL_RGB565, 5, 6, 5, 0, 0, 0},
        {GL_STENCIL_INDEX8, 0, 0, 0, 0, 0, 8},
};

static const GlRenderbufferState kGLES2TestRenderbufferStates[] = {
        {1, 1, kGLES2RenderbufferFormatFieldSizes[0]},
        {0, 3, kGLES2RenderbufferFormatFieldSizes[1]},
        {2, 2, kGLES2RenderbufferFormatFieldSizes[1]},
        {4, 4, kGLES2RenderbufferFormatFieldSizes[2]},
        {8, 8, kGLES2RenderbufferFormatFieldSizes[3]},
        {16, 16, kGLES2RenderbufferFormatFieldSizes[4]},
};

class SnapshotGlRenderbufferTest : public SnapshotPreserveTest {
public:
    void defaultStateCheck() override {
        EXPECT_EQ(GL_FALSE, gl->glIsRenderbuffer(m_renderbuffer_name));
        EXPECT_TRUE(compareGlobalGlInt(gl, GL_RENDERBUFFER_BINDING, 0));
    }

    void changedStateCheck() override {
        EXPECT_EQ(GL_TRUE, gl->glIsRenderbuffer(m_renderbuffer_name));
        EXPECT_TRUE(compareGlobalGlInt(gl, GL_RENDERBUFFER_BINDING,
                                       m_renderbuffer_name));

        EXPECT_TRUE(compareParameter(GL_RENDERBUFFER_WIDTH, m_state.width));
        EXPECT_TRUE(compareParameter(GL_RENDERBUFFER_HEIGHT, m_state.height));
        EXPECT_TRUE(compareParameter(GL_RENDERBUFFER_INTERNAL_FORMAT,
                                     m_state.format.name));
        EXPECT_TRUE(
                compareParameter(GL_RENDERBUFFER_RED_SIZE, m_state.format.red));
        EXPECT_TRUE(compareParameter(GL_RENDERBUFFER_GREEN_SIZE,
                                     m_state.format.green));
        EXPECT_TRUE(compareParameter(GL_RENDERBUFFER_BLUE_SIZE,
                                     m_state.format.blue));
        EXPECT_TRUE(compareParameter(GL_RENDERBUFFER_ALPHA_SIZE,
                                     m_state.format.alpha));
        EXPECT_TRUE(compareParameter(GL_RENDERBUFFER_DEPTH_SIZE,
                                     m_state.format.depth));
        EXPECT_TRUE(compareParameter(GL_RENDERBUFFER_STENCIL_SIZE,
                                     m_state.format.stencil));
    }

    void stateChange() override {
        gl->glGenRenderbuffers(1, &m_renderbuffer_name);
        gl->glBindRenderbuffer(GL_RENDERBUFFER, m_renderbuffer_name);

        m_state_changer();
    }

    void setStateChanger(std::function<void()> changer) {
        m_state_changer = changer;
    }

protected:
    testing::AssertionResult compareParameter(GLenum name, GLint expected) {
        GLint actual;
        gl->glGetRenderbufferParameteriv(GL_RENDERBUFFER, name, &actual);
        return compareValue<GLint>(
                expected, actual,
                "GL Renderbuffer object mismatch for param " +
                        describeGlEnum(name) + ":");
    }

    GLuint m_renderbuffer_name = 0;
    GlRenderbufferState m_state = kGLES2DefaultRenderbufferState;
    std::function<void()> m_state_changer = [] {};
};

TEST_F(SnapshotGlRenderbufferTest, CreateAndBind) {
    const char* kAngleName = "ANGLE";
    const char* kSwiftshaderName = "SwiftShader";
    const char* kRendererString
            = reinterpret_cast<const char*>(gl->glGetString(GL_RENDERER));
    if (strncmp(kRendererString, kAngleName, strlen(kAngleName))
            && strncmp(kRendererString, kSwiftshaderName,
                    strlen(kSwiftshaderName))) {
        GTEST_SKIP() << "b/247873185 SwANGLE has wrong default format.";
    }
    doCheckedSnapshot();
}

class SnapshotGlRenderbufferFormatTest
    : public SnapshotGlRenderbufferTest,
      public ::testing::WithParamInterface<GlRenderbufferState> {};

TEST_P(SnapshotGlRenderbufferFormatTest, SetFormat) {
    setStateChanger([this] {
        GLint maxSize;
        gl->glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &maxSize);
        m_state = GetParam();
        if (maxSize < m_state.width || maxSize < m_state.height) {
            fprintf(stderr,
                    "test dimensions exceed max renderbuffer size %d; "
                    "using max size instead\n",
                    maxSize);
            m_state.width = maxSize;
            m_state.height = maxSize;
        }
        gl->glRenderbufferStorage(GL_RENDERBUFFER, m_state.format.name,
                                  m_state.width, m_state.height);

        // The actual number of bits used for each format doesn't necessarily
        // match how they are defined.
        GLint fieldSize;
        gl->glGetRenderbufferParameteriv(GL_RENDERBUFFER,
                                         GL_RENDERBUFFER_RED_SIZE, &fieldSize);
        if (fieldSize != m_state.format.red) {
            fprintf(stderr,
                    "format 0x%x internal RED uses %d bits instead of %d\n",
                    m_state.format.name, fieldSize, m_state.format.red);
            m_state.format.red = fieldSize;
        }
        gl->glGetRenderbufferParameteriv(
                GL_RENDERBUFFER, GL_RENDERBUFFER_GREEN_SIZE, &fieldSize);
        if (fieldSize != m_state.format.green) {
            fprintf(stderr,
                    "format 0x%x internal GREEN uses %d bits instead of %d\n",
                    m_state.format.name, fieldSize, m_state.format.green);
            m_state.format.green = fieldSize;
        }
        gl->glGetRenderbufferParameteriv(GL_RENDERBUFFER,
                                         GL_RENDERBUFFER_BLUE_SIZE, &fieldSize);
        if (fieldSize != m_state.format.blue) {
            fprintf(stderr,
                    "format 0x%x internal BLUE uses %d bits instead of %d\n",
                    m_state.format.name, fieldSize, m_state.format.blue);
            m_state.format.blue = fieldSize;
        }
        gl->glGetRenderbufferParameteriv(
                GL_RENDERBUFFER, GL_RENDERBUFFER_ALPHA_SIZE, &fieldSize);
        if (fieldSize != m_state.format.alpha) {
            fprintf(stderr,
                    "format 0x%x internal ALPHA uses %d bits instead of %d\n",
                    m_state.format.name, fieldSize, m_state.format.alpha);
            m_state.format.alpha = fieldSize;
        }
        gl->glGetRenderbufferParameteriv(
                GL_RENDERBUFFER, GL_RENDERBUFFER_DEPTH_SIZE, &fieldSize);
        if (fieldSize != m_state.format.depth) {
            fprintf(stderr,
                    "format 0x%x internal DEPTH uses %d bits instead of %d\n",
                    m_state.format.name, fieldSize, m_state.format.depth);
            m_state.format.depth = fieldSize;
        }
        gl->glGetRenderbufferParameteriv(
                GL_RENDERBUFFER, GL_RENDERBUFFER_STENCIL_SIZE, &fieldSize);
        if (fieldSize != m_state.format.stencil) {
            fprintf(stderr,
                    "format 0x%x internal STENCIL uses %d bits instead of %d\n",
                    m_state.format.name, fieldSize, m_state.format.stencil);
            m_state.format.stencil = fieldSize;
        }
        EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
    });
    doCheckedSnapshot();
}

INSTANTIATE_TEST_SUITE_P(GLES2SnapshotRenderbuffers,
                         SnapshotGlRenderbufferFormatTest,
                         ::testing::ValuesIn(kGLES2TestRenderbufferStates));

}  // namespace
}  // namespace gl
}  // namespace gfxstream