// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// 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 <stdint.h>

#include <limits>

#include "base/check_op.h"
#include "base/format_macros.h"
#include "base/notreached.h"
#include "base/numerics/checked_math.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/trace_event.h"
#include "gpu/command_buffer/service/context_state.h"
#include "gpu/command_buffer/service/error_state.h"
#include "gpu/command_buffer/service/feature_info.h"
#include "gpu/command_buffer/service/memory_tracking.h"
#include "gpu/command_buffer/service/transform_feedback_manager.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_version_info.h"
#include "ui/gl/trace_util.h"

namespace gpu {
namespace gles2 {
namespace {
static const GLsizeiptr kDefaultMaxBufferSize = 1u << 30;  // 1GB
}

BufferManager::BufferManager(MemoryTracker* memory_tracker,
                             FeatureInfo* feature_info)
    : memory_type_tracker_(
          new MemoryTypeTracker(memory_tracker)),
      memory_tracker_(memory_tracker),
      feature_info_(feature_info),
      max_buffer_size_(kDefaultMaxBufferSize),
      allow_buffers_on_multiple_targets_(false),
      allow_fixed_attribs_(false),
      buffer_count_(0),
      primitive_restart_fixed_index_(0),
      lost_context_(false),
      use_client_side_arrays_for_stream_buffers_(
          feature_info
              ? feature_info->workarounds()
                    .use_client_side_arrays_for_stream_buffers
              : 0) {
  // When created from InProcessCommandBuffer, we won't have a |memory_tracker_|
  // so don't register a dump provider.
  if (memory_tracker_) {
    base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
        this, "gpu::BufferManager", base::ThreadTaskRunnerHandle::Get());
  }
}

BufferManager::~BufferManager() {
  DCHECK(buffers_.empty());
  CHECK_EQ(buffer_count_, 0u);

  base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
      this);
}

void BufferManager::MarkContextLost() {
  lost_context_ = true;
}

void BufferManager::Destroy() {
  buffers_.clear();
  DCHECK_EQ(0u, memory_type_tracker_->GetMemRepresented());
}

void BufferManager::CreateBuffer(GLuint client_id, GLuint service_id) {
  scoped_refptr<Buffer> buffer(new Buffer(this, service_id));
  std::pair<BufferMap::iterator, bool> result =
      buffers_.insert(std::make_pair(client_id, buffer));
  DCHECK(result.second);
}

Buffer* BufferManager::GetBuffer(
    GLuint client_id) {
  BufferMap::iterator it = buffers_.find(client_id);
  return it != buffers_.end() ? it->second.get() : nullptr;
}

void BufferManager::RemoveBuffer(GLuint client_id) {
  BufferMap::iterator it = buffers_.find(client_id);
  if (it != buffers_.end()) {
    Buffer* buffer = it->second.get();
    buffer->MarkAsDeleted();
    buffers_.erase(it);
  }
}

void BufferManager::StartTracking(Buffer* /* buffer */) {
  ++buffer_count_;
}

void BufferManager::StopTracking(Buffer* buffer) {
  memory_type_tracker_->TrackMemFree(buffer->size());
  --buffer_count_;
}

Buffer::MappedRange::MappedRange(
    GLintptr offset, GLsizeiptr size, GLenum access, void* pointer,
    scoped_refptr<gpu::Buffer> shm, unsigned int shm_offset)
    : offset(offset),
      size(size),
      access(access),
      pointer(pointer),
      shm(shm),
      shm_offset(shm_offset) {
  DCHECK(pointer);
  DCHECK(shm.get() && GetShmPointer());
}

Buffer::MappedRange::~MappedRange() = default;

void* Buffer::MappedRange::GetShmPointer() const {
  DCHECK(shm.get());
  return shm->GetDataAddress(shm_offset, static_cast<unsigned int>(size));
}

Buffer::Buffer(BufferManager* manager, GLuint service_id)
    : manager_(manager),
      size_(0),
      deleted_(false),
      is_client_side_array_(false),
      service_id_(service_id),
      initial_target_(0),
      usage_(GL_STATIC_DRAW) {
  manager_->StartTracking(this);
}

Buffer::~Buffer() {
  if (manager_) {
    if (!manager_->lost_context_) {
      GLuint id = service_id();
      glDeleteBuffersARB(1, &id);
    }
    RemoveMappedRange();
    manager_->StopTracking(this);
    manager_ = nullptr;
  }
}

void Buffer::OnBind(GLenum target, bool indexed) {
  if (target == GL_TRANSFORM_FEEDBACK_BUFFER && indexed) {
    ++transform_feedback_indexed_binding_count_;
  } else if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
    ++non_transform_feedback_binding_count_;
  }
  // Note that the transform feedback generic (non-indexed) binding point does
  // not count as a transform feedback indexed binding point *or* a non-
  // transform- feedback binding point, so no reference counts need to change in
  // that case. See https://crbug.com/853978
}

void Buffer::OnUnbind(GLenum target, bool indexed) {
  if (target == GL_TRANSFORM_FEEDBACK_BUFFER && indexed) {
    --transform_feedback_indexed_binding_count_;
  } else if (target != GL_TRANSFORM_FEEDBACK_BUFFER) {
    --non_transform_feedback_binding_count_;
  }
  DCHECK(transform_feedback_indexed_binding_count_ >= 0);
  DCHECK(non_transform_feedback_binding_count_ >= 0);
}

const GLvoid* Buffer::StageShadow(bool use_shadow,
                                  GLsizeiptr size,
                                  const GLvoid* data) {
  shadow_.clear();
  if (use_shadow) {
    if (data) {
      shadow_.insert(shadow_.begin(),
                     static_cast<const uint8_t*>(data),
                     static_cast<const uint8_t*>(data) + size);
    } else {
      shadow_.resize(size);
      memset(shadow_.data(), 0, static_cast<size_t>(size));
    }
    return shadow_.data();
  } else {
    return data;
  }
}

void Buffer::SetInfo(GLsizeiptr size,
                     GLenum usage,
                     bool use_shadow,
                     bool is_client_side_array) {
  usage_ = usage;
  is_client_side_array_ = is_client_side_array;
  ClearCache();

  // Shadow must have been setup already.
  DCHECK_EQ(shadow_.size(), static_cast<size_t>(use_shadow ? size : 0u));
  size_ = size;

  mapped_range_.reset(nullptr);
  readback_shm_ = nullptr;
  readback_shm_offset_ = 0;
}

bool Buffer::CheckRange(GLintptr offset, GLsizeiptr size) const {
  if (offset < 0 || offset > std::numeric_limits<int32_t>::max() ||
      size < 0 || size > std::numeric_limits<int32_t>::max()) {
    return false;
  }
  int32_t max;
  return base::CheckAdd(offset, size).AssignIfValid(&max) && max <= size_;
}

void Buffer::SetRange(GLintptr offset, GLsizeiptr size, const GLvoid * data) {
  DCHECK(CheckRange(offset, size));
  if (!shadow_.empty()) {
    DCHECK_LE(static_cast<size_t>(offset + size), shadow_.size());
    memcpy(shadow_.data() + offset, data, size);
    ClearCache();
  }
}

const void* Buffer::GetRange(GLintptr offset, GLsizeiptr size) const {
  if (shadow_.empty()) {
    return nullptr;
  }
  if (!CheckRange(offset, size)) {
    return nullptr;
  }
  DCHECK_LE(static_cast<size_t>(offset + size), shadow_.size());
  return shadow_.data() + offset;
}

void Buffer::ClearCache() {
  range_set_.clear();
}

template <typename T>
GLuint GetMaxValue(const void* data, GLuint offset, GLsizei count,
    GLuint primitive_restart_index) {
  GLuint max_value = 0;
  const T* element =
      reinterpret_cast<const T*>(static_cast<const int8_t*>(data) + offset);
  const T* end = element + count;
  for (; element < end; ++element) {
    if (*element > max_value) {
      if (*element == primitive_restart_index) {
        continue;
      }
      max_value = *element;
    }
  }
  return max_value;
}

bool Buffer::GetMaxValueForRange(
    GLuint offset, GLsizei count, GLenum type, bool primitive_restart_enabled,
    GLuint* max_value) {
  GLuint primitive_restart_index = 0;
  if (primitive_restart_enabled) {
    switch (type) {
      case GL_UNSIGNED_BYTE:
        primitive_restart_index = 0xFF;
        break;
      case GL_UNSIGNED_SHORT:
        primitive_restart_index = 0xFFFF;
        break;
      case GL_UNSIGNED_INT:
        primitive_restart_index = 0xFFFFFFFF;
        break;
      default:
        NOTREACHED();  // should never get here by validation.
        break;
    }
  }

  Range range(offset, count, type, primitive_restart_enabled);
  RangeToMaxValueMap::iterator it = range_set_.find(range);
  if (it != range_set_.end()) {
    *max_value = it->second;
    return true;
  }
  // Optimization. If:
  //  - primitive restart is enabled
  //  - we don't have an entry in the range set for these parameters
  //    for the situation when primitive restart is enabled
  //  - we do have an entry in the range set for these parameters for
  //    the situation when primitive restart is disabled
  //  - this entry is less than the primitive restart index
  // Then we can repurpose this entry for the situation when primitive
  // restart is enabled. Otherwise, we need to compute the max index
  // from scratch.
  if (primitive_restart_enabled) {
    Range disabled_range(offset, count, type, false);
    it = range_set_.find(disabled_range);
    if (it != range_set_.end() && it->second < primitive_restart_index) {
      // This reuses the max value for the case where primitive
      // restart is enabled.
      range_set_.insert(std::make_pair(range, it->second));
      *max_value = it->second;
      return true;
    }
  }

  uint32_t size;
  if (!base::CheckAdd(
           offset,
           base::CheckMul(count, GLES2Util::GetGLTypeSizeForBuffers(type)))
           .AssignIfValid(&size)) {
    return false;
  }

  if (size > static_cast<uint32_t>(size_)) {
    return false;
  }

  if (shadow_.empty()) {
    return false;
  }

  // Scan the range for the max value and store
  GLuint max_v = 0;
  switch (type) {
    case GL_UNSIGNED_BYTE:
      max_v = GetMaxValue<uint8_t>(shadow_.data(), offset, count,
                                   primitive_restart_index);
      break;
    case GL_UNSIGNED_SHORT:
      // Check we are not accessing an odd byte for a 2 byte value.
      if ((offset & 1) != 0) {
        return false;
      }
      max_v = GetMaxValue<uint16_t>(shadow_.data(), offset, count,
                                    primitive_restart_index);
      break;
    case GL_UNSIGNED_INT:
      // Check we are not accessing a non aligned address for a 4 byte value.
      if ((offset & 3) != 0) {
        return false;
      }
      max_v = GetMaxValue<uint32_t>(shadow_.data(), offset, count,
                                    primitive_restart_index);
      break;
    default:
      NOTREACHED();  // should never get here by validation.
      break;
  }
  range_set_.insert(std::make_pair(range, max_v));
  *max_value = max_v;
  return true;
}

void Buffer::SetMappedRange(GLintptr offset, GLsizeiptr size, GLenum access,
                            void* pointer, scoped_refptr<gpu::Buffer> shm,
                            unsigned int shm_offset) {
  mapped_range_.reset(
      new MappedRange(offset, size, access, pointer, shm, shm_offset));
}

void Buffer::RemoveMappedRange() {
  mapped_range_.reset(nullptr);
}

void Buffer::SetReadbackShadowAllocation(scoped_refptr<gpu::Buffer> shm,
                                         uint32_t shm_offset) {
  DCHECK(shm);
  readback_shm_ = std::move(shm);
  readback_shm_offset_ = shm_offset;
}

scoped_refptr<gpu::Buffer> Buffer::TakeReadbackShadowAllocation(void** data) {
  DCHECK(readback_shm_);
  *data = readback_shm_->GetDataAddress(readback_shm_offset_, size_);
  readback_shm_offset_ = 0;
  return std::move(readback_shm_);
}

bool BufferManager::GetClientId(GLuint service_id, GLuint* client_id) const {
  // This doesn't need to be fast. It's only used during slow queries.
  for (BufferMap::const_iterator it = buffers_.begin();
       it != buffers_.end(); ++it) {
    if (it->second->service_id() == service_id) {
      *client_id = it->first;
      return true;
    }
  }
  return false;
}

bool BufferManager::IsUsageClientSideArray(GLenum usage) {
  return usage == GL_STREAM_DRAW && use_client_side_arrays_for_stream_buffers_;
}

bool BufferManager::UseNonZeroSizeForClientSideArrayBuffer() {
  return feature_info_.get() &&
         feature_info_->workarounds()
             .use_non_zero_size_for_client_side_stream_buffers;
}

bool BufferManager::UseShadowBuffer(GLenum target, GLenum usage) {
  const bool is_client_side_array = IsUsageClientSideArray(usage);
  // feature_info_ can be null in some unit tests.
  const bool support_fixed_attribs =
      !feature_info_ || feature_info_->gl_version_info().SupportsFixedType();

  // TODO(zmo): Don't shadow buffer data on ES3. crbug.com/491002.
  return (
      target == GL_ELEMENT_ARRAY_BUFFER || allow_buffers_on_multiple_targets_ ||
      (allow_fixed_attribs_ && !support_fixed_attribs) || is_client_side_array);
}

void BufferManager::SetInfo(Buffer* buffer,
                            GLenum target,
                            GLsizeiptr size,
                            GLenum usage,
                            bool use_shadow) {
  DCHECK(buffer);
  memory_type_tracker_->TrackMemFree(buffer->size());
  buffer->SetInfo(size, usage, use_shadow, IsUsageClientSideArray(usage));
  memory_type_tracker_->TrackMemAlloc(buffer->size());
}

void BufferManager::ValidateAndDoBufferData(ContextState* context_state,
                                            ErrorState* error_state,
                                            GLenum target,
                                            GLsizeiptr size,
                                            const GLvoid* data,
                                            GLenum usage) {
  if (!feature_info_->validators()->buffer_target.IsValid(target)) {
    ERRORSTATE_SET_GL_ERROR_INVALID_ENUM(
        error_state, "glBufferData", target, "target");
    return;
  }
  if (!feature_info_->validators()->buffer_usage.IsValid(usage)) {
    ERRORSTATE_SET_GL_ERROR_INVALID_ENUM(
        error_state, "glBufferData", usage, "usage");
    return;
  }
  if (size < 0) {
    ERRORSTATE_SET_GL_ERROR(
        error_state, GL_INVALID_VALUE, "glBufferData", "size < 0");
    return;
  }

  if (size > max_buffer_size_) {
    ERRORSTATE_SET_GL_ERROR(error_state, GL_OUT_OF_MEMORY, "glBufferData",
                            "cannot allocate more than 1GB.");
    return;
  }

  Buffer* buffer = GetBufferInfoForTarget(context_state, target);
  if (!buffer) {
    ERRORSTATE_SET_GL_ERROR(
        error_state, GL_INVALID_VALUE, "glBufferData", "unknown buffer");
    return;
  }

  if (buffer->IsBoundForTransformFeedbackAndOther()) {
    ERRORSTATE_SET_GL_ERROR(
        error_state, GL_INVALID_OPERATION, "glBufferData",
        "buffer is bound for transform feedback and other use simultaneously");
    return;
  }

  DoBufferData(error_state, buffer, target, size, usage, data);

  if (context_state->bound_transform_feedback.get()) {
    // buffer size might have changed, and on Desktop GL lower than 4.2,
    // we might need to reset transform feedback buffer range.
    context_state->bound_transform_feedback->OnBufferData(buffer);
  }
}

void BufferManager::DoBufferData(
    ErrorState* error_state,
    Buffer* buffer,
    GLenum target,
    GLsizeiptr size,
    GLenum usage,
    const GLvoid* data) {
  // Stage the shadow buffer first if we are using a shadow buffer so that we
  // validate what we store internally.
  const bool use_shadow = UseShadowBuffer(buffer->initial_target(), usage);
  data = buffer->StageShadow(use_shadow, size, data);

  ERRORSTATE_COPY_REAL_GL_ERRORS_TO_WRAPPER(error_state, "glBufferData");
  if (IsUsageClientSideArray(usage)) {
    GLsizei empty_size = UseNonZeroSizeForClientSideArrayBuffer() ? 1 : 0;
    glBufferData(target, empty_size, nullptr, usage);
  } else {
    if (data || !size) {
      glBufferData(target, size, data, usage);
    } else {
      std::unique_ptr<char[]> zero(new char[size]);
      memset(zero.get(), 0, size);
      glBufferData(target, size, zero.get(), usage);
    }
  }
  GLenum error = ERRORSTATE_PEEK_GL_ERROR(error_state, "glBufferData");
  if (error != GL_NO_ERROR) {
    DCHECK_EQ(static_cast<GLenum>(GL_OUT_OF_MEMORY), error);
    size = 0;
    // TODO(zmo): This doesn't seem correct. There might be shadow data from
    // a previous successful BufferData() call.
    buffer->StageShadow(false, 0, nullptr);  // Also clear the shadow.
    return;
  }

  SetInfo(buffer, target, size, usage, use_shadow);
}

void BufferManager::ValidateAndDoBufferSubData(ContextState* context_state,
                                               ErrorState* error_state,
                                               GLenum target,
                                               GLintptr offset,
                                               GLsizeiptr size,
                                               const GLvoid* data) {
  Buffer* buffer = RequestBufferAccess(context_state, error_state, target,
                                       offset, size, "glBufferSubData");
  if (!buffer) {
    return;
  }
  DoBufferSubData(buffer, target, offset, size, data);
}

void BufferManager::DoBufferSubData(
    Buffer* buffer, GLenum target, GLintptr offset, GLsizeiptr size,
    const GLvoid* data) {
  buffer->SetRange(offset, size, data);

  if (!buffer->IsClientSideArray()) {
    glBufferSubData(target, offset, size, data);
  }
}

void BufferManager::ValidateAndDoCopyBufferSubData(ContextState* context_state,
                                                   ErrorState* error_state,
                                                   GLenum readtarget,
                                                   GLenum writetarget,
                                                   GLintptr readoffset,
                                                   GLintptr writeoffset,
                                                   GLsizeiptr size) {
  const char* func_name = "glCopyBufferSubData";
  Buffer* readbuffer = RequestBufferAccess(
      context_state, error_state, readtarget, readoffset, size, func_name);
  if (!readbuffer)
    return;
  Buffer* writebuffer = RequestBufferAccess(
      context_state, error_state, writetarget, writeoffset, size, func_name);
  if (!writebuffer)
    return;

  if (readbuffer == writebuffer &&
      ((writeoffset >= readoffset && writeoffset < readoffset + size) ||
       (readoffset >= writeoffset && readoffset < writeoffset + size))) {
    ERRORSTATE_SET_GL_ERROR(error_state, GL_INVALID_VALUE, func_name,
        "read/write ranges overlap");
    return;
  }

  if (!allow_buffers_on_multiple_targets_) {
    if ((readbuffer->initial_target() == GL_ELEMENT_ARRAY_BUFFER &&
         writebuffer->initial_target() != GL_ELEMENT_ARRAY_BUFFER) ||
        (writebuffer->initial_target() == GL_ELEMENT_ARRAY_BUFFER &&
         readbuffer->initial_target() != GL_ELEMENT_ARRAY_BUFFER)) {
      ERRORSTATE_SET_GL_ERROR(error_state, GL_INVALID_OPERATION, func_name,
          "copying between ELEMENT_ARRAY_BUFFER and another buffer type");
      return;
    }
  }

  DoCopyBufferSubData(readbuffer, readtarget, readoffset,
                      writebuffer, writetarget, writeoffset, size);
}

void BufferManager::DoCopyBufferSubData(
    Buffer* readbuffer,
    GLenum readtarget,
    GLintptr readoffset,
    Buffer* writebuffer,
    GLenum writetarget,
    GLintptr writeoffset,
    GLsizeiptr size) {
  DCHECK(readbuffer);
  DCHECK(writebuffer);
  if (writebuffer->shadowed()) {
    const void* data = readbuffer->GetRange(readoffset, size);
    DCHECK(data);
    writebuffer->SetRange(writeoffset, size, data);
  }

  glCopyBufferSubData(readtarget, writetarget, readoffset, writeoffset, size);
}

void BufferManager::ValidateAndDoGetBufferParameteri64v(
    ContextState* context_state,
    ErrorState* error_state,
    GLenum target,
    GLenum pname,
    GLint64* params) {
  Buffer* buffer = GetBufferInfoForTarget(context_state, target);
  if (!buffer) {
    ERRORSTATE_SET_GL_ERROR(error_state, GL_INVALID_OPERATION,
                            "glGetBufferParameteri64v",
                            "no buffer bound for target");
    return;
  }
  switch (pname) {
    case GL_BUFFER_SIZE:
      *params = buffer->size();
      break;
    case GL_BUFFER_MAP_LENGTH:
      {
        const Buffer::MappedRange* mapped_range = buffer->GetMappedRange();
        *params = mapped_range ? mapped_range->size : 0;
        break;
      }
    case GL_BUFFER_MAP_OFFSET:
      {
        const Buffer::MappedRange* mapped_range = buffer->GetMappedRange();
        *params = mapped_range ? mapped_range->offset : 0;
        break;
      }
    default:
      NOTREACHED();
  }
}

void BufferManager::ValidateAndDoGetBufferParameteriv(
    ContextState* context_state,
    ErrorState* error_state,
    GLenum target,
    GLenum pname,
    GLint* params) {
  Buffer* buffer = GetBufferInfoForTarget(context_state, target);
  if (!buffer) {
    ERRORSTATE_SET_GL_ERROR(error_state, GL_INVALID_OPERATION,
                            "glGetBufferParameteriv",
                            "no buffer bound for target");
    return;
  }
  switch (pname) {
    case GL_BUFFER_SIZE:
      *params = buffer->size();
      break;
    case GL_BUFFER_USAGE:
      *params = buffer->usage();
      break;
    case GL_BUFFER_ACCESS_FLAGS:
      {
        const Buffer::MappedRange* mapped_range = buffer->GetMappedRange();
        *params = mapped_range ? mapped_range->access : 0;
        break;
      }
    case GL_BUFFER_MAPPED:
      *params = buffer->GetMappedRange() == nullptr ? false : true;
      break;
    default:
      NOTREACHED();
  }
}

bool BufferManager::SetTarget(Buffer* buffer, GLenum target) {
  if (!allow_buffers_on_multiple_targets_) {
    // After being bound to ELEMENT_ARRAY_BUFFER target, a buffer cannot be
    // bound to any other targets except for COPY_READ/WRITE_BUFFER target;
    // After being bound to non ELEMENT_ARRAY_BUFFER target, a buffer cannot
    // be bound to ELEMENT_ARRAY_BUFFER target.

    switch (buffer->initial_target()) {
      case GL_ELEMENT_ARRAY_BUFFER:
        switch (target) {
          case GL_ARRAY_BUFFER:
          case GL_PIXEL_PACK_BUFFER:
          case GL_PIXEL_UNPACK_BUFFER:
          case GL_TRANSFORM_FEEDBACK_BUFFER:
          case GL_UNIFORM_BUFFER:
            return false;
          default:
            break;
        }
        break;
      case GL_ARRAY_BUFFER:
      case GL_COPY_READ_BUFFER:
      case GL_COPY_WRITE_BUFFER:
      case GL_PIXEL_PACK_BUFFER:
      case GL_PIXEL_UNPACK_BUFFER:
      case GL_TRANSFORM_FEEDBACK_BUFFER:
      case GL_UNIFORM_BUFFER:
        if (target == GL_ELEMENT_ARRAY_BUFFER) {
          return false;
        }
        break;
      default:
        break;
    }
  }
  if (buffer->initial_target() == 0)
    buffer->set_initial_target(target);
  return true;
}

// Since one BufferManager can be shared by multiple decoders, ContextState is
// passed in each time and not just passed in during initialization.
Buffer* BufferManager::GetBufferInfoForTarget(
    ContextState* state, GLenum target) const {
  switch (target) {
    case GL_ARRAY_BUFFER:
      return state->bound_array_buffer.get();
    case GL_ELEMENT_ARRAY_BUFFER:
      return state->vertex_attrib_manager->element_array_buffer();
    case GL_COPY_READ_BUFFER:
      return state->bound_copy_read_buffer.get();
    case GL_COPY_WRITE_BUFFER:
      return state->bound_copy_write_buffer.get();
    case GL_PIXEL_PACK_BUFFER:
      return state->bound_pixel_pack_buffer.get();
    case GL_PIXEL_UNPACK_BUFFER:
      return state->bound_pixel_unpack_buffer.get();
    case GL_TRANSFORM_FEEDBACK_BUFFER:
      return state->bound_transform_feedback_buffer.get();
    case GL_UNIFORM_BUFFER:
      return state->bound_uniform_buffer.get();
    default:
      NOTREACHED();
      return nullptr;
  }
}

void BufferManager::SetPrimitiveRestartFixedIndexIfNecessary(GLenum type) {
  GLuint index = 0;
  switch (type) {
    case GL_UNSIGNED_BYTE:
      index = 0xFF;
      break;
    case GL_UNSIGNED_SHORT:
      index = 0xFFFF;
      break;
    case GL_UNSIGNED_INT:
      index = 0xFFFFFFFF;
      break;
    default:
      NOTREACHED();  // should never get here by validation.
      break;
  }
  if (primitive_restart_fixed_index_ != index) {
    glPrimitiveRestartIndex(index);
    primitive_restart_fixed_index_ = index;
  }
}

bool BufferManager::OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
                                 base::trace_event::ProcessMemoryDump* pmd) {
  using base::trace_event::MemoryAllocatorDump;
  using base::trace_event::MemoryDumpLevelOfDetail;

  if (args.level_of_detail == MemoryDumpLevelOfDetail::BACKGROUND) {
    std::string dump_name =
        base::StringPrintf("gpu/gl/buffers/context_group_0x%" PRIX64 "",
                           memory_tracker_->ContextGroupTracingId());
    MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(dump_name);
    dump->AddScalar(MemoryAllocatorDump::kNameSize,
                    MemoryAllocatorDump::kUnitsBytes, mem_represented());

    // Early out, no need for more detail in a BACKGROUND dump.
    return true;
  }

  for (const auto& buffer_entry : buffers_) {
    const auto& client_buffer_id = buffer_entry.first;
    const auto& buffer = buffer_entry.second;

    std::string dump_name = base::StringPrintf(
        "gpu/gl/buffers/context_group_0x%" PRIX64 "/buffer_0x%" PRIX32,
        memory_tracker_->ContextGroupTracingId(), client_buffer_id);
    MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(dump_name);
    dump->AddScalar(MemoryAllocatorDump::kNameSize,
                    MemoryAllocatorDump::kUnitsBytes,
                    static_cast<uint64_t>(buffer->size()));

    auto* mapped_range = buffer->GetMappedRange();
    if (!mapped_range)
      continue;
    auto shared_memory_guid = mapped_range->shm->backing()->GetGUID();
    if (!shared_memory_guid.is_empty()) {
      pmd->CreateSharedMemoryOwnershipEdge(dump->guid(), shared_memory_guid,
                                           0 /* importance */);
    } else {
      auto guid = gl::GetGLBufferGUIDForTracing(
          memory_tracker_->ContextGroupTracingId(), client_buffer_id);
      pmd->CreateSharedGlobalAllocatorDump(guid);
      pmd->AddOwnershipEdge(dump->guid(), guid);
    }
  }

  return true;
}

Buffer* BufferManager::RequestBufferAccess(ContextState* context_state,
                                           ErrorState* error_state,
                                           GLenum target,
                                           GLintptr offset,
                                           GLsizeiptr size,
                                           const char* func_name) {
  DCHECK(context_state);
  Buffer* buffer = GetBufferInfoForTarget(context_state, target);
  if (!RequestBufferAccess(error_state, buffer, func_name,
                           "bound to target 0x%04x", target)) {
    return nullptr;
  }
  if (!buffer->CheckRange(offset, size)) {
    std::string msg = base::StringPrintf(
        "bound to target 0x%04x : offset/size out of range", target);
    ERRORSTATE_SET_GL_ERROR(error_state, GL_INVALID_VALUE, func_name,
                            msg.c_str());
    return nullptr;
  }
  return buffer;
}

Buffer* BufferManager::RequestBufferAccess(ContextState* context_state,
                                           ErrorState* error_state,
                                           GLenum target,
                                           const char* func_name) {
  DCHECK(context_state);
  Buffer* buffer = GetBufferInfoForTarget(context_state, target);
  return RequestBufferAccess(error_state, buffer, func_name,
                             "bound to target 0x%04x", target)
             ? buffer
             : nullptr;
}

bool BufferManager::RequestBufferAccess(ErrorState* error_state,
                                        Buffer* buffer,
                                        const char* func_name,
                                        const char* error_message_format,
                                        ...) {
  DCHECK(error_state);

  va_list varargs;
  va_start(varargs, error_message_format);
  bool result = RequestBufferAccessV(error_state, buffer, func_name,
                                     error_message_format, varargs);
  va_end(varargs);
  return result;
}

bool BufferManager::RequestBufferAccess(ErrorState* error_state,
                                        Buffer* buffer,
                                        GLintptr offset,
                                        GLsizeiptr size,
                                        const char* func_name,
                                        const char* error_message) {
  if (!RequestBufferAccess(error_state, buffer, func_name, error_message)) {
    return false;
  }
  if (!buffer->CheckRange(offset, size)) {
    std::string msg = base::StringPrintf(
        "%s : offset/size out of range", error_message);
    ERRORSTATE_SET_GL_ERROR(
        error_state, GL_INVALID_OPERATION, func_name, msg.c_str());
    return false;
  }
  return true;
}

bool BufferManager::RequestBuffersAccess(
    ErrorState* error_state,
    const IndexedBufferBindingHost* bindings,
    const std::vector<GLsizeiptr>& variable_sizes,
    GLsizei count,
    const char* func_name,
    const char* message_tag) {
  DCHECK(error_state);
  DCHECK(bindings);

  for (size_t ii = 0; ii < variable_sizes.size(); ++ii) {
    if (variable_sizes[ii] == 0)
      continue;
    Buffer* buffer = bindings->GetBufferBinding(ii);
    if (!buffer) {
      std::string msg = base::StringPrintf(
          "%s : no buffer bound at index %zu", message_tag, ii);
      ERRORSTATE_SET_GL_ERROR(
          error_state, GL_INVALID_OPERATION, func_name, msg.c_str());
      return false;
    }
    if (buffer->GetMappedRange()) {
      std::string msg = base::StringPrintf(
          "%s : buffer is mapped at index %zu", message_tag, ii);
      ERRORSTATE_SET_GL_ERROR(
          error_state, GL_INVALID_OPERATION, func_name, msg.c_str());
      return false;
    }
    if (buffer->IsBoundForTransformFeedbackAndOther()) {
      std::string msg = base::StringPrintf(
          "%s : buffer at index %zu is bound for transform feedback and other "
          "use simultaneously",
          message_tag, ii);
      ERRORSTATE_SET_GL_ERROR(error_state, GL_INVALID_OPERATION, func_name,
                              msg.c_str());
      return false;
    }
    GLsizeiptr size = bindings->GetEffectiveBufferSize(ii);
    GLsizeiptr required_size;
    if (!base::CheckMul(variable_sizes[ii], count)
             .AssignIfValid(&required_size) ||
        size < required_size) {
      std::string msg = base::StringPrintf(
          "%s : buffer or buffer range at index %zu not large enough",
          message_tag, ii);
      ERRORSTATE_SET_GL_ERROR(
          error_state, GL_INVALID_OPERATION, func_name, msg.c_str());
      return false;
    }
  }
  return true;
}

bool BufferManager::RequestBufferAccessV(ErrorState* error_state,
                                         Buffer* buffer,
                                         const char* func_name,
                                         const char* error_message_format,
                                         va_list varargs) {
  DCHECK(error_state);

  if (!buffer || buffer->IsDeleted()) {
    std::string message_tag = base::StringPrintV(error_message_format, varargs);
    std::string msg = base::StringPrintf("%s : no buffer", message_tag.c_str());
    ERRORSTATE_SET_GL_ERROR(error_state, GL_INVALID_OPERATION, func_name,
                            msg.c_str());
    return false;
  }
  if (buffer->GetMappedRange()) {
    std::string message_tag = base::StringPrintV(error_message_format, varargs);
    std::string msg = base::StringPrintf("%s : buffer is mapped",
                                         message_tag.c_str());
    ERRORSTATE_SET_GL_ERROR(error_state, GL_INVALID_OPERATION, func_name,
                            msg.c_str());
    return false;
  }
  if (buffer->IsBoundForTransformFeedbackAndOther()) {
    std::string message_tag = base::StringPrintV(error_message_format, varargs);
    std::string msg = base::StringPrintf(
        "%s : buffer is bound for transform feedback and other use "
        "simultaneously",
        message_tag.c_str());
    ERRORSTATE_SET_GL_ERROR(error_state, GL_INVALID_OPERATION, func_name,
                            msg.c_str());
    return false;
  }
  return true;
}

}  // namespace gles2
}  // namespace gpu
