This source file includes following definitions.
- RoundUpToMultipleOf4
- ToGLuint
- divisor_
- enabled
- set_enabled
- buffer_id
- set_buffer_id
- type
- size
- stride
- normalized
- pointer
- IsClientSide
- divisor
- SetInfo
- SetDivisor
- vertex_attribs
- bound_element_array_buffer
- bound_element_array_buffer_id_
- UnbindBuffer
- BindElementArray
- HaveEnabledClientSideBuffers
- SetAttribEnable
- SetAttribPointer
- GetVertexAttrib
- SetAttribDivisor
- GetAttribPointer
- GetAttrib
- bound_vertex_array_object_
- IsReservedId
- bound_element_array_buffer
- UnbindBuffer
- BindElementArray
- GenVertexArrays
- DeleteVertexArrays
- BindVertexArray
- HaveEnabledClientSideBuffers
- SetAttribEnable
- GetVertexAttrib
- GetAttribPointer
- SetAttribPointer
- SetAttribDivisor
- CollectData
- IsDefaultVAOBound
- SetupSimulatedClientSideBuffers
- SetupSimulatedIndexAndClientSideBuffers
#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
static GLuint ToGLuint(const void* ptr) {
return static_cast<GLuint>(reinterpret_cast<size_t>(ptr));
}
class GLES2_IMPL_EXPORT VertexArrayObject {
public:
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:
bool enabled_;
GLuint buffer_id_;
GLint size_;
GLenum type_;
GLboolean normalized_;
const GLvoid* pointer_;
GLsizei gl_stride_;
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_;
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;
break;
}
return true;
}
void VertexArrayObject::SetAttribDivisor(GLuint index, GLuint divisor) {
if (index < vertex_attribs_.size()) {
VertexAttrib& attrib = vertex_attribs_[index];
attrib.SetDivisor(divisor);
}
}
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;
}
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) {
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);
}
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_;
}
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;
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
return true;
}
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) {
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()) {
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
return true;
}
}
}