//
// Copyright 2018 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.
//
// MultiviewTest:
//   Implementation of helpers for multiview testing.
//

#ifndef ANGLE_TESTS_TESTUTILS_MULTIVIEWTEST_H_
#define ANGLE_TESTS_TESTUTILS_MULTIVIEWTEST_H_

#include "test_utils/ANGLETest.h"

namespace angle
{

// Creates a simple program that passes through two-dimensional vertices and renders green
// fragments.
GLuint CreateSimplePassthroughProgram(int numViews);

// Create a set of textures to use for multiview rendering. If multiviewLayout is
// GL_FRAMEBUFFER_MULTIVIEW_SIDE_BY_SIDE_ANGLE, then 2D textures are created. If multiviewLayout is
// GL_FRAMEBUFFER_MULTIVIEW_LAYERED_ANGLE, then 2D texture arrays are created. Texture ids should be
// created beforehand. If depthTexture or stencilTexture is 0, it will not be initialized.
// If samples is 0, then non-multisampled textures are created. Otherwise multisampled textures are
// created with the requested sample count.
void CreateMultiviewBackingTextures(GLenum multiviewLayout,
                                    int samples,
                                    int viewWidth,
                                    int height,
                                    int numLayers,
                                    std::vector<GLuint> colorTextures,
                                    GLuint depthTexture,
                                    GLuint depthStencilTexture);
void CreateMultiviewBackingTextures(GLenum multiviewLayout,
                                    int samples,
                                    int viewWidth,
                                    int height,
                                    int numLayers,
                                    GLuint colorTexture,
                                    GLuint depthTexture,
                                    GLuint depthStencilTexture);

// Attach multiview textures to the framebuffer denoted by target. If there are multiple color
// textures they get attached to different color attachments starting from 0. If multiviewLayout is
// GL_FRAMEBUFFER_MULTIVIEW_SIDE_BY_SIDE_ANGLE, then the viewport offsets are set so that the views
// are tightly packed inside the attachments.
void AttachMultiviewTextures(GLenum target,
                             GLenum multiviewLayout,
                             int viewWidth,
                             int numViews,
                             int baseViewIndex,
                             std::vector<GLuint> colorTextures,
                             GLuint depthTexture,
                             GLuint depthStencilTexture);
void AttachMultiviewTextures(GLenum target,
                             GLenum multiviewLayout,
                             int viewWidth,
                             int numViews,
                             int baseViewIndex,
                             GLuint colorTexture,
                             GLuint depthTexture,
                             GLuint depthStencilTexture);

struct MultiviewImplementationParams : public PlatformParameters
{
    MultiviewImplementationParams(GLint majorVersion,
                                  GLint minorVersion,
                                  bool forceUseGeometryShaderOnD3D,
                                  const EGLPlatformParameters &eglPlatformParameters)
        : PlatformParameters(majorVersion, minorVersion, eglPlatformParameters),
          mForceUseGeometryShaderOnD3D(forceUseGeometryShaderOnD3D)
    {}
    bool mForceUseGeometryShaderOnD3D;
};
std::ostream &operator<<(std::ostream &os, const MultiviewImplementationParams &params);

MultiviewImplementationParams VertexShaderOpenGL(GLint majorVersion, GLint minorVersion);
MultiviewImplementationParams VertexShaderD3D11(GLint majorVersion, GLint minorVersion);
MultiviewImplementationParams GeomShaderD3D11(GLint majorVersion, GLint minorVersion);

class MultiviewTestBase : public ANGLETestBase
{
  protected:
    MultiviewTestBase(const PlatformParameters &params) : ANGLETestBase(params)
    {
        setWindowWidth(128);
        setWindowHeight(128);
        setWebGLCompatibilityEnabled(true);
    }
    virtual ~MultiviewTestBase() {}

    void MultiviewTestBaseSetUp()
    {
        ANGLETestBase::ANGLETestSetUp();

        glRequestExtensionANGLE = reinterpret_cast<PFNGLREQUESTEXTENSIONANGLEPROC>(
            eglGetProcAddress("glRequestExtensionANGLE"));
    }

    void MultiviewTestBaseTearDown() { ANGLETestBase::ANGLETestTearDown(); }

    // Requests the ANGLE_multiview extension and returns true if the operation succeeds.
    bool requestMultiviewExtension(bool requireMultiviewMultisample)
    {
        if (extensionRequestable("GL_ANGLE_multiview"))
        {
            glRequestExtensionANGLE("GL_ANGLE_multiview");
        }

        if (!extensionEnabled("GL_ANGLE_multiview"))
        {
            std::cout << "Test skipped due to missing GL_ANGLE_multiview." << std::endl;
            return false;
        }

        if (requireMultiviewMultisample)
        {
            if (extensionRequestable("GL_OES_texture_storage_multisample_2d_array"))
            {
                glRequestExtensionANGLE("GL_OES_texture_storage_multisample_2d_array");
            }
            if (extensionRequestable("GL_ANGLE_multiview_multisample"))
            {
                glRequestExtensionANGLE("GL_ANGLE_multiview_multisample");
            }

            if (!extensionEnabled("GL_ANGLE_multiview_multisample"))
            {
                std::cout << "Test skipped due to missing GL_ANGLE_multiview_multisample."
                          << std::endl;
                return false;
            }
        }
        return true;
    }

    bool requestMultiviewExtension() { return requestMultiviewExtension(false); }

    PFNGLREQUESTEXTENSIONANGLEPROC glRequestExtensionANGLE = nullptr;
};

// Base class for multiview tests that don't need specific helper functions.
class MultiviewTest : public MultiviewTestBase,
                      public ::testing::TestWithParam<MultiviewImplementationParams>
{
  protected:
    MultiviewTest() : MultiviewTestBase(GetParam()) {}
    void SetUp() override { MultiviewTestBase::MultiviewTestBaseSetUp(); }
    void TearDown() override { MultiviewTestBase::MultiviewTestBaseTearDown(); }

    void overrideWorkaroundsD3D(WorkaroundsD3D *workarounds) final;
};

}  // namespace angle

#endif  // ANGLE_TESTS_TESTUTILS_MULTIVIEWTEST_H_
