910e62b5创建于 1月15日历史提交
// Copyright 2014 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 <stdint.h>

#include <string_view>

#include "base/compiler_specific.h"
#include "base/containers/contains.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/tests/gl_manager.h"
#include "gpu/command_buffer/tests/gl_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace gpu {

namespace {

// Blits texture bound to active texture unit to currently bound framebuffer.
// Viewport must be set by caller.
void BlitTexture() {
  const GLuint kVertexPositionAttrib = 0;
  const GLfloat kQuadVertices[] = {-1.0f, -1.0f, 1.0f,  -1.0f,
                                   1.0f,  1.0f,  -1.0f, 1.0f};
  GLuint buffer_id = 0;
  glGenBuffers(1, &buffer_id);
  glBindBuffer(GL_ARRAY_BUFFER, buffer_id);
  glBufferData(GL_ARRAY_BUFFER, sizeof(kQuadVertices), kQuadVertices,
               GL_STATIC_DRAW);

  glEnableVertexAttribArray(kVertexPositionAttrib);
  glVertexAttribPointer(kVertexPositionAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0);

  GLuint shader_program = glCreateProgram();

  static const char* kBlitTextureVertexShader =
      "precision mediump float;\n"
      "attribute vec2 a_position;\n"
      "varying mediump vec2 v_uv;\n"
      "void main(void) {\n"
      "  gl_Position = vec4(a_position, 0, 1);\n"
      "  v_uv = 0.5 * (a_position + vec2(1, 1));\n"
      "}\n";
  GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
  glShaderSource(vertex_shader, 1, &kBlitTextureVertexShader, 0);
  glCompileShader(vertex_shader);
  glAttachShader(shader_program, vertex_shader);

  static const char* kBlitTextureFragmentShader =
      "uniform sampler2D u_sampler;\n"
      "varying mediump vec2 v_uv;\n"
      "void main(void) {\n"
      "  gl_FragColor = texture2D(u_sampler, v_uv);\n"
      "}\n";
  GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
  glShaderSource(fragment_shader, 1, &kBlitTextureFragmentShader, 0);
  glCompileShader(fragment_shader);
  glAttachShader(shader_program, fragment_shader);

  glBindAttribLocation(shader_program, kVertexPositionAttrib, "a_position");

  glLinkProgram(shader_program);
  glUseProgram(shader_program);

  GLuint sampler_handle = glGetUniformLocation(shader_program, "u_sampler");
  glUniform1i(sampler_handle, 0);

  glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
  ASSERT_TRUE(glGetError() == GL_NONE);
}

}  // namespace

class TextureStorageTest : public testing::Test {
 protected:
  static const GLsizei kResolution = 64;
  void SetUp() override {
    GLManager::Options options;
    options.size = gfx::Size(kResolution, kResolution);
    gl_.Initialize(options);
    gl_.MakeCurrent();

    glGenTextures(1, &tex_);
    glBindTexture(GL_TEXTURE_2D, tex_);

    glGenFramebuffers(1, &fbo_);
    glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                           tex_, 0);

    std::string_view extensions =
        reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
    ext_texture_storage_available_ =
        base::Contains(extensions, "GL_EXT_texture_storage");
    oes_required_internal_format_available_ =
        base::Contains(extensions, "GL_OES_required_internalformat");
  }

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

  GLManager gl_;
  GLuint tex_ = 0;
  GLuint fbo_ = 0;
  bool ext_texture_storage_available_ = false;
  bool oes_required_internal_format_available_ = false;
};

TEST_F(TextureStorageTest, CorrectPixels) {
  if (!ext_texture_storage_available_)
    return;

  glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8_OES, 2, 2);

  // Mesa drivers crash without rebinding to FBO. It's why
  // DISABLE_TEXTURE_STORAGE workaround is introduced. crbug.com/521904
  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                         tex_, 0);

  uint8_t source_pixels[16] = {1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4};
  glTexSubImage2D(GL_TEXTURE_2D,
                  0,
                  0, 0,
                  2, 2,
                  GL_RGBA, GL_UNSIGNED_BYTE,
                  source_pixels);
  EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, 2, 2, 0, source_pixels, nullptr));
}

TEST_F(TextureStorageTest, IsImmutable) {
  if (!ext_texture_storage_available_)
    return;

  glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8_OES, 4, 4);

  GLint param = 0;
  glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_IMMUTABLE_FORMAT_EXT, &param);
  EXPECT_TRUE(param);
}

TEST_F(TextureStorageTest, OneLevel) {
  if (!ext_texture_storage_available_)
    return;

  glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8_OES, 4, 4);

  uint8_t source_pixels[64] = {};

  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 4, 4,
                  GL_RGBA, GL_UNSIGNED_BYTE, source_pixels);
  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
  glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, 2, 2,
                  GL_RGBA, GL_UNSIGNED_BYTE, source_pixels);
  EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError());
}

TEST_F(TextureStorageTest, MultipleLevels) {
  if (!ext_texture_storage_available_)
    return;

  glTexStorage2DEXT(GL_TEXTURE_2D, 2, GL_RGBA8_OES, 2, 2);

  uint8_t source_pixels[16] = {};

  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 2,
                  GL_RGBA, GL_UNSIGNED_BYTE, source_pixels);
  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
  glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, 1, 1,
                  GL_RGBA, GL_UNSIGNED_BYTE, source_pixels);
  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
  glTexSubImage2D(GL_TEXTURE_2D, 2, 0, 0, 1, 1,
                  GL_RGBA, GL_UNSIGNED_BYTE, source_pixels);
  EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError());
}

TEST_F(TextureStorageTest, BadTarget) {
  if (!ext_texture_storage_available_)
    return;

  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
  glTexStorage2DEXT(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 1, GL_RGBA8_OES, 4, 4);
  EXPECT_EQ(static_cast<GLenum>(GL_INVALID_ENUM), glGetError());
}

TEST_F(TextureStorageTest, InvalidId) {
  if (!ext_texture_storage_available_)
    return;

  glDeleteTextures(1, &tex_);
  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
  glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8_OES, 4, 4);
  EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError());
}

TEST_F(TextureStorageTest, CannotRedefine) {
  if (!ext_texture_storage_available_)
    return;

  glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8_OES, 4, 4);

  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
  glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8_OES, 4, 4);
  EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError());

  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE,
               nullptr);
  EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError());
}

TEST_F(TextureStorageTest, InternalFormatBleedingToTexImage) {
  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
  // The context is ES2 context.
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8_OES, 4, 4, 0, GL_RGBA,
               GL_UNSIGNED_BYTE, nullptr);
  if (oes_required_internal_format_available_) {
    // GL_OES_required_internalformat allows sized texture formats to be passed
    // to glTexImage2D even in ES2.
    EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
  } else {
    EXPECT_NE(static_cast<GLenum>(GL_NO_ERROR), glGetError());
  }
}

TEST_F(TextureStorageTest, LuminanceEmulation) {
  if (!ext_texture_storage_available_)
    return;

  glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8_OES, 2, 2);
  // Mesa drivers crash without rebinding to FBO. It's why
  // DISABLE_TEXTURE_STORAGE workaround is introduced. crbug.com/521904
  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                         tex_, 0);
  ASSERT_TRUE(glGetError() == GL_NONE);

  GLuint luminance_tex = 0;
  glGenTextures(1, &luminance_tex);
  glBindTexture(GL_TEXTURE_2D, luminance_tex);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_LUMINANCE8_EXT, 2, 2);
  ASSERT_TRUE(glGetError() == GL_NONE);

  const uint8_t source_data[4] = {1, 1, 1, 1};
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 2, GL_LUMINANCE, GL_UNSIGNED_BYTE,
                  source_data);
  ASSERT_TRUE(glGetError() == GL_NONE);

  BlitTexture();

  const uint8_t swizzled_pixel[4] = {1, 1, 1, 255};
  EXPECT_TRUE(
      GLTestHelper::CheckPixels(0, 0, 2, 2, 0, swizzled_pixel, nullptr));
}

TEST_F(TextureStorageTest, AlphaEmulation) {
  if (!ext_texture_storage_available_)
    return;

  glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8_OES, 2, 2);
  // Mesa drivers crash without rebinding to FBO. It's why
  // DISABLE_TEXTURE_STORAGE workaround is introduced. crbug.com/521904
  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                         tex_, 0);
  ASSERT_TRUE(glGetError() == GL_NONE);

  GLuint alpha_tex = 0;
  glGenTextures(1, &alpha_tex);
  glBindTexture(GL_TEXTURE_2D, alpha_tex);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_ALPHA8_EXT, 2, 2);
  ASSERT_TRUE(glGetError() == GL_NONE);

  const uint8_t source_data[4] = {1, 1, 1, 1};
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 2, GL_ALPHA, GL_UNSIGNED_BYTE,
                  source_data);
  ASSERT_TRUE(glGetError() == GL_NONE);

  BlitTexture();

  const uint8_t swizzled_pixel[4] = {0, 0, 0, 1};
  EXPECT_TRUE(
      GLTestHelper::CheckPixels(0, 0, 2, 2, 0, swizzled_pixel, nullptr));
}

TEST_F(TextureStorageTest, LuminanceAlphaEmulation) {
  if (!ext_texture_storage_available_)
    return;

  glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8_OES, 2, 2);
  // Mesa drivers crash without rebinding to FBO. It's why
  // DISABLE_TEXTURE_STORAGE workaround is introduced. crbug.com/521904
  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                         tex_, 0);
  ASSERT_TRUE(glGetError() == GL_NONE);

  GLuint luminance_alpha_tex = 0;
  glGenTextures(1, &luminance_alpha_tex);
  glBindTexture(GL_TEXTURE_2D, luminance_alpha_tex);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_LUMINANCE8_ALPHA8_EXT, 2, 2);
  ASSERT_TRUE(glGetError() == GL_NONE);

  const uint8_t source_data[8] = {1, 2, 1, 2, 1, 2, 1, 2};
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 2, GL_LUMINANCE_ALPHA,
                  GL_UNSIGNED_BYTE, source_data);
  ASSERT_TRUE(glGetError() == GL_NONE);

  BlitTexture();

  const uint8_t swizzled_pixel[4] = {1, 1, 1, 2};
  EXPECT_TRUE(
      GLTestHelper::CheckPixels(0, 0, 2, 2, 0, swizzled_pixel, nullptr));
}

}  // namespace gpu