root/gpu/command_buffer/client/vertex_array_object_manager.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. RoundUpToMultipleOf4
  2. ToGLuint
  3. divisor_
  4. enabled
  5. set_enabled
  6. buffer_id
  7. set_buffer_id
  8. type
  9. size
  10. stride
  11. normalized
  12. pointer
  13. IsClientSide
  14. divisor
  15. SetInfo
  16. SetDivisor
  17. vertex_attribs
  18. bound_element_array_buffer
  19. bound_element_array_buffer_id_
  20. UnbindBuffer
  21. BindElementArray
  22. HaveEnabledClientSideBuffers
  23. SetAttribEnable
  24. SetAttribPointer
  25. GetVertexAttrib
  26. SetAttribDivisor
  27. GetAttribPointer
  28. GetAttrib
  29. bound_vertex_array_object_
  30. IsReservedId
  31. bound_element_array_buffer
  32. UnbindBuffer
  33. BindElementArray
  34. GenVertexArrays
  35. DeleteVertexArrays
  36. BindVertexArray
  37. HaveEnabledClientSideBuffers
  38. SetAttribEnable
  39. GetVertexAttrib
  40. GetAttribPointer
  41. SetAttribPointer
  42. SetAttribDivisor
  43. CollectData
  44. IsDefaultVAOBound
  45. SetupSimulatedClientSideBuffers
  46. SetupSimulatedIndexAndClientSideBuffers

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

#include "base/logging.h"
#include "gpu/command_buffer/client/gles2_cmd_helper.h"
#include "gpu/command_buffer/client/gles2_implementation.h"

#if defined(__native_client__) && !defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
#define GLES2_SUPPORT_CLIENT_SIDE_ARRAYS
#endif

namespace gpu {
namespace gles2 {

#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)

static GLsizei RoundUpToMultipleOf4(GLsizei size) {
  return (size + 3) & ~3;
}

#endif  // defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)

// A 32-bit and 64-bit compatible way of converting a pointer to a GLuint.
static GLuint ToGLuint(const void* ptr) {
  return static_cast<GLuint>(reinterpret_cast<size_t>(ptr));
}

// This class tracks VertexAttribPointers and helps emulate client side buffers.
//
// The way client side buffers work is we shadow all the Vertex Attribs so we
// know which ones are pointing to client side buffers.
//
// At Draw time, for any attribs pointing to client side buffers we copy them
// to a special VBO and reset the actual vertex attrib pointers to point to this
// VBO.
//
// This also means we have to catch calls to query those values so that when
// an attrib is a client side buffer we pass the info back the user expects.

class GLES2_IMPL_EXPORT VertexArrayObject {
 public:
  // Info about Vertex Attributes. This is used to track what the user currently
  // has bound on each Vertex Attribute so we can simulate client side buffers
  // at glDrawXXX time.
  class VertexAttrib {
   public:
    VertexAttrib()
        : enabled_(false),
          buffer_id_(0),
          size_(4),
          type_(GL_FLOAT),
          normalized_(GL_FALSE),
          pointer_(NULL),
          gl_stride_(0),
          divisor_(0) {
    }

    bool enabled() const {
      return enabled_;
    }

    void set_enabled(bool enabled) {
      enabled_ = enabled;
    }

    GLuint buffer_id() const {
      return buffer_id_;
    }

    void set_buffer_id(GLuint id) {
      buffer_id_ = id;
    }

    GLenum type() const {
      return type_;
    }

    GLint size() const {
      return size_;
    }

    GLsizei stride() const {
      return gl_stride_;
    }

    GLboolean normalized() const {
      return normalized_;
    }

    const GLvoid* pointer() const {
      return pointer_;
    }

    bool IsClientSide() const {
      return buffer_id_ == 0;
    }

    GLuint divisor() const {
      return divisor_;
    }

    void SetInfo(
        GLuint buffer_id,
        GLint size,
        GLenum type,
        GLboolean normalized,
        GLsizei gl_stride,
        const GLvoid* pointer) {
      buffer_id_ = buffer_id;
      size_ = size;
      type_ = type;
      normalized_ = normalized;
      gl_stride_ = gl_stride;
      pointer_ = pointer;
    }

    void SetDivisor(GLuint divisor) {
      divisor_ = divisor;
    }

   private:
    // Whether or not this attribute is enabled.
    bool enabled_;

    // The id of the buffer. 0 = client side buffer.
    GLuint buffer_id_;

    // Number of components (1, 2, 3, 4).
    GLint size_;

    // GL_BYTE, GL_FLOAT, etc. See glVertexAttribPointer.
    GLenum type_;

    // GL_TRUE or GL_FALSE
    GLboolean normalized_;

    // The pointer/offset into the buffer.
    const GLvoid* pointer_;

    // The stride that will be used to access the buffer. This is the bogus GL
    // stride where 0 = compute the stride based on size and type.
    GLsizei gl_stride_;

    // Divisor, for geometry instancing.
    GLuint divisor_;
  };

  typedef std::vector<VertexAttrib> VertexAttribs;

  explicit VertexArrayObject(GLuint max_vertex_attribs);

  void UnbindBuffer(GLuint id);

  bool BindElementArray(GLuint id);

  bool HaveEnabledClientSideBuffers() const;

  void SetAttribEnable(GLuint index, bool enabled);

  void SetAttribPointer(
    GLuint buffer_id,
    GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride,
    const void* ptr);

  bool GetVertexAttrib(
      GLuint index, GLenum pname, uint32* param) const;

  void SetAttribDivisor(GLuint index, GLuint divisor);

  bool GetAttribPointer(GLuint index, GLenum pname, void** ptr) const;

  const VertexAttribs& vertex_attribs() const {
    return vertex_attribs_;
  }

  GLuint bound_element_array_buffer() const {
    return bound_element_array_buffer_id_;
  }

 private:
  const VertexAttrib* GetAttrib(GLuint index) const;

  int num_client_side_pointers_enabled_;

  // The currently bound element array buffer.
  GLuint bound_element_array_buffer_id_;

  VertexAttribs vertex_attribs_;

  DISALLOW_COPY_AND_ASSIGN(VertexArrayObject);
};

VertexArrayObject::VertexArrayObject(GLuint max_vertex_attribs)
    : num_client_side_pointers_enabled_(0),
      bound_element_array_buffer_id_(0) {
  vertex_attribs_.resize(max_vertex_attribs);
}

void VertexArrayObject::UnbindBuffer(GLuint id) {
  if (id == 0) {
    return;
  }
  for (size_t ii = 0; ii < vertex_attribs_.size(); ++ii) {
    VertexAttrib& attrib = vertex_attribs_[ii];
    if (attrib.buffer_id() == id) {
      attrib.set_buffer_id(0);
      if (attrib.enabled()) {
        ++num_client_side_pointers_enabled_;
      }
    }
  }
  if (bound_element_array_buffer_id_ == id) {
    bound_element_array_buffer_id_ = 0;
  }
}

bool VertexArrayObject::BindElementArray(GLuint id) {
  if (id == bound_element_array_buffer_id_) {
    return false;
  }
  bound_element_array_buffer_id_ = id;
  return true;
}
bool VertexArrayObject::HaveEnabledClientSideBuffers() const {
  return num_client_side_pointers_enabled_ > 0;
}

void VertexArrayObject::SetAttribEnable(GLuint index, bool enabled) {
  if (index < vertex_attribs_.size()) {
    VertexAttrib& attrib = vertex_attribs_[index];
    if (attrib.enabled() != enabled) {
      if (attrib.IsClientSide()) {
        num_client_side_pointers_enabled_ += enabled ? 1 : -1;
        DCHECK_GE(num_client_side_pointers_enabled_, 0);
      }
      attrib.set_enabled(enabled);
    }
  }
}

void VertexArrayObject::SetAttribPointer(
    GLuint buffer_id,
    GLuint index,
    GLint size,
    GLenum type,
    GLboolean normalized,
    GLsizei stride,
    const void* ptr) {
  if (index < vertex_attribs_.size()) {
    VertexAttrib& attrib = vertex_attribs_[index];
    if (attrib.IsClientSide() && attrib.enabled()) {
      --num_client_side_pointers_enabled_;
      DCHECK_GE(num_client_side_pointers_enabled_, 0);
    }

    attrib.SetInfo(buffer_id, size, type, normalized, stride, ptr);

    if (attrib.IsClientSide() && attrib.enabled()) {
      ++num_client_side_pointers_enabled_;
    }
  }
}

bool VertexArrayObject::GetVertexAttrib(
    GLuint index, GLenum pname, uint32* param) const {
  const VertexAttrib* attrib = GetAttrib(index);
  if (!attrib) {
    return false;
  }

  switch (pname) {
    case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
      *param = attrib->buffer_id();
      break;
    case GL_VERTEX_ATTRIB_ARRAY_ENABLED:
      *param = attrib->enabled();
      break;
    case GL_VERTEX_ATTRIB_ARRAY_SIZE:
      *param = attrib->size();
      break;
    case GL_VERTEX_ATTRIB_ARRAY_STRIDE:
      *param = attrib->stride();
      break;
    case GL_VERTEX_ATTRIB_ARRAY_TYPE:
      *param = attrib->type();
      break;
    case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED:
      *param = attrib->normalized();
      break;
    default:
      return false;  // pass through to service side.
      break;
  }
  return true;
}

void VertexArrayObject::SetAttribDivisor(GLuint index, GLuint divisor) {
  if (index < vertex_attribs_.size()) {
    VertexAttrib& attrib = vertex_attribs_[index];
    attrib.SetDivisor(divisor);
  }
}

// Gets the Attrib pointer for an attrib but only if it's a client side
// pointer. Returns true if it got the pointer.
bool VertexArrayObject::GetAttribPointer(
    GLuint index, GLenum pname, void** ptr) const {
  const VertexAttrib* attrib = GetAttrib(index);
  if (attrib && pname == GL_VERTEX_ATTRIB_ARRAY_POINTER) {
    *ptr = const_cast<void*>(attrib->pointer());
    return true;
  }
  return false;
}

// Gets an attrib if it's in range and it's client side.
const VertexArrayObject::VertexAttrib* VertexArrayObject::GetAttrib(
    GLuint index) const {
  if (index < vertex_attribs_.size()) {
    const VertexAttrib* attrib = &vertex_attribs_[index];
    return attrib;
  }
  return NULL;
}

VertexArrayObjectManager::VertexArrayObjectManager(
    GLuint max_vertex_attribs,
    GLuint array_buffer_id,
    GLuint element_array_buffer_id)
    : max_vertex_attribs_(max_vertex_attribs),
      array_buffer_id_(array_buffer_id),
      array_buffer_size_(0),
      array_buffer_offset_(0),
      element_array_buffer_id_(element_array_buffer_id),
      element_array_buffer_size_(0),
      collection_buffer_size_(0),
      default_vertex_array_object_(new VertexArrayObject(max_vertex_attribs)),
      bound_vertex_array_object_(default_vertex_array_object_) {
}

VertexArrayObjectManager::~VertexArrayObjectManager() {
  for (VertexArrayObjectMap::iterator it = vertex_array_objects_.begin();
       it != vertex_array_objects_.end(); ++it) {
    delete it->second;
  }
  delete default_vertex_array_object_;
}

bool VertexArrayObjectManager::IsReservedId(GLuint id) const {
  return (id != 0 &&
          (id == array_buffer_id_ || id == element_array_buffer_id_));
}

GLuint VertexArrayObjectManager::bound_element_array_buffer() const {
  return bound_vertex_array_object_->bound_element_array_buffer();
}

void VertexArrayObjectManager::UnbindBuffer(GLuint id) {
  bound_vertex_array_object_->UnbindBuffer(id);
}

bool VertexArrayObjectManager::BindElementArray(GLuint id) {
  return  bound_vertex_array_object_->BindElementArray(id);
}

void VertexArrayObjectManager::GenVertexArrays(
    GLsizei n, const GLuint* arrays) {
  DCHECK_GE(n, 0);
  for (GLsizei i = 0; i < n; ++i) {
    std::pair<VertexArrayObjectMap::iterator, bool> result =
        vertex_array_objects_.insert(std::make_pair(
            arrays[i], new VertexArrayObject(max_vertex_attribs_)));
    DCHECK(result.second);
  }
}

void VertexArrayObjectManager::DeleteVertexArrays(
    GLsizei n, const GLuint* arrays) {
  DCHECK_GE(n, 0);
  for (GLsizei i = 0; i < n; ++i) {
    GLuint id = arrays[i];
    if (id) {
      VertexArrayObjectMap::iterator it = vertex_array_objects_.find(id);
      if (it != vertex_array_objects_.end()) {
        if (bound_vertex_array_object_ == it->second) {
          bound_vertex_array_object_ = default_vertex_array_object_;
        }
        delete it->second;
        vertex_array_objects_.erase(it);
      }
    }
  }
}

bool VertexArrayObjectManager::BindVertexArray(GLuint array, bool* changed) {
  *changed = false;
  VertexArrayObject* vertex_array_object = default_vertex_array_object_;
  if (array != 0) {
    VertexArrayObjectMap::iterator it = vertex_array_objects_.find(array);
    if (it == vertex_array_objects_.end()) {
      return false;
    }
    vertex_array_object = it->second;
  }
  *changed = vertex_array_object != bound_vertex_array_object_;
  bound_vertex_array_object_ = vertex_array_object;
  return true;
}

bool VertexArrayObjectManager::HaveEnabledClientSideBuffers() const {
  return bound_vertex_array_object_->HaveEnabledClientSideBuffers();
}

void VertexArrayObjectManager::SetAttribEnable(GLuint index, bool enabled) {
  bound_vertex_array_object_->SetAttribEnable(index, enabled);
}

bool VertexArrayObjectManager::GetVertexAttrib(
    GLuint index, GLenum pname, uint32* param) {
  return bound_vertex_array_object_->GetVertexAttrib(index, pname, param);
}

bool VertexArrayObjectManager::GetAttribPointer(
    GLuint index, GLenum pname, void** ptr) const {
  return bound_vertex_array_object_->GetAttribPointer(index, pname, ptr);
}

bool VertexArrayObjectManager::SetAttribPointer(
    GLuint buffer_id,
    GLuint index,
    GLint size,
    GLenum type,
    GLboolean normalized,
    GLsizei stride,
    const void* ptr) {
  // Client side arrays are not allowed in vaos.
  if (buffer_id == 0 && !IsDefaultVAOBound()) {
    return false;
  }
  bound_vertex_array_object_->SetAttribPointer(
      buffer_id, index, size, type, normalized, stride, ptr);
  return true;
}

void VertexArrayObjectManager::SetAttribDivisor(GLuint index, GLuint divisor) {
  bound_vertex_array_object_->SetAttribDivisor(index, divisor);
}

// Collects the data into the collection buffer and returns the number of
// bytes collected.
GLsizei VertexArrayObjectManager::CollectData(
    const void* data,
    GLsizei bytes_per_element,
    GLsizei real_stride,
    GLsizei num_elements) {
  GLsizei bytes_needed = bytes_per_element * num_elements;
  if (collection_buffer_size_ < bytes_needed) {
    collection_buffer_.reset(new int8[bytes_needed]);
    collection_buffer_size_ = bytes_needed;
  }
  const int8* src = static_cast<const int8*>(data);
  int8* dst = collection_buffer_.get();
  int8* end = dst + bytes_per_element * num_elements;
  for (; dst < end; src += real_stride, dst += bytes_per_element) {
    memcpy(dst, src, bytes_per_element);
  }
  return bytes_needed;
}

bool VertexArrayObjectManager::IsDefaultVAOBound() const {
  return bound_vertex_array_object_ == default_vertex_array_object_;
}

// Returns true if buffers were setup.
bool VertexArrayObjectManager::SetupSimulatedClientSideBuffers(
    const char* function_name,
    GLES2Implementation* gl,
    GLES2CmdHelper* gl_helper,
    GLsizei num_elements,
    GLsizei primcount,
    bool* simulated) {
  *simulated = false;
#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
  if (!bound_vertex_array_object_->HaveEnabledClientSideBuffers()) {
    return true;
  }
  if (!IsDefaultVAOBound()) {
    gl->SetGLError(
        GL_INVALID_OPERATION, function_name,
        "client side arrays not allowed with vertex array object");
    return false;
  }
  *simulated = true;
  GLsizei total_size = 0;
  // Compute the size of the buffer we need.
  const VertexArrayObject::VertexAttribs& vertex_attribs =
      bound_vertex_array_object_->vertex_attribs();
  for (GLuint ii = 0; ii < vertex_attribs.size(); ++ii) {
    const VertexArrayObject::VertexAttrib& attrib = vertex_attribs[ii];
    if (attrib.IsClientSide() && attrib.enabled()) {
      size_t bytes_per_element =
          GLES2Util::GetGLTypeSizeForTexturesAndBuffers(attrib.type()) *
          attrib.size();
      GLsizei elements = (primcount && attrib.divisor() > 0) ?
          ((primcount - 1) / attrib.divisor() + 1) : num_elements;
      total_size += RoundUpToMultipleOf4(bytes_per_element * elements);
    }
  }
  gl_helper->BindBuffer(GL_ARRAY_BUFFER, array_buffer_id_);
  array_buffer_offset_ = 0;
  if (total_size > array_buffer_size_) {
    gl->BufferDataHelper(GL_ARRAY_BUFFER, total_size, NULL, GL_DYNAMIC_DRAW);
    array_buffer_size_ = total_size;
  }
  for (GLuint ii = 0; ii < vertex_attribs.size(); ++ii) {
    const VertexArrayObject::VertexAttrib& attrib = vertex_attribs[ii];
    if (attrib.IsClientSide() && attrib.enabled()) {
      size_t bytes_per_element =
          GLES2Util::GetGLTypeSizeForTexturesAndBuffers(attrib.type()) *
          attrib.size();
      GLsizei real_stride = attrib.stride() ?
          attrib.stride() : static_cast<GLsizei>(bytes_per_element);
      GLsizei elements = (primcount && attrib.divisor() > 0) ?
          ((primcount - 1) / attrib.divisor() + 1) : num_elements;
      GLsizei bytes_collected = CollectData(
          attrib.pointer(), bytes_per_element, real_stride, elements);
      gl->BufferSubDataHelper(
          GL_ARRAY_BUFFER, array_buffer_offset_, bytes_collected,
          collection_buffer_.get());
      gl_helper->VertexAttribPointer(
          ii, attrib.size(), attrib.type(), attrib.normalized(), 0,
          array_buffer_offset_);
      array_buffer_offset_ += RoundUpToMultipleOf4(bytes_collected);
      DCHECK_LE(array_buffer_offset_, array_buffer_size_);
    }
  }
#endif  // defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
  return true;
}

// Copies in indices to the service and returns the highest index accessed + 1
bool VertexArrayObjectManager::SetupSimulatedIndexAndClientSideBuffers(
    const char* function_name,
    GLES2Implementation* gl,
    GLES2CmdHelper* gl_helper,
    GLsizei count,
    GLenum type,
    GLsizei primcount,
    const void* indices,
    GLuint* offset,
    bool* simulated) {
  *simulated = false;
  *offset = ToGLuint(indices);
#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
  GLsizei num_elements = 0;
  if (bound_vertex_array_object_->bound_element_array_buffer() == 0) {
    *simulated = true;
    *offset = 0;
    GLsizei max_index = -1;
    switch (type) {
      case GL_UNSIGNED_BYTE: {
        const uint8* src = static_cast<const uint8*>(indices);
        for (GLsizei ii = 0; ii < count; ++ii) {
          if (src[ii] > max_index) {
            max_index = src[ii];
          }
        }
        break;
      }
      case GL_UNSIGNED_SHORT: {
        const uint16* src = static_cast<const uint16*>(indices);
        for (GLsizei ii = 0; ii < count; ++ii) {
          if (src[ii] > max_index) {
            max_index = src[ii];
          }
        }
        break;
      }
      case GL_UNSIGNED_INT: {
        uint32 max_glsizei = static_cast<uint32>(
            std::numeric_limits<GLsizei>::max());
        const uint32* src = static_cast<const uint32*>(indices);
        for (GLsizei ii = 0; ii < count; ++ii) {
          // Other parts of the API use GLsizei (signed) to store limits.
          // As such, if we encounter a index that cannot be represented with
          // an unsigned int we need to flag it as an error here.
          if(src[ii] > max_glsizei) {
            gl->SetGLError(
                GL_INVALID_OPERATION, function_name, "index too large.");
            return false;
          }
          GLsizei signed_index = static_cast<GLsizei>(src[ii]);
          if (signed_index > max_index) {
            max_index = signed_index;
          }
        }
        break;
      }
      default:
        break;
    }
    gl_helper->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_array_buffer_id_);
    GLsizei bytes_per_element =
        GLES2Util::GetGLTypeSizeForTexturesAndBuffers(type);
    GLsizei bytes_needed = bytes_per_element * count;
    if (bytes_needed > element_array_buffer_size_) {
      element_array_buffer_size_ = bytes_needed;
      gl->BufferDataHelper(
          GL_ELEMENT_ARRAY_BUFFER, bytes_needed, NULL, GL_DYNAMIC_DRAW);
    }
    gl->BufferSubDataHelper(
        GL_ELEMENT_ARRAY_BUFFER, 0, bytes_needed, indices);

    num_elements = max_index + 1;
  } else if (bound_vertex_array_object_->HaveEnabledClientSideBuffers()) {
    // Index buffer is GL buffer. Ask the service for the highest vertex
    // that will be accessed. Note: It doesn't matter if another context
    // changes the contents of any of the buffers. The service will still
    // validate the indices. We just need to know how much to copy across.
    num_elements = gl->GetMaxValueInBufferCHROMIUMHelper(
        bound_vertex_array_object_->bound_element_array_buffer(),
        count, type, ToGLuint(indices)) + 1;
  }

  bool simulated_client_side_buffers = false;
  SetupSimulatedClientSideBuffers(
      function_name, gl, gl_helper, num_elements, primcount,
      &simulated_client_side_buffers);
  *simulated = *simulated || simulated_client_side_buffers;
#endif  // defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
  return true;
}

}  // namespace gles2
}  // namespace gpu



/* [<][>][^][v][top][bottom][index][help] */