summaryrefslogtreecommitdiffstats
path: root/gpu/command_buffer/client/gles2_implementation.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gpu/command_buffer/client/gles2_implementation.cc')
-rw-r--r--gpu/command_buffer/client/gles2_implementation.cc552
1 files changed, 547 insertions, 5 deletions
diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc
index b3fe538..5eff5df 100644
--- a/gpu/command_buffer/client/gles2_implementation.cc
+++ b/gpu/command_buffer/client/gles2_implementation.cc
@@ -15,10 +15,318 @@ static GLuint ToGLuint(const void* ptr) {
return static_cast<GLuint>(reinterpret_cast<size_t>(ptr));
}
+#if defined(GLES2_SUPPORT_CLIENT_SIDE_BUFFERS)
+
+static GLsizei RoundUpToMultipleOf4(GLsizei size) {
+ return (size + 3) & ~3;
+}
+
+// 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 ClientSideBufferHelper {
+ 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 VertexAttribInfo {
+ public:
+ VertexAttribInfo()
+ : enabled_(false),
+ buffer_id_(0),
+ size_(0),
+ type_(0),
+ normalized_(GL_FALSE),
+ pointer_(NULL),
+ gl_stride_(0) {
+ }
+
+ bool enabled() const {
+ return enabled_;
+ }
+
+ void set_enabled(bool enabled) {
+ enabled_ = enabled;
+ }
+
+ GLuint buffer_id() const {
+ return buffer_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;
+ }
+
+ 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;
+ }
+
+ 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_;
+ };
+
+ ClientSideBufferHelper(GLuint max_vertex_attribs,
+ GLuint array_buffer_id,
+ GLuint element_array_buffer_id)
+ : max_vertex_attribs_(max_vertex_attribs),
+ num_client_side_pointers_enabled_(0),
+ 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) {
+ vertex_attrib_infos_.reset(new VertexAttribInfo[max_vertex_attribs]);
+ }
+
+ bool HaveEnabledClientSideBuffers() const {
+ return num_client_side_pointers_enabled_ > 0;
+ }
+
+ void SetAttribEnable(GLuint index, bool enabled) {
+ if (index < max_vertex_attribs_) {
+ VertexAttribInfo& info = vertex_attrib_infos_[index];
+ if (info.enabled() != enabled) {
+ if (info.IsClientSide()) {
+ num_client_side_pointers_enabled_ += enabled ? 1 : -1;
+ }
+ info.set_enabled(enabled);
+ }
+ }
+ }
+
+ void SetAttribPointer(
+ GLuint buffer_id,
+ GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride,
+ const void* ptr) {
+ if (index < max_vertex_attribs_) {
+ VertexAttribInfo& info = vertex_attrib_infos_[index];
+ if (info.IsClientSide() && info.enabled()) {
+ --num_client_side_pointers_enabled_;
+ }
+
+ info.SetInfo(buffer_id, size, type, normalized, stride, ptr);
+
+ if (info.IsClientSide() && info.enabled()) {
+ ++num_client_side_pointers_enabled_;
+ }
+ }
+ }
+
+ // Gets the Attrib pointer for an attrib but only if it's a client side
+ // pointer. Returns true if it got the pointer.
+ bool GetAttribPointer(GLuint index, GLenum pname, void** ptr) const {
+ const VertexAttribInfo* info = GetAttribInfo(index);
+ if (info && pname == GL_VERTEX_ATTRIB_ARRAY_POINTER) {
+ *ptr = const_cast<void*>(info->pointer());
+ return true;
+ }
+ return false;
+ }
+
+ // Gets an attrib info if it's in range and it's client side.
+ const VertexAttribInfo* GetAttribInfo(GLuint index) const {
+ if (index < max_vertex_attribs_) {
+ VertexAttribInfo* info = &vertex_attrib_infos_[index];
+ if (info->IsClientSide()) {
+ return info;
+ }
+ }
+ return NULL;
+ }
+
+ // Collects the data into the collection buffer and returns the number of
+ // bytes collected.
+ GLsizei 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;
+ }
+
+ // Returns true if buffers were setup.
+ void SetupSimualtedClientSideBuffers(
+ GLES2Implementation* gl,
+ GLES2CmdHelper* gl_helper,
+ GLsizei num_elements) {
+ GLsizei total_size = 0;
+ // Compute the size of the buffer we need.
+ for (GLuint ii = 0; ii < max_vertex_attribs_; ++ii) {
+ VertexAttribInfo& info = vertex_attrib_infos_[ii];
+ if (info.IsClientSide() && info.enabled()) {
+ size_t bytes_per_element =
+ GLES2Util::GetGLTypeSizeForTexturesAndBuffers(info.type()) *
+ info.size();
+ total_size += RoundUpToMultipleOf4(
+ bytes_per_element * num_elements);
+ }
+ }
+ gl_helper->BindBuffer(GL_ARRAY_BUFFER, array_buffer_id_);
+ array_buffer_offset_ = 0;
+ if (total_size > array_buffer_size_) {
+ gl->BufferData(GL_ARRAY_BUFFER, total_size, NULL, GL_DYNAMIC_DRAW);
+ array_buffer_size_ = total_size;
+ }
+ for (GLuint ii = 0; ii < max_vertex_attribs_; ++ii) {
+ VertexAttribInfo& info = vertex_attrib_infos_[ii];
+ if (info.IsClientSide() && info.enabled()) {
+ size_t bytes_per_element =
+ GLES2Util::GetGLTypeSizeForTexturesAndBuffers(info.type()) *
+ info.size();
+ GLsizei real_stride =
+ info.stride() ? info.stride() : bytes_per_element;
+ GLsizei bytes_collected = CollectData(
+ info.pointer(), bytes_per_element, real_stride, num_elements);
+ gl->BufferSubData(
+ GL_ARRAY_BUFFER, array_buffer_offset_, bytes_collected,
+ collection_buffer_.get());
+ gl_helper->VertexAttribPointer(
+ ii, info.size(), info.type(), info.normalized(), 0,
+ array_buffer_offset_);
+ array_buffer_offset_ += RoundUpToMultipleOf4(bytes_collected);
+ DCHECK_LE(array_buffer_offset_, array_buffer_size_);
+ }
+ }
+ }
+
+ // Copies in indices to the service and returns the highest index accessed + 1
+ GLsizei SetupSimulatedIndexBuffer(
+ GLES2Implementation* gl,
+ GLES2CmdHelper* gl_helper,
+ GLsizei count,
+ GLenum type,
+ const void* indices) {
+ 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->BufferData(GL_ELEMENT_ARRAY_BUFFER, bytes_needed, NULL,
+ GL_DYNAMIC_DRAW);
+ }
+ gl->BufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, bytes_needed, indices);
+ 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;
+ }
+ default:
+ break;
+ }
+ return max_index + 1;
+ }
+
+ private:
+ GLuint max_vertex_attribs_;
+ GLuint num_client_side_pointers_enabled_;
+ GLuint array_buffer_id_;
+ GLsizei array_buffer_size_;
+ GLsizei array_buffer_offset_;
+ GLuint element_array_buffer_id_;
+ GLsizei element_array_buffer_size_;
+ scoped_array<VertexAttribInfo> vertex_attrib_infos_;
+ GLsizei collection_buffer_size_;
+ scoped_array<int8> collection_buffer_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClientSideBufferHelper);
+};
+
+#endif // defined(GLES2_SUPPORT_CLIENT_SIDE_BUFFERS)
+
+
#if !defined(COMPILER_MSVC)
const size_t GLES2Implementation::kMaxSizeOfSimpleResult;
#endif
+COMPILE_ASSERT(gpu::kInvalidResource == 0,
+ INVALID_RESOURCE_NOT_0_AS_GL_EXPECTS);
+
GLES2Implementation::GLES2Implementation(
GLES2CmdHelper* helper,
size_t transfer_buffer_size,
@@ -30,16 +338,34 @@ GLES2Implementation::GLES2Implementation(
transfer_buffer_id_(transfer_buffer_id),
pack_alignment_(4),
unpack_alignment_(4),
+#if defined(GLES2_SUPPORT_CLIENT_SIDE_BUFFERS)
+ bound_array_buffer_id_(0),
+ bound_element_array_buffer_id_(0),
+#endif
error_bits_(0) {
- // Eat 1 id so we start at 1 instead of 0.
- GLuint eat;
- MakeIds(1, &eat);
// Allocate space for simple GL results.
result_buffer_ = transfer_buffer_.Alloc(kMaxSizeOfSimpleResult);
result_shm_offset_ = transfer_buffer_.GetOffset(result_buffer_);
+
+#if defined(GLES2_SUPPORT_CLIENT_SIDE_BUFFERS)
+ GLint max_vertex_attribs;
+ GetIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_vertex_attribs);
+ id_allocator_.MarkAsUsed(kClientSideArrayId);
+ id_allocator_.MarkAsUsed(kClientSideElementArrayId);
+
+ reserved_ids_[0] = kClientSideArrayId;
+ reserved_ids_[1] = kClientSideElementArrayId;
+
+ client_side_buffer_helper_.reset(new ClientSideBufferHelper(
+ max_vertex_attribs,
+ kClientSideArrayId,
+ kClientSideElementArrayId));
+#endif
}
GLES2Implementation::~GLES2Implementation() {
+ GLuint buffers[] = { kClientSideArrayId, kClientSideElementArrayId, };
+ DeleteBuffers(arraysize(buffers), &buffers[0]);
transfer_buffer_.Free(result_buffer_);
}
@@ -178,7 +504,50 @@ void GLES2Implementation::SetBucketAsString(
void GLES2Implementation::DrawElements(
GLenum mode, GLsizei count, GLenum type, const void* indices) {
+ if (count < 0) {
+ SetGLError(GL_INVALID_VALUE);
+ return;
+ }
+ if (count == 0) {
+ return;
+ }
+#if defined(GLES2_SUPPORT_CLIENT_SIDE_BUFFERS)
+ bool have_client_side =
+ client_side_buffer_helper_->HaveEnabledClientSideBuffers();
+ GLsizei num_elements = 0;
+ GLuint offset = ToGLuint(indices);
+ if (bound_element_array_buffer_id_ == 0) {
+ // Index buffer is client side array.
+ // Copy to buffer, scan for highest index.
+ num_elements = client_side_buffer_helper_->SetupSimulatedIndexBuffer(
+ this, helper_, count, type, indices);
+ offset = 0;
+ } else {
+ // 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.
+ if (have_client_side) {
+ num_elements = GetMaxValueInBuffer(
+ bound_element_array_buffer_id_, count, type, ToGLuint(indices)) + 1;
+ }
+ }
+ if (have_client_side) {
+ client_side_buffer_helper_->SetupSimualtedClientSideBuffers(
+ this, helper_, num_elements);
+ }
+ helper_->DrawElements(mode, count, type, offset);
+ if (have_client_side) {
+ // Restore the user's current binding.
+ helper_->BindBuffer(GL_ARRAY_BUFFER, bound_array_buffer_id_);
+ }
+ if (bound_element_array_buffer_id_ == 0) {
+ // Restore the element array binding.
+ helper_->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+#else
helper_->DrawElements(mode, count, type, ToGLuint(indices));
+#endif
}
void GLES2Implementation::Flush() {
@@ -212,11 +581,20 @@ void GLES2Implementation::BindAttribLocation(
void GLES2Implementation::GetVertexAttribPointerv(
GLuint index, GLenum pname, void** ptr) {
+#if defined(GLES2_SUPPORT_CLIENT_SIDE_BUFFERS)
+ // If it's a client side buffer the client has the data.
+ if (client_side_buffer_helper_->GetAttribPointer(index, pname, ptr)) {
+ return;
+ }
+#endif // defined(GLES2_SUPPORT_CLIENT_SIDE_BUFFERS)
+
+ typedef gles2::GetVertexAttribPointerv::Result Result;
+ Result* result = GetResultAs<Result*>();
+ result->SetNumResults(0);
helper_->GetVertexAttribPointerv(
index, pname, result_shm_id(), result_shm_offset());
WaitForCmd();
- static_cast<gles2::GetVertexAttribPointerv::Result*>(
- result_buffer_)->CopyResult(ptr);
+ result->CopyResult(ptr);
};
GLint GLES2Implementation::GetAttribLocation(
@@ -287,8 +665,19 @@ void GLES2Implementation::PixelStorei(GLenum pname, GLint param) {
void GLES2Implementation::VertexAttribPointer(
GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride,
const void* ptr) {
+#if defined(GLES2_SUPPORT_CLIENT_SIDE_BUFFERS)
+ // Record the info on the client side.
+ client_side_buffer_helper_->SetAttribPointer(
+ bound_array_buffer_id_, index, size, type, normalized, stride, ptr);
+ if (bound_array_buffer_id_ != 0) {
+ // Only report NON client side buffers to the service.
+ helper_->VertexAttribPointer(index, size, type, normalized, stride,
+ ToGLuint(ptr));
+ }
+#else // !defined(GLES2_SUPPORT_CLIENT_SIDE_BUFFERS)
helper_->VertexAttribPointer(index, size, type, normalized, stride,
ToGLuint(ptr));
+#endif // !defined(GLES2_SUPPORT_CLIENT_SIDE_BUFFERS)
}
void GLES2Implementation::ShaderSource(
@@ -770,5 +1159,158 @@ void GLES2Implementation::ReadPixels(
}
}
+#if defined(GLES2_SUPPORT_CLIENT_SIDE_BUFFERS)
+bool GLES2Implementation::IsReservedId(GLuint id) {
+ for (size_t ii = 0; ii < arraysize(reserved_ids_); ++ii) {
+ if (id == reserved_ids_[ii]) {
+ return true;
+ }
+ }
+ return false;
+}
+#else
+bool GLES2Implementation::IsReservedId(GLuint) { // NOLINT
+ return false;
+}
+#endif
+
+#if defined(GLES2_SUPPORT_CLIENT_SIDE_BUFFERS)
+
+void GLES2Implementation::BindBuffer(GLenum target, GLuint buffer) {
+ if (IsReservedId(buffer)) {
+ SetGLError(GL_INVALID_OPERATION);
+ return;
+ }
+ if (buffer != 0) {
+ id_allocator_.MarkAsUsed(buffer);
+ }
+ switch (target) {
+ case GL_ARRAY_BUFFER:
+ bound_array_buffer_id_ = buffer;
+ break;
+ case GL_ELEMENT_ARRAY_BUFFER:
+ bound_element_array_buffer_id_ = buffer;
+ break;
+ default:
+ break;
+ }
+ helper_->BindBuffer(target, buffer);
+}
+
+void GLES2Implementation::DeleteBuffers(GLsizei n, const GLuint* buffers) {
+ FreeIds(n, buffers);
+ for (GLsizei ii = 0; ii < n; ++ii) {
+ if (buffers[ii] == bound_array_buffer_id_) {
+ bound_array_buffer_id_ = 0;
+ }
+ if (buffers[ii] == bound_element_array_buffer_id_) {
+ bound_element_array_buffer_id_ = 0;
+ }
+ }
+ // TODO(gman): compute the number of buffers we can delete in 1 call
+ // based on the size of command buffer and the limit of argument size
+ // for comments then loop to delete all the buffers. The same needs to
+ // happen for GenBuffer, GenTextures, DeleteTextures, etc...
+ helper_->DeleteBuffersImmediate(n, buffers);
+}
+
+void GLES2Implementation::DisableVertexAttribArray(GLuint index) {
+ client_side_buffer_helper_->SetAttribEnable(index, false);
+ helper_->DisableVertexAttribArray(index);
+}
+
+void GLES2Implementation::EnableVertexAttribArray(GLuint index) {
+ client_side_buffer_helper_->SetAttribEnable(index, true);
+ helper_->EnableVertexAttribArray(index);
+}
+
+void GLES2Implementation::DrawArrays(GLenum mode, GLint first, GLsizei count) {
+ if (count < 0) {
+ SetGLError(GL_INVALID_VALUE);
+ return;
+ }
+ bool have_client_side =
+ client_side_buffer_helper_->HaveEnabledClientSideBuffers();
+ if (have_client_side) {
+ client_side_buffer_helper_->SetupSimualtedClientSideBuffers(
+ this, helper_, first + count);
+ }
+ helper_->DrawArrays(mode, first, count);
+ if (have_client_side) {
+ // Restore the user's current binding.
+ helper_->BindBuffer(GL_ARRAY_BUFFER, bound_array_buffer_id_);
+ }
+}
+
+bool GLES2Implementation::GetVertexAttribHelper(
+ GLuint index, GLenum pname, uint32* param) {
+ const ClientSideBufferHelper::VertexAttribInfo* info =
+ client_side_buffer_helper_->GetAttribInfo(index);
+ if (!info) {
+ return false;
+ }
+
+ switch (pname) {
+ case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
+ *param = info->buffer_id();
+ break;
+ case GL_VERTEX_ATTRIB_ARRAY_ENABLED:
+ *param = info->enabled();
+ break;
+ case GL_VERTEX_ATTRIB_ARRAY_SIZE:
+ *param = info->size();
+ break;
+ case GL_VERTEX_ATTRIB_ARRAY_STRIDE:
+ *param = info->stride();
+ break;
+ case GL_VERTEX_ATTRIB_ARRAY_TYPE:
+ *param = info->type();
+ break;
+ case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED:
+ *param = info->normalized();
+ break;
+ case GL_CURRENT_VERTEX_ATTRIB:
+ return false; // pass through to service side.
+ default:
+ SetGLError(GL_INVALID_ENUM);
+ break;
+ }
+ return true;
+}
+
+void GLES2Implementation::GetVertexAttribfv(
+ GLuint index, GLenum pname, GLfloat* params) {
+ uint32 value = 0;
+ if (GetVertexAttribHelper(index, pname, &value)) {
+ *params = static_cast<float>(value);
+ return;
+ }
+ typedef GetVertexAttribfv::Result Result;
+ Result* result = GetResultAs<Result*>();
+ result->SetNumResults(0);
+ helper_->GetVertexAttribfv(
+ index, pname, result_shm_id(), result_shm_offset());
+ WaitForCmd();
+ result->CopyResult(params);
+}
+
+void GLES2Implementation::GetVertexAttribiv(
+ GLuint index, GLenum pname, GLint* params) {
+ uint32 value = 0;
+ if (GetVertexAttribHelper(index, pname, &value)) {
+ *params = value;
+ return;
+ }
+ typedef GetVertexAttribiv::Result Result;
+ Result* result = GetResultAs<Result*>();
+ result->SetNumResults(0);
+ helper_->GetVertexAttribiv(
+ index, pname, result_shm_id(), result_shm_offset());
+ WaitForCmd();
+ result->CopyResult(params);
+}
+
+#endif // defined(GLES2_SUPPORT_CLIENT_SIDE_BUFFERS)
+
} // namespace gles2
} // namespace gpu