summaryrefslogtreecommitdiffstats
path: root/gpu/command_buffer/client
diff options
context:
space:
mode:
authorgman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-13 08:12:28 +0000
committergman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-13 08:12:28 +0000
commit2fe6c79fdf2d51e309bfd4d6a3b21cdf5442dc69 (patch)
treec099bef596ace5aa5a3459e1006bbf588c7c857d /gpu/command_buffer/client
parentb1d7db76383c55c7bff0551270d8fdd339f0bdec (diff)
downloadchromium_src-2fe6c79fdf2d51e309bfd4d6a3b21cdf5442dc69.zip
chromium_src-2fe6c79fdf2d51e309bfd4d6a3b21cdf5442dc69.tar.gz
chromium_src-2fe6c79fdf2d51e309bfd4d6a3b21cdf5442dc69.tar.bz2
Adds client side arrays
The code is conditional. I guess my gaming side spidey senses can't stand the overhead but I could be convinced to make it non-conditional. TEST=various unit tests BUG=none Review URL: http://codereview.chromium.org/1629004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@44334 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gpu/command_buffer/client')
-rw-r--r--gpu/command_buffer/client/gles2_c_lib_autogen.h5
-rw-r--r--gpu/command_buffer/client/gles2_cmd_helper_autogen.h7
-rw-r--r--gpu/command_buffer/client/gles2_implementation.cc552
-rw-r--r--gpu/command_buffer/client/gles2_implementation.h137
-rw-r--r--gpu/command_buffer/client/gles2_implementation_autogen.h110
-rw-r--r--gpu/command_buffer/client/gles2_implementation_unittest.cc677
-rw-r--r--gpu/command_buffer/client/id_allocator.cc49
-rw-r--r--gpu/command_buffer/client/id_allocator.h37
-rw-r--r--gpu/command_buffer/client/id_allocator_test.cc36
9 files changed, 1473 insertions, 137 deletions
diff --git a/gpu/command_buffer/client/gles2_c_lib_autogen.h b/gpu/command_buffer/client/gles2_c_lib_autogen.h
index 777a2d6..c3e445a 100644
--- a/gpu/command_buffer/client/gles2_c_lib_autogen.h
+++ b/gpu/command_buffer/client/gles2_c_lib_autogen.h
@@ -503,6 +503,11 @@ void GLES2Viewport(GLint x, GLint y, GLsizei width, GLsizei height) {
void GLES2SwapBuffers() {
gles2::GetGLContext()->SwapBuffers();
}
+GLuint GLES2GetMaxValueInBuffer(
+ GLuint buffer_id, GLsizei count, GLenum type, GLuint offset) {
+ return gles2::GetGLContext()->GetMaxValueInBuffer(
+ buffer_id, count, type, offset);
+}
#endif // GPU_COMMAND_BUFFER_CLIENT_GLES2_C_LIB_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
index 42f6608..bb37150 100644
--- a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
+++ b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
@@ -1120,5 +1120,12 @@
c.Init();
}
+ void GetMaxValueInBuffer(
+ GLuint buffer_id, GLsizei count, GLenum type, GLuint offset,
+ uint32 result_shm_id, uint32 result_shm_offset) {
+ gles2::GetMaxValueInBuffer& c = GetCmdSpace<gles2::GetMaxValueInBuffer>();
+ c.Init(buffer_id, count, type, offset, result_shm_id, result_shm_offset);
+ }
+
#endif // GPU_COMMAND_BUFFER_CLIENT_GLES2_CMD_HELPER_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc
index b3fe538..c39af8a 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;
+ 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;
+ 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
diff --git a/gpu/command_buffer/client/gles2_implementation.h b/gpu/command_buffer/client/gles2_implementation.h
index fd98686..4200458 100644
--- a/gpu/command_buffer/client/gles2_implementation.h
+++ b/gpu/command_buffer/client/gles2_implementation.h
@@ -9,13 +9,18 @@
#include <string>
#include <vector>
#include "../common/gles2_cmd_utils.h"
+#include "../common/scoped_ptr.h"
#include "../client/gles2_cmd_helper.h"
#include "../client/id_allocator.h"
#include "../client/fenced_allocator.h"
+#define GLES2_SUPPORT_CLIENT_SIDE_BUFFERS 1
+
namespace gpu {
namespace gles2 {
+class ClientSideBufferHelper;
+
// This class emulates GLES2 over command buffers. It can be used by a client
// program so that the program does not need deal with shared memory and command
// buffer management. See gl2_lib.h. Note that there is a performance gain to
@@ -24,6 +29,22 @@ namespace gles2 {
// shared memory and synchronization issues.
class GLES2Implementation {
public:
+ // The maxiumum result size from simple GL get commands.
+ static const size_t kMaxSizeOfSimpleResult = 16 * sizeof(uint32); // NOLINT.
+
+ // used for testing only. If more things are reseved add them here.
+ static const unsigned int kStartingOffset = kMaxSizeOfSimpleResult;
+
+ // The bucket used for results. Public for testing only.
+ static const uint32 kResultBucketId = 1;
+
+ // Alignment of allocations.
+ static const unsigned int kAlignment = 4;
+
+ // GL names for the buffers used to emulate client side buffers.
+ static const GLuint kClientSideArrayId = 0xFEDCBA98u;
+ static const GLuint kClientSideElementArrayId = 0xFEDCBA99u;
+
GLES2Implementation(
GLES2CmdHelper* helper,
size_t transfer_buffer_size,
@@ -43,6 +64,68 @@ class GLES2Implementation {
// this file instead of having to edit some template or the code generator.
#include "../client/gles2_implementation_autogen.h"
+ #if defined(GLES2_SUPPORT_CLIENT_SIDE_BUFFERS)
+ void BindBuffer(GLenum target, GLuint buffer);
+ void DeleteBuffers(GLsizei n, const GLuint* buffers);
+ void DisableVertexAttribArray(GLuint index);
+ void DrawArrays(GLenum mode, GLint first, GLsizei count);
+ void EnableVertexAttribArray(GLuint index);
+ void GetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params);
+ void GetVertexAttribiv(GLuint index, GLenum pname, GLint* params);
+ #else
+ void BindBuffer(GLenum target, GLuint buffer) {
+ if (IsReservedId(buffer)) {
+ SetGLError(GL_INVALID_OPERATION);
+ return;
+ }
+ if (buffer != 0) {
+ id_allocator_.MarkAsUsed(buffer);
+ }
+ helper_->BindBuffer(target, buffer);
+ }
+
+ void DeleteBuffers(GLsizei n, const GLuint* buffers) {
+ FreeIds(n, buffers);
+ helper_->DeleteBuffersImmediate(n, buffers);
+ }
+
+ void DisableVertexAttribArray(GLuint index) {
+ helper_->DisableVertexAttribArray(index);
+ }
+
+ void DrawArrays(GLenum mode, GLint first, GLsizei count) {
+ if (count < 0) {
+ SetGLError(GL_INVALID_VALUE);
+ return;
+ }
+ helper_->DrawArrays(mode, first, count);
+ }
+
+ void EnableVertexAttribArray(GLuint index) {
+ helper_->EnableVertexAttribArray(index);
+ }
+
+ void GetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params) {
+ 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 GetVertexAttribiv(GLuint index, GLenum pname, GLint* params) {
+ 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
+
// Makes a set of Ids for glGen___ functions.
void MakeIds(GLsizei n, GLuint* ids);
@@ -50,6 +133,30 @@ class GLES2Implementation {
void FreeIds(GLsizei n, const GLuint* ids);
private:
+ // Wraps FencedAllocatorWrapper to provide aligned allocations.
+ class AlignedFencedAllocator : public FencedAllocatorWrapper {
+ public:
+ AlignedFencedAllocator(unsigned int size,
+ CommandBufferHelper *helper,
+ void *base)
+ : FencedAllocatorWrapper(size, helper, base) {
+ }
+
+ static unsigned int RoundToAlignment(unsigned int size) {
+ return (size + kAlignment - 1) & ~(kAlignment - 1);
+ }
+
+ // Overrriden from FencedAllocatorWrapper
+ void *Alloc(unsigned int size) {
+ return FencedAllocatorWrapper::Alloc(RoundToAlignment(size));
+ }
+
+ // Overrriden from FencedAllocatorWrapper
+ template <typename T> T *AllocTyped(unsigned int count) {
+ return static_cast<T *>(Alloc(count * sizeof(T)));
+ }
+ };
+
// Gets the shared memory id for the result buffer.
uint32 result_shm_id() const {
return transfer_buffer_id_;
@@ -95,14 +202,22 @@ class GLES2Implementation {
// Sets the contents of a bucket as a string.
void SetBucketAsString(uint32 bucket_id, const std::string& str);
- // The maxiumum result size from simple GL get commands.
- static const size_t kMaxSizeOfSimpleResult = 16 * sizeof(uint32); // NOLINT.
- static const uint32 kResultBucketId = 1;
+ // Returns true if id is reserved.
+ bool IsReservedId(GLuint id);
+
+#if defined(GLES2_SUPPORT_CLIENT_SIDE_BUFFERS)
+ // Helper for GetVertexAttrib
+ bool GetVertexAttribHelper(GLuint index, GLenum pname, uint32* param);
+
+ // Asks the service for the max index in an element array buffer.
+ GLsizei GetMaxIndexInElementArrayBuffer(
+ GLuint buffer_id, GLsizei count, GLenum type, GLuint offset);
+#endif
GLES2Util util_;
GLES2CmdHelper* helper_;
IdAllocator id_allocator_;
- FencedAllocatorWrapper transfer_buffer_;
+ AlignedFencedAllocator transfer_buffer_;
int transfer_buffer_id_;
void* result_buffer_;
uint32 result_shm_offset_;
@@ -113,6 +228,20 @@ class GLES2Implementation {
// unpack alignment as last set by glPixelStorei
GLint unpack_alignment_;
+#if defined(GLES2_SUPPORT_CLIENT_SIDE_BUFFERS)
+ // The currently bound array buffer.
+ GLuint bound_array_buffer_id_;
+
+ // The currently bound element array buffer.
+ GLuint bound_element_array_buffer_id_;
+
+ // Info for each vertex attribute saved so we can simulate client side
+ // buffers.
+ scoped_ptr<ClientSideBufferHelper> client_side_buffer_helper_;
+
+ GLuint reserved_ids_[2];
+#endif
+
// Current GL error bits.
uint32 error_bits_;
diff --git a/gpu/command_buffer/client/gles2_implementation_autogen.h b/gpu/command_buffer/client/gles2_implementation_autogen.h
index 04e5d76..9938cb8 100644
--- a/gpu/command_buffer/client/gles2_implementation_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_autogen.h
@@ -19,19 +19,36 @@ void AttachShader(GLuint program, GLuint shader) {
void BindAttribLocation(GLuint program, GLuint index, const char* name);
-void BindBuffer(GLenum target, GLuint buffer) {
- helper_->BindBuffer(target, buffer);
-}
-
void BindFramebuffer(GLenum target, GLuint framebuffer) {
+ if (IsReservedId(framebuffer)) {
+ SetGLError(GL_INVALID_OPERATION);
+ return;
+ }
+ if (framebuffer != 0) {
+ id_allocator_.MarkAsUsed(framebuffer);
+ }
helper_->BindFramebuffer(target, framebuffer);
}
void BindRenderbuffer(GLenum target, GLuint renderbuffer) {
+ if (IsReservedId(renderbuffer)) {
+ SetGLError(GL_INVALID_OPERATION);
+ return;
+ }
+ if (renderbuffer != 0) {
+ id_allocator_.MarkAsUsed(renderbuffer);
+ }
helper_->BindRenderbuffer(target, renderbuffer);
}
void BindTexture(GLenum target, GLuint texture) {
+ if (IsReservedId(texture)) {
+ SetGLError(GL_INVALID_OPERATION);
+ return;
+ }
+ if (texture != 0) {
+ id_allocator_.MarkAsUsed(texture);
+ }
helper_->BindTexture(target, texture);
}
@@ -153,11 +170,6 @@ void CullFace(GLenum mode) {
helper_->CullFace(mode);
}
-void DeleteBuffers(GLsizei n, const GLuint* buffers) {
- FreeIds(n, buffers);
- helper_->DeleteBuffersImmediate(n, buffers);
-}
-
void DeleteFramebuffers(GLsizei n, const GLuint* framebuffers) {
FreeIds(n, framebuffers);
helper_->DeleteFramebuffersImmediate(n, framebuffers);
@@ -201,18 +213,6 @@ void Disable(GLenum cap) {
helper_->Disable(cap);
}
-void DisableVertexAttribArray(GLuint index) {
- helper_->DisableVertexAttribArray(index);
-}
-
-void DrawArrays(GLenum mode, GLint first, GLsizei count) {
- if (count < 0) {
- SetGLError(GL_INVALID_VALUE);
- return;
- }
- helper_->DrawArrays(mode, first, count);
-}
-
void DrawElements(
GLenum mode, GLsizei count, GLenum type, const void* indices);
@@ -220,10 +220,6 @@ void Enable(GLenum cap) {
helper_->Enable(cap);
}
-void EnableVertexAttribArray(GLuint index) {
- helper_->EnableVertexAttribArray(index);
-}
-
void Finish();
void Flush();
@@ -286,7 +282,8 @@ void GetBooleanv(GLenum pname, GLboolean* params) {
typedef GetBooleanv::Result Result;
Result* result = GetResultAs<Result*>();
result->SetNumResults(0);
- helper_->GetBooleanv(pname, result_shm_id(), result_shm_offset());
+ helper_->GetBooleanv(pname,
+ result_shm_id(), result_shm_offset());
WaitForCmd();
result->CopyResult(params);
}
@@ -294,8 +291,8 @@ void GetBufferParameteriv(GLenum target, GLenum pname, GLint* params) {
typedef GetBufferParameteriv::Result Result;
Result* result = GetResultAs<Result*>();
result->SetNumResults(0);
- helper_->GetBufferParameteriv(
- target, pname, result_shm_id(), result_shm_offset());
+ helper_->GetBufferParameteriv(target, pname,
+ result_shm_id(), result_shm_offset());
WaitForCmd();
result->CopyResult(params);
}
@@ -305,7 +302,8 @@ void GetFloatv(GLenum pname, GLfloat* params) {
typedef GetFloatv::Result Result;
Result* result = GetResultAs<Result*>();
result->SetNumResults(0);
- helper_->GetFloatv(pname, result_shm_id(), result_shm_offset());
+ helper_->GetFloatv(pname,
+ result_shm_id(), result_shm_offset());
WaitForCmd();
result->CopyResult(params);
}
@@ -314,8 +312,8 @@ void GetFramebufferAttachmentParameteriv(
typedef GetFramebufferAttachmentParameteriv::Result Result;
Result* result = GetResultAs<Result*>();
result->SetNumResults(0);
- helper_->GetFramebufferAttachmentParameteriv(
- target, attachment, pname, result_shm_id(), result_shm_offset());
+ helper_->GetFramebufferAttachmentParameteriv(target, attachment, pname,
+ result_shm_id(), result_shm_offset());
WaitForCmd();
result->CopyResult(params);
}
@@ -323,7 +321,8 @@ void GetIntegerv(GLenum pname, GLint* params) {
typedef GetIntegerv::Result Result;
Result* result = GetResultAs<Result*>();
result->SetNumResults(0);
- helper_->GetIntegerv(pname, result_shm_id(), result_shm_offset());
+ helper_->GetIntegerv(pname,
+ result_shm_id(), result_shm_offset());
WaitForCmd();
result->CopyResult(params);
}
@@ -331,7 +330,8 @@ void GetProgramiv(GLuint program, GLenum pname, GLint* params) {
typedef GetProgramiv::Result Result;
Result* result = GetResultAs<Result*>();
result->SetNumResults(0);
- helper_->GetProgramiv(program, pname, result_shm_id(), result_shm_offset());
+ helper_->GetProgramiv(program, pname,
+ result_shm_id(), result_shm_offset());
WaitForCmd();
result->CopyResult(params);
}
@@ -356,8 +356,8 @@ void GetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params) {
typedef GetRenderbufferParameteriv::Result Result;
Result* result = GetResultAs<Result*>();
result->SetNumResults(0);
- helper_->GetRenderbufferParameteriv(
- target, pname, result_shm_id(), result_shm_offset());
+ helper_->GetRenderbufferParameteriv(target, pname,
+ result_shm_id(), result_shm_offset());
WaitForCmd();
result->CopyResult(params);
}
@@ -365,7 +365,8 @@ void GetShaderiv(GLuint shader, GLenum pname, GLint* params) {
typedef GetShaderiv::Result Result;
Result* result = GetResultAs<Result*>();
result->SetNumResults(0);
- helper_->GetShaderiv(shader, pname, result_shm_id(), result_shm_offset());
+ helper_->GetShaderiv(shader, pname,
+ result_shm_id(), result_shm_offset());
WaitForCmd();
result->CopyResult(params);
}
@@ -412,8 +413,8 @@ void GetTexParameterfv(GLenum target, GLenum pname, GLfloat* params) {
typedef GetTexParameterfv::Result Result;
Result* result = GetResultAs<Result*>();
result->SetNumResults(0);
- helper_->GetTexParameterfv(
- target, pname, result_shm_id(), result_shm_offset());
+ helper_->GetTexParameterfv(target, pname,
+ result_shm_id(), result_shm_offset());
WaitForCmd();
result->CopyResult(params);
}
@@ -421,8 +422,8 @@ void GetTexParameteriv(GLenum target, GLenum pname, GLint* params) {
typedef GetTexParameteriv::Result Result;
Result* result = GetResultAs<Result*>();
result->SetNumResults(0);
- helper_->GetTexParameteriv(
- target, pname, result_shm_id(), result_shm_offset());
+ helper_->GetTexParameteriv(target, pname,
+ result_shm_id(), result_shm_offset());
WaitForCmd();
result->CopyResult(params);
}
@@ -432,24 +433,6 @@ void GetUniformiv(GLuint program, GLint location, GLint* params);
GLint GetUniformLocation(GLuint program, const char* name);
-void GetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params) {
- 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 GetVertexAttribiv(GLuint index, GLenum pname, GLint* params) {
- 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);
-}
void GetVertexAttribPointerv(GLuint index, GLenum pname, void** pointer);
void Hint(GLenum target, GLenum mode) {
@@ -763,5 +746,16 @@ void Viewport(GLint x, GLint y, GLsizei width, GLsizei height) {
void SwapBuffers();
+GLuint GetMaxValueInBuffer(
+ GLuint buffer_id, GLsizei count, GLenum type, GLuint offset) {
+ typedef GetMaxValueInBuffer::Result Result;
+ Result* result = GetResultAs<Result*>();
+ *result = 0;
+ helper_->GetMaxValueInBuffer(
+ buffer_id, count, type, offset, result_shm_id(), result_shm_offset());
+ WaitForCmd();
+ return *result;
+}
+
#endif // GPU_COMMAND_BUFFER_CLIENT_GLES2_IMPLEMENTATION_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/gles2_implementation_unittest.cc b/gpu/command_buffer/client/gles2_implementation_unittest.cc
new file mode 100644
index 0000000..79cf95e
--- /dev/null
+++ b/gpu/command_buffer/client/gles2_implementation_unittest.cc
@@ -0,0 +1,677 @@
+// Copyright (c) 2009 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.
+
+// Tests for the Command Buffer Helper.
+
+#include "gpu/command_buffer/client/gles2_implementation.h"
+#include "gpu/command_buffer/common/command_buffer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace gpu {
+
+class GLES2MockCommandBufferHelper : public CommandBuffer {
+ public:
+ static const int32 kTransferBufferId = 0x123;
+
+ GLES2MockCommandBufferHelper() { }
+ virtual ~GLES2MockCommandBufferHelper() { }
+
+ // CommandBuffer implementation:
+ virtual bool Initialize(int32 size) {
+ ring_buffer_.reset(new CommandBufferEntry[size]);
+ ring_buffer_buffer_.ptr = ring_buffer_.get();
+ ring_buffer_buffer_.size = size * sizeof(ring_buffer_[0]);
+ state_.size = size;
+ state_.token = 10000; // All token checks in the tests should pass.
+ return true;
+ }
+
+ virtual Buffer GetRingBuffer() {
+ return ring_buffer_buffer_;
+ }
+
+ virtual State GetState() {
+ return state_;
+ }
+
+ virtual State Flush(int32 put_offset) {
+ state_.put_offset = put_offset;
+ state_.get_offset = put_offset;
+ OnFlush(transfer_buffer_buffer_.ptr);
+
+ return state_;
+ }
+
+ virtual void SetGetOffset(int32 get_offset) {
+ state_.get_offset = get_offset;
+ }
+
+ virtual int32 CreateTransferBuffer(size_t size) {
+ transfer_buffer_.reset(new int8[size]);
+ transfer_buffer_buffer_.ptr = transfer_buffer_.get();
+ transfer_buffer_buffer_.size = size;
+ return kTransferBufferId;
+ }
+
+ virtual void DestroyTransferBuffer(int32) { // NOLINT
+ NOTREACHED();
+ }
+
+ virtual Buffer GetTransferBuffer(int32 id) {
+ DCHECK_EQ(id, kTransferBufferId);
+ return transfer_buffer_buffer_;
+ }
+
+ virtual void SetToken(int32 token) {
+ NOTREACHED();
+ state_.token = token;
+ }
+
+ virtual void SetParseError(error::Error error) {
+ NOTREACHED();
+ state_.error = error;
+ }
+
+ virtual void OnFlush(void* transfer_buffer) = 0;
+
+ private:
+ scoped_array<int8> transfer_buffer_;
+ Buffer transfer_buffer_buffer_;
+ scoped_array<CommandBufferEntry> ring_buffer_;
+ Buffer ring_buffer_buffer_;
+ State state_;
+};
+
+class MockGLES2CommandBuffer : public GLES2MockCommandBufferHelper {
+ public:
+ virtual ~MockGLES2CommandBuffer() {
+ }
+
+ // This is so we can use all the gmock functions when Flush is called.
+ MOCK_METHOD1(OnFlush, void(void* result));
+};
+
+namespace gles2 {
+
+using testing::Return;
+using testing::Mock;
+using testing::Truly;
+using testing::Sequence;
+using testing::DoAll;
+using testing::Invoke;
+using testing::_;
+
+ACTION_P(SetMemory, obj) {
+ memcpy(arg0, &obj, sizeof(obj));
+}
+
+// Used to help set the transfer buffer result to SizedResult of a single value.
+template <typename T>
+class SizedResultHelper {
+ public:
+ explicit SizedResultHelper(T result)
+ : size_(sizeof(result)),
+ result_(result) {
+ }
+
+ private:
+ uint32 size_;
+ T result_;
+};
+
+// Struct to make it easy to pass a vec4 worth of floats.
+struct FourFloats {
+ FourFloats(float _x, float _y, float _z, float _w)
+ : x(_x),
+ y(_y),
+ z(_z),
+ w(_w) {
+ }
+
+ float x;
+ float y;
+ float z;
+ float w;
+};
+
+// Test fixture for CommandBufferHelper test.
+class GLES2ImplementationTest : public testing::Test {
+ protected:
+ static const int32 kNumCommandEntries = 100;
+ static const int32 kCommandBufferSizeBytes =
+ kNumCommandEntries * sizeof(CommandBufferEntry);
+ static const size_t kTransferBufferSize = 256;
+ static const int32 kTransferBufferId =
+ GLES2MockCommandBufferHelper::kTransferBufferId;
+ static const uint8 kInitialValue = 0xBD;
+ static const GLint kMaxVertexAttribs = 8;
+
+ virtual void SetUp() {
+ command_buffer_.reset(new MockGLES2CommandBuffer());
+ command_buffer_->Initialize(kNumCommandEntries);
+
+
+ EXPECT_EQ(kTransferBufferId,
+ command_buffer_->CreateTransferBuffer(kTransferBufferSize));
+ transfer_buffer_ = command_buffer_->GetTransferBuffer(kTransferBufferId);
+ ClearTransferBuffer();
+
+ helper_.reset(new GLES2CmdHelper(command_buffer_.get()));
+ helper_->Initialize();
+
+ #if defined(GLES2_SUPPORT_CLIENT_SIDE_BUFFERS)
+ EXPECT_CALL(*command_buffer_, OnFlush(_))
+ .WillOnce(SetMemory(SizedResultHelper<GLint>(kMaxVertexAttribs)))
+ .RetiresOnSaturation();
+ #endif
+
+ gl_.reset(new GLES2Implementation(
+ helper_.get(),
+ kTransferBufferSize,
+ transfer_buffer_.ptr,
+ kTransferBufferId));
+
+ EXPECT_CALL(*command_buffer_, OnFlush(_)).Times(1).RetiresOnSaturation();
+ helper_->CommandBufferHelper::Flush();
+ Buffer ring_buffer = command_buffer_->GetRingBuffer();
+ commands_ = static_cast<CommandBufferEntry*>(ring_buffer.ptr) +
+ command_buffer_->GetState().put_offset;
+ }
+
+ virtual void TearDown() {
+ }
+
+ void ClearTransferBuffer() {
+ memset(transfer_buffer_.ptr, kInitialValue, kTransferBufferSize);
+ }
+
+ static unsigned int RoundToAlignment(unsigned int size) {
+ return (size + GLES2Implementation::kAlignment - 1) &
+ ~(GLES2Implementation::kAlignment - 1);
+ }
+
+ Buffer transfer_buffer_;
+ CommandBufferEntry* commands_;
+ scoped_ptr<MockGLES2CommandBuffer> command_buffer_;
+ scoped_ptr<GLES2CmdHelper> helper_;
+ Sequence sequence_;
+ scoped_ptr<GLES2Implementation> gl_;
+};
+
+// GCC requires these declarations, but MSVC requires they not be present
+#ifndef COMPILER_MSVC
+const int32 GLES2ImplementationTest::kTransferBufferId;
+#endif
+
+
+TEST_F(GLES2ImplementationTest, ShaderSource) {
+ const uint32 kBucketId = 1; // This id is hardcoded into GLES2Implemenation
+ const GLuint kShaderId = 456;
+ const char* kString1 = "foobar";
+ const char* kString2 = "barfoo";
+ const size_t kString1Size = strlen(kString1);
+ const size_t kString2Size = strlen(kString2);
+ const size_t kString3Size = 1; // Want the NULL;
+ const size_t kSourceSize = kString1Size + kString2Size + kString3Size;
+ const size_t kPaddedString1Size = RoundToAlignment(kString1Size);
+ const size_t kPaddedString2Size = RoundToAlignment(kString2Size);
+ struct Cmds {
+ cmd::SetBucketSize set_bucket_size;
+ cmd::SetBucketData set_bucket_data1;
+ cmd::SetToken set_token1;
+ cmd::SetBucketData set_bucket_data2;
+ cmd::SetToken set_token2;
+ cmd::SetBucketData set_bucket_data3;
+ cmd::SetToken set_token3;
+ ShaderSourceBucket shader_source_bucket;
+ cmd::SetBucketSize clear_bucket_size;
+ };
+ int32 token = 1;
+ uint32 offset = GLES2Implementation::kStartingOffset;
+ Cmds expected;
+ expected.set_bucket_size.Init(kBucketId, kSourceSize);
+ expected.set_bucket_data1.Init(
+ kBucketId, 0, kString1Size, kTransferBufferId, offset);
+ expected.set_token1.Init(token++);
+ expected.set_bucket_data2.Init(
+ kBucketId, kString1Size, kString2Size, kTransferBufferId,
+ offset + kPaddedString1Size);
+ expected.set_token2.Init(token++);
+ expected.set_bucket_data3.Init(
+ kBucketId, kString1Size + kString2Size,
+ kString3Size, kTransferBufferId,
+ offset + kPaddedString1Size + kPaddedString2Size);
+ expected.set_token3.Init(token++);
+ expected.shader_source_bucket.Init(kShaderId, kBucketId);
+ expected.clear_bucket_size.Init(kBucketId, 0);
+ const char* strings[] = {
+ kString1,
+ kString2,
+ };
+ gl_->ShaderSource(kShaderId, 2, strings, NULL);
+ EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
+#if defined(GLES2_SUPPORT_CLIENT_SIDE_BUFFERS)
+
+TEST_F(GLES2ImplementationTest, DrawArraysClientSideBuffers) {
+ static const float verts[][4] = {
+ { 12.0f, 23.0f, 34.0f, 45.0f, },
+ { 56.0f, 67.0f, 78.0f, 89.0f, },
+ { 13.0f, 24.0f, 35.0f, 46.0f, },
+ };
+ struct Cmds {
+ EnableVertexAttribArray enable1;
+ EnableVertexAttribArray enable2;
+ BindBuffer bind_to_emu;
+ BufferData set_size;
+ BufferSubData copy_data1;
+ cmd::SetToken set_token1;
+ VertexAttribPointer set_pointer1;
+ BufferSubData copy_data2;
+ cmd::SetToken set_token2;
+ VertexAttribPointer set_pointer2;
+ DrawArrays draw;
+ BindBuffer restore;
+ };
+ const GLuint kEmuBufferId = GLES2Implementation::kClientSideArrayId;
+ const GLuint kAttribIndex1 = 1;
+ const GLuint kAttribIndex2 = 3;
+ const GLint kNumComponents1 = 3;
+ const GLint kNumComponents2 = 2;
+ const GLsizei kClientStride = sizeof(verts[0]);
+ const GLint kFirst = 1;
+ const GLsizei kCount = 2;
+ const GLsizei kSize1 =
+ arraysize(verts) * kNumComponents1 * sizeof(verts[0][0]);
+ const GLsizei kSize2 =
+ arraysize(verts) * kNumComponents2 * sizeof(verts[0][0]);
+ const GLsizei kEmuOffset1 = 0;
+ const GLsizei kEmuOffset2 = kSize1;
+
+ const GLsizei kTotalSize = kSize1 + kSize2;
+ int32 token = 1;
+ uint32 offset = GLES2Implementation::kStartingOffset;
+ Cmds expected;
+ expected.enable1.Init(kAttribIndex1);
+ expected.enable2.Init(kAttribIndex2);
+ expected.bind_to_emu.Init(GL_ARRAY_BUFFER, kEmuBufferId);
+ expected.set_size.Init(GL_ARRAY_BUFFER, kTotalSize, 0, 0, GL_DYNAMIC_DRAW);
+ expected.copy_data1.Init(
+ GL_ARRAY_BUFFER, kEmuOffset1, kSize1, kTransferBufferId, offset);
+ expected.set_token1.Init(token++);
+ expected.set_pointer1.Init(kAttribIndex1, kNumComponents1,
+ GL_FLOAT, GL_FALSE, 0, kEmuOffset1);
+ expected.copy_data2.Init(
+ GL_ARRAY_BUFFER, kEmuOffset2, kSize2, kTransferBufferId, offset + kSize1);
+ expected.set_token2.Init(token++);
+ expected.set_pointer2.Init(kAttribIndex2, kNumComponents2,
+ GL_FLOAT, GL_FALSE, 0, kEmuOffset2);
+ expected.draw.Init(GL_POINTS, kFirst, kCount);
+ expected.restore.Init(GL_ARRAY_BUFFER, 0);
+ gl_->EnableVertexAttribArray(kAttribIndex1);
+ gl_->EnableVertexAttribArray(kAttribIndex2);
+ gl_->VertexAttribPointer(kAttribIndex1, kNumComponents1,
+ GL_FLOAT, GL_FALSE, kClientStride, verts);
+ gl_->VertexAttribPointer(kAttribIndex2, kNumComponents2,
+ GL_FLOAT, GL_FALSE, kClientStride, verts);
+ gl_->DrawArrays(GL_POINTS, kFirst, kCount);
+ EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
+TEST_F(GLES2ImplementationTest, DrawElementsClientSideBuffers) {
+ static const float verts[][4] = {
+ { 12.0f, 23.0f, 34.0f, 45.0f, },
+ { 56.0f, 67.0f, 78.0f, 89.0f, },
+ { 13.0f, 24.0f, 35.0f, 46.0f, },
+ };
+ static const uint16 indices[] = {
+ 1, 2,
+ };
+ struct Cmds {
+ EnableVertexAttribArray enable1;
+ EnableVertexAttribArray enable2;
+ BindBuffer bind_to_index_emu;
+ BufferData set_index_size;
+ BufferSubData copy_data0;
+ cmd::SetToken set_token0;
+ BindBuffer bind_to_emu;
+ BufferData set_size;
+ BufferSubData copy_data1;
+ cmd::SetToken set_token1;
+ VertexAttribPointer set_pointer1;
+ BufferSubData copy_data2;
+ cmd::SetToken set_token2;
+ VertexAttribPointer set_pointer2;
+ DrawElements draw;
+ BindBuffer restore;
+ BindBuffer restore_element;
+ };
+ const GLsizei kIndexSize = sizeof(indices);
+ const GLuint kEmuBufferId = GLES2Implementation::kClientSideArrayId;
+ const GLuint kEmuIndexBufferId =
+ GLES2Implementation::kClientSideElementArrayId;
+ const GLuint kAttribIndex1 = 1;
+ const GLuint kAttribIndex2 = 3;
+ const GLint kNumComponents1 = 3;
+ const GLint kNumComponents2 = 2;
+ const GLsizei kClientStride = sizeof(verts[0]);
+ const GLsizei kCount = 2;
+ const GLsizei kSize1 =
+ arraysize(verts) * kNumComponents1 * sizeof(verts[0][0]);
+ const GLsizei kSize2 =
+ arraysize(verts) * kNumComponents2 * sizeof(verts[0][0]);
+ const GLsizei kEmuOffset1 = 0;
+ const GLsizei kEmuOffset2 = kSize1;
+
+ const GLsizei kTotalSize = kSize1 + kSize2;
+ int32 token = 1;
+ uint32 offset = GLES2Implementation::kStartingOffset;
+ Cmds expected;
+ expected.enable1.Init(kAttribIndex1);
+ expected.enable2.Init(kAttribIndex2);
+ expected.bind_to_index_emu.Init(GL_ELEMENT_ARRAY_BUFFER, kEmuIndexBufferId);
+ expected.set_index_size.Init(
+ GL_ELEMENT_ARRAY_BUFFER, kIndexSize, 0, 0, GL_DYNAMIC_DRAW);
+ expected.copy_data0.Init(
+ GL_ELEMENT_ARRAY_BUFFER, 0, kIndexSize, kTransferBufferId, offset);
+ offset += kIndexSize;
+ expected.set_token0.Init(token++);
+ expected.bind_to_emu.Init(GL_ARRAY_BUFFER, kEmuBufferId);
+ expected.set_size.Init(GL_ARRAY_BUFFER, kTotalSize, 0, 0, GL_DYNAMIC_DRAW);
+ expected.copy_data1.Init(
+ GL_ARRAY_BUFFER, kEmuOffset1, kSize1, kTransferBufferId, offset);
+ offset += kSize1;
+ expected.set_token1.Init(token++);
+ expected.set_pointer1.Init(kAttribIndex1, kNumComponents1,
+ GL_FLOAT, GL_FALSE, 0, kEmuOffset1);
+ expected.copy_data2.Init(
+ GL_ARRAY_BUFFER, kEmuOffset2, kSize2, kTransferBufferId, offset);
+ expected.set_token2.Init(token++);
+ expected.set_pointer2.Init(kAttribIndex2, kNumComponents2,
+ GL_FLOAT, GL_FALSE, 0, kEmuOffset2);
+ expected.draw.Init(GL_POINTS, kCount, GL_UNSIGNED_SHORT, 0);
+ expected.restore.Init(GL_ARRAY_BUFFER, 0);
+ expected.restore_element.Init(GL_ELEMENT_ARRAY_BUFFER, 0);
+ gl_->EnableVertexAttribArray(kAttribIndex1);
+ gl_->EnableVertexAttribArray(kAttribIndex2);
+ gl_->VertexAttribPointer(kAttribIndex1, kNumComponents1,
+ GL_FLOAT, GL_FALSE, kClientStride, verts);
+ gl_->VertexAttribPointer(kAttribIndex2, kNumComponents2,
+ GL_FLOAT, GL_FALSE, kClientStride, verts);
+ gl_->DrawElements(GL_POINTS, kCount, GL_UNSIGNED_SHORT, indices);
+ EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
+TEST_F(GLES2ImplementationTest,
+ DrawElementsClientSideBuffersServiceSideIndices) {
+ static const float verts[][4] = {
+ { 12.0f, 23.0f, 34.0f, 45.0f, },
+ { 56.0f, 67.0f, 78.0f, 89.0f, },
+ { 13.0f, 24.0f, 35.0f, 46.0f, },
+ };
+ struct Cmds {
+ EnableVertexAttribArray enable1;
+ EnableVertexAttribArray enable2;
+ BindBuffer bind_to_index;
+ GetMaxValueInBuffer get_max;
+ BindBuffer bind_to_emu;
+ BufferData set_size;
+ BufferSubData copy_data1;
+ cmd::SetToken set_token1;
+ VertexAttribPointer set_pointer1;
+ BufferSubData copy_data2;
+ cmd::SetToken set_token2;
+ VertexAttribPointer set_pointer2;
+ DrawElements draw;
+ BindBuffer restore;
+ };
+ const GLuint kEmuBufferId = GLES2Implementation::kClientSideArrayId;
+ const GLuint kClientIndexBufferId = 0x789;
+ const GLuint kIndexOffset = 0x40;
+ const GLuint kMaxIndex = 2;
+ const GLuint kAttribIndex1 = 1;
+ const GLuint kAttribIndex2 = 3;
+ const GLint kNumComponents1 = 3;
+ const GLint kNumComponents2 = 2;
+ const GLsizei kClientStride = sizeof(verts[0]);
+ const GLsizei kCount = 2;
+ const GLsizei kSize1 =
+ arraysize(verts) * kNumComponents1 * sizeof(verts[0][0]);
+ const GLsizei kSize2 =
+ arraysize(verts) * kNumComponents2 * sizeof(verts[0][0]);
+ const GLsizei kEmuOffset1 = 0;
+ const GLsizei kEmuOffset2 = kSize1;
+
+ const GLsizei kTotalSize = kSize1 + kSize2;
+ int32 token = 1;
+ uint32 offset = GLES2Implementation::kStartingOffset;
+ Cmds expected;
+ expected.enable1.Init(kAttribIndex1);
+ expected.enable2.Init(kAttribIndex2);
+ expected.bind_to_index.Init(GL_ELEMENT_ARRAY_BUFFER, kClientIndexBufferId);
+ expected.get_max.Init(kClientIndexBufferId, kCount, GL_UNSIGNED_SHORT,
+ kIndexOffset, kTransferBufferId, 0);
+ expected.bind_to_emu.Init(GL_ARRAY_BUFFER, kEmuBufferId);
+ expected.set_size.Init(GL_ARRAY_BUFFER, kTotalSize, 0, 0, GL_DYNAMIC_DRAW);
+ expected.copy_data1.Init(
+ GL_ARRAY_BUFFER, kEmuOffset1, kSize1, kTransferBufferId, offset);
+ offset += kSize1;
+ expected.set_token1.Init(token++);
+ expected.set_pointer1.Init(kAttribIndex1, kNumComponents1,
+ GL_FLOAT, GL_FALSE, 0, kEmuOffset1);
+ expected.copy_data2.Init(
+ GL_ARRAY_BUFFER, kEmuOffset2, kSize2, kTransferBufferId, offset);
+ expected.set_token2.Init(token++);
+ expected.set_pointer2.Init(kAttribIndex2, kNumComponents2,
+ GL_FLOAT, GL_FALSE, 0, kEmuOffset2);
+ expected.draw.Init(GL_POINTS, kCount, GL_UNSIGNED_SHORT, kIndexOffset);
+ expected.restore.Init(GL_ARRAY_BUFFER, 0);
+
+ EXPECT_CALL(*command_buffer_, OnFlush(_))
+ .WillOnce(SetMemory(kMaxIndex))
+ .RetiresOnSaturation();
+
+ gl_->EnableVertexAttribArray(kAttribIndex1);
+ gl_->EnableVertexAttribArray(kAttribIndex2);
+ gl_->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, kClientIndexBufferId);
+ gl_->VertexAttribPointer(kAttribIndex1, kNumComponents1,
+ GL_FLOAT, GL_FALSE, kClientStride, verts);
+ gl_->VertexAttribPointer(kAttribIndex2, kNumComponents2,
+ GL_FLOAT, GL_FALSE, kClientStride, verts);
+ gl_->DrawElements(GL_POINTS, kCount, GL_UNSIGNED_SHORT,
+ reinterpret_cast<const void*>(kIndexOffset));
+ EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
+TEST_F(GLES2ImplementationTest, GetVertexBufferPointerv) {
+ static const float verts[1] = { 0.0f, };
+ const GLuint kAttribIndex1 = 1;
+ const GLuint kAttribIndex2 = 3;
+ const GLint kNumComponents1 = 3;
+ const GLint kNumComponents2 = 2;
+ const GLsizei kStride1 = 12;
+ const GLsizei kStride2 = 0;
+ const GLuint kBufferId = 0x123;
+ const GLint kOffset2 = 0x456;
+
+ // Only one set and one get because the client side buffer's info is stored
+ // on the client side.
+ struct Cmds {
+ BindBuffer bind;
+ VertexAttribPointer set_pointer;
+ GetVertexAttribPointerv get_pointer;
+ };
+ Cmds expected;
+ expected.bind.Init(GL_ARRAY_BUFFER, kBufferId);
+ expected.set_pointer.Init(kAttribIndex2, kNumComponents2, GL_FLOAT, GL_FALSE,
+ kStride2, kOffset2);
+ expected.get_pointer.Init(kAttribIndex2, GL_VERTEX_ATTRIB_ARRAY_POINTER,
+ kTransferBufferId, 0);
+
+ // One call to flush to way for GetVertexAttribPointerv
+ EXPECT_CALL(*command_buffer_, OnFlush(_))
+ .WillOnce(SetMemory(SizedResultHelper<uint32>(kOffset2)))
+ .RetiresOnSaturation();
+
+ // Set one client side buffer.
+ gl_->VertexAttribPointer(kAttribIndex1, kNumComponents1,
+ GL_FLOAT, GL_FALSE, kStride1, verts);
+ // Set one VBO
+ gl_->BindBuffer(GL_ARRAY_BUFFER, kBufferId);
+ gl_->VertexAttribPointer(kAttribIndex2, kNumComponents2,
+ GL_FLOAT, GL_FALSE, kStride2,
+ reinterpret_cast<const void*>(kOffset2));
+ // now get them both.
+ void* ptr1 = NULL;
+ void* ptr2 = NULL;
+
+ gl_->GetVertexAttribPointerv(
+ kAttribIndex1, GL_VERTEX_ATTRIB_ARRAY_POINTER, &ptr1);
+ gl_->GetVertexAttribPointerv(
+ kAttribIndex2, GL_VERTEX_ATTRIB_ARRAY_POINTER, &ptr2);
+
+ EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+ EXPECT_TRUE(static_cast<const void*>(&verts) == ptr1);
+ // because the service is not running ptr2 is not read.
+ EXPECT_TRUE(ptr2 == reinterpret_cast<void*>(kOffset2));
+}
+
+TEST_F(GLES2ImplementationTest, GetVertexAttrib) {
+ static const float verts[1] = { 0.0f, };
+ const GLuint kAttribIndex1 = 1;
+ const GLuint kAttribIndex2 = 3;
+ const GLint kNumComponents1 = 3;
+ const GLint kNumComponents2 = 2;
+ const GLsizei kStride1 = 12;
+ const GLsizei kStride2 = 0;
+ const GLuint kBufferId = 0x123;
+ const GLint kOffset2 = 0x456;
+
+ // Only one set and one get because the client side buffer's info is stored
+ // on the client side.
+ struct Cmds {
+ EnableVertexAttribArray enable;
+ BindBuffer bind;
+ VertexAttribPointer set_pointer;
+ GetVertexAttribiv get1; // for getting the buffer from attrib2
+ GetVertexAttribfv get2; // for getting the value from attrib1
+ };
+ Cmds expected;
+ expected.enable.Init(kAttribIndex1);
+ expected.bind.Init(GL_ARRAY_BUFFER, kBufferId);
+ expected.set_pointer.Init(kAttribIndex2, kNumComponents2, GL_FLOAT, GL_FALSE,
+ kStride2, kOffset2);
+ expected.get1.Init(kAttribIndex2,
+ GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING,
+ kTransferBufferId, 0);
+ expected.get2.Init(kAttribIndex1,
+ GL_CURRENT_VERTEX_ATTRIB,
+ kTransferBufferId, 0);
+
+ FourFloats current_attrib(1.2f, 3.4f, 5.6f, 7.8f);
+
+ // One call to flush to way for GetVertexAttribiv
+ EXPECT_CALL(*command_buffer_, OnFlush(_))
+ .WillOnce(SetMemory(SizedResultHelper<GLuint>(kBufferId)))
+ .WillOnce(SetMemory(SizedResultHelper<FourFloats>(current_attrib)))
+ .RetiresOnSaturation();
+
+ gl_->EnableVertexAttribArray(kAttribIndex1);
+ // Set one client side buffer.
+ gl_->VertexAttribPointer(kAttribIndex1, kNumComponents1,
+ GL_FLOAT, GL_FALSE, kStride1, verts);
+ // Set one VBO
+ gl_->BindBuffer(GL_ARRAY_BUFFER, kBufferId);
+ gl_->VertexAttribPointer(kAttribIndex2, kNumComponents2,
+ GL_FLOAT, GL_FALSE, kStride2,
+ reinterpret_cast<const void*>(kOffset2));
+ // first get the service side once to see that we make a command
+ GLint buffer_id = 0;
+ GLint enabled = 0;
+ GLint size = 0;
+ GLint stride = 0;
+ GLint type = 0;
+ GLint normalized = 1;
+ float current[4] = { 0.0f, };
+
+ gl_->GetVertexAttribiv(
+ kAttribIndex2, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &buffer_id);
+ EXPECT_EQ(kBufferId, static_cast<GLuint>(buffer_id));
+ gl_->GetVertexAttribiv(
+ kAttribIndex1, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &buffer_id);
+ gl_->GetVertexAttribiv(
+ kAttribIndex1, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &enabled);
+ gl_->GetVertexAttribiv(
+ kAttribIndex1, GL_VERTEX_ATTRIB_ARRAY_SIZE, &size);
+ gl_->GetVertexAttribiv(
+ kAttribIndex1, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &stride);
+ gl_->GetVertexAttribiv(
+ kAttribIndex1, GL_VERTEX_ATTRIB_ARRAY_TYPE, &type);
+ gl_->GetVertexAttribiv(
+ kAttribIndex1, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &normalized);
+ gl_->GetVertexAttribfv(
+ kAttribIndex1, GL_CURRENT_VERTEX_ATTRIB, &current[0]);
+
+ EXPECT_EQ(0, buffer_id);
+ EXPECT_EQ(GL_TRUE, enabled);
+ EXPECT_EQ(kNumComponents1, size);
+ EXPECT_EQ(kStride1, stride);
+ EXPECT_EQ(GL_FLOAT, type);
+ EXPECT_EQ(GL_FALSE, normalized);
+ EXPECT_EQ(0, memcmp(&current_attrib, &current, sizeof(current_attrib)));
+
+ EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
+TEST_F(GLES2ImplementationTest, ReservedIds) {
+ // Only the get error command should be issued.
+ struct Cmds {
+ GetError get;
+ };
+ Cmds expected;
+ expected.get.Init(kTransferBufferId, 0);
+
+ // One call to flush to way for GetError
+ EXPECT_CALL(*command_buffer_, OnFlush(_))
+ .WillOnce(SetMemory(GLuint(GL_NO_ERROR)))
+ .RetiresOnSaturation();
+
+ gl_->BindBuffer(
+ GL_ARRAY_BUFFER,
+ GLES2Implementation::kClientSideArrayId);
+ gl_->BindBuffer(
+ GL_ARRAY_BUFFER,
+ GLES2Implementation::kClientSideElementArrayId);
+ gl_->BindFramebuffer(
+ GL_FRAMEBUFFER,
+ GLES2Implementation::kClientSideArrayId);
+ gl_->BindFramebuffer(
+ GL_FRAMEBUFFER,
+ GLES2Implementation::kClientSideElementArrayId);
+ gl_->BindRenderbuffer(
+ GL_RENDERBUFFER,
+ GLES2Implementation::kClientSideArrayId);
+ gl_->BindRenderbuffer(
+ GL_RENDERBUFFER,
+ GLES2Implementation::kClientSideElementArrayId);
+ gl_->BindTexture(
+ GL_TEXTURE_2D,
+ GLES2Implementation::kClientSideArrayId);
+ gl_->BindTexture(
+ GL_TEXTURE_2D,
+ GLES2Implementation::kClientSideElementArrayId);
+ GLenum err = gl_->GetError();
+ EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), err);
+ EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
+#endif // defined(GLES2_SUPPORT_CLIENT_SIDE_BUFFERS)
+
+
+} // namespace gles2
+} // namespace gpu
+
+
diff --git a/gpu/command_buffer/client/id_allocator.cc b/gpu/command_buffer/client/id_allocator.cc
index e6f9b6c..2d244d0 100644
--- a/gpu/command_buffer/client/id_allocator.cc
+++ b/gpu/command_buffer/client/id_allocator.cc
@@ -9,50 +9,19 @@
namespace gpu {
-IdAllocator::IdAllocator() : bitmap_(1) { bitmap_[0] = 0; }
-
-static const unsigned int kBitsPerUint32 = sizeof(Uint32) * 8; // NOLINT
+IdAllocator::IdAllocator() {
+}
-// Looks for the first non-full entry, and return the first free bit in that
-// entry. If all the entries are full, it will return the first bit of an entry
-// that would be appended, but doesn't actually append that entry to the vector.
unsigned int IdAllocator::FindFirstFree() const {
- size_t size = bitmap_.size();
- for (unsigned int i = 0; i < size; ++i) {
- Uint32 value = bitmap_[i];
- if (value != 0xffffffffU) {
- for (unsigned int j = 0; j < kBitsPerUint32; ++j) {
- if (!(value & (1 << j))) return i * kBitsPerUint32 + j;
- }
- DLOG(FATAL) << "Code should not reach here.";
+ ResourceId id = 1;
+ for (ResourceIdSet::const_iterator it = used_ids_.begin();
+ it != used_ids_.end(); ++it) {
+ if ((*it) != id) {
+ return id;
}
+ ++id;
}
- return size*kBitsPerUint32;
-}
-
-// Sets the correct bit in the proper entry, resizing the vector if needed.
-void IdAllocator::SetBit(unsigned int bit, bool value) {
- size_t size = bitmap_.size();
- if (bit >= size * kBitsPerUint32) {
- size_t newsize = bit / kBitsPerUint32 + 1;
- bitmap_.resize(newsize);
- for (size_t i = size; i < newsize; ++i) bitmap_[i] = 0;
- }
- Uint32 mask = 1U << (bit % kBitsPerUint32);
- if (value) {
- bitmap_[bit / kBitsPerUint32] |= mask;
- } else {
- bitmap_[bit / kBitsPerUint32] &= ~mask;
- }
-}
-
-// Gets the bit from the proper entry. This doesn't resize the vector, just
-// returns false if the bit is beyond the last entry.
-bool IdAllocator::GetBit(unsigned int bit) const {
- size_t size = bitmap_.size();
- if (bit / kBitsPerUint32 >= size) return false;
- Uint32 mask = 1U << (bit % kBitsPerUint32);
- return (bitmap_[bit / kBitsPerUint32] & mask) != 0;
+ return id;
}
} // namespace gpu
diff --git a/gpu/command_buffer/client/id_allocator.h b/gpu/command_buffer/client/id_allocator.h
index 8e1ccdb..615f020 100644
--- a/gpu/command_buffer/client/id_allocator.h
+++ b/gpu/command_buffer/client/id_allocator.h
@@ -7,7 +7,8 @@
#ifndef GPU_COMMAND_BUFFER_CLIENT_ID_ALLOCATOR_H_
#define GPU_COMMAND_BUFFER_CLIENT_ID_ALLOCATOR_H_
-#include <vector>
+#include <set>
+#include <utility>
#include "../common/types.h"
namespace gpu {
@@ -15,36 +16,44 @@ namespace gpu {
// A resource ID, key to the resource maps.
typedef uint32 ResourceId;
// Invalid resource ID.
-static const ResourceId kInvalidResource = 0xffffffffU;
+static const ResourceId kInvalidResource = 0u;
-// A class to manage the allocation of resource IDs. It uses a bitfield stored
-// into a vector of unsigned ints.
+// A class to manage the allocation of resource IDs.
class IdAllocator {
public:
IdAllocator();
// Allocates a new resource ID.
ResourceId AllocateID() {
- unsigned int bit = FindFirstFree();
- SetBit(bit, true);
- return bit;
+ ResourceId id = FindFirstFree();
+ MarkAsUsed(id);
+ return id;
+ }
+
+ // Marks an id as used. Returns false if id was already used.
+ bool MarkAsUsed(ResourceId id) {
+ std::pair<ResourceIdSet::iterator, bool> result = used_ids_.insert(id);
+ return result.second;
}
// Frees a resource ID.
void FreeID(ResourceId id) {
- SetBit(id, false);
+ used_ids_.erase(id);
}
// Checks whether or not a resource ID is in use.
- bool InUse(ResourceId id) {
- return GetBit(id);
+ bool InUse(ResourceId id) const {
+ return used_ids_.find(id) != used_ids_.end();
}
+
private:
- void SetBit(unsigned int bit, bool value);
- bool GetBit(unsigned int bit) const;
- unsigned int FindFirstFree() const;
+ // TODO(gman): This would work much better with ranges.
+ typedef std::set<ResourceId> ResourceIdSet;
+
+ ResourceId FindFirstFree() const;
+
+ ResourceIdSet used_ids_;
- std::vector<Uint32> bitmap_;
DISALLOW_COPY_AND_ASSIGN(IdAllocator);
};
diff --git a/gpu/command_buffer/client/id_allocator_test.cc b/gpu/command_buffer/client/id_allocator_test.cc
index df457db..eafadd7 100644
--- a/gpu/command_buffer/client/id_allocator_test.cc
+++ b/gpu/command_buffer/client/id_allocator_test.cc
@@ -23,8 +23,8 @@ class IdAllocatorTest : public testing::Test {
// Checks basic functionality: AllocateID, FreeID, InUse.
TEST_F(IdAllocatorTest, TestBasic) {
IdAllocator *allocator = id_allocator();
- // Check that resource 0 is not in use
- EXPECT_FALSE(allocator->InUse(0));
+ // Check that resource 1 is not in use
+ EXPECT_FALSE(allocator->InUse(1));
// Allocate an ID, check that it's in use.
ResourceId id1 = allocator->AllocateID();
@@ -45,8 +45,7 @@ TEST_F(IdAllocatorTest, TestBasic) {
EXPECT_FALSE(allocator->InUse(id2));
}
-// Checks that the resource IDs are allocated conservatively, and re-used after
-// being freed.
+// Checks that the resource IDs are re-used after being freed.
TEST_F(IdAllocatorTest, TestAdvanced) {
IdAllocator *allocator = id_allocator();
@@ -58,18 +57,6 @@ TEST_F(IdAllocatorTest, TestAdvanced) {
EXPECT_TRUE(allocator->InUse(ids[i]));
}
- // Check that the allocation is conservative with resource IDs, that is that
- // the resource IDs don't go over kNumResources - so that the service doesn't
- // have to allocate too many internal structures when the resources are used.
- for (unsigned int i = 0; i < kNumResources; ++i) {
- EXPECT_GT(kNumResources, ids[i]);
- }
-
- // Check that the next resources are still free.
- for (unsigned int i = 0; i < kNumResources; ++i) {
- EXPECT_FALSE(allocator->InUse(kNumResources + i));
- }
-
// Check that a new allocation re-uses the resource we just freed.
ResourceId id1 = ids[kNumResources / 2];
allocator->FreeID(id1);
@@ -79,4 +66,21 @@ TEST_F(IdAllocatorTest, TestAdvanced) {
EXPECT_EQ(id1, id2);
}
+// Check that we can choose our own ids and they won't be reused.
+TEST_F(IdAllocatorTest, MarkAsUsed) {
+ IdAllocator* allocator = id_allocator();
+ ResourceId id = allocator->AllocateID();
+ allocator->FreeID(id);
+ EXPECT_FALSE(allocator->InUse(id));
+ EXPECT_TRUE(allocator->MarkAsUsed(id));
+ EXPECT_TRUE(allocator->InUse(id));
+ ResourceId id2 = allocator->AllocateID();
+ EXPECT_NE(id, id2);
+ EXPECT_TRUE(allocator->MarkAsUsed(id2 + 1));
+ ResourceId id3 = allocator->AllocateID();
+ // Checks our algorithm. If the algorithm changes this check should be
+ // changed.
+ EXPECT_EQ(id3, id2 + 2);
+}
+
} // namespace gpu