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/buffer_manager.h"

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

#include <array>
#include <memory>

#include "base/compiler_specific.h"
#include "base/containers/heap_array.h"
#include "gpu/command_buffer/service/error_state_mock.h"
#include "gpu/command_buffer/service/feature_info.h"
#include "gpu/command_buffer/service/gpu_service_test.h"
#include "gpu/command_buffer/service/mocks.h"
#include "gpu/command_buffer/service/test_helper.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gl/gl_mock.h"

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

namespace gpu {
namespace gles2 {

class BufferManagerTestBase : public GpuServiceTest {
 protected:
  void SetUpBase(scoped_refptr<MemoryTracker> memory_tracker,
                 FeatureInfo* feature_info,
                 const char* extensions) {
    GpuServiceTest::SetUp();
    if (feature_info) {
      TestHelper::SetupFeatureInfoInitExpectations(gl_.get(), extensions);
      feature_info->InitializeForTesting();
    }
    error_state_ = std::make_unique<MockErrorState>();
    manager_ = std::make_unique<BufferManager>(std::move(memory_tracker),
                                               feature_info);
  }

  void TearDown() override {
    manager_->MarkContextLost();
    manager_->Destroy();
    manager_.reset();
    error_state_.reset();
    GpuServiceTest::TearDown();
  }

  GLenum GetInitialTarget(const Buffer* buffer) const {
    return buffer->initial_target();
  }

  void DoBufferData(
      Buffer* buffer, GLenum target, GLsizeiptr size, GLenum usage,
      const GLvoid* data, GLenum error) {
    TestHelper::DoBufferData(
        gl_.get(), error_state_.get(), manager_.get(),
        buffer, target, size, usage, data, error);
  }

  bool DoBufferSubData(
      Buffer* buffer, GLenum target, GLintptr offset, GLsizeiptr size,
      const GLvoid* data) {
    if (!buffer->CheckRange(offset, size)) {
      return false;
    }
    if (!buffer->IsClientSideArray()) {
      EXPECT_CALL(*gl_, BufferSubData(target, offset, size, _))
          .Times(1)
          .RetiresOnSaturation();
    }
    manager_->DoBufferSubData(buffer, target, offset, size, data);
    return true;
  }

  void RunGetMaxValueForRangeUint8Test(bool enable_primitive_restart)
  {
    const GLenum kTarget = GL_ELEMENT_ARRAY_BUFFER;
    const GLuint kClientBufferId = 1;
    const GLuint kServiceBufferId = 11;
    const uint8_t data[] = {10, 9, 8, 7, 6, 0xFFu, 4, 3, 2, 1};
    const uint8_t new_data[] = {100, 120, 110};
    manager_->CreateBuffer(kClientBufferId, kServiceBufferId);
    Buffer* buffer = manager_->GetBuffer(kClientBufferId);
    ASSERT_TRUE(buffer != nullptr);
    manager_->SetTarget(buffer, kTarget);
    DoBufferData(buffer, kTarget, sizeof(data), GL_STATIC_DRAW, nullptr,
                 GL_NO_ERROR);
    EXPECT_TRUE(DoBufferSubData(buffer, kTarget, 0, sizeof(data), data));
    GLuint max_value;
    // Check entire range succeeds.
    EXPECT_TRUE(buffer->GetMaxValueForRange(
        0, 10, GL_UNSIGNED_BYTE, enable_primitive_restart, &max_value));
    if (enable_primitive_restart) {
      EXPECT_EQ(10u, max_value);
    } else {
      EXPECT_EQ(0xFFu, max_value);
    }
    // Check sub range succeeds.
    EXPECT_TRUE(buffer->GetMaxValueForRange(
        4, 3, GL_UNSIGNED_BYTE, enable_primitive_restart, &max_value));
    if (enable_primitive_restart) {
      EXPECT_EQ(6u, max_value);
    } else {
      EXPECT_EQ(0xFFu, max_value);
    }
    // Check changing sub range succeeds.
    EXPECT_TRUE(DoBufferSubData(buffer, kTarget, 4, sizeof(new_data),
                                new_data));
    EXPECT_TRUE(buffer->GetMaxValueForRange(
        4, 3, GL_UNSIGNED_BYTE, enable_primitive_restart, &max_value));
    EXPECT_EQ(120u, max_value);
    max_value = 0;
    EXPECT_TRUE(buffer->GetMaxValueForRange(
        0, 10, GL_UNSIGNED_BYTE, enable_primitive_restart, &max_value));
    EXPECT_EQ(120u, max_value);
    // Check out of range fails.
    EXPECT_FALSE(buffer->GetMaxValueForRange(
        0, 11, GL_UNSIGNED_BYTE, enable_primitive_restart, &max_value));
    EXPECT_FALSE(buffer->GetMaxValueForRange(
        10, 1, GL_UNSIGNED_BYTE, enable_primitive_restart, &max_value));
  }

  void RunGetMaxValueForRangeUint16Test(bool enable_primitive_restart) {
    const GLenum kTarget = GL_ELEMENT_ARRAY_BUFFER;
    const GLuint kClientBufferId = 1;
    const GLuint kServiceBufferId = 11;
    const uint16_t data[] = {10, 9, 8, 7, 6, 0xFFFF, 4, 3, 2, 1};
    const uint16_t new_data[] = {100, 120, 110};
    manager_->CreateBuffer(kClientBufferId, kServiceBufferId);
    Buffer* buffer = manager_->GetBuffer(kClientBufferId);
    ASSERT_TRUE(buffer != nullptr);
    manager_->SetTarget(buffer, kTarget);
    DoBufferData(buffer, kTarget, sizeof(data), GL_STATIC_DRAW, nullptr,
                 GL_NO_ERROR);
    EXPECT_TRUE(DoBufferSubData(buffer, kTarget, 0, sizeof(data), data));
    GLuint max_value;
    // Check entire range succeeds.
    EXPECT_TRUE(buffer->GetMaxValueForRange(
        0, 10, GL_UNSIGNED_SHORT, enable_primitive_restart, &max_value));
    if (enable_primitive_restart) {
      EXPECT_EQ(10u, max_value);
    } else {
      EXPECT_EQ(0xFFFFu, max_value);
    }
    // Check odd offset fails for GL_UNSIGNED_SHORT.
    EXPECT_FALSE(buffer->GetMaxValueForRange(
        1, 10, GL_UNSIGNED_SHORT, enable_primitive_restart, &max_value));
    // Check sub range succeeds.
    EXPECT_TRUE(buffer->GetMaxValueForRange(
        8, 3, GL_UNSIGNED_SHORT, enable_primitive_restart, &max_value));
    if (enable_primitive_restart) {
      EXPECT_EQ(6u, max_value);
    } else {
      EXPECT_EQ(0xFFFFu, max_value);
    }
    // Check changing sub range succeeds.
    EXPECT_TRUE(DoBufferSubData(buffer, kTarget, 8, sizeof(new_data),
                                new_data));
    EXPECT_TRUE(buffer->GetMaxValueForRange(
        8, 3, GL_UNSIGNED_SHORT, enable_primitive_restart, &max_value));
    EXPECT_EQ(120u, max_value);
    max_value = 0;
    EXPECT_TRUE(buffer->GetMaxValueForRange(
        0, 10, GL_UNSIGNED_SHORT, enable_primitive_restart, &max_value));
    EXPECT_EQ(120u, max_value);
    // Check out of range fails.
    EXPECT_FALSE(buffer->GetMaxValueForRange(
        0, 11, GL_UNSIGNED_SHORT, enable_primitive_restart, &max_value));
    EXPECT_FALSE(buffer->GetMaxValueForRange(
        20, 1, GL_UNSIGNED_SHORT, enable_primitive_restart, &max_value));
  }

  void RunGetMaxValueForRangeUint32Test(bool enable_primitive_restart) {
    const GLenum kTarget = GL_ELEMENT_ARRAY_BUFFER;
    const GLuint kClientBufferId = 1;
    const GLuint kServiceBufferId = 11;
    const uint32_t data[] = {10, 9, 8, 7, 6, 0xFFFFFFFFu, 4, 3, 2, 1};
    const uint32_t new_data[] = {100, 120, 110};
    manager_->CreateBuffer(kClientBufferId, kServiceBufferId);
    Buffer* buffer = manager_->GetBuffer(kClientBufferId);
    ASSERT_TRUE(buffer != nullptr);
    manager_->SetTarget(buffer, kTarget);
    DoBufferData(buffer, kTarget, sizeof(data), GL_STATIC_DRAW, nullptr,
                 GL_NO_ERROR);
    EXPECT_TRUE(DoBufferSubData(buffer, kTarget, 0, sizeof(data), data));
    GLuint max_value;
    // Check entire range succeeds.
    EXPECT_TRUE(buffer->GetMaxValueForRange(
        0, 10, GL_UNSIGNED_INT, enable_primitive_restart, &max_value));
    if (enable_primitive_restart) {
      EXPECT_EQ(10u, max_value);
    } else {
      EXPECT_EQ(0xFFFFFFFFu, max_value);
    }
    // Check non aligned offsets fails for GL_UNSIGNED_INT.
    EXPECT_FALSE(buffer->GetMaxValueForRange(
        1, 10, GL_UNSIGNED_INT, enable_primitive_restart, &max_value));
    EXPECT_FALSE(buffer->GetMaxValueForRange(
        2, 10, GL_UNSIGNED_INT, enable_primitive_restart, &max_value));
    EXPECT_FALSE(buffer->GetMaxValueForRange(
        3, 10, GL_UNSIGNED_INT, enable_primitive_restart, &max_value));
    // Check sub range succeeds.
    EXPECT_TRUE(buffer->GetMaxValueForRange(
        16, 3, GL_UNSIGNED_INT, enable_primitive_restart, &max_value));
    if (enable_primitive_restart) {
      EXPECT_EQ(6u, max_value);
    } else {
      EXPECT_EQ(0xFFFFFFFFu, max_value);
    }
    // Check changing sub range succeeds.
    EXPECT_TRUE(DoBufferSubData(buffer, kTarget, 16, sizeof(new_data),
                                new_data));
    EXPECT_TRUE(buffer->GetMaxValueForRange(
        16, 3, GL_UNSIGNED_INT, enable_primitive_restart, &max_value));
    EXPECT_EQ(120u, max_value);
    max_value = 0;
    EXPECT_TRUE(buffer->GetMaxValueForRange(
        0, 10, GL_UNSIGNED_INT, enable_primitive_restart, &max_value));
    EXPECT_EQ(120u, max_value);
    // Check out of range fails.
    EXPECT_FALSE(buffer->GetMaxValueForRange(
        0, 11, GL_UNSIGNED_INT, enable_primitive_restart, &max_value));
    EXPECT_FALSE(buffer->GetMaxValueForRange(
        40, 1, GL_UNSIGNED_INT, enable_primitive_restart, &max_value));
  }

  std::unique_ptr<BufferManager> manager_;
  std::unique_ptr<MockErrorState> error_state_;
};

class BufferManagerTest : public BufferManagerTestBase {
 protected:
  void SetUp() override { SetUpBase(nullptr, nullptr, ""); }
};

class BufferManagerMemoryTrackerTest : public BufferManagerTestBase {
 protected:
  void SetUp() override {
    mock_memory_tracker_ =
        base::MakeRefCounted<StrictMock<MockMemoryTracker>>();

    SetUpBase(mock_memory_tracker_, nullptr, "");
  }

  scoped_refptr<StrictMock<MockMemoryTracker>> mock_memory_tracker_;
};

class BufferManagerClientSideArraysTest : public BufferManagerTestBase {
 protected:
  void SetUp() override {
    GpuDriverBugWorkarounds gpu_driver_bug_workarounds;
    gpu_driver_bug_workarounds.use_client_side_arrays_for_stream_buffers = true;
    GpuFeatureInfo gpu_feature_info;
    feature_info_ =
        new FeatureInfo(gpu_driver_bug_workarounds, gpu_feature_info);
    SetUpBase(nullptr, feature_info_.get(), "");
  }

  scoped_refptr<FeatureInfo> feature_info_;
};

#define EXPECT_MEMORY_ALLOCATION_CHANGE(old_size, new_size)    \
  EXPECT_CALL(*mock_memory_tracker_,                           \
              TrackMemoryAllocatedChange(new_size - old_size)) \
      .Times(1)                                                \
      .RetiresOnSaturation()

TEST_F(BufferManagerTest, Basic) {
  const GLenum kTarget = GL_ELEMENT_ARRAY_BUFFER;
  const GLuint kClientBuffer1Id = 1;
  const GLuint kServiceBuffer1Id = 11;
  const GLsizeiptr kBuffer1Size = 123;
  const GLuint kClientBuffer2Id = 2;
  // Check we can create buffer.
  manager_->CreateBuffer(kClientBuffer1Id, kServiceBuffer1Id);
  // Check buffer got created.
  Buffer* buffer1 = manager_->GetBuffer(kClientBuffer1Id);
  ASSERT_TRUE(buffer1 != nullptr);
  EXPECT_EQ(0u, GetInitialTarget(buffer1));
  EXPECT_EQ(0, buffer1->size());
  EXPECT_EQ(static_cast<GLenum>(GL_STATIC_DRAW), buffer1->usage());
  EXPECT_FALSE(buffer1->IsDeleted());
  EXPECT_FALSE(buffer1->IsClientSideArray());
  EXPECT_EQ(kServiceBuffer1Id, buffer1->service_id());
  GLuint client_id = 0;
  EXPECT_TRUE(manager_->GetClientId(buffer1->service_id(), &client_id));
  EXPECT_EQ(kClientBuffer1Id, client_id);
  manager_->SetTarget(buffer1, kTarget);
  EXPECT_EQ(kTarget, GetInitialTarget(buffer1));
  // Check we and set its size.
  DoBufferData(buffer1, kTarget, kBuffer1Size, GL_DYNAMIC_DRAW, nullptr,
               GL_NO_ERROR);
  EXPECT_EQ(kBuffer1Size, buffer1->size());
  EXPECT_EQ(static_cast<GLenum>(GL_DYNAMIC_DRAW), buffer1->usage());
  // Check we get nothing for a non-existent buffer.
  EXPECT_TRUE(manager_->GetBuffer(kClientBuffer2Id) == nullptr);
  // Check trying to a remove non-existent buffers does not crash.
  manager_->RemoveBuffer(kClientBuffer2Id);
  // Check that it gets deleted when the last reference is released.
  EXPECT_CALL(*gl_, DeleteBuffersARB(1, ::testing::Pointee(kServiceBuffer1Id)))
      .Times(1)
      .RetiresOnSaturation();
  // Check we can't get the buffer after we remove it.
  manager_->RemoveBuffer(kClientBuffer1Id);
  EXPECT_TRUE(manager_->GetBuffer(kClientBuffer1Id) == nullptr);
}

TEST_F(BufferManagerMemoryTrackerTest, Basic) {
  const GLenum kTarget = GL_ELEMENT_ARRAY_BUFFER;
  const GLuint kClientBuffer1Id = 1;
  const GLuint kServiceBuffer1Id = 11;
  const GLsizeiptr kBuffer1Size1 = 123;
  const GLsizeiptr kBuffer1Size2 = 456;
  // Check we can create buffer.
  manager_->CreateBuffer(kClientBuffer1Id, kServiceBuffer1Id);
  // Check buffer got created.
  Buffer* buffer1 = manager_->GetBuffer(kClientBuffer1Id);
  ASSERT_TRUE(buffer1 != nullptr);
  manager_->SetTarget(buffer1, kTarget);
  // Check we and set its size.
  EXPECT_MEMORY_ALLOCATION_CHANGE(0, kBuffer1Size1);
  DoBufferData(buffer1, kTarget, kBuffer1Size1, GL_DYNAMIC_DRAW, nullptr,
               GL_NO_ERROR);
  EXPECT_MEMORY_ALLOCATION_CHANGE(kBuffer1Size1, 0);
  EXPECT_MEMORY_ALLOCATION_CHANGE(0, kBuffer1Size2);
  DoBufferData(buffer1, kTarget, kBuffer1Size2, GL_DYNAMIC_DRAW, nullptr,
               GL_NO_ERROR);
  // On delete it will get freed.
  EXPECT_MEMORY_ALLOCATION_CHANGE(kBuffer1Size2, 0);
}

TEST_F(BufferManagerTest, Destroy) {
  const GLuint kClient1Id = 1;
  const GLuint kService1Id = 11;
  // Check we can create buffer.
  manager_->CreateBuffer(kClient1Id, kService1Id);
  // Check buffer got created.
  Buffer* buffer1 = manager_->GetBuffer(kClient1Id);
  ASSERT_TRUE(buffer1 != nullptr);
  EXPECT_CALL(*gl_, DeleteBuffersARB(1, ::testing::Pointee(kService1Id)))
      .Times(1)
      .RetiresOnSaturation();
  manager_->Destroy();
  // Check the resources were released.
  buffer1 = manager_->GetBuffer(kClient1Id);
  ASSERT_TRUE(buffer1 == nullptr);
}

TEST_F(BufferManagerTest, DoBufferSubData) {
  const GLenum kTarget = GL_ELEMENT_ARRAY_BUFFER;
  const GLuint kClientBufferId = 1;
  const GLuint kServiceBufferId = 11;
  const uint8_t data[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
  manager_->CreateBuffer(kClientBufferId, kServiceBufferId);
  Buffer* buffer = manager_->GetBuffer(kClientBufferId);
  ASSERT_TRUE(buffer != nullptr);
  manager_->SetTarget(buffer, kTarget);
  DoBufferData(buffer, kTarget, sizeof(data), GL_STATIC_DRAW, nullptr,
               GL_NO_ERROR);
  EXPECT_TRUE(DoBufferSubData(buffer, kTarget, 0, sizeof(data), data));
  EXPECT_TRUE(DoBufferSubData(buffer, kTarget, sizeof(data), 0, data));
  EXPECT_FALSE(DoBufferSubData(buffer, kTarget, sizeof(data), 1, data));
  EXPECT_FALSE(DoBufferSubData(buffer, kTarget, 0, sizeof(data) + 1, data));
  EXPECT_FALSE(DoBufferSubData(buffer, kTarget, -1, sizeof(data), data));
  EXPECT_FALSE(DoBufferSubData(buffer, kTarget, 0, -1, data));
  DoBufferData(buffer, kTarget, 1, GL_STATIC_DRAW, nullptr, GL_NO_ERROR);
  const int size = 0x20000;
  auto temp = base::HeapArray<uint8_t>::Uninit(size);
  EXPECT_FALSE(DoBufferSubData(buffer, kTarget, 0 - size, size, temp.data()));
  EXPECT_FALSE(DoBufferSubData(buffer, kTarget, 1, size / 2, temp.data()));
}

TEST_F(BufferManagerTest, GetRange) {
  const GLenum kTarget = GL_ELEMENT_ARRAY_BUFFER;
  const GLuint kClientBufferId = 1;
  const GLuint kServiceBufferId = 11;
  const GLsizeiptr kDataSize = 10;
  manager_->CreateBuffer(kClientBufferId, kServiceBufferId);
  Buffer* buffer = manager_->GetBuffer(kClientBufferId);
  ASSERT_TRUE(buffer != nullptr);
  manager_->SetTarget(buffer, kTarget);
  DoBufferData(buffer, kTarget, kDataSize, GL_STATIC_DRAW, nullptr,
               GL_NO_ERROR);
  const char* buf =
      static_cast<const char*>(buffer->GetRange(0, kDataSize));
  ASSERT_TRUE(buf != nullptr);
  const char* buf1 =
      static_cast<const char*>(buffer->GetRange(1, kDataSize - 1));
  UNSAFE_TODO(EXPECT_EQ(buf + 1, buf1));
  EXPECT_TRUE(buffer->GetRange(kDataSize, 1) == nullptr);
  EXPECT_TRUE(buffer->GetRange(0, kDataSize + 1) == nullptr);
  EXPECT_TRUE(buffer->GetRange(-1, kDataSize) == nullptr);
  EXPECT_TRUE(buffer->GetRange(-0, -1) == nullptr);
  const int size = 0x20000;
  DoBufferData(buffer, kTarget, size / 2, GL_STATIC_DRAW, nullptr, GL_NO_ERROR);
  EXPECT_TRUE(buffer->GetRange(0 - size, size) == nullptr);
  EXPECT_TRUE(buffer->GetRange(1, size / 2) == nullptr);
}

TEST_F(BufferManagerTest, GetMaxValueForRangeUint8) {
  RunGetMaxValueForRangeUint8Test(false);
}

TEST_F(BufferManagerTest, GetMaxValueForRangeUint8PrimitiveRestart) {
  RunGetMaxValueForRangeUint8Test(true);
}

TEST_F(BufferManagerTest, GetMaxValueForRangeUint16) {
  RunGetMaxValueForRangeUint16Test(false);
}

TEST_F(BufferManagerTest, GetMaxValueForRangeUint16PrimitiveRestart) {
  RunGetMaxValueForRangeUint16Test(true);
}

TEST_F(BufferManagerTest, GetMaxValueForRangeUint32) {
  RunGetMaxValueForRangeUint32Test(false);
}

TEST_F(BufferManagerTest, GetMaxValueForRangeUint32PrimitiveRestart) {
  RunGetMaxValueForRangeUint32Test(true);
}

TEST_F(BufferManagerTest, UseDeletedBuffer) {
  const GLenum kTarget = GL_ARRAY_BUFFER;
  const GLuint kClientBufferId = 1;
  const GLuint kServiceBufferId = 11;
  const GLsizeiptr kDataSize = 10;
  manager_->CreateBuffer(kClientBufferId, kServiceBufferId);
  scoped_refptr<Buffer> buffer = manager_->GetBuffer(kClientBufferId);
  ASSERT_TRUE(buffer.get() != nullptr);
  manager_->SetTarget(buffer.get(), kTarget);
  // Remove buffer
  manager_->RemoveBuffer(kClientBufferId);
  // Use it after removing
  DoBufferData(buffer.get(), kTarget, kDataSize, GL_STATIC_DRAW, nullptr,
               GL_NO_ERROR);
  // Check that it gets deleted when the last reference is released.
  EXPECT_CALL(*gl_, DeleteBuffersARB(1, ::testing::Pointee(kServiceBufferId)))
      .Times(1)
      .RetiresOnSaturation();
  buffer = nullptr;
}

// Test buffers get shadowed when they are supposed to be.
TEST_F(BufferManagerClientSideArraysTest, StreamBuffersAreShadowed) {
  const GLenum kTarget = GL_ARRAY_BUFFER;
  const GLuint kClientBufferId = 1;
  const GLuint kServiceBufferId = 11;
  static const uint32_t data[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
  manager_->CreateBuffer(kClientBufferId, kServiceBufferId);
  Buffer* buffer = manager_->GetBuffer(kClientBufferId);
  ASSERT_TRUE(buffer != nullptr);
  manager_->SetTarget(buffer, kTarget);
  DoBufferData(
      buffer, kTarget, sizeof(data), GL_STREAM_DRAW, data, GL_NO_ERROR);
  EXPECT_TRUE(buffer->IsClientSideArray());
  UNSAFE_TODO(EXPECT_EQ(
      0, memcmp(data, buffer->GetRange(0, sizeof(data)), sizeof(data))));
  DoBufferData(
      buffer, kTarget, sizeof(data), GL_DYNAMIC_DRAW, data, GL_NO_ERROR);
  EXPECT_FALSE(buffer->IsClientSideArray());
}

TEST_F(BufferManagerTest, MaxValueCacheClearedCorrectly) {
  const GLenum kTarget = GL_ELEMENT_ARRAY_BUFFER;
  const GLuint kClientBufferId = 1;
  const GLuint kServiceBufferId = 11;
  const uint32_t data1[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
  const uint32_t data2[] = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
  const uint32_t data3[] = {30, 29, 28};
  manager_->CreateBuffer(kClientBufferId, kServiceBufferId);
  Buffer* buffer = manager_->GetBuffer(kClientBufferId);
  ASSERT_TRUE(buffer != nullptr);
  manager_->SetTarget(buffer, kTarget);
  GLuint max_value;
  // Load the buffer with some initial data, and then get the maximum value for
  // a range, which has the side effect of caching it.
  DoBufferData(
      buffer, kTarget, sizeof(data1), GL_STATIC_DRAW, data1, GL_NO_ERROR);
  EXPECT_TRUE(
      buffer->GetMaxValueForRange(0, 10, GL_UNSIGNED_INT, false, &max_value));
  EXPECT_EQ(10u, max_value);
  // Check that any cached values are invalidated if the buffer is reloaded
  // with the same amount of data (but different content)
  ASSERT_EQ(sizeof(data2), sizeof(data1));
  DoBufferData(
      buffer, kTarget, sizeof(data2), GL_STATIC_DRAW, data2, GL_NO_ERROR);
  EXPECT_TRUE(
      buffer->GetMaxValueForRange(0, 10, GL_UNSIGNED_INT, false, &max_value));
  EXPECT_EQ(20u, max_value);
  // Check that any cached values are invalidated if the buffer is reloaded
  // with entirely different content.
  ASSERT_NE(sizeof(data3), sizeof(data1));
  DoBufferData(
      buffer, kTarget, sizeof(data3), GL_STATIC_DRAW, data3, GL_NO_ERROR);
  EXPECT_TRUE(
      buffer->GetMaxValueForRange(0, 3, GL_UNSIGNED_INT, false, &max_value));
  EXPECT_EQ(30u, max_value);
}

TEST_F(BufferManagerTest, BindBufferConflicts) {
  manager_->set_allow_buffers_on_multiple_targets(false);
  GLuint client_id = 1;
  GLuint service_id = 101;

  {
    // Once a buffer is bound to ELEMENT_ARRAY_BUFFER, it can't be bound to
    // any other targets except for GL_COPY_READ/WRITE_BUFFER.
    manager_->CreateBuffer(client_id, service_id);
    Buffer* buffer = manager_->GetBuffer(client_id);
    ASSERT_TRUE(buffer != nullptr);
    EXPECT_TRUE(manager_->SetTarget(buffer, GL_ELEMENT_ARRAY_BUFFER));
    EXPECT_TRUE(manager_->SetTarget(buffer, GL_COPY_READ_BUFFER));
    EXPECT_TRUE(manager_->SetTarget(buffer, GL_COPY_WRITE_BUFFER));
    EXPECT_FALSE(manager_->SetTarget(buffer, GL_ARRAY_BUFFER));
    EXPECT_FALSE(manager_->SetTarget(buffer, GL_PIXEL_PACK_BUFFER));
    EXPECT_FALSE(manager_->SetTarget(buffer, GL_PIXEL_UNPACK_BUFFER));
    EXPECT_FALSE(manager_->SetTarget(buffer, GL_TRANSFORM_FEEDBACK_BUFFER));
    EXPECT_FALSE(manager_->SetTarget(buffer, GL_UNIFORM_BUFFER));
  }

  {
    // Except for ELEMENT_ARRAY_BUFFER, a buffer can switch to any targets.
    const auto kTargets = std::to_array<GLenum>(
        {GL_ARRAY_BUFFER, GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER,
         GL_PIXEL_PACK_BUFFER, GL_PIXEL_UNPACK_BUFFER,
         GL_TRANSFORM_FEEDBACK_BUFFER, GL_UNIFORM_BUFFER});
    for (size_t ii = 0; ii < std::size(kTargets); ++ii) {
      client_id++;
      service_id++;
      manager_->CreateBuffer(client_id, service_id);
      Buffer* buffer = manager_->GetBuffer(client_id);
      ASSERT_TRUE(buffer != nullptr);

      EXPECT_TRUE(manager_->SetTarget(buffer, kTargets[ii]));
      for (size_t jj = 0; jj < std::size(kTargets); ++jj) {
        EXPECT_TRUE(manager_->SetTarget(buffer, kTargets[jj]));
      }
      EXPECT_EQ(kTargets[ii], GetInitialTarget(buffer));
    }
  }

  {
    // Once a buffer is bound to non ELEMENT_ARRAY_BUFFER target, it can't be
    // bound to ELEMENT_ARRAY_BUFFER target.
    const auto kTargets = std::to_array<GLenum>(
        {GL_ARRAY_BUFFER, GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER,
         GL_PIXEL_PACK_BUFFER, GL_PIXEL_UNPACK_BUFFER,
         GL_TRANSFORM_FEEDBACK_BUFFER, GL_UNIFORM_BUFFER});
    for (size_t ii = 0; ii < std::size(kTargets); ++ii) {
      client_id++;
      service_id++;
      manager_->CreateBuffer(client_id, service_id);
      Buffer* buffer = manager_->GetBuffer(client_id);
      ASSERT_TRUE(buffer != nullptr);

      EXPECT_TRUE(manager_->SetTarget(buffer, kTargets[ii]));
      for (size_t jj = 0; jj < std::size(kTargets); ++jj) {
        EXPECT_TRUE(manager_->SetTarget(buffer, kTargets[jj]));
      }
    }
  }
}

TEST_F(BufferManagerTest, DeleteBufferAfterContextLost) {
  const GLuint kClient1Id = 1;
  const GLuint kService1Id = 11;
  manager_->CreateBuffer(kClient1Id, kService1Id);
  Buffer* buffer1 = manager_->GetBuffer(kClient1Id);
  ASSERT_TRUE(buffer1 != nullptr);
  manager_->MarkContextLost();
  // Removing buffers after MarkContextLost cause no GL calls.
  manager_->RemoveBuffer(kClient1Id);
  manager_->Destroy();
  // Check the resources were released.
  buffer1 = manager_->GetBuffer(kClient1Id);
  ASSERT_TRUE(buffer1 == nullptr);
}

}  // namespace gles2
}  // namespace gpu