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


#include "gpu/command_buffer/service/framebuffer_manager.h"

#include <stddef.h>
#include <stdint.h>

#include <array>
#include <memory>

#include "base/memory/raw_ptr.h"
#include "gpu/command_buffer/client/client_test_helper.h"
#include "gpu/command_buffer/service/error_state_mock.h"
#include "gpu/command_buffer/service/feature_info.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder_mock.h"
#include "gpu/command_buffer/service/gpu_service_test.h"
#include "gpu/command_buffer/service/gpu_tracer.h"
#include "gpu/command_buffer/service/renderbuffer_manager.h"
#include "gpu/command_buffer/service/test_helper.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "gpu/config/gpu_preferences.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gl/gl_mock.h"

using ::testing::_;
using ::testing::Return;

namespace gpu {
namespace gles2 {
namespace {

const GLint kMaxTextureSize = 64;
const GLint kMaxCubemapSize = 64;
const GLint kMaxRectangleTextureSize = 64;
const GLint kMax3DTextureSize = 256;
const GLint kMaxArrayTextureLayers = 256;
const GLint kMaxRenderbufferSize = 64;
const GLint kMaxSamples = 4;
const uint32_t kMaxDrawBuffers = 16;
const uint32_t kMaxColorAttachments = 16;
const bool kUseDefaultTextures = false;

}  // namespace

class FramebufferManagerTest : public GpuServiceTest {
 public:
  FramebufferManagerTest()
      : manager_(1, 1, nullptr), feature_info_(new FeatureInfo()) {
    texture_manager_ = std::make_unique<TextureManager>(
        nullptr, feature_info_.get(), kMaxTextureSize, kMaxCubemapSize,
        kMaxRectangleTextureSize, kMax3DTextureSize, kMaxArrayTextureLayers,
        kUseDefaultTextures, nullptr);
    renderbuffer_manager_ = std::make_unique<RenderbufferManager>(
        nullptr, kMaxRenderbufferSize, kMaxSamples, feature_info_.get());
  }
  ~FramebufferManagerTest() override {
    manager_.Destroy(false);
    texture_manager_->MarkContextLost();
    texture_manager_->Destroy();
    renderbuffer_manager_->Destroy(false);
  }

 protected:
  FramebufferManager manager_;
  scoped_refptr<FeatureInfo> feature_info_;
  std::unique_ptr<TextureManager> texture_manager_;
  std::unique_ptr<RenderbufferManager> renderbuffer_manager_;
};

TEST_F(FramebufferManagerTest, Basic) {
  const GLuint kClient1Id = 1;
  const GLuint kService1Id = 11;
  const GLuint kClient2Id = 2;
  // Check we can create framebuffer.
  manager_.CreateFramebuffer(kClient1Id, kService1Id);
  // Check framebuffer got created.
  Framebuffer* framebuffer1 = manager_.GetFramebuffer(kClient1Id);
  ASSERT_TRUE(framebuffer1 != nullptr);
  EXPECT_FALSE(framebuffer1->IsDeleted());
  EXPECT_EQ(kService1Id, framebuffer1->service_id());
  GLuint client_id = 0;
  EXPECT_TRUE(manager_.GetClientId(framebuffer1->service_id(), &client_id));
  EXPECT_EQ(kClient1Id, client_id);
  // Check we get nothing for a non-existent framebuffer.
  EXPECT_TRUE(manager_.GetFramebuffer(kClient2Id) == nullptr);
  // Check trying to a remove non-existent framebuffers does not crash.
  manager_.RemoveFramebuffer(kClient2Id);
  // Check framebuffer gets deleted when last reference is released.
  EXPECT_CALL(*gl_, DeleteFramebuffersEXT(1, ::testing::Pointee(kService1Id)))
      .Times(1)
      .RetiresOnSaturation();
  // Check we can't get the framebuffer after we remove it.
  manager_.RemoveFramebuffer(kClient1Id);
  EXPECT_TRUE(manager_.GetFramebuffer(kClient1Id) == nullptr);
}

TEST_F(FramebufferManagerTest, Destroy) {
  const GLuint kClient1Id = 1;
  const GLuint kService1Id = 11;
  // Check we can create framebuffer.
  manager_.CreateFramebuffer(kClient1Id, kService1Id);
  // Check framebuffer got created.
  Framebuffer* framebuffer1 = manager_.GetFramebuffer(kClient1Id);
  ASSERT_TRUE(framebuffer1 != nullptr);
  EXPECT_CALL(*gl_, DeleteFramebuffersEXT(1, ::testing::Pointee(kService1Id)))
      .Times(1)
      .RetiresOnSaturation();
  manager_.Destroy(true);
  // Check the resources were released.
  framebuffer1 = manager_.GetFramebuffer(kClient1Id);
  ASSERT_TRUE(framebuffer1 == nullptr);
}

class FramebufferInfoTestBase : public GpuServiceTest {
 public:
  static const GLuint kClient1Id = 1;
  static const GLuint kService1Id = 11;

  explicit FramebufferInfoTestBase(ContextType context_type)
      : context_type_(context_type),
        manager_(kMaxDrawBuffers,
                 kMaxColorAttachments,
                 &framebuffer_completeness_cache_),
        feature_info_(new FeatureInfo()) {
    texture_manager_ = std::make_unique<TextureManager>(
        nullptr, feature_info_.get(), kMaxTextureSize, kMaxCubemapSize,
        kMaxRectangleTextureSize, kMax3DTextureSize, kMaxArrayTextureLayers,
        kUseDefaultTextures, nullptr);
    renderbuffer_manager_ = std::make_unique<RenderbufferManager>(
        nullptr, kMaxRenderbufferSize, kMaxSamples, feature_info_.get());
  }
  ~FramebufferInfoTestBase() override {
    // Drop unowned reference before `manager_` destroys it.
    framebuffer_ = nullptr;
    manager_.Destroy(false);
    texture_manager_->MarkContextLost();
    texture_manager_->Destroy();
    renderbuffer_manager_->Destroy(false);
  }

 protected:
  void SetUp() override {
    bool is_es3 = false;
    if (context_type_ == CONTEXT_TYPE_WEBGL2 ||
        context_type_ == CONTEXT_TYPE_OPENGLES3)
      is_es3 = true;
    InitializeContext(is_es3 ? "OpenGL ES 3.0" : "OpenGL ES 2.0",
                      "GL_EXT_framebuffer_object");
  }

  void InitializeContext(const char* gl_version, const char* extensions) {
    GpuServiceTest::SetUpWithGLVersion(gl_version, extensions);
    TestHelper::SetupFeatureInfoInitExpectationsWithGLVersion(gl_.get(),
        extensions, "", gl_version, context_type_);
    feature_info_->InitializeForTesting(context_type_);
    decoder_ = std::make_unique<MockGLES2Decoder>(
        &client_, &command_buffer_service_, &outputter_);
    manager_.CreateFramebuffer(kClient1Id, kService1Id);
    error_state_ =
        std::make_unique<::testing::StrictMock<gles2::MockErrorState>>();
    framebuffer_ = manager_.GetFramebuffer(kClient1Id);
    ASSERT_TRUE(framebuffer_ != nullptr);
  }

  ContextType context_type_;
  FramebufferCompletenessCache framebuffer_completeness_cache_;
  FramebufferManager manager_;
  raw_ptr<Framebuffer> framebuffer_;
  scoped_refptr<FeatureInfo> feature_info_;
  std::unique_ptr<TextureManager> texture_manager_;
  std::unique_ptr<RenderbufferManager> renderbuffer_manager_;
  std::unique_ptr<MockErrorState> error_state_;
  FakeCommandBufferServiceBase command_buffer_service_;
  FakeDecoderClient client_;
  TraceOutputter outputter_;
  std::unique_ptr<MockGLES2Decoder> decoder_;
};

class FramebufferInfoTest : public FramebufferInfoTestBase {
 public:
  FramebufferInfoTest() : FramebufferInfoTestBase(CONTEXT_TYPE_OPENGLES2) {}
};

class FramebufferInfoES3Test : public FramebufferInfoTestBase {
 public:
  FramebufferInfoES3Test() : FramebufferInfoTestBase(CONTEXT_TYPE_WEBGL2) {}
};

// GCC requires these declarations, but MSVC requires they not be present
#ifndef COMPILER_MSVC
const GLuint FramebufferInfoTestBase::kClient1Id;
const GLuint FramebufferInfoTestBase::kService1Id;
#endif

TEST_F(FramebufferInfoTest, Basic) {
  EXPECT_EQ(kService1Id, framebuffer_->service_id());
  EXPECT_FALSE(framebuffer_->IsDeleted());
  EXPECT_TRUE(nullptr == framebuffer_->GetAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_TRUE(nullptr == framebuffer_->GetAttachment(GL_DEPTH_ATTACHMENT));
  EXPECT_TRUE(nullptr == framebuffer_->GetAttachment(GL_STENCIL_ATTACHMENT));
  EXPECT_FALSE(framebuffer_->HasDepthAttachment());
  EXPECT_FALSE(framebuffer_->HasStencilAttachment());
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));
  EXPECT_TRUE(framebuffer_->IsCleared());
  EXPECT_EQ(static_cast<GLenum>(0),
            framebuffer_->GetReadBufferInternalFormat());
  EXPECT_FALSE(manager_.IsComplete(framebuffer_));
}

TEST_F(FramebufferInfoTest, AttachRenderbuffer) {
  const GLuint kRenderbufferClient1Id = 33;
  const GLuint kRenderbufferService1Id = 333;
  const GLuint kRenderbufferClient2Id = 34;
  const GLuint kRenderbufferService2Id = 334;
  const GLuint kRenderbufferClient3Id = 35;
  const GLuint kRenderbufferService3Id = 335;
  const GLuint kRenderbufferClient4Id = 36;
  const GLuint kRenderbufferService4Id = 336;
  const GLuint kRenderbufferClient5Id = 37;
  const GLuint kRenderbufferService5Id = 337;
  const GLsizei kWidth1 = 16;
  const GLsizei kHeight1 = 32;
  const GLenum kFormat1 = GL_RGBA4;
  const GLenum kBadFormat1 = GL_DEPTH_COMPONENT16;
  const GLsizei kSamples1 = 0;
  const GLsizei kWidth2 = 16;
  const GLsizei kHeight2 = 32;
  const GLenum kFormat2 = GL_DEPTH_COMPONENT16;
  const GLsizei kSamples2 = 0;
  const GLsizei kWidth3 = 16;
  const GLsizei kHeight3 = 32;
  const GLenum kFormat3 = GL_STENCIL_INDEX8;
  const GLsizei kSamples3 = 0;
  const GLsizei kWidth4 = 16;
  const GLsizei kHeight4 = 32;
  const GLenum kFormat4 = GL_DEPTH24_STENCIL8;
  const GLsizei kSamples4 = 0;
  const GLsizei kWidth5 = 16;
  const GLsizei kHeight5 = 32;
  const GLenum kFormat5 = GL_DEPTH24_STENCIL8;
  const GLsizei kSamples5 = 0;
  const GLsizei kDifferentSamples5 = 1;

  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_DEPTH_ATTACHMENT));
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_STENCIL_ATTACHMENT));

  renderbuffer_manager_->CreateRenderbuffer(
      kRenderbufferClient1Id, kRenderbufferService1Id);
  Renderbuffer* renderbuffer1 =
      renderbuffer_manager_->GetRenderbuffer(kRenderbufferClient1Id);
  ASSERT_TRUE(renderbuffer1 != nullptr);

  // Check adding one attachment.
  framebuffer_->AttachRenderbuffer(GL_COLOR_ATTACHMENT0, renderbuffer1);
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_DEPTH_ATTACHMENT));
  EXPECT_EQ(static_cast<GLenum>(GL_RGBA4),
            framebuffer_->GetReadBufferInternalFormat());
  EXPECT_FALSE(framebuffer_->HasDepthAttachment());
  EXPECT_FALSE(framebuffer_->HasStencilAttachment());
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));
  EXPECT_TRUE(framebuffer_->IsCleared());

  // Try a format that's not good for COLOR_ATTACHMENT0.
  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer1, kSamples1,
                                              kBadFormat1, kWidth1, kHeight1);
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));

  // Try a good format.
  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer1, kSamples1,
                                              kFormat1, kWidth1, kHeight1);
  EXPECT_EQ(static_cast<GLenum>(kFormat1),
            framebuffer_->GetReadBufferInternalFormat());
  EXPECT_FALSE(framebuffer_->HasDepthAttachment());
  EXPECT_FALSE(framebuffer_->HasStencilAttachment());
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));
  EXPECT_FALSE(framebuffer_->IsCleared());

  // Check adding another.
  renderbuffer_manager_->CreateRenderbuffer(
      kRenderbufferClient2Id, kRenderbufferService2Id);
  Renderbuffer* renderbuffer2 =
      renderbuffer_manager_->GetRenderbuffer(kRenderbufferClient2Id);
  ASSERT_TRUE(renderbuffer2 != nullptr);
  framebuffer_->AttachRenderbuffer(GL_DEPTH_ATTACHMENT, renderbuffer2);
  EXPECT_TRUE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_DEPTH_ATTACHMENT));
  EXPECT_EQ(static_cast<GLenum>(kFormat1),
            framebuffer_->GetReadBufferInternalFormat());
  EXPECT_TRUE(framebuffer_->HasDepthAttachment());
  EXPECT_FALSE(framebuffer_->HasStencilAttachment());
  // The attachment has a size of 0,0 so depending on the order of the map
  // of attachments it could either get INCOMPLETE_ATTACHMENT because it's 0,0
  // or INCOMPLETE_DIMENSIONS because it's not the same size as the other
  // attachment.
  GLenum status = framebuffer_->IsPossiblyComplete(feature_info_.get());
  EXPECT_TRUE(status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT ||
              status == GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
  EXPECT_FALSE(framebuffer_->IsCleared());

  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer2, kSamples2,
                                              kFormat2, kWidth2, kHeight2);
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));
  EXPECT_FALSE(framebuffer_->IsCleared());
  EXPECT_TRUE(framebuffer_->HasUnclearedAttachment(GL_DEPTH_ATTACHMENT));

  // Check marking them as cleared.
  manager_.MarkAttachmentsAsCleared(
      framebuffer_, renderbuffer_manager_.get(), texture_manager_.get());
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_DEPTH_ATTACHMENT));
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));
  EXPECT_TRUE(framebuffer_->IsCleared());

  // Add another one to stencil attachment point.
  renderbuffer_manager_->CreateRenderbuffer(
      kRenderbufferClient3Id, kRenderbufferService3Id);
  Renderbuffer* renderbuffer3 =
      renderbuffer_manager_->GetRenderbuffer(kRenderbufferClient3Id);
  ASSERT_TRUE(renderbuffer3 != nullptr);
  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer3, kSamples3,
                                              kFormat3, kWidth3, kHeight3);
  renderbuffer_manager_->SetCleared(renderbuffer3, true);

  framebuffer_->AttachRenderbuffer(GL_STENCIL_ATTACHMENT, renderbuffer3);
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_STENCIL_ATTACHMENT));
  EXPECT_EQ(static_cast<GLenum>(kFormat1),
            framebuffer_->GetReadBufferInternalFormat());
  EXPECT_TRUE(framebuffer_->HasDepthAttachment());
  EXPECT_TRUE(framebuffer_->HasStencilAttachment());
  // Binding different images to depth and stencil attachment points should
  // return FRAMEBUFFER_UNSUPPORTED.
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_UNSUPPORTED),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));

  // Bind a renderbufer in format DEPTH_STENCIL to depth and stencil
  // attachment points.
  renderbuffer_manager_->CreateRenderbuffer(
      kRenderbufferClient4Id, kRenderbufferService4Id);
  Renderbuffer* renderbuffer4 =
      renderbuffer_manager_->GetRenderbuffer(kRenderbufferClient4Id);
  ASSERT_TRUE(renderbuffer4 != nullptr);
  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer4, kSamples4,
                                              kFormat4, kWidth4, kHeight4);
  renderbuffer_manager_->SetCleared(renderbuffer4, true);

  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_DEPTH_ATTACHMENT));
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_STENCIL_ATTACHMENT));
  framebuffer_->AttachRenderbuffer(GL_DEPTH_ATTACHMENT, renderbuffer4);
  framebuffer_->AttachRenderbuffer(GL_STENCIL_ATTACHMENT, renderbuffer4);
  EXPECT_EQ(static_cast<GLenum>(kFormat1),
            framebuffer_->GetReadBufferInternalFormat());
  EXPECT_TRUE(framebuffer_->HasDepthAttachment());
  EXPECT_TRUE(framebuffer_->HasStencilAttachment());
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));
  EXPECT_TRUE(framebuffer_->IsCleared());

  // Check marking the renderbuffer as uncleared.
  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer1, kSamples1,
                                              kFormat1, kWidth1, kHeight1);
  EXPECT_EQ(static_cast<GLenum>(kFormat1),
            framebuffer_->GetReadBufferInternalFormat());
  EXPECT_TRUE(framebuffer_->HasDepthAttachment());
  EXPECT_TRUE(framebuffer_->HasStencilAttachment());
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));
  EXPECT_FALSE(framebuffer_->IsCleared());

  const Framebuffer::Attachment* attachment =
      framebuffer_->GetAttachment(GL_COLOR_ATTACHMENT0);
  ASSERT_TRUE(attachment != nullptr);
  EXPECT_EQ(kWidth1, attachment->width());
  EXPECT_EQ(kHeight1, attachment->height());
  EXPECT_EQ(kSamples1, attachment->samples());
  EXPECT_EQ(kFormat1, attachment->internal_format());
  EXPECT_FALSE(attachment->cleared());

  EXPECT_TRUE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));

  // Clear it.
  manager_.MarkAttachmentsAsCleared(
      framebuffer_, renderbuffer_manager_.get(), texture_manager_.get());
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_TRUE(framebuffer_->IsCleared());

  // Check replacing one attachment when both depth and stencil attachments
  // are present.
  renderbuffer_manager_->CreateRenderbuffer(
      kRenderbufferClient5Id, kRenderbufferService5Id);
  Renderbuffer* renderbuffer5 =
      renderbuffer_manager_->GetRenderbuffer(kRenderbufferClient5Id);
  ASSERT_TRUE(renderbuffer5 != nullptr);
  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer5, kSamples5,
                                              kFormat5, kWidth5, kHeight5);

  framebuffer_->AttachRenderbuffer(GL_STENCIL_ATTACHMENT, renderbuffer5);
  EXPECT_TRUE(framebuffer_->HasUnclearedAttachment(GL_STENCIL_ATTACHMENT));
  EXPECT_FALSE(framebuffer_->IsCleared());

  attachment = framebuffer_->GetAttachment(GL_STENCIL_ATTACHMENT);
  ASSERT_TRUE(attachment != nullptr);
  EXPECT_EQ(kWidth5, attachment->width());
  EXPECT_EQ(kHeight5, attachment->height());
  EXPECT_EQ(kSamples5, attachment->samples());
  EXPECT_EQ(kFormat5, attachment->internal_format());
  EXPECT_FALSE(attachment->cleared());
  // Binding different images to depth and stencil attachment points should
  // return FRAMEBUFFER_UNSUPPORTED.
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_UNSUPPORTED),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));

  // Check replacing both depth and stencil attachments.
  framebuffer_->AttachRenderbuffer(GL_DEPTH_ATTACHMENT, renderbuffer5);
  EXPECT_TRUE(framebuffer_->HasUnclearedAttachment(GL_DEPTH_ATTACHMENT));
  EXPECT_TRUE(framebuffer_->HasUnclearedAttachment(GL_STENCIL_ATTACHMENT));
  EXPECT_FALSE(framebuffer_->IsCleared());

  attachment = framebuffer_->GetAttachment(GL_DEPTH_ATTACHMENT);
  EXPECT_EQ(kWidth5, attachment->width());
  EXPECT_EQ(kHeight5, attachment->height());
  EXPECT_EQ(kSamples5, attachment->samples());
  EXPECT_EQ(kFormat5, attachment->internal_format());
  EXPECT_FALSE(attachment->cleared());
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));

  // Change samples.
  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer5, kDifferentSamples5,
                                              kFormat5, kWidth5, kHeight5);
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));
  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer5, kSamples5,
                                              kFormat5, kWidth5, kHeight5);
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));

  // Check changing an attachment.
  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer5, kSamples5,
                                              kFormat5, kWidth5 + 1, kHeight5);

  attachment = framebuffer_->GetAttachment(GL_STENCIL_ATTACHMENT);
  ASSERT_TRUE(attachment != nullptr);
  EXPECT_EQ(kWidth5 + 1, attachment->width());
  EXPECT_EQ(kHeight5, attachment->height());
  EXPECT_EQ(kSamples5, attachment->samples());
  EXPECT_EQ(kFormat5, attachment->internal_format());
  EXPECT_FALSE(attachment->cleared());
  EXPECT_FALSE(framebuffer_->IsCleared());
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));

  // Check removing it.
  // Restore the width of renderbuffer5 to avoid INCOMPLETE_DIMENSIONS_EXT.
  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer5, kSamples5,
                                              kFormat5, kWidth5, kHeight5);

  framebuffer_->AttachRenderbuffer(GL_STENCIL_ATTACHMENT, nullptr);
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_STENCIL_ATTACHMENT));
  EXPECT_EQ(static_cast<GLenum>(kFormat1),
            framebuffer_->GetReadBufferInternalFormat());
  EXPECT_TRUE(framebuffer_->HasDepthAttachment());
  EXPECT_FALSE(framebuffer_->HasStencilAttachment());
  EXPECT_FALSE(framebuffer_->IsCleared());
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));

  // Remove depth, Set color to 0 size.
  framebuffer_->AttachRenderbuffer(GL_DEPTH_ATTACHMENT, nullptr);
  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer1, kSamples1,
                                              kFormat1, 0, 0);
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));

  // Remove color.
  framebuffer_->AttachRenderbuffer(GL_COLOR_ATTACHMENT0, nullptr);
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));
}

TEST_F(FramebufferInfoTest, AttachTexture2D) {
  const GLuint kTextureClient1Id = 33;
  const GLuint kTextureService1Id = 333;
  const GLuint kTextureClient2Id = 34;
  const GLuint kTextureService2Id = 334;
  const GLint kDepth = 1;
  const GLint kBorder = 0;
  const GLenum kType = GL_UNSIGNED_BYTE;
  const GLsizei kWidth1 = 16;
  const GLsizei kHeight1 = 32;
  const GLint kLevel1 = 0;
  const GLenum kFormat1 = GL_RGBA;
  const GLenum kBadFormat1 = GL_DEPTH_COMPONENT16;
  const GLenum kTarget1 = GL_TEXTURE_2D;
  const GLsizei kSamples1 = 0;
  const GLsizei kWidth2 = 16;
  const GLsizei kHeight2 = 32;
  const GLint kLevel2 = 0;
  const GLenum kFormat2 = GL_RGB;
  const GLenum kTarget2 = GL_TEXTURE_2D;
  const GLsizei kSamples2 = 0;
  const GLsizei kWidth3 = 75;
  const GLsizei kHeight3 = 123;
  const GLint kLevel3 = 0;
  const GLenum kFormat3 = GL_RGBA;
  const GLsizei kSamples3 = 0;
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_DEPTH_ATTACHMENT));
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_STENCIL_ATTACHMENT));
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));

  texture_manager_->CreateTexture(kTextureClient1Id, kTextureService1Id);
  scoped_refptr<TextureRef> texture1(
      texture_manager_->GetTexture(kTextureClient1Id));
  ASSERT_TRUE(texture1.get() != nullptr);

  // check adding one attachment
  framebuffer_->AttachTexture(
      GL_COLOR_ATTACHMENT0, texture1.get(), kTarget1, kLevel1, kSamples1);
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));
  EXPECT_TRUE(framebuffer_->IsCleared());
  EXPECT_EQ(static_cast<GLenum>(0),
            framebuffer_->GetReadBufferInternalFormat());

  // Try format that doesn't work with COLOR_ATTACHMENT0
  texture_manager_->SetTarget(texture1.get(), GL_TEXTURE_2D);
  texture_manager_->SetLevelInfo(
      texture1.get(), GL_TEXTURE_2D, kLevel1, kBadFormat1, kWidth1, kHeight1,
      kDepth, kBorder, kBadFormat1, kType, gfx::Rect(kWidth1, kHeight1));
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));

  // Try a good format.
  texture_manager_->SetLevelInfo(texture1.get(), GL_TEXTURE_2D, kLevel1,
                                 kFormat1, kWidth1, kHeight1, kDepth, kBorder,
                                 kFormat1, kType, gfx::Rect());
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));
  EXPECT_FALSE(framebuffer_->IsCleared());
  texture_manager_->SetLevelInfo(texture1.get(), GL_TEXTURE_2D, kLevel1,
                                 kFormat1, kWidth1, kHeight1, kDepth, kBorder,
                                 kFormat1, kType, gfx::Rect(kWidth1, kHeight1));
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));
  EXPECT_TRUE(framebuffer_->IsCleared());
  EXPECT_EQ(static_cast<GLenum>(kFormat1),
            framebuffer_->GetReadBufferInternalFormat());

  const Framebuffer::Attachment* attachment =
      framebuffer_->GetAttachment(GL_COLOR_ATTACHMENT0);
  ASSERT_TRUE(attachment != nullptr);
  EXPECT_EQ(kWidth1, attachment->width());
  EXPECT_EQ(kHeight1, attachment->height());
  EXPECT_EQ(kSamples1, attachment->samples());
  EXPECT_EQ(kFormat1, attachment->internal_format());
  EXPECT_TRUE(attachment->cleared());

  // Check replacing an attachment
  texture_manager_->CreateTexture(kTextureClient2Id, kTextureService2Id);
  scoped_refptr<TextureRef> texture2(
      texture_manager_->GetTexture(kTextureClient2Id));
  ASSERT_TRUE(texture2.get() != nullptr);
  texture_manager_->SetTarget(texture2.get(), GL_TEXTURE_2D);
  texture_manager_->SetLevelInfo(texture2.get(), GL_TEXTURE_2D, kLevel2,
                                 kFormat2, kWidth2, kHeight2, kDepth, kBorder,
                                 kFormat2, kType, gfx::Rect(kWidth2, kHeight2));

  framebuffer_->AttachTexture(
      GL_COLOR_ATTACHMENT0, texture2.get(), kTarget2, kLevel2, kSamples2);
  EXPECT_EQ(static_cast<GLenum>(kFormat2),
            framebuffer_->GetReadBufferInternalFormat());
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));
  EXPECT_TRUE(framebuffer_->IsCleared());

  attachment = framebuffer_->GetAttachment(GL_COLOR_ATTACHMENT0);
  ASSERT_TRUE(attachment != nullptr);
  EXPECT_EQ(kWidth2, attachment->width());
  EXPECT_EQ(kHeight2, attachment->height());
  EXPECT_EQ(kSamples2, attachment->samples());
  EXPECT_EQ(kFormat2, attachment->internal_format());
  EXPECT_TRUE(attachment->cleared());

  // Check changing attachment
  texture_manager_->SetLevelInfo(texture2.get(), GL_TEXTURE_2D, kLevel3,
                                 kFormat3, kWidth3, kHeight3, kDepth, kBorder,
                                 kFormat3, kType, gfx::Rect());
  attachment = framebuffer_->GetAttachment(GL_COLOR_ATTACHMENT0);
  ASSERT_TRUE(attachment != nullptr);
  EXPECT_EQ(kWidth3, attachment->width());
  EXPECT_EQ(kHeight3, attachment->height());
  EXPECT_EQ(kSamples3, attachment->samples());
  EXPECT_EQ(kFormat3, attachment->internal_format());
  EXPECT_FALSE(attachment->cleared());
  EXPECT_EQ(static_cast<GLenum>(kFormat3),
            framebuffer_->GetReadBufferInternalFormat());
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));
  EXPECT_FALSE(framebuffer_->IsCleared());

  // Set to size 0
  texture_manager_->SetLevelInfo(texture2.get(), GL_TEXTURE_2D, kLevel3,
                                 kFormat3, 0, 0, kDepth, kBorder, kFormat3,
                                 kType, gfx::Rect());
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));

  // Check removing it.
  framebuffer_->AttachTexture(GL_COLOR_ATTACHMENT0, nullptr, 0, 0, 0);
  EXPECT_TRUE(framebuffer_->GetAttachment(GL_COLOR_ATTACHMENT0) == nullptr);
  EXPECT_EQ(static_cast<GLenum>(0),
            framebuffer_->GetReadBufferInternalFormat());

  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));
  EXPECT_TRUE(framebuffer_->IsCleared());
}

TEST_F(FramebufferInfoTest, AttachTextureCube) {
  const GLuint kTextureClientId = 33;
  const GLuint kTextureServiceId = 333;
  const GLint kDepth = 1;
  const GLint kBorder = 0;
  const GLenum kType = GL_UNSIGNED_BYTE;
  const GLsizei kWidth = 16;
  const GLsizei kHeight = 16;
  const GLint kLevel = 0;
  const GLenum kFormat = GL_RGBA;
  const GLenum kTarget = GL_TEXTURE_CUBE_MAP;
  const GLsizei kSamples = 0;

  const auto kTexTargets = std::to_array<GLenum>(
      {GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
       GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
       GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z});

  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));

  texture_manager_->CreateTexture(kTextureClientId, kTextureServiceId);
  scoped_refptr<TextureRef> texture(
      texture_manager_->GetTexture(kTextureClientId));
  ASSERT_TRUE(texture.get());

  texture_manager_->SetTarget(texture.get(), kTarget);
  framebuffer_->AttachTexture(
      GL_COLOR_ATTACHMENT0, texture.get(), kTexTargets[0], kLevel, kSamples);
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));
  EXPECT_TRUE(framebuffer_->IsCleared());
  EXPECT_EQ(static_cast<GLenum>(0),
            framebuffer_->GetReadBufferInternalFormat());

  texture_manager_->SetLevelInfo(texture.get(), kTexTargets[0], kLevel,
                                 kFormat, kWidth, kHeight, kDepth, kBorder,
                                 kFormat, kType, gfx::Rect(kWidth, kHeight));
  EXPECT_TRUE(framebuffer_->IsCleared());
  EXPECT_EQ(static_cast<GLenum>(kFormat),
            framebuffer_->GetReadBufferInternalFormat());
  // Cube incomplete.
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));

  for (size_t ii = 1; ii < 6; ++ii) {
    texture_manager_->SetLevelInfo(texture.get(), kTexTargets[ii], kLevel,
                                   kFormat, kWidth, kHeight, kDepth, kBorder,
                                   kFormat, kType, gfx::Rect(kWidth, kHeight));
  }
  // Cube complete.
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));
}

TEST_F(FramebufferInfoTest, AttachTextureLayer) {
  const GLuint kTextureClientId = 33;
  const GLuint kTextureServiceId = 333;
  const GLint kBorder = 0;
  const GLenum kType = GL_UNSIGNED_BYTE;
  const GLsizei kWidth = 16;
  const GLsizei kHeight = 32;
  const GLint kDepth = 2;
  const GLint kLevel = 0;
  const GLenum kFormat = GL_RGBA;
  const GLenum kTarget = GL_TEXTURE_2D_ARRAY;
  const GLsizei kLayer = 0;
  const GLint kWrongLayer = kDepth;

  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));

  texture_manager_->CreateTexture(kTextureClientId, kTextureServiceId);
  scoped_refptr<TextureRef> texture(
      texture_manager_->GetTexture(kTextureClientId));
  ASSERT_TRUE(texture.get());

  framebuffer_->AttachTextureLayer(
      GL_COLOR_ATTACHMENT0, texture.get(), kTarget, kLevel, kWrongLayer);
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));
  EXPECT_TRUE(framebuffer_->IsCleared());
  EXPECT_EQ(static_cast<GLenum>(0),
            framebuffer_->GetReadBufferInternalFormat());

  texture_manager_->SetTarget(texture.get(), kTarget);
  texture_manager_->SetLevelInfo(texture.get(), kTarget, kLevel,
                                 kFormat, kWidth, kHeight, kDepth, kBorder,
                                 kFormat, kType, gfx::Rect());
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));

  framebuffer_->AttachTextureLayer(
      GL_COLOR_ATTACHMENT0, texture.get(), kTarget, kLevel, kLayer);
  EXPECT_TRUE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));
  EXPECT_FALSE(framebuffer_->IsCleared());
  EXPECT_EQ(static_cast<GLenum>(kFormat),
            framebuffer_->GetReadBufferInternalFormat());

  const Framebuffer::Attachment* attachment =
      framebuffer_->GetAttachment(GL_COLOR_ATTACHMENT0);
  ASSERT_TRUE(attachment);
  EXPECT_EQ(kWidth, attachment->width());
  EXPECT_EQ(kHeight, attachment->height());
  EXPECT_EQ(kFormat, attachment->internal_format());
  EXPECT_FALSE(attachment->cleared());
}

TEST_F(FramebufferInfoTest, ClearPartiallyClearedAttachments) {
  const GLuint kTextureClientId = 33;
  const GLuint kTextureServiceId = 333;
  texture_manager_->CreateTexture(kTextureClientId, kTextureServiceId);
  scoped_refptr<TextureRef> texture(
      texture_manager_->GetTexture(kTextureClientId));
  ASSERT_TRUE(texture.get() != nullptr);
  texture_manager_->SetTarget(texture.get(), GL_TEXTURE_2D);
  framebuffer_->AttachTexture(
      GL_COLOR_ATTACHMENT0, texture.get(), GL_TEXTURE_2D, 0, 0);
  const Framebuffer::Attachment* attachment =
      framebuffer_->GetAttachment(GL_COLOR_ATTACHMENT0);
  ASSERT_TRUE(attachment != nullptr);

  // Not cleared at all.
  texture_manager_->SetLevelInfo(texture.get(), GL_TEXTURE_2D, 0, GL_RGBA, 4,
                                 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                                 gfx::Rect());
  EXPECT_FALSE(attachment->cleared());
  EXPECT_FALSE(attachment->IsPartiallyCleared());
  EXPECT_FALSE(framebuffer_->IsCleared());
  EXPECT_TRUE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_TRUE(framebuffer_->HasUnclearedColorAttachments());
  // Clear it but nothing happens.
  EXPECT_CALL(*decoder_.get(), GetFeatureInfo())
     .WillRepeatedly(Return(feature_info_.get()));
  framebuffer_->ClearUnclearedIntOr3DTexturesOrPartiallyClearedTextures(
      decoder_.get(), texture_manager_.get());
  EXPECT_FALSE(attachment->cleared());
  EXPECT_FALSE(attachment->IsPartiallyCleared());
  EXPECT_FALSE(framebuffer_->IsCleared());
  EXPECT_TRUE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_TRUE(framebuffer_->HasUnclearedColorAttachments());

  // Fully cleared.
  texture_manager_->SetLevelInfo(texture.get(), GL_TEXTURE_2D, 0, GL_RGBA, 4,
                                 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                                 gfx::Rect(0, 0, 4, 4));
  EXPECT_TRUE(attachment->cleared());
  EXPECT_FALSE(attachment->IsPartiallyCleared());
  EXPECT_TRUE(framebuffer_->IsCleared());
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_FALSE(framebuffer_->HasUnclearedColorAttachments());
  // Clear it but nothing happens.
  framebuffer_->ClearUnclearedIntOr3DTexturesOrPartiallyClearedTextures(
      decoder_.get(), texture_manager_.get());
  EXPECT_TRUE(attachment->cleared());
  EXPECT_FALSE(attachment->IsPartiallyCleared());
  EXPECT_TRUE(framebuffer_->IsCleared());
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_FALSE(framebuffer_->HasUnclearedColorAttachments());

  // Partially cleared.
  texture_manager_->SetLevelInfo(texture.get(), GL_TEXTURE_2D, 0, GL_RGBA, 4,
                                 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                                 gfx::Rect(1, 1, 2, 2));
  EXPECT_FALSE(attachment->cleared());
  EXPECT_TRUE(attachment->IsPartiallyCleared());
  EXPECT_FALSE(framebuffer_->IsCleared());
  EXPECT_TRUE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_TRUE(framebuffer_->HasUnclearedColorAttachments());
  // Now clear it.
  EXPECT_CALL(*decoder_.get(), ClearLevel(texture->texture(),
                                          GL_TEXTURE_2D,
                                          0,
                                          GL_RGBA,
                                          GL_UNSIGNED_BYTE,
                                          _, _, _, _))
      .WillOnce(Return(true))
      .WillOnce(Return(true))
      .WillOnce(Return(true))
      .WillOnce(Return(true))
      .WillOnce(Return(true))
      .WillOnce(Return(true))
      .WillOnce(Return(true))
      .WillOnce(Return(true))
      .RetiresOnSaturation();
  framebuffer_->ClearUnclearedIntOr3DTexturesOrPartiallyClearedTextures(
      decoder_.get(), texture_manager_.get());
  EXPECT_TRUE(attachment->cleared());
  EXPECT_FALSE(attachment->IsPartiallyCleared());
  EXPECT_TRUE(framebuffer_->IsCleared());
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_FALSE(framebuffer_->HasUnclearedColorAttachments());
}

TEST_F(FramebufferInfoTest, Clear3DTextureAttachments) {
  const GLuint kTextureClientId = 33;
  const GLuint kTextureServiceId = 333;
  texture_manager_->CreateTexture(kTextureClientId, kTextureServiceId);
  scoped_refptr<TextureRef> texture(
      texture_manager_->GetTexture(kTextureClientId));
  ASSERT_TRUE(texture.get() != nullptr);
  texture_manager_->SetTarget(texture.get(), GL_TEXTURE_3D);
  framebuffer_->AttachTexture(
      GL_COLOR_ATTACHMENT0, texture.get(), GL_TEXTURE_3D, 0, 0);
  const Framebuffer::Attachment* attachment =
      framebuffer_->GetAttachment(GL_COLOR_ATTACHMENT0);
  ASSERT_TRUE(attachment != nullptr);

  const int kWidth = 4;
  const int kHeight = 8;
  const int kDepth = 2;

  // Fully cleared.
  texture_manager_->SetLevelInfo(texture.get(), GL_TEXTURE_2D, 0, GL_RGBA8,
                                 kWidth, kHeight, kDepth,
                                 0, GL_RGBA, GL_UNSIGNED_BYTE,
                                 gfx::Rect(0, 0, kWidth, kHeight));
  EXPECT_TRUE(attachment->cleared());
  EXPECT_FALSE(attachment->IsPartiallyCleared());
  EXPECT_TRUE(framebuffer_->IsCleared());
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_FALSE(framebuffer_->HasUnclearedColorAttachments());
  // Clear it but nothing happens.
  EXPECT_CALL(*decoder_.get(), GetFeatureInfo())
     .WillRepeatedly(Return(feature_info_.get()));
  framebuffer_->ClearUnclearedIntOr3DTexturesOrPartiallyClearedTextures(
      decoder_.get(), texture_manager_.get());
  EXPECT_TRUE(attachment->cleared());
  EXPECT_FALSE(attachment->IsPartiallyCleared());
  EXPECT_TRUE(framebuffer_->IsCleared());
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_FALSE(framebuffer_->HasUnclearedColorAttachments());

  // Not cleared at all.
  texture_manager_->SetLevelInfo(texture.get(), GL_TEXTURE_3D, 0, GL_RGBA8,
                                 kWidth, kHeight, kDepth,
                                 0, GL_RGBA, GL_UNSIGNED_BYTE,
                                 gfx::Rect());
  EXPECT_FALSE(attachment->cleared());
  EXPECT_FALSE(attachment->IsPartiallyCleared());
  EXPECT_FALSE(framebuffer_->IsCleared());
  EXPECT_TRUE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_TRUE(framebuffer_->HasUnclearedColorAttachments());
  // Now clear it.
  EXPECT_CALL(*decoder_.get(), ClearLevel3D(texture->texture(),
                                            GL_TEXTURE_3D,
                                            0,
                                            GL_RGBA,
                                            GL_UNSIGNED_BYTE,
                                            kWidth, kHeight, kDepth))
      .WillOnce(Return(true))
      .RetiresOnSaturation();
  framebuffer_->ClearUnclearedIntOr3DTexturesOrPartiallyClearedTextures(
      decoder_.get(), texture_manager_.get());
  EXPECT_TRUE(attachment->cleared());
  EXPECT_FALSE(attachment->IsPartiallyCleared());
  EXPECT_TRUE(framebuffer_->IsCleared());
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_FALSE(framebuffer_->HasUnclearedColorAttachments());
}

TEST_F(FramebufferInfoTest, Clear3DOutsideRenderableRange) {
  const GLuint kTextureClientId = 33;
  const GLuint kTextureServiceId = 333;
  texture_manager_->CreateTexture(kTextureClientId, kTextureServiceId);
  scoped_refptr<TextureRef> texture(
      texture_manager_->GetTexture(kTextureClientId));
  ASSERT_TRUE(texture.get() != nullptr);
  texture_manager_->SetTarget(texture.get(), GL_TEXTURE_3D);
  // Set base level to 1 but attach level 0.
  TestHelper::SetTexParameteriWithExpectations(gl_.get(),
                                               error_state_.get(),
                                               texture_manager_.get(),
                                               texture.get(),
                                               GL_TEXTURE_BASE_LEVEL,
                                               1,
                                               GL_NO_ERROR);
  framebuffer_->AttachTexture(
      GL_COLOR_ATTACHMENT0, texture.get(), GL_TEXTURE_3D, 0, 0);
  const Framebuffer::Attachment* attachment =
      framebuffer_->GetAttachment(GL_COLOR_ATTACHMENT0);
  ASSERT_TRUE(attachment != nullptr);

  // Level 0 is not cleared at all.
  texture_manager_->SetLevelInfo(texture.get(), GL_TEXTURE_3D, 0, GL_RGBA, 4,
                                 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                                 gfx::Rect());
  // Level 1 is cleared.
  texture_manager_->SetLevelInfo(texture.get(), GL_TEXTURE_3D, 1, GL_RGBA, 2,
                                 2, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                                 gfx::Rect(0, 0, 2, 2));
  EXPECT_FALSE(attachment->cleared());
  EXPECT_FALSE(attachment->IsPartiallyCleared());
  EXPECT_FALSE(framebuffer_->IsCleared());
  EXPECT_TRUE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_TRUE(framebuffer_->HasUnclearedColorAttachments());
  EXPECT_CALL(*decoder_.get(),
              ClearLevel3D(texture->texture(), GL_TEXTURE_3D, 0, GL_RGBA,
                           GL_UNSIGNED_BYTE, _, _, _))
      .WillOnce(Return(true))
      .RetiresOnSaturation();
  EXPECT_CALL(*decoder_.get(), GetFeatureInfo())
     .WillRepeatedly(Return(feature_info_.get()));
  framebuffer_->ClearUnclearedIntOr3DTexturesOrPartiallyClearedTextures(
      decoder_.get(), texture_manager_.get());
  EXPECT_TRUE(attachment->cleared());
  EXPECT_FALSE(attachment->IsPartiallyCleared());
  EXPECT_TRUE(framebuffer_->IsCleared());
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_FALSE(framebuffer_->HasUnclearedColorAttachments());
}

TEST_F(FramebufferInfoTest, ClearIntegerTextureAttachments) {
  const GLuint kTextureClientId = 33;
  const GLuint kTextureServiceId = 333;
  texture_manager_->CreateTexture(kTextureClientId, kTextureServiceId);
  scoped_refptr<TextureRef> texture(
      texture_manager_->GetTexture(kTextureClientId));
  ASSERT_TRUE(texture.get() != nullptr);
  texture_manager_->SetTarget(texture.get(), GL_TEXTURE_2D);
  framebuffer_->AttachTexture(
      GL_COLOR_ATTACHMENT0, texture.get(), GL_TEXTURE_2D, 0, 0);
  const Framebuffer::Attachment* attachment =
      framebuffer_->GetAttachment(GL_COLOR_ATTACHMENT0);
  ASSERT_TRUE(attachment != nullptr);

  const int kWidth = 4;
  const int kHeight = 8;

  // Fully cleared.
  texture_manager_->SetLevelInfo(texture.get(), GL_TEXTURE_2D, 0, GL_RGBA8UI,
                                 kWidth, kHeight, 1,
                                 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE,
                                 gfx::Rect(0, 0, kWidth, kHeight));
  EXPECT_TRUE(attachment->cleared());
  EXPECT_FALSE(attachment->IsPartiallyCleared());
  EXPECT_TRUE(framebuffer_->IsCleared());
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_FALSE(framebuffer_->HasUnclearedColorAttachments());
  // Clear it but nothing happens.
  EXPECT_CALL(*decoder_.get(), GetFeatureInfo())
     .WillRepeatedly(Return(feature_info_.get()));
  framebuffer_->ClearUnclearedIntOr3DTexturesOrPartiallyClearedTextures(
      decoder_.get(), texture_manager_.get());
  EXPECT_TRUE(attachment->cleared());
  EXPECT_FALSE(attachment->IsPartiallyCleared());
  EXPECT_TRUE(framebuffer_->IsCleared());
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_FALSE(framebuffer_->HasUnclearedColorAttachments());

  // Not cleared at all.
  texture_manager_->SetLevelInfo(texture.get(), GL_TEXTURE_2D, 0, GL_RGBA8UI,
                                 kWidth, kHeight, 1,
                                 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE,
                                 gfx::Rect());
  EXPECT_FALSE(attachment->cleared());
  EXPECT_FALSE(attachment->IsPartiallyCleared());
  EXPECT_FALSE(framebuffer_->IsCleared());
  EXPECT_TRUE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_TRUE(framebuffer_->HasUnclearedColorAttachments());
  // Now clear it.
  EXPECT_CALL(*decoder_.get(), IsCompressedTextureFormat(GL_RGBA8UI))
      .WillOnce(Return(false))
      .RetiresOnSaturation();
  EXPECT_CALL(*decoder_.get(), ClearLevel(texture->texture(),
                                          GL_TEXTURE_2D,
                                          0,
                                          GL_RGBA_INTEGER,
                                          GL_UNSIGNED_BYTE,
                                          0, 0, kWidth, kHeight))
      .WillOnce(Return(true))
      .RetiresOnSaturation();
  framebuffer_->ClearUnclearedIntOr3DTexturesOrPartiallyClearedTextures(
      decoder_.get(), texture_manager_.get());
  EXPECT_TRUE(attachment->cleared());
  EXPECT_FALSE(attachment->IsPartiallyCleared());
  EXPECT_TRUE(framebuffer_->IsCleared());
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_FALSE(framebuffer_->HasUnclearedColorAttachments());
}

TEST_F(FramebufferInfoTest, ClearIntegerOutsideRenderableRange) {
  const GLuint kTextureClientId = 33;
  const GLuint kTextureServiceId = 333;
  texture_manager_->CreateTexture(kTextureClientId, kTextureServiceId);
  scoped_refptr<TextureRef> texture(
      texture_manager_->GetTexture(kTextureClientId));
  ASSERT_TRUE(texture.get() != nullptr);
  texture_manager_->SetTarget(texture.get(), GL_TEXTURE_2D);
  // Set base level to 1 but attach level 0.
  TestHelper::SetTexParameteriWithExpectations(gl_.get(),
                                               error_state_.get(),
                                               texture_manager_.get(),
                                               texture.get(),
                                               GL_TEXTURE_BASE_LEVEL,
                                               1,
                                               GL_NO_ERROR);
  framebuffer_->AttachTexture(
      GL_COLOR_ATTACHMENT0, texture.get(), GL_TEXTURE_2D, 0, 0);
  const Framebuffer::Attachment* attachment =
      framebuffer_->GetAttachment(GL_COLOR_ATTACHMENT0);
  ASSERT_TRUE(attachment != nullptr);

  // Level 0 is not cleared at all.
  texture_manager_->SetLevelInfo(texture.get(), GL_TEXTURE_2D, 0, GL_RGBA8UI, 4,
                                 4, 1, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE,
                                 gfx::Rect());
  // Level 1 is cleared.
  texture_manager_->SetLevelInfo(texture.get(), GL_TEXTURE_2D, 1, GL_RGBA8UI, 2,
                                 2, 1, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE,
                                 gfx::Rect(0, 0, 2, 2));
  EXPECT_FALSE(attachment->cleared());
  EXPECT_FALSE(attachment->IsPartiallyCleared());
  EXPECT_FALSE(framebuffer_->IsCleared());
  EXPECT_TRUE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_TRUE(framebuffer_->HasUnclearedColorAttachments());
  EXPECT_CALL(*decoder_.get(), IsCompressedTextureFormat(GL_RGBA8UI))
      .WillOnce(Return(false))
      .RetiresOnSaturation();
  EXPECT_CALL(*decoder_.get(),
              ClearLevel(texture->texture(), GL_TEXTURE_2D, 0, GL_RGBA_INTEGER,
                         GL_UNSIGNED_BYTE, _, _, _, _))
      .WillOnce(Return(true))
      .RetiresOnSaturation();
  EXPECT_CALL(*decoder_.get(), GetFeatureInfo())
     .WillRepeatedly(Return(feature_info_.get()));
  framebuffer_->ClearUnclearedIntOr3DTexturesOrPartiallyClearedTextures(
      decoder_.get(), texture_manager_.get());
  EXPECT_TRUE(attachment->cleared());
  EXPECT_FALSE(attachment->IsPartiallyCleared());
  EXPECT_TRUE(framebuffer_->IsCleared());
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_FALSE(framebuffer_->HasUnclearedColorAttachments());
}

TEST_F(FramebufferInfoES3Test, DrawBuffers) {
  const auto kTextureClientId = std::to_array<GLuint>({33, 34});
  const auto kTextureServiceId = std::to_array<GLuint>({333, 334});
  for (GLenum i = GL_COLOR_ATTACHMENT0;
       i < GL_COLOR_ATTACHMENT0 + kMaxColorAttachments; ++i) {
    EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(i));
  }
  EXPECT_FALSE(framebuffer_->HasUnclearedColorAttachments());

  EXPECT_EQ(static_cast<GLenum>(GL_COLOR_ATTACHMENT0),
            framebuffer_->GetDrawBuffer(GL_DRAW_BUFFER0));
  for (GLenum i = GL_DRAW_BUFFER1; i < GL_DRAW_BUFFER0 + kMaxDrawBuffers; ++i) {
    EXPECT_EQ(static_cast<GLenum>(GL_NONE),
              framebuffer_->GetDrawBuffer(i));
  }

  for (size_t ii = 0; ii < std::size(kTextureClientId); ++ii) {
    texture_manager_->CreateTexture(
        kTextureClientId[ii], kTextureServiceId[ii]);
    scoped_refptr<TextureRef> texture(
        texture_manager_->GetTexture(kTextureClientId[ii]));
    ASSERT_TRUE(texture.get());

    framebuffer_->AttachTexture(
        GL_COLOR_ATTACHMENT0 + ii, texture.get(), GL_TEXTURE_2D, 0, 0);
    EXPECT_FALSE(
        framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0 + ii));

    const Framebuffer::Attachment* attachment =
        framebuffer_->GetAttachment(GL_COLOR_ATTACHMENT0 + ii);
    ASSERT_TRUE(attachment);
    EXPECT_TRUE(attachment->cleared());
  }
  EXPECT_TRUE(framebuffer_->IsCleared());
  EXPECT_FALSE(framebuffer_->HasUnclearedColorAttachments());

  // Set draw buffer 1 as uncleared.
  scoped_refptr<TextureRef> texture1(
      texture_manager_->GetTexture(kTextureClientId[1]));
  texture_manager_->SetTarget(texture1.get(), GL_TEXTURE_2D);
  texture_manager_->SetLevelInfo(texture1.get(), GL_TEXTURE_2D, 0, GL_RGBA, 4,
                                 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                                 gfx::Rect());

  const Framebuffer::Attachment* attachment1 =
      framebuffer_->GetAttachment(GL_COLOR_ATTACHMENT1);
  ASSERT_TRUE(attachment1);
  EXPECT_FALSE(attachment1->cleared());
  EXPECT_FALSE(framebuffer_->IsCleared());
  EXPECT_TRUE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT1));
  EXPECT_TRUE(framebuffer_->HasUnclearedColorAttachments());

  GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
  framebuffer_->SetDrawBuffers(buffers);
  EXPECT_EQ(static_cast<GLenum>(GL_COLOR_ATTACHMENT0),
            framebuffer_->GetDrawBuffer(GL_DRAW_BUFFER0));
  EXPECT_EQ(static_cast<GLenum>(GL_COLOR_ATTACHMENT1),
            framebuffer_->GetDrawBuffer(GL_DRAW_BUFFER1));
  for (GLenum i = GL_DRAW_BUFFER2; i < GL_DRAW_BUFFER0 + kMaxDrawBuffers; ++i) {
    EXPECT_EQ(static_cast<GLenum>(GL_NONE),
              framebuffer_->GetDrawBuffer(i));
  }

  // Only draw buffer 1 needs clearing, so we need to mask draw buffer 0.
  EXPECT_CALL(*gl_, DrawBuffersARB(kMaxDrawBuffers, _))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_TRUE(
      framebuffer_->PrepareDrawBuffersForClearingUninitializedAttachments());

  // Now we disable draw buffer 1.
  buffers[1] = GL_NONE;
  framebuffer_->SetDrawBuffers(buffers);
  // We will enable the disabled draw buffer for clear(), and disable it
  // after the clear.
  EXPECT_CALL(*gl_, DrawBuffersARB(kMaxDrawBuffers, _))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_TRUE(
      framebuffer_->PrepareDrawBuffersForClearingUninitializedAttachments());

  // Now we disable draw buffer 0, enable draw buffer 1.
  buffers[0] = GL_NONE;
  buffers[1] = GL_COLOR_ATTACHMENT1;
  framebuffer_->SetDrawBuffers(buffers);
  // This is the perfect setting for clear. No need to call DrawBuffers().
  EXPECT_FALSE(
      framebuffer_->PrepareDrawBuffersForClearingUninitializedAttachments());
}

TEST_F(FramebufferInfoTest, DrawBufferMasks) {
  const auto kTextureClientId = std::to_array<GLuint>({33, 34, 35, 36, 37});
  const auto kTextureServiceId =
      std::to_array<GLuint>({333, 334, 335, 336, 337});
  const auto kAttachment = std::to_array<GLenum>(
      {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2,
       GL_COLOR_ATTACHMENT4, GL_DEPTH_ATTACHMENT});
  const auto kInternalFormat = std::to_array<GLenum>(
      {GL_RGBA8, GL_RG32UI, GL_R16I, GL_R16F, GL_DEPTH_COMPONENT24});
  const auto kFormat = std::to_array<GLenum>(
      {GL_RGBA, GL_RG_INTEGER, GL_RED_INTEGER, GL_RED, GL_DEPTH_COMPONENT});
  const auto kType = std::to_array<GLenum>(
      {GL_UNSIGNED_BYTE, GL_UNSIGNED_INT, GL_SHORT, GL_FLOAT, GL_UNSIGNED_INT});

  for (size_t ii = 0; ii < std::size(kTextureClientId); ++ii) {
    texture_manager_->CreateTexture(
        kTextureClientId[ii], kTextureServiceId[ii]);
    scoped_refptr<TextureRef> texture(
        texture_manager_->GetTexture(kTextureClientId[ii]));
    ASSERT_TRUE(texture.get());
    texture_manager_->SetTarget(texture.get(), GL_TEXTURE_2D);
    texture_manager_->SetLevelInfo(texture.get(), GL_TEXTURE_2D, 0,
                                   kInternalFormat[ii], 4, 4, 1, 0,
                                   kFormat[ii], kType[ii], gfx::Rect());
    framebuffer_->AttachTexture(
        kAttachment[ii], texture.get(), GL_TEXTURE_2D, 0, 0);
    ASSERT_TRUE(framebuffer_->GetAttachment(kAttachment[ii]));
  }

  manager_.MarkAsComplete(framebuffer_);

  {  // Default draw buffer settings
    EXPECT_EQ(0x3u, framebuffer_->draw_buffer_type_mask());
    EXPECT_EQ(0x3u, framebuffer_->draw_buffer_bound_mask());
    EXPECT_FALSE(framebuffer_->ContainsActiveIntegerAttachments());
  }

  {  // Exact draw buffer settings.
    GLenum buffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1,
                        GL_COLOR_ATTACHMENT2, GL_NONE, GL_COLOR_ATTACHMENT4};
    framebuffer_->SetDrawBuffers(buffers);
    EXPECT_EQ(0x31Bu, framebuffer_->draw_buffer_type_mask());
    EXPECT_EQ(0x33Fu, framebuffer_->draw_buffer_bound_mask());
    EXPECT_TRUE(framebuffer_->ContainsActiveIntegerAttachments());
  }

  {  // All disabled draw buffer settings.
    GLenum buffers[] = {GL_NONE};
    framebuffer_->SetDrawBuffers(buffers);
    EXPECT_EQ(0u, framebuffer_->draw_buffer_type_mask());
    EXPECT_EQ(0u, framebuffer_->draw_buffer_bound_mask());
    EXPECT_FALSE(framebuffer_->ContainsActiveIntegerAttachments());
  }

  {  // Filter out integer buffers.
    GLenum buffers[] = {GL_COLOR_ATTACHMENT0, GL_NONE, GL_NONE, GL_NONE,
                        GL_COLOR_ATTACHMENT4};
    framebuffer_->SetDrawBuffers(buffers);
    EXPECT_EQ(0x303u, framebuffer_->draw_buffer_type_mask());
    EXPECT_EQ(0x303u, framebuffer_->draw_buffer_bound_mask());
    EXPECT_FALSE(framebuffer_->ContainsActiveIntegerAttachments());
  }

  {  // All enabled draw buffer settings.
    GLenum buffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1,
                        GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3,
                        GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT5,
                        GL_COLOR_ATTACHMENT6, GL_COLOR_ATTACHMENT7};
    framebuffer_->SetDrawBuffers(buffers);
    EXPECT_EQ(0x31Bu, framebuffer_->draw_buffer_type_mask());
    EXPECT_EQ(0x33Fu, framebuffer_->draw_buffer_bound_mask());
    EXPECT_TRUE(framebuffer_->ContainsActiveIntegerAttachments());
  }

  // Test ValidateAndAdjustDrawBuffers().

  // gl_FragColor situation.
  EXPECT_CALL(*gl_, DrawBuffersARB(kMaxDrawBuffers, _)).Times(0);
  EXPECT_FALSE(framebuffer_->ValidateAndAdjustDrawBuffers(0x3u, 0x3u));
  // gl_FragData situation.
  EXPECT_CALL(*gl_, DrawBuffersARB(kMaxDrawBuffers, _))
      .Times(0);
  EXPECT_FALSE(
      framebuffer_->ValidateAndAdjustDrawBuffers(0xFFFFFFFFu, 0xFFFFFFFFu));
  // User defined output variables, fully match.
  EXPECT_CALL(*gl_, DrawBuffersARB(kMaxDrawBuffers, _)).Times(0);
  EXPECT_TRUE(
      framebuffer_->ValidateAndAdjustDrawBuffers(0x31Bu, 0x33Fu));
  // User defined output variables, fully on, one type mismatch.
  EXPECT_CALL(*gl_, DrawBuffersARB(kMaxDrawBuffers, _))
      .Times(0);
  EXPECT_FALSE(
      framebuffer_->ValidateAndAdjustDrawBuffers(0x32Bu, 0x33Fu));
  // Empty output.
  EXPECT_CALL(*gl_, DrawBuffersARB(kMaxDrawBuffers, _)).Times(0);
  EXPECT_FALSE(framebuffer_->ValidateAndAdjustDrawBuffers(0u, 0u));
  // User defined output variables, some active buffers have no corresponding
  // output variables, but if they do, types match.
  EXPECT_CALL(*gl_, DrawBuffersARB(kMaxDrawBuffers, _)).Times(0);
  EXPECT_FALSE(framebuffer_->ValidateAndAdjustDrawBuffers(0x310u, 0x330u));
}

class FramebufferInfoFloatTest : public FramebufferInfoTestBase {
 public:
  FramebufferInfoFloatTest()
      : FramebufferInfoTestBase(CONTEXT_TYPE_OPENGLES3) {}
  ~FramebufferInfoFloatTest() override = default;

 protected:
  void SetUp() override {
    InitializeContext("OpenGL ES 3.0",
        "GL_OES_texture_float GL_EXT_color_buffer_float");
  }
};

TEST_F(FramebufferInfoFloatTest, AttachFloatTexture) {
  const GLuint kTextureClientId = 33;
  const GLuint kTextureServiceId = 333;
  const GLint kDepth = 1;
  const GLint kBorder = 0;
  const GLenum kType = GL_FLOAT;
  const GLsizei kWidth = 16;
  const GLsizei kHeight = 32;
  const GLint kLevel = 0;
  const GLenum kFormat = GL_RGBA;
  const GLenum kInternalFormat = GL_RGBA32F;
  const GLenum kTarget = GL_TEXTURE_2D;
  const GLsizei kSamples = 0;
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_DEPTH_ATTACHMENT));
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_STENCIL_ATTACHMENT));

  texture_manager_->CreateTexture(kTextureClientId, kTextureServiceId);
  scoped_refptr<TextureRef> texture(
      texture_manager_->GetTexture(kTextureClientId));
  ASSERT_TRUE(texture.get() != nullptr);

  framebuffer_->AttachTexture(
      GL_COLOR_ATTACHMENT0, texture.get(), kTarget, kLevel, kSamples);
  EXPECT_EQ(static_cast<GLenum>(0),
            framebuffer_->GetReadBufferInternalFormat());

  texture_manager_->SetTarget(texture.get(), GL_TEXTURE_2D);
  texture_manager_->SetLevelInfo(texture.get(), GL_TEXTURE_2D, kLevel,
                                 kInternalFormat, kWidth, kHeight, kDepth,
                                 kBorder, kFormat, kType, gfx::Rect());
  // Texture with a sized float internalformat is allowed as an attachment
  // since float color attachment extension is present.
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));
}

TEST_F(FramebufferInfoTest, UnbindRenderbuffer) {
  const GLuint kRenderbufferClient1Id = 33;
  const GLuint kRenderbufferService1Id = 333;
  const GLuint kRenderbufferClient2Id = 34;
  const GLuint kRenderbufferService2Id = 334;

  renderbuffer_manager_->CreateRenderbuffer(
      kRenderbufferClient1Id, kRenderbufferService1Id);
  Renderbuffer* renderbuffer1 =
      renderbuffer_manager_->GetRenderbuffer(kRenderbufferClient1Id);
  ASSERT_TRUE(renderbuffer1 != nullptr);
  renderbuffer_manager_->CreateRenderbuffer(
      kRenderbufferClient2Id, kRenderbufferService2Id);
  Renderbuffer* renderbuffer2 =
      renderbuffer_manager_->GetRenderbuffer(kRenderbufferClient2Id);
  ASSERT_TRUE(renderbuffer2 != nullptr);

  // Attach to 2 attachment points.
  framebuffer_->AttachRenderbuffer(GL_COLOR_ATTACHMENT0, renderbuffer1);
  framebuffer_->AttachRenderbuffer(GL_DEPTH_ATTACHMENT, renderbuffer1);
  // Check they were attached.
  EXPECT_TRUE(framebuffer_->GetAttachment(GL_COLOR_ATTACHMENT0) != nullptr);
  EXPECT_TRUE(framebuffer_->GetAttachment(GL_DEPTH_ATTACHMENT) != nullptr);
  // Unbind unattached renderbuffer.
  framebuffer_->UnbindRenderbuffer(GL_RENDERBUFFER, renderbuffer2);
  // Should be no-op.
  EXPECT_TRUE(framebuffer_->GetAttachment(GL_COLOR_ATTACHMENT0) != nullptr);
  EXPECT_TRUE(framebuffer_->GetAttachment(GL_DEPTH_ATTACHMENT) != nullptr);
  // Unbind renderbuffer.
  framebuffer_->UnbindRenderbuffer(GL_RENDERBUFFER, renderbuffer1);
  // Check they were detached
  EXPECT_TRUE(framebuffer_->GetAttachment(GL_COLOR_ATTACHMENT0) == nullptr);
  EXPECT_TRUE(framebuffer_->GetAttachment(GL_DEPTH_ATTACHMENT) == nullptr);
}

TEST_F(FramebufferInfoTest, UnbindTexture) {
  const GLuint kTextureClient1Id = 33;
  const GLuint kTextureService1Id = 333;
  const GLuint kTextureClient2Id = 34;
  const GLuint kTextureService2Id = 334;
  const GLenum kTarget1 = GL_TEXTURE_2D;
  const GLint kLevel1 = 0;
  const GLint kSamples1 = 0;

  texture_manager_->CreateTexture(kTextureClient1Id, kTextureService1Id);
  scoped_refptr<TextureRef> texture1(
      texture_manager_->GetTexture(kTextureClient1Id));
  ASSERT_TRUE(texture1.get() != nullptr);
  texture_manager_->CreateTexture(kTextureClient2Id, kTextureService2Id);
  scoped_refptr<TextureRef> texture2(
      texture_manager_->GetTexture(kTextureClient2Id));
  ASSERT_TRUE(texture2.get() != nullptr);

  // Attach to 2 attachment points.
  framebuffer_->AttachTexture(
      GL_COLOR_ATTACHMENT0, texture1.get(), kTarget1, kLevel1, kSamples1);
  framebuffer_->AttachTexture(
      GL_DEPTH_ATTACHMENT, texture1.get(), kTarget1, kLevel1, kSamples1);
  // Check they were attached.
  EXPECT_TRUE(framebuffer_->GetAttachment(GL_COLOR_ATTACHMENT0) != nullptr);
  EXPECT_TRUE(framebuffer_->GetAttachment(GL_DEPTH_ATTACHMENT) != nullptr);
  // Unbind unattached texture.
  framebuffer_->UnbindTexture(kTarget1, texture2.get());
  // Should be no-op.
  EXPECT_TRUE(framebuffer_->GetAttachment(GL_COLOR_ATTACHMENT0) != nullptr);
  EXPECT_TRUE(framebuffer_->GetAttachment(GL_DEPTH_ATTACHMENT) != nullptr);
  // Unbind texture.
  framebuffer_->UnbindTexture(kTarget1, texture1.get());
  // Check they were detached
  EXPECT_TRUE(framebuffer_->GetAttachment(GL_COLOR_ATTACHMENT0) == nullptr);
  EXPECT_TRUE(framebuffer_->GetAttachment(GL_DEPTH_ATTACHMENT) == nullptr);
}

TEST_F(FramebufferInfoTest, LastColorAttachmentIdTest) {
  const GLuint kTextureClient1Id = 33;
  const GLuint kTextureService1Id = 333;
  const GLuint kTextureClient2Id = 34;
  const GLuint kTextureService2Id = 334;
  const GLuint kTextureClient3Id = 35;
  const GLuint kTextureService3Id = 335;
  const GLuint kRenderbufferClientId = 36;
  const GLuint kRenderbufferServiceId = 336;
  const GLuint kTextureLayerClientId = 37;
  const GLuint kTextureLayerServiceId = 337;

  const GLenum kTarget1 = GL_TEXTURE_2D;
  const GLint kLevel1 = 0;
  const GLint kSamples1 = 0;

  const GLenum kTargetTextureLayer = GL_TEXTURE_2D_ARRAY;
  const GLint kBorder = 0;
  const GLenum kType = GL_UNSIGNED_BYTE;
  const GLsizei kWidth = 16;
  const GLsizei kHeight = 32;
  const GLint kDepth = 2;
  const GLint kLevel = 0;
  const GLenum kFormat = GL_RGBA;
  const GLsizei kLayer = 0;

  texture_manager_->CreateTexture(kTextureClient1Id, kTextureService1Id);
  scoped_refptr<TextureRef> texture1(
      texture_manager_->GetTexture(kTextureClient1Id));
  ASSERT_TRUE(texture1.get() != nullptr);
  texture_manager_->CreateTexture(kTextureClient2Id, kTextureService2Id);
  scoped_refptr<TextureRef> texture2(
      texture_manager_->GetTexture(kTextureClient2Id));
  ASSERT_TRUE(texture2.get() != nullptr);
  texture_manager_->CreateTexture(kTextureClient3Id, kTextureService3Id);
  scoped_refptr<TextureRef> texture3(
      texture_manager_->GetTexture(kTextureClient3Id));
  ASSERT_TRUE(texture3.get() != nullptr);

  renderbuffer_manager_->CreateRenderbuffer(kRenderbufferClientId,
                                            kRenderbufferServiceId);
  Renderbuffer* renderbuffer =
      renderbuffer_manager_->GetRenderbuffer(kRenderbufferClientId);
  ASSERT_TRUE(renderbuffer != nullptr);

  texture_manager_->CreateTexture(kTextureLayerClientId,
                                  kTextureLayerServiceId);
  scoped_refptr<TextureRef> textureLayer(
      texture_manager_->GetTexture(kTextureLayerClientId));
  ASSERT_TRUE(textureLayer.get());

  texture_manager_->SetTarget(textureLayer.get(), kTargetTextureLayer);
  texture_manager_->SetLevelInfo(textureLayer.get(), kTargetTextureLayer,
                                 kLevel, kFormat, kWidth, kHeight, kDepth,
                                 kBorder, kFormat, kType, gfx::Rect());

  EXPECT_EQ(framebuffer_->last_color_attachment_id(), -1);
  framebuffer_->AttachTexture(GL_COLOR_ATTACHMENT0, texture1.get(), kTarget1,
                              kLevel1, kSamples1);
  EXPECT_EQ(framebuffer_->last_color_attachment_id(), 0);
  framebuffer_->AttachTexture(GL_COLOR_ATTACHMENT2, texture3.get(), kTarget1,
                              kLevel1, kSamples1);
  EXPECT_EQ(framebuffer_->last_color_attachment_id(), 2);
  framebuffer_->AttachTexture(GL_COLOR_ATTACHMENT1, texture2.get(), kTarget1,
                              kLevel1, kSamples1);
  EXPECT_EQ(framebuffer_->last_color_attachment_id(), 2);
  framebuffer_->AttachRenderbuffer(GL_DEPTH_ATTACHMENT, renderbuffer);
  EXPECT_EQ(framebuffer_->last_color_attachment_id(), 2);
  framebuffer_->AttachRenderbuffer(GL_COLOR_ATTACHMENT3, renderbuffer);
  EXPECT_EQ(framebuffer_->last_color_attachment_id(), 3);
  framebuffer_->AttachTexture(GL_COLOR_ATTACHMENT4, texture1.get(), kTarget1,
                              kLevel1, kSamples1);
  EXPECT_EQ(framebuffer_->last_color_attachment_id(), 4);
  EXPECT_TRUE(framebuffer_->GetAttachment(GL_COLOR_ATTACHMENT0) != nullptr);
  framebuffer_->AttachTextureLayer(GL_COLOR_ATTACHMENT5, textureLayer.get(),
                                   kTargetTextureLayer, kLevel, kLayer);
  EXPECT_EQ(framebuffer_->last_color_attachment_id(), 5);

  framebuffer_->UnbindTexture(kTargetTextureLayer, textureLayer.get());
  EXPECT_EQ(framebuffer_->last_color_attachment_id(), 4);
  framebuffer_->UnbindTexture(kTarget1, texture2.get());
  EXPECT_EQ(framebuffer_->last_color_attachment_id(), 4);
  framebuffer_->UnbindTexture(kTarget1, texture1.get());
  EXPECT_EQ(framebuffer_->last_color_attachment_id(), 3);
  framebuffer_->UnbindRenderbuffer(GL_COLOR_ATTACHMENT3, renderbuffer);
  EXPECT_EQ(framebuffer_->last_color_attachment_id(), 2);
  framebuffer_->UnbindTexture(kTarget1, texture3.get());
  EXPECT_EQ(framebuffer_->last_color_attachment_id(), -1);
}

TEST_F(FramebufferInfoTest, IsCompleteMarkAsComplete) {
  const GLuint kRenderbufferClient1Id = 33;
  const GLuint kRenderbufferService1Id = 333;
  const GLuint kTextureClient2Id = 34;
  const GLuint kTextureService2Id = 334;
  const GLenum kTarget1 = GL_TEXTURE_2D;
  const GLint kLevel1 = 0;
  const GLint kSamples1 = 0;

  renderbuffer_manager_->CreateRenderbuffer(
      kRenderbufferClient1Id, kRenderbufferService1Id);
  Renderbuffer* renderbuffer1 =
      renderbuffer_manager_->GetRenderbuffer(kRenderbufferClient1Id);
  ASSERT_TRUE(renderbuffer1 != nullptr);
  texture_manager_->CreateTexture(kTextureClient2Id, kTextureService2Id);
  scoped_refptr<TextureRef> texture2(
      texture_manager_->GetTexture(kTextureClient2Id));
  ASSERT_TRUE(texture2.get() != nullptr);

  // Check MarkAsComlete marks as complete.
  manager_.MarkAsComplete(framebuffer_);
  EXPECT_TRUE(manager_.IsComplete(framebuffer_));

  // Check at attaching marks as not complete.
  framebuffer_->AttachTexture(
      GL_COLOR_ATTACHMENT0, texture2.get(), kTarget1, kLevel1, kSamples1);
  EXPECT_FALSE(manager_.IsComplete(framebuffer_));
  manager_.MarkAsComplete(framebuffer_);
  EXPECT_TRUE(manager_.IsComplete(framebuffer_));
  framebuffer_->AttachRenderbuffer(GL_DEPTH_ATTACHMENT, renderbuffer1);
  EXPECT_FALSE(manager_.IsComplete(framebuffer_));

  // Check MarkAttachmentsAsCleared marks as complete.
  manager_.MarkAttachmentsAsCleared(
      framebuffer_, renderbuffer_manager_.get(), texture_manager_.get());
  EXPECT_TRUE(manager_.IsComplete(framebuffer_));

  // Check Unbind marks as not complete.
  framebuffer_->UnbindRenderbuffer(GL_RENDERBUFFER, renderbuffer1);
  EXPECT_FALSE(manager_.IsComplete(framebuffer_));
  manager_.MarkAsComplete(framebuffer_);
  EXPECT_TRUE(manager_.IsComplete(framebuffer_));
  framebuffer_->UnbindTexture(kTarget1, texture2.get());
  EXPECT_FALSE(manager_.IsComplete(framebuffer_));
}

TEST_F(FramebufferInfoTest, GetStatus) {
  const GLuint kRenderbufferClient1Id = 33;
  const GLuint kRenderbufferService1Id = 333;
  const GLuint kTextureClient2Id = 34;
  const GLuint kTextureService2Id = 334;
  const GLenum kTarget1 = GL_TEXTURE_2D;
  const GLint kLevel1 = 0;
  const GLint kSamples1 = 0;

  renderbuffer_manager_->CreateRenderbuffer(
      kRenderbufferClient1Id, kRenderbufferService1Id);
  Renderbuffer* renderbuffer1 =
      renderbuffer_manager_->GetRenderbuffer(kRenderbufferClient1Id);
  ASSERT_TRUE(renderbuffer1 != nullptr);
  texture_manager_->CreateTexture(kTextureClient2Id, kTextureService2Id);
  scoped_refptr<TextureRef> texture2(
      texture_manager_->GetTexture(kTextureClient2Id));
  ASSERT_TRUE(texture2.get() != nullptr);
  texture_manager_->SetTarget(texture2.get(), GL_TEXTURE_2D);

  EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(GL_FRAMEBUFFER))
      .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE))
      .RetiresOnSaturation();
  framebuffer_->GetStatus(texture_manager_.get(), GL_FRAMEBUFFER);

  // Check a second call for the same type does not call anything
  framebuffer_->GetStatus(texture_manager_.get(), GL_FRAMEBUFFER);

  // Check changing the attachments calls CheckFramebufferStatus.
  framebuffer_->AttachTexture(
      GL_COLOR_ATTACHMENT0, texture2.get(), kTarget1, kLevel1, kSamples1);
  EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(GL_FRAMEBUFFER))
      .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE)).RetiresOnSaturation();
  framebuffer_->GetStatus(texture_manager_.get(), GL_FRAMEBUFFER);

  // Check a second call for the same type does not call anything.
  framebuffer_->GetStatus(texture_manager_.get(), GL_FRAMEBUFFER);

  // Check a second call with a different target calls CheckFramebufferStatus.
  EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(GL_READ_FRAMEBUFFER))
      .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE))
      .RetiresOnSaturation();
  framebuffer_->GetStatus(texture_manager_.get(), GL_READ_FRAMEBUFFER);

  // Check a second call for the same type does not call anything.
  framebuffer_->GetStatus(texture_manager_.get(), GL_READ_FRAMEBUFFER);

  // Check adding another attachment calls CheckFramebufferStatus.
  framebuffer_->AttachRenderbuffer(GL_DEPTH_ATTACHMENT, renderbuffer1);
  EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(GL_READ_FRAMEBUFFER))
      .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE))
      .RetiresOnSaturation();
  framebuffer_->GetStatus(texture_manager_.get(), GL_READ_FRAMEBUFFER);

  // Check a second call for the same type does not call anything.
  framebuffer_->GetStatus(texture_manager_.get(), GL_READ_FRAMEBUFFER);

  // Check changing the format calls CheckFramebuffferStatus.
  TestHelper::SetTexParameteriWithExpectations(gl_.get(),
                                               error_state_.get(),
                                               texture_manager_.get(),
                                               texture2.get(),
                                               GL_TEXTURE_WRAP_S,
                                               GL_CLAMP_TO_EDGE,
                                               GL_NO_ERROR);

  EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(GL_READ_FRAMEBUFFER))
      .WillOnce(Return(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT))
      .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE))
      .RetiresOnSaturation();
  framebuffer_->GetStatus(texture_manager_.get(), GL_READ_FRAMEBUFFER);

  // Check since it did not return FRAMEBUFFER_COMPLETE that it calls
  // CheckFramebufferStatus
  framebuffer_->GetStatus(texture_manager_.get(), GL_READ_FRAMEBUFFER);

  // Check putting it back does not call CheckFramebufferStatus.
  TestHelper::SetTexParameteriWithExpectations(gl_.get(),
                                               error_state_.get(),
                                               texture_manager_.get(),
                                               texture2.get(),
                                               GL_TEXTURE_WRAP_S,
                                               GL_REPEAT,
                                               GL_NO_ERROR);
  framebuffer_->GetStatus(texture_manager_.get(), GL_READ_FRAMEBUFFER);

  // Check Unbinding does not call CheckFramebufferStatus
  framebuffer_->UnbindRenderbuffer(GL_RENDERBUFFER, renderbuffer1);
  framebuffer_->GetStatus(texture_manager_.get(), GL_READ_FRAMEBUFFER);
}

TEST_F(FramebufferInfoES3Test, DifferentDimensions) {
  const GLuint kRenderbufferClient1Id = 33;
  const GLuint kRenderbufferService1Id = 333;
  const GLuint kRenderbufferClient2Id = 34;
  const GLuint kRenderbufferService2Id = 334;
  const GLsizei kWidth1 = 16;
  const GLsizei kHeight1 = 32;
  const GLenum kFormat1 = GL_RGBA4;
  const GLsizei kSamples1 = 0;
  const GLsizei kWidth2 = 32;  // Different from kWidth1
  const GLsizei kHeight2 = 32;
  const GLenum kFormat2 = GL_DEPTH_COMPONENT16;
  const GLsizei kSamples2 = 0;

  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0));
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_DEPTH_ATTACHMENT));
  EXPECT_FALSE(framebuffer_->HasUnclearedAttachment(GL_STENCIL_ATTACHMENT));

  renderbuffer_manager_->CreateRenderbuffer(
      kRenderbufferClient1Id, kRenderbufferService1Id);
  Renderbuffer* renderbuffer1 =
      renderbuffer_manager_->GetRenderbuffer(kRenderbufferClient1Id);
  ASSERT_TRUE(renderbuffer1 != nullptr);
  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer1, kSamples1,
                                              kFormat1, kWidth1, kHeight1);
  framebuffer_->AttachRenderbuffer(GL_COLOR_ATTACHMENT0, renderbuffer1);

  renderbuffer_manager_->CreateRenderbuffer(
      kRenderbufferClient2Id, kRenderbufferService2Id);
  Renderbuffer* renderbuffer2 =
      renderbuffer_manager_->GetRenderbuffer(kRenderbufferClient2Id);
  ASSERT_TRUE(renderbuffer2 != nullptr);
  renderbuffer_manager_->SetInfoAndInvalidate(renderbuffer2, kSamples2,
                                              kFormat2, kWidth2, kHeight2);
  framebuffer_->AttachRenderbuffer(GL_DEPTH_ATTACHMENT, renderbuffer2);

  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));
}

TEST_F(FramebufferInfoES3Test, DuplicatedAttachments) {
  const GLuint kTextureClientId = 33;
  const GLuint kTextureServiceId = 333;
  const GLenum kTarget = GL_TEXTURE_2D;
  const GLint kLevel = 0;
  const GLenum kFormat = GL_RGBA;
  const GLenum kType = GL_UNSIGNED_BYTE;
  const GLint kWidth = 16;
  const GLint kHeight = 32;
  const GLint kDepth = 1;
  const GLint kBorder = 0;
  const GLint kSamples = 0;

  texture_manager_->CreateTexture(kTextureClientId, kTextureServiceId);
  scoped_refptr<TextureRef> texture(
      texture_manager_->GetTexture(kTextureClientId));
  ASSERT_TRUE(texture.get() != nullptr);
  texture_manager_->SetTarget(texture.get(), GL_TEXTURE_2D);
  texture_manager_->SetLevelInfo(texture.get(), GL_TEXTURE_2D, kLevel,
                                 kFormat, kWidth, kHeight, kDepth, kBorder,
                                 kFormat, kType, gfx::Rect());

  // Check an image is attached to more than one color attachment point
  // in a framebuffer.
  framebuffer_->AttachTexture(
      GL_COLOR_ATTACHMENT0, texture.get(), kTarget, kLevel, kSamples);
  framebuffer_->AttachTexture(
      GL_COLOR_ATTACHMENT1, texture.get(), kTarget, kLevel, kSamples);
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_UNSUPPORTED),
      framebuffer_->IsPossiblyComplete(feature_info_.get()));
}

TEST_F(FramebufferInfoES3Test, ReadBuffer) {
  const GLuint kRenderbufferClientId = 33;
  const GLuint kRenderbufferServiceId = 333;

  EXPECT_EQ(static_cast<GLenum>(GL_COLOR_ATTACHMENT0),
            framebuffer_->read_buffer());
  framebuffer_->set_read_buffer(GL_NONE);
  EXPECT_EQ(static_cast<GLenum>(GL_NONE), framebuffer_->read_buffer());
  EXPECT_FALSE(framebuffer_->GetReadBufferAttachment());

  framebuffer_->set_read_buffer(GL_COLOR_ATTACHMENT1);
  EXPECT_FALSE(framebuffer_->GetReadBufferAttachment());

  renderbuffer_manager_->CreateRenderbuffer(
      kRenderbufferClientId, kRenderbufferServiceId);
  Renderbuffer* renderbuffer =
      renderbuffer_manager_->GetRenderbuffer(kRenderbufferClientId);
  ASSERT_TRUE(renderbuffer != nullptr);
  framebuffer_->AttachRenderbuffer(GL_COLOR_ATTACHMENT1, renderbuffer);
  EXPECT_TRUE(framebuffer_->GetReadBufferAttachment());
}

TEST_F(FramebufferInfoES3Test, AttachNonLevel0Texture) {
  const GLuint kTextureClientId = 33;
  const GLuint kTextureServiceId = 333;
  const GLint kBorder = 0;
  const GLenum kType = GL_UNSIGNED_BYTE;
  const GLsizei kWidth = 16;
  const GLsizei kHeight = 32;
  const GLint kLevel = 2;
  const GLenum kInternalFormat = GL_RGBA8;
  const GLenum kFormat = GL_RGBA;
  const GLenum kTarget = GL_TEXTURE_2D;
  const GLsizei kSamples = 0;

  texture_manager_->CreateTexture(kTextureClientId, kTextureServiceId);
  scoped_refptr<TextureRef> texture(
      texture_manager_->GetTexture(kTextureClientId));
  ASSERT_TRUE(texture.get());

  texture_manager_->SetTarget(texture.get(), kTarget);
  texture_manager_->SetLevelInfo(texture.get(), kTarget, kLevel,
                                 kInternalFormat, kWidth, kHeight, 0, kBorder,
                                 kFormat, kType, gfx::Rect());

  framebuffer_->AttachTexture(GL_COLOR_ATTACHMENT0, texture.get(), kTarget,
                              kLevel, kSamples);
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));

  EXPECT_CALL(*gl_, TexParameteri(kTarget, GL_TEXTURE_BASE_LEVEL, kLevel))
      .Times(1)
      .RetiresOnSaturation();
  texture_manager_->SetParameteri("FramebufferInfoTest.AttachNonLevel0Texturer",
                                  error_state_.get(), texture.get(),
                                  GL_TEXTURE_BASE_LEVEL, kLevel);
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
            framebuffer_->IsPossiblyComplete(feature_info_.get()));
}

}  // namespace gles2
}  // namespace gpu