910e62b5创建于 1月15日历史提交
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.


#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <GLES3/gl3.h>
#include <stdint.h>

#include <array>

#include "gpu/command_buffer/tests/gl_manager.h"
#include "gpu/command_buffer/tests/gl_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace gpu {

// A collection of tests that exercise the GL_EXT_window_rectangles extension.
class GLEXTWindowRectanglesTest : public testing::Test {
 protected:
  void SetUp() override {
    GLManager::Options options;
    options.context_type = CONTEXT_TYPE_OPENGLES3;
    gl_.Initialize(options);
  }

  void TearDown() override { gl_.Destroy(); }

  bool IsApplicable() const {
    // Not applicable for devices not supporting OpenGLES3.
    if (!gl_.IsInitialized())
      return false;
    bool have_ext = GLTestHelper::HasExtension("GL_EXT_window_rectangles");
    return have_ext;
  }

  GLuint SetupFramebuffer() {
    GLuint fbo = 0;
    glGenFramebuffers(1, &fbo);
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    GLuint tex = 0;
    glGenTextures(1, &tex);
    glBindTexture(GL_TEXTURE_2D, tex);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 32, 32, 0, GL_RGBA,
                 GL_UNSIGNED_BYTE, nullptr);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                           tex, 0);
    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), status);
    EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
    return fbo;
  }

  void ClearTo(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
    glClearColor(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
  }

  GLManager gl_;
};

#define CHECK_PIXEL(x, y, r, g, b, a)                               \
  do {                                                              \
    uint8_t pixel[4];                                               \
    glReadPixels((x), (y), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); \
    ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());      \
    EXPECT_NEAR((r), pixel[0], 2);                                  \
    EXPECT_NEAR((g), pixel[1], 2);                                  \
    EXPECT_NEAR((b), pixel[2], 2);                                  \
    EXPECT_NEAR((a), pixel[3], 2);                                  \
  } while (0)

// TODO(crbug.com/40246425): Re-enable this test
TEST_F(GLEXTWindowRectanglesTest, DISABLED_Defaults) {
  if (!IsApplicable()) {
    return;
  }

  GLint max = -1;
  {
    glGetIntegerv(GL_MAX_WINDOW_RECTANGLES_EXT, &max);
    ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
    EXPECT_GE(max, 4);
  }

  {
    GLint mode = -1;
    glGetIntegerv(GL_WINDOW_RECTANGLE_MODE_EXT, &mode);
    ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
    EXPECT_EQ(GL_EXCLUSIVE_EXT, mode);
  }
  {
    GLint num = -1;
    glGetIntegerv(GL_NUM_WINDOW_RECTANGLES_EXT, &num);
    ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
    EXPECT_EQ(0, num);
  }

  for (int i = 0; i < max; ++i) {
    GLint rect[4] = {-1, -1, -1, -1};
    glGetIntegeri_v(GL_WINDOW_RECTANGLE_EXT, i, rect);
    ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
    EXPECT_EQ(0, rect[0]);
    EXPECT_EQ(0, rect[1]);
    EXPECT_EQ(0, rect[2]);
    EXPECT_EQ(0, rect[3]);
  }
}

TEST_F(GLEXTWindowRectanglesTest, Defaults64) {
  if (!IsApplicable()) {
    return;
  }

  int64_t max = -1;
  {
    glGetInteger64v(GL_MAX_WINDOW_RECTANGLES_EXT, &max);
    ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
    EXPECT_GE(max, 4);
  }

  {
    int64_t mode = -1;
    glGetInteger64v(GL_WINDOW_RECTANGLE_MODE_EXT, &mode);
    ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
    EXPECT_EQ(GL_EXCLUSIVE_EXT, mode);
  }
  {
    int64_t num = -1;
    glGetInteger64v(GL_NUM_WINDOW_RECTANGLES_EXT, &num);
    ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
    EXPECT_EQ(0, num);
  }

  for (int i = 0; i < max; ++i) {
    int64_t rect[4] = {-1, -1, -1, -1};
    glGetInteger64i_v(GL_WINDOW_RECTANGLE_EXT, i, rect);
    ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
    EXPECT_EQ(0, rect[0]);
    EXPECT_EQ(0, rect[1]);
    EXPECT_EQ(0, rect[2]);
    EXPECT_EQ(0, rect[3]);
  }
}

TEST_F(GLEXTWindowRectanglesTest, BasicState) {
  if (!IsApplicable()) {
    return;
  }

  std::array<GLint, 12> box_exp = {};
  for (int i = 0; i < 12; ++i) {
    box_exp[i] = i;
  }
  glWindowRectanglesEXT(GL_INCLUSIVE_EXT, 3, box_exp.data());
  ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());

  {
    GLint mode = -1;
    glGetIntegerv(GL_WINDOW_RECTANGLE_MODE_EXT, &mode);
    ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
    EXPECT_EQ(GL_INCLUSIVE_EXT, mode);
  }
  {
    GLint num = -1;
    glGetIntegerv(GL_NUM_WINDOW_RECTANGLES_EXT, &num);
    ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
    EXPECT_EQ(3, num);
  }
  {
    std::array<GLint, 12> box = {};
    for (int i = 0; i < 12; ++i) {
      box[i] = -1;
    }
    glGetIntegeri_v(GL_WINDOW_RECTANGLE_EXT, 0, &box[0]);
    glGetIntegeri_v(GL_WINDOW_RECTANGLE_EXT, 1, &box[4]);
    glGetIntegeri_v(GL_WINDOW_RECTANGLE_EXT, 2, &box[8]);
    ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
    for (int i = 0; i < 12; ++i) {
      EXPECT_EQ(box_exp[i], box[i]);
    }
  }
}

TEST_F(GLEXTWindowRectanglesTest, DefaultFramebuffer) {
  if (!IsApplicable()) {
    return;
  }

  glBindFramebuffer(GL_FRAMEBUFFER, 0);

  GLint box[4] = {0, 0, 1, 1};

  ClearTo(255, 0, 0, 255);
  // This says "DO NOT render inside 0,0", but
  // it should have no effect for the default framebuffer.
  glWindowRectanglesEXT(GL_EXCLUSIVE_EXT, 1, box);
  ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());

  ClearTo(0, 255, 0, 255);
  // Inside rectangle
  CHECK_PIXEL(0, 0, 0, 255, 0, 255);

  ClearTo(255, 0, 0, 255);
  // This says "ONLY render inside 0,0", but
  // it should have no effect for the default framebuffer.
  glWindowRectanglesEXT(GL_INCLUSIVE_EXT, 1, box);
  ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());

  ClearTo(0, 255, 0, 255);
  // Inside rectangle
  CHECK_PIXEL(0, 0, 0, 255, 0, 255);
}

TEST_F(GLEXTWindowRectanglesTest, OneRectangle) {
  if (!IsApplicable()) {
    return;
  }

  GLint box[4] = {
      8, 4, 1, 1,
  };
  SetupFramebuffer();

  ClearTo(255, 0, 0, 255);
  // "DO NOT render inside the pixel 8,4"
  glWindowRectanglesEXT(GL_EXCLUSIVE_EXT, 1, box);
  ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());

  ClearTo(0, 255, 0, 255);
  // Inside rectangle
  CHECK_PIXEL(8, 4, 255, 0, 0, 255);
  // Outside rectangle
  CHECK_PIXEL(4, 8, 0, 255, 0, 255);

  glWindowRectanglesEXT(GL_EXCLUSIVE_EXT, 0, nullptr);
  ClearTo(255, 0, 0, 255);
  // "ONLY render inside the pixel 8,4"
  glWindowRectanglesEXT(GL_INCLUSIVE_EXT, 1, box);
  ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());

  ClearTo(0, 255, 0, 255);
  // Inside rectangle
  CHECK_PIXEL(8, 4, 0, 255, 0, 255);
  // Outside rectangle
  CHECK_PIXEL(4, 8, 255, 0, 0, 255);
}

TEST_F(GLEXTWindowRectanglesTest, TwoRectangles) {
  if (!IsApplicable()) {
    return;
  }

  GLint box[8] = {
      8, 4, 1, 1, 2, 5, 1, 1,
  };
  SetupFramebuffer();

  ClearTo(255, 0, 0, 255);
  // "DO NOT render inside the pixels 8,4 and 2,5"
  glWindowRectanglesEXT(GL_EXCLUSIVE_EXT, 2, box);
  ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());

  ClearTo(0, 255, 0, 255);
  // Inside rectangles
  CHECK_PIXEL(8, 4, 255, 0, 0, 255);
  CHECK_PIXEL(2, 5, 255, 0, 0, 255);
  // Outside rectangles
  CHECK_PIXEL(4, 8, 0, 255, 0, 255);
  CHECK_PIXEL(5, 2, 0, 255, 0, 255);

  glWindowRectanglesEXT(GL_EXCLUSIVE_EXT, 0, nullptr);
  ClearTo(255, 0, 0, 255);
  // "ONLY render inside the pixels 8,4 and 4,8"
  glWindowRectanglesEXT(GL_INCLUSIVE_EXT, 2, box);
  ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());

  ClearTo(0, 255, 0, 255);
  // Inside rectangles
  CHECK_PIXEL(8, 4, 0, 255, 0, 255);
  CHECK_PIXEL(2, 5, 0, 255, 0, 255);
  // Outside rectangles
  CHECK_PIXEL(4, 8, 255, 0, 0, 255);
  CHECK_PIXEL(5, 2, 255, 0, 0, 255);
}

TEST_F(GLEXTWindowRectanglesTest, InclusiveEmptyBox) {
  if (!IsApplicable()) {
    return;
  }

  GLint box[4] = {
      0, 0, 0, 0,
  };
  SetupFramebuffer();

  ClearTo(255, 0, 0, 255);
  // "ONLY render inside the 0-by-0 box"
  glWindowRectanglesEXT(GL_INCLUSIVE_EXT, 1, box);
  ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());

  ClearTo(0, 255, 0, 255);
  // Outside rectangle
  CHECK_PIXEL(4, 8, 255, 0, 0, 255);
}

TEST_F(GLEXTWindowRectanglesTest, TextureClearStateClearAndRestore) {
  if (!IsApplicable()) {
    return;
  }

  GLint box[4] = {
      0, 0, 0, 0,
  };
  GLuint main_fbo = SetupFramebuffer();

  ClearTo(255, 0, 0, 255);
  // "ONLY render inside the 0-by-0 box"
  glWindowRectanglesEXT(GL_INCLUSIVE_EXT, 1, box);
  ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());

  GLuint test_fbo = 0;
  {
    glGenFramebuffers(1, &test_fbo);
    glBindFramebuffer(GL_FRAMEBUFFER, test_fbo);
    GLuint tex = 0;
    glGenTextures(1, &tex);
    glBindTexture(GL_TEXTURE_2D, tex);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 32, 32, 0, GL_RGBA,
                 GL_UNSIGNED_BYTE, nullptr);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                           tex, 0);
    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    ASSERT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), status);
    ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
  }

  glBindFramebuffer(GL_FRAMEBUFFER, test_fbo);
  ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
  // Check the texture is initialized
  {
    constexpr size_t PIXELS_SIZE = 4 * 32 * 32;
    std::array<uint8_t, PIXELS_SIZE> pixels;
    glReadPixels(0, 0, 32, 32, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
    ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
    for (size_t i = 0; i < PIXELS_SIZE; ++i) {
      EXPECT_NEAR(0, pixels[i], 2);
    }
  }

  glBindFramebuffer(GL_FRAMEBUFFER, main_fbo);
  ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
  // Make sure the window rectangle state is preserved
  ClearTo(0, 255, 0, 255);
  // So this clear doesn't do anything.
  CHECK_PIXEL(4, 8, 255, 0, 0, 255);
}

}  // namespace gpu