summaryrefslogtreecommitdiffstats
path: root/gpu/command_buffer
diff options
context:
space:
mode:
authorgman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-13 09:04:23 +0000
committergman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-13 09:04:23 +0000
commit29a9eb570a57f7c8d2204e18bb11be2b7d565b74 (patch)
treec65da5b390965d788ffd1f5946edafe730af9499 /gpu/command_buffer
parente8824b5c455da4ecbc012be47b651e28e11828b5 (diff)
downloadchromium_src-29a9eb570a57f7c8d2204e18bb11be2b7d565b74.zip
chromium_src-29a9eb570a57f7c8d2204e18bb11be2b7d565b74.tar.gz
chromium_src-29a9eb570a57f7c8d2204e18bb11be2b7d565b74.tar.bz2
Resubmit CL#1629004 Support for client side buffers
TEST=none BUG=none git-svn-id: svn://svn.chromium.org/chrome/trunk/src@44337 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gpu/command_buffer')
-rwxr-xr-xgpu/command_buffer/build_gles2_cmd_buffer.py136
-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
-rw-r--r--gpu/command_buffer/common/gles2_cmd_format_autogen.h63
-rw-r--r--gpu/command_buffer/common/gles2_cmd_format_test_autogen.h23
-rw-r--r--gpu/command_buffer/common/gles2_cmd_id_test_autogen.h2
-rw-r--r--gpu/command_buffer/common/gles2_cmd_ids_autogen.h1
-rw-r--r--gpu/command_buffer/common/scoped_ptr.h385
-rw-r--r--gpu/command_buffer/docs/gles2_cmd_format_docs.txt15
-rw-r--r--gpu/command_buffer/service/gl_interface.h3
-rw-r--r--gpu/command_buffer/service/gl_mock.h3
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder.cc18
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_autogen.h28
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc55
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h2
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest_2_autogen.h3
23 files changed, 2177 insertions, 170 deletions
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py
index bae1b5f..7b9c86e 100755
--- a/gpu/command_buffer/build_gles2_cmd_buffer.py
+++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -176,6 +176,7 @@ GL_APICALL void GL_APIENTRY glVertexAttribPointer (GLuint indx, GLintVer
GL_APICALL void GL_APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height);
// Non-GL commands.
GL_APICALL void GL_APIENTRY glSwapBuffers (void);
+GL_APICALL GLuint GL_APIENTRY glGetMaxValueInBuffer (GLidBuffer buffer_id, GLsizei count, GLenumIndexType type, GLuint offset);
"""
# This is the list of all commmands that will be generated and their Id.
@@ -366,6 +367,7 @@ _CMD_ID_TABLE = {
'ShaderSourceBucket': 435,
'ShaderBinary': 436,
'ReleaseShaderCompiler': 437,
+ 'GetMaxValueInBuffer': 438,
}
# This is a list of enum names and their valid values. It is used to map
@@ -943,6 +945,8 @@ _ENUM_LISTS = {
# bucket: True to generate a bucket version of the command.
# impl_func: Whether or not to generate the GLES2Implementation part of this
# command.
+# impl_decl: Whether or not to generate the GLES2Implementation declaration
+# for this command.
# needs_size: If true a data_size field is added to the command.
# data_type: The type of data the command uses. For PUTn or PUT types.
# count: The number of units per element. For PUTn or PUT types.
@@ -956,6 +960,7 @@ _FUNCTION_INFO = {
'BindAttribLocation': {'type': 'GLchar', 'bucket': True, 'needs_size': True},
'BindBuffer': {
'type': 'Bind',
+ 'impl_decl': False,
'decoder_func': 'DoBindBuffer',
'gen_func': 'GenBuffersARB',
},
@@ -990,7 +995,11 @@ _FUNCTION_INFO = {
'CompressedTexSubImage2D': {'type': 'Data'},
'CreateProgram': {'type': 'Create'},
'CreateShader': {'type': 'Create'},
- 'DeleteBuffers': {'type': 'DELn', 'gl_test_func': 'glDeleteBuffersARB'},
+ 'DeleteBuffers': {
+ 'type': 'DELn',
+ 'gl_test_func': 'glDeleteBuffersARB',
+ 'impl_decl': False,
+ },
'DeleteFramebuffers': {
'type': 'DELn',
'gl_test_func': 'glDeleteFramebuffersEXT',
@@ -1003,13 +1012,23 @@ _FUNCTION_INFO = {
'DeleteShader': {'type': 'Custom', 'decoder_func': 'DoDeleteShader'},
'DeleteTextures': {'type': 'DELn'},
'DepthRangef': {'decoder_func': 'glDepthRange'},
- 'DisableVertexAttribArray': {'decoder_func': 'DoDisableVertexAttribArray'},
- 'DrawArrays': { 'decoder_func': 'DoDrawArrays', 'unit_test': False},
+ 'DisableVertexAttribArray': {
+ 'decoder_func': 'DoDisableVertexAttribArray',
+ 'impl_decl': False,
+ },
+ 'DrawArrays': {
+ 'decoder_func': 'DoDrawArrays',
+ 'unit_test': False,
+ 'impl_decl': False,
+ },
'DrawElements': {
'type': 'Manual',
'cmd_args': 'GLenum mode, GLsizei count, GLenum type, GLuint index_offset',
},
- 'EnableVertexAttribArray': {'decoder_func': 'DoEnableVertexAttribArray'},
+ 'EnableVertexAttribArray': {
+ 'decoder_func': 'DoEnableVertexAttribArray',
+ 'impl_decl': False,
+ },
'Finish': {'impl_func': False},
'Flush': {'impl_func': False},
'FramebufferRenderbuffer': {
@@ -1095,6 +1114,12 @@ _FUNCTION_INFO = {
'result': ['SizedResult<GLint>'],
'decoder_func': 'DoGetIntegerv',
},
+ 'GetMaxValueInBuffer': {
+ 'type': 'Is',
+ 'decoder_func': 'DoGetMaxValueInBuffer',
+ 'result': ['GLuint'],
+ 'unit_test': False,
+ },
'GetProgramiv': {'type': 'GETn', 'result': ['SizedResult<GLint>']},
'GetProgramInfoLog': {
'type': 'STRn',
@@ -1162,8 +1187,16 @@ _FUNCTION_INFO = {
'GLidProgram program, const char* name, NonImmediate GLint* location',
'result': ['GLint'],
},
- 'GetVertexAttribfv': {'type': 'GETn', 'result': ['SizedResult<GLfloat>']},
- 'GetVertexAttribiv': {'type': 'GETn', 'result': ['SizedResult<GLint>']},
+ 'GetVertexAttribfv': {
+ 'type': 'GETn',
+ 'result': ['SizedResult<GLfloat>'],
+ 'impl_decl': False,
+ },
+ 'GetVertexAttribiv': {
+ 'type': 'GETn',
+ 'result': ['SizedResult<GLint>'],
+ 'impl_decl': False,
+ },
'GetVertexAttribPointerv': {
'type': 'Custom',
'immediate': False,
@@ -1730,15 +1763,20 @@ TEST_F(%(test_name)s, %(name)sInvalidArgs%(arg_index)d_%(value_index)d) {
def WriteGLES2ImplementationDeclaration(self, func, file):
"""Writes the GLES2 Implemention declaration."""
- file.Write("%s %s(%s);\n" %
- (func.return_type, func.original_name,
- func.MakeTypedOriginalArgString("")))
- file.Write("\n")
+ impl_decl = func.GetInfo('impl_decl')
+ if impl_decl == None or impl_decl == True:
+ file.Write("%s %s(%s);\n" %
+ (func.return_type, func.original_name,
+ func.MakeTypedOriginalArgString("")))
+ file.Write("\n")
def WriteGLES2ImplementationHeader(self, func, file):
"""Writes the GLES2 Implemention."""
impl_func = func.GetInfo('impl_func')
- if func.can_auto_generate and (impl_func == None or impl_func == True):
+ impl_decl = func.GetInfo('impl_decl')
+ if (func.can_auto_generate and
+ (impl_func == None or impl_func == True) and
+ (impl_decl == None or impl_decl == True)):
file.Write("%s %s(%s) {\n" %
(func.return_type, func.original_name,
func.MakeTypedOriginalArgString("")))
@@ -2118,6 +2156,37 @@ TEST_F(%(test_name)s, %(name)sInvalidArgs%(arg_index)d_%(value_index)d) {
"""
self.WriteInvalidUnitTest(func, file, invalid_test)
+ def WriteGLES2ImplementationHeader(self, func, file):
+ """Writes the GLES2 Implemention."""
+ impl_func = func.GetInfo('impl_func')
+ impl_decl = func.GetInfo('impl_decl')
+ if (func.can_auto_generate and
+ (impl_func == None or impl_func == True) and
+ (impl_decl == None or impl_decl == True)):
+ file.Write("%s %s(%s) {\n" %
+ (func.return_type, func.original_name,
+ func.MakeTypedOriginalArgString("")))
+ for arg in func.GetOriginalArgs():
+ arg.WriteClientSideValidationCode(file)
+ code = """ if (IsReservedId(%(id)s)) {
+ SetGLError(GL_INVALID_OPERATION);
+ return;
+ }
+ if (%(id)s != 0) {
+ id_allocator_.MarkAsUsed(%(id)s);
+ }
+ helper_->%(name)s(%(arg_string)s);
+}
+
+"""
+ file.Write(code % {
+ 'name': func.name,
+ 'arg_string': func.MakeOriginalArgString(""),
+ 'id': func.GetOriginalArgs()[1].name,
+ })
+ else:
+ self.WriteGLES2ImplementationDeclaration(func, file)
+
class GENnHandler(TypeHandler):
"""Handler for glGen___ type functions."""
@@ -2482,14 +2551,16 @@ TEST_F(%(test_name)s, %(name)sInvalidArgs) {
def WriteGLES2ImplementationHeader(self, func, file):
"""Overrriden from TypeHandler."""
- file.Write("%s %s(%s) {\n" %
- (func.return_type, func.original_name,
- func.MakeTypedOriginalArgString("")))
- file.Write(" FreeIds(%s);\n" % func.MakeOriginalArgString(""))
- file.Write(" helper_->%sImmediate(%s);\n" %
- (func.name, func.MakeOriginalArgString("")))
- file.Write("}\n")
- file.Write("\n")
+ impl_decl = func.GetInfo('impl_decl')
+ if impl_decl == None or impl_decl == True:
+ file.Write("%s %s(%s) {\n" %
+ (func.return_type, func.original_name,
+ func.MakeTypedOriginalArgString("")))
+ file.Write(" FreeIds(%s);\n" % func.MakeOriginalArgString(""))
+ file.Write(" helper_->%sImmediate(%s);\n" %
+ (func.name, func.MakeOriginalArgString("")))
+ file.Write("}\n")
+ file.Write("\n")
def WriteImmediateCmdComputeSize(self, func, file):
"""Overrriden from TypeHandler."""
@@ -2647,24 +2718,27 @@ class GETnHandler(TypeHandler):
def WriteGLES2ImplementationHeader(self, func, file):
"""Overrriden from TypeHandler."""
- file.Write("%s %s(%s) {\n" %
- (func.return_type, func.original_name,
- func.MakeTypedOriginalArgString("")))
- all_but_last_args = func.GetOriginalArgs()[:-1]
- arg_string = (
- ", ".join(["%s" % arg.name for arg in all_but_last_args]))
- code = """ typedef %(func_name)s::Result Result;
+ impl_decl = func.GetInfo('impl_decl')
+ if impl_decl == None or impl_decl == True:
+ file.Write("%s %s(%s) {\n" %
+ (func.return_type, func.original_name,
+ func.MakeTypedOriginalArgString("")))
+ all_but_last_args = func.GetOriginalArgs()[:-1]
+ arg_string = (
+ ", ".join(["%s" % arg.name for arg in all_but_last_args]))
+ code = """ typedef %(func_name)s::Result Result;
Result* result = GetResultAs<Result*>();
result->SetNumResults(0);
- helper_->%(func_name)s(%(arg_string)s, result_shm_id(), result_shm_offset());
+ helper_->%(func_name)s(%(arg_string)s,
+ result_shm_id(), result_shm_offset());
WaitForCmd();
result->CopyResult(params);
}
"""
- file.Write(code % {
- 'func_name': func.name,
- 'arg_string': arg_string,
- })
+ file.Write(code % {
+ 'func_name': func.name,
+ 'arg_string': arg_string,
+ })
def WriteServiceUnitTest(self, func, file):
"""Overrriden from TypeHandler."""
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..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
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
diff --git a/gpu/command_buffer/common/gles2_cmd_format_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
index c772b1e..b6b8128 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
@@ -8217,6 +8217,69 @@ COMPILE_ASSERT(sizeof(SwapBuffers) == 4,
COMPILE_ASSERT(offsetof(SwapBuffers, header) == 0,
OffsetOf_SwapBuffers_header_not_0);
+struct GetMaxValueInBuffer {
+ typedef GetMaxValueInBuffer ValueType;
+ static const CommandId kCmdId = kGetMaxValueInBuffer;
+ static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+
+ typedef GLuint Result;
+
+ static uint32 ComputeSize() {
+ return static_cast<uint32>(sizeof(ValueType)); // NOLINT
+ }
+
+ void SetHeader() {
+ header.SetCmd<ValueType>();
+ }
+
+ void Init(
+ GLuint _buffer_id, GLsizei _count, GLenum _type, GLuint _offset,
+ uint32 _result_shm_id, uint32 _result_shm_offset) {
+ SetHeader();
+ buffer_id = _buffer_id;
+ count = _count;
+ type = _type;
+ offset = _offset;
+ result_shm_id = _result_shm_id;
+ result_shm_offset = _result_shm_offset;
+ }
+
+ void* Set(
+ void* cmd, GLuint _buffer_id, GLsizei _count, GLenum _type,
+ GLuint _offset, uint32 _result_shm_id, uint32 _result_shm_offset) {
+ static_cast<ValueType*>(
+ cmd)->Init(
+ _buffer_id, _count, _type, _offset, _result_shm_id,
+ _result_shm_offset);
+ return NextCmdAddress<ValueType>(cmd);
+ }
+
+ gpu::CommandHeader header;
+ uint32 buffer_id;
+ int32 count;
+ uint32 type;
+ uint32 offset;
+ uint32 result_shm_id;
+ uint32 result_shm_offset;
+};
+
+COMPILE_ASSERT(sizeof(GetMaxValueInBuffer) == 28,
+ Sizeof_GetMaxValueInBuffer_is_not_28);
+COMPILE_ASSERT(offsetof(GetMaxValueInBuffer, header) == 0,
+ OffsetOf_GetMaxValueInBuffer_header_not_0);
+COMPILE_ASSERT(offsetof(GetMaxValueInBuffer, buffer_id) == 4,
+ OffsetOf_GetMaxValueInBuffer_buffer_id_not_4);
+COMPILE_ASSERT(offsetof(GetMaxValueInBuffer, count) == 8,
+ OffsetOf_GetMaxValueInBuffer_count_not_8);
+COMPILE_ASSERT(offsetof(GetMaxValueInBuffer, type) == 12,
+ OffsetOf_GetMaxValueInBuffer_type_not_12);
+COMPILE_ASSERT(offsetof(GetMaxValueInBuffer, offset) == 16,
+ OffsetOf_GetMaxValueInBuffer_offset_not_16);
+COMPILE_ASSERT(offsetof(GetMaxValueInBuffer, result_shm_id) == 20,
+ OffsetOf_GetMaxValueInBuffer_result_shm_id_not_20);
+COMPILE_ASSERT(offsetof(GetMaxValueInBuffer, result_shm_offset) == 24,
+ OffsetOf_GetMaxValueInBuffer_result_shm_offset_not_24);
+
#endif // GPU_COMMAND_BUFFER_COMMON_GLES2_CMD_FORMAT_AUTOGEN_H_
diff --git a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
index 5a07409..d498249 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
@@ -3247,5 +3247,28 @@ TEST(GLES2FormatTest, SwapBuffers) {
reinterpret_cast<char*>(&cmd) + sizeof(cmd));
}
+TEST(GLES2FormatTest, GetMaxValueInBuffer) {
+ GetMaxValueInBuffer cmd = { { 0 } };
+ void* next_cmd = cmd.Set(
+ &cmd,
+ static_cast<GLuint>(11),
+ static_cast<GLsizei>(12),
+ static_cast<GLenum>(13),
+ static_cast<GLuint>(14),
+ static_cast<uint32>(15),
+ static_cast<uint32>(16));
+ EXPECT_EQ(static_cast<uint32>(GetMaxValueInBuffer::kCmdId),
+ cmd.header.command);
+ EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+ EXPECT_EQ(static_cast<char*>(next_cmd),
+ reinterpret_cast<char*>(&cmd) + sizeof(cmd));
+ EXPECT_EQ(static_cast<GLuint>(11), cmd.buffer_id);
+ EXPECT_EQ(static_cast<GLsizei>(12), cmd.count);
+ EXPECT_EQ(static_cast<GLenum>(13), cmd.type);
+ EXPECT_EQ(static_cast<GLuint>(14), cmd.offset);
+ EXPECT_EQ(static_cast<uint32>(15), cmd.result_shm_id);
+ EXPECT_EQ(static_cast<uint32>(16), cmd.result_shm_offset);
+}
+
#endif // GPU_COMMAND_BUFFER_COMMON_GLES2_CMD_FORMAT_TEST_AUTOGEN_H_
diff --git a/gpu/command_buffer/common/gles2_cmd_id_test_autogen.h b/gpu/command_buffer/common/gles2_cmd_id_test_autogen.h
index 451b2ff..7092c1e 100644
--- a/gpu/command_buffer/common/gles2_cmd_id_test_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_id_test_autogen.h
@@ -375,6 +375,8 @@ TEST(GLES2CommandIdTest, CommandIdsMatch) {
GLES2_Viewport_kCmdId_mismatch);
COMPILE_ASSERT(SwapBuffers::kCmdId == 431,
GLES2_SwapBuffers_kCmdId_mismatch);
+ COMPILE_ASSERT(GetMaxValueInBuffer::kCmdId == 438,
+ GLES2_GetMaxValueInBuffer_kCmdId_mismatch);
}
#endif // GPU_COMMAND_BUFFER_COMMON_GLES2_CMD_ID_TEST_AUTOGEN_H_
diff --git a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
index 92699ce..4ce2215 100644
--- a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
@@ -190,6 +190,7 @@
OP(ShaderSourceBucket) /* 435 */ \
OP(ShaderBinary) /* 436 */ \
OP(ReleaseShaderCompiler) /* 437 */ \
+ OP(GetMaxValueInBuffer) /* 438 */ \
enum CommandId {
kStartPoint = cmd::kLastCommonId, // All GLES2 commands start after this.
diff --git a/gpu/command_buffer/common/scoped_ptr.h b/gpu/command_buffer/common/scoped_ptr.h
new file mode 100644
index 0000000..5f7adf7
--- /dev/null
+++ b/gpu/command_buffer/common/scoped_ptr.h
@@ -0,0 +1,385 @@
+// Copyright (c) 2006-2008 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.
+
+// Scopers help you manage ownership of a pointer, helping you easily manage the
+// a pointer within a scope, and automatically destroying the pointer at the
+// end of a scope. There are two main classes you will use, which coorespond
+// to the operators new/delete and new[]/delete[].
+//
+// Example usage (scoped_ptr):
+// {
+// scoped_ptr<Foo> foo(new Foo("wee"));
+// } // foo goes out of scope, releasing the pointer with it.
+//
+// {
+// scoped_ptr<Foo> foo; // No pointer managed.
+// foo.reset(new Foo("wee")); // Now a pointer is managed.
+// foo.reset(new Foo("wee2")); // Foo("wee") was destroyed.
+// foo.reset(new Foo("wee3")); // Foo("wee2") was destroyed.
+// foo->Method(); // Foo::Method() called.
+// foo.get()->Method(); // Foo::Method() called.
+// SomeFunc(foo.release()); // SomeFunc takes owernship, foo no longer
+// // manages a pointer.
+// foo.reset(new Foo("wee4")); // foo manages a pointer again.
+// foo.reset(); // Foo("wee4") destroyed, foo no longer
+// // manages a pointer.
+// } // foo wasn't managing a pointer, so nothing was destroyed.
+//
+// Example usage (scoped_array):
+// {
+// scoped_array<Foo> foo(new Foo[100]);
+// foo.get()->Method(); // Foo::Method on the 0th element.
+// foo[10].Method(); // Foo::Method on the 10th element.
+// }
+
+#ifndef GPU_COMMAND_BUFFER_COMMON_SCOPED_PTR_H_
+#define GPU_COMMAND_BUFFER_COMMON_SCOPED_PTR_H_
+
+// This is an implementation designed to match the anticipated future TR2
+// implementation of the scoped_ptr class, and its closely-related brethren,
+// scoped_array, scoped_ptr_malloc.
+
+#include <assert.h>
+#include <stdlib.h>
+#include <cstddef>
+
+#ifndef __native_client__
+#include "base/scoped_ptr.h"
+#else
+
+// A scoped_ptr<T> is like a T*, except that the destructor of scoped_ptr<T>
+// automatically deletes the pointer it holds (if any).
+// That is, scoped_ptr<T> owns the T object that it points to.
+// Like a T*, a scoped_ptr<T> may hold either NULL or a pointer to a T object.
+// Also like T*, scoped_ptr<T> is thread-compatible, and once you
+// dereference it, you get the threadsafety guarantees of T.
+//
+// The size of a scoped_ptr is small:
+// sizeof(scoped_ptr<C>) == sizeof(C*)
+template <class C>
+class scoped_ptr {
+ public:
+
+ // The element type
+ typedef C element_type;
+
+ // Constructor. Defaults to intializing with NULL.
+ // There is no way to create an uninitialized scoped_ptr.
+ // The input parameter must be allocated with new.
+ explicit scoped_ptr(C* p = NULL) : ptr_(p) { }
+
+ // Destructor. If there is a C object, delete it.
+ // We don't need to test ptr_ == NULL because C++ does that for us.
+ ~scoped_ptr() {
+ enum { type_must_be_complete = sizeof(C) };
+ delete ptr_;
+ }
+
+ // Reset. Deletes the current owned object, if any.
+ // Then takes ownership of a new object, if given.
+ // this->reset(this->get()) works.
+ void reset(C* p = NULL) {
+ if (p != ptr_) {
+ enum { type_must_be_complete = sizeof(C) };
+ delete ptr_;
+ ptr_ = p;
+ }
+ }
+
+ // Accessors to get the owned object.
+ // operator* and operator-> will assert() if there is no current object.
+ C& operator*() const {
+ assert(ptr_ != NULL);
+ return *ptr_;
+ }
+ C* operator->() const {
+ assert(ptr_ != NULL);
+ return ptr_;
+ }
+ C* get() const { return ptr_; }
+
+ // Comparison operators.
+ // These return whether two scoped_ptr refer to the same object, not just to
+ // two different but equal objects.
+ bool operator==(C* p) const { return ptr_ == p; }
+ bool operator!=(C* p) const { return ptr_ != p; }
+
+ // Swap two scoped pointers.
+ void swap(scoped_ptr& p2) {
+ C* tmp = ptr_;
+ ptr_ = p2.ptr_;
+ p2.ptr_ = tmp;
+ }
+
+ // Release a pointer.
+ // The return value is the current pointer held by this object.
+ // If this object holds a NULL pointer, the return value is NULL.
+ // After this operation, this object will hold a NULL pointer,
+ // and will not own the object any more.
+ C* release() {
+ C* retVal = ptr_;
+ ptr_ = NULL;
+ return retVal;
+ }
+
+ private:
+ C* ptr_;
+
+ // Forbid comparison of scoped_ptr types. If C2 != C, it totally doesn't
+ // make sense, and if C2 == C, it still doesn't make sense because you should
+ // never have the same object owned by two different scoped_ptrs.
+ template <class C2> bool operator==(scoped_ptr<C2> const& p2) const;
+ template <class C2> bool operator!=(scoped_ptr<C2> const& p2) const;
+
+ // Disallow evil constructors
+ scoped_ptr(const scoped_ptr&);
+ void operator=(const scoped_ptr&);
+};
+
+// Free functions
+template <class C>
+void swap(scoped_ptr<C>& p1, scoped_ptr<C>& p2) {
+ p1.swap(p2);
+}
+
+template <class C>
+bool operator==(C* p1, const scoped_ptr<C>& p2) {
+ return p1 == p2.get();
+}
+
+template <class C>
+bool operator!=(C* p1, const scoped_ptr<C>& p2) {
+ return p1 != p2.get();
+}
+
+// scoped_array<C> is like scoped_ptr<C>, except that the caller must allocate
+// with new [] and the destructor deletes objects with delete [].
+//
+// As with scoped_ptr<C>, a scoped_array<C> either points to an object
+// or is NULL. A scoped_array<C> owns the object that it points to.
+// scoped_array<T> is thread-compatible, and once you index into it,
+// the returned objects have only the threadsafety guarantees of T.
+//
+// Size: sizeof(scoped_array<C>) == sizeof(C*)
+template <class C>
+class scoped_array {
+ public:
+
+ // The element type
+ typedef C element_type;
+
+ // Constructor. Defaults to intializing with NULL.
+ // There is no way to create an uninitialized scoped_array.
+ // The input parameter must be allocated with new [].
+ explicit scoped_array(C* p = NULL) : array_(p) { }
+
+ // Destructor. If there is a C object, delete it.
+ // We don't need to test ptr_ == NULL because C++ does that for us.
+ ~scoped_array() {
+ enum { type_must_be_complete = sizeof(C) };
+ delete[] array_;
+ }
+
+ // Reset. Deletes the current owned object, if any.
+ // Then takes ownership of a new object, if given.
+ // this->reset(this->get()) works.
+ void reset(C* p = NULL) {
+ if (p != array_) {
+ enum { type_must_be_complete = sizeof(C) };
+ delete[] array_;
+ array_ = p;
+ }
+ }
+
+ // Get one element of the current object.
+ // Will assert() if there is no current object, or index i is negative.
+ C& operator[](std::ptrdiff_t i) const {
+ assert(i >= 0);
+ assert(array_ != NULL);
+ return array_[i];
+ }
+
+ // Get a pointer to the zeroth element of the current object.
+ // If there is no current object, return NULL.
+ C* get() const {
+ return array_;
+ }
+
+ // Comparison operators.
+ // These return whether two scoped_array refer to the same object, not just to
+ // two different but equal objects.
+ bool operator==(C* p) const { return array_ == p; }
+ bool operator!=(C* p) const { return array_ != p; }
+
+ // Swap two scoped arrays.
+ void swap(scoped_array& p2) {
+ C* tmp = array_;
+ array_ = p2.array_;
+ p2.array_ = tmp;
+ }
+
+ // Release an array.
+ // The return value is the current pointer held by this object.
+ // If this object holds a NULL pointer, the return value is NULL.
+ // After this operation, this object will hold a NULL pointer,
+ // and will not own the object any more.
+ C* release() {
+ C* retVal = array_;
+ array_ = NULL;
+ return retVal;
+ }
+
+ private:
+ C* array_;
+
+ // Forbid comparison of different scoped_array types.
+ template <class C2> bool operator==(scoped_array<C2> const& p2) const;
+ template <class C2> bool operator!=(scoped_array<C2> const& p2) const;
+
+ // Disallow evil constructors
+ scoped_array(const scoped_array&);
+ void operator=(const scoped_array&);
+};
+
+// Free functions
+template <class C>
+void swap(scoped_array<C>& p1, scoped_array<C>& p2) {
+ p1.swap(p2);
+}
+
+template <class C>
+bool operator==(C* p1, const scoped_array<C>& p2) {
+ return p1 == p2.get();
+}
+
+template <class C>
+bool operator!=(C* p1, const scoped_array<C>& p2) {
+ return p1 != p2.get();
+}
+
+// This class wraps the c library function free() in a class that can be
+// passed as a template argument to scoped_ptr_malloc below.
+class ScopedPtrMallocFree {
+ public:
+ inline void operator()(void* x) const {
+ free(x);
+ }
+};
+
+// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a
+// second template argument, the functor used to free the object.
+
+template<class C, class FreeProc = ScopedPtrMallocFree>
+class scoped_ptr_malloc {
+ public:
+
+ // The element type
+ typedef C element_type;
+
+ // Constructor. Defaults to intializing with NULL.
+ // There is no way to create an uninitialized scoped_ptr.
+ // The input parameter must be allocated with an allocator that matches the
+ // Free functor. For the default Free functor, this is malloc, calloc, or
+ // realloc.
+ explicit scoped_ptr_malloc(C* p = NULL): ptr_(p) {}
+
+ // Destructor. If there is a C object, call the Free functor.
+ ~scoped_ptr_malloc() {
+ free_(ptr_);
+ }
+
+ // Reset. Calls the Free functor on the current owned object, if any.
+ // Then takes ownership of a new object, if given.
+ // this->reset(this->get()) works.
+ void reset(C* p = NULL) {
+ if (ptr_ != p) {
+ free_(ptr_);
+ ptr_ = p;
+ }
+ }
+
+ // Get the current object.
+ // operator* and operator-> will cause an assert() failure if there is
+ // no current object.
+ C& operator*() const {
+ assert(ptr_ != NULL);
+ return *ptr_;
+ }
+
+ C* operator->() const {
+ assert(ptr_ != NULL);
+ return ptr_;
+ }
+
+ C* get() const {
+ return ptr_;
+ }
+
+ // Comparison operators.
+ // These return whether a scoped_ptr_malloc and a plain pointer refer
+ // to the same object, not just to two different but equal objects.
+ // For compatibility wwith the boost-derived implementation, these
+ // take non-const arguments.
+ bool operator==(C* p) const {
+ return ptr_ == p;
+ }
+
+ bool operator!=(C* p) const {
+ return ptr_ != p;
+ }
+
+ // Swap two scoped pointers.
+ void swap(scoped_ptr_malloc & b) {
+ C* tmp = b.ptr_;
+ b.ptr_ = ptr_;
+ ptr_ = tmp;
+ }
+
+ // Release a pointer.
+ // The return value is the current pointer held by this object.
+ // If this object holds a NULL pointer, the return value is NULL.
+ // After this operation, this object will hold a NULL pointer,
+ // and will not own the object any more.
+ C* release() {
+ C* tmp = ptr_;
+ ptr_ = NULL;
+ return tmp;
+ }
+
+ private:
+ C* ptr_;
+
+ // no reason to use these: each scoped_ptr_malloc should have its own object
+ template <class C2, class GP>
+ bool operator==(scoped_ptr_malloc<C2, GP> const& p) const;
+ template <class C2, class GP>
+ bool operator!=(scoped_ptr_malloc<C2, GP> const& p) const;
+
+ static FreeProc const free_;
+
+ // Disallow evil constructors
+ scoped_ptr_malloc(const scoped_ptr_malloc&);
+ void operator=(const scoped_ptr_malloc&);
+};
+
+template<class C, class FP>
+FP const scoped_ptr_malloc<C, FP>::free_ = FP();
+
+template<class C, class FP> inline
+void swap(scoped_ptr_malloc<C, FP>& a, scoped_ptr_malloc<C, FP>& b) {
+ a.swap(b);
+}
+
+template<class C, class FP> inline
+bool operator==(C* p, const scoped_ptr_malloc<C, FP>& b) {
+ return p == b.get();
+}
+
+template<class C, class FP> inline
+bool operator!=(C* p, const scoped_ptr_malloc<C, FP>& b) {
+ return p != b.get();
+}
+
+#endif // __native_client__
+#endif // GPU_COMMAND_BUFFER_COMMON_SCOPED_PTR_H_
diff --git a/gpu/command_buffer/docs/gles2_cmd_format_docs.txt b/gpu/command_buffer/docs/gles2_cmd_format_docs.txt
index 8d1b360..d1f7326 100644
--- a/gpu/command_buffer/docs/gles2_cmd_format_docs.txt
+++ b/gpu/command_buffer/docs/gles2_cmd_format_docs.txt
@@ -2164,4 +2164,19 @@ struct SwapBuffers {
CommandHeader header;
};
+//! Command that corresponds to GetMaxValueInBuffer.
+struct GetMaxValueInBuffer {
+ static const CommandId kCmdId = 436;
+
+ typedef GLuint Result;
+
+ CommandHeader header;
+ uint32 buffer_id; //!< GLuint
+ int32 count; //!< GLsizei
+ uint32 type; //!< GLenum
+ uint32 offset; //!< GLuint
+ uint32 result_shm_id; //!< uint32
+ uint32 result_shm_offset; //!< uint32
+};
+
diff --git a/gpu/command_buffer/service/gl_interface.h b/gpu/command_buffer/service/gl_interface.h
index 0629e20..fd934bf 100644
--- a/gpu/command_buffer/service/gl_interface.h
+++ b/gpu/command_buffer/service/gl_interface.h
@@ -400,6 +400,9 @@ class GLInterface {
virtual void SwapBuffers() = 0;
+ virtual GLuint GetMaxValueInBuffer(
+ GLuint buffer_id, GLsizei count, GLenum type, GLuint offset) = 0;
+
private:
static GLInterface* interface_;
};
diff --git a/gpu/command_buffer/service/gl_mock.h b/gpu/command_buffer/service/gl_mock.h
index ca038bb..41be895 100644
--- a/gpu/command_buffer/service/gl_mock.h
+++ b/gpu/command_buffer/service/gl_mock.h
@@ -376,6 +376,9 @@ class MockGLInterface : public GLInterface {
MOCK_METHOD4(Viewport, void(GLint x, GLint y, GLsizei width, GLsizei height));
MOCK_METHOD0(SwapBuffers, void());
+
+ MOCK_METHOD4(GetMaxValueInBuffer, GLuint(
+ GLuint buffer_id, GLsizei count, GLenum type, GLuint offset));
};
} // namespace gles2
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 706407a..3d8b0af 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -679,6 +679,10 @@ class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>,
// Wrapper for DoGetIntegerv.
void DoGetIntegerv(GLenum pname, GLint* params);
+ // Gets the max value in a range in a buffer.
+ GLuint DoGetMaxValueInBuffer(
+ GLuint buffer_id, GLsizei count, GLenum type, GLuint offset);
+
// Wrapper for glRenderbufferParameteriv.
void DoGetRenderbufferParameteriv(
GLenum target, GLenum pname, GLint* params);
@@ -2354,6 +2358,20 @@ error::Error GLES2DecoderImpl::HandleDrawElements(
return error::kNoError;
}
+GLuint GLES2DecoderImpl::DoGetMaxValueInBuffer(
+ GLuint buffer_id, GLsizei count, GLenum type, GLuint offset) {
+ GLuint max_vertex_accessed = 0;
+ BufferManager::BufferInfo* info = GetBufferInfo(buffer_id);
+ if (info->target() != GL_ELEMENT_ARRAY_BUFFER) {
+ SetGLError(GL_INVALID_OPERATION);
+ } else {
+ if (!info->GetMaxValueForRange(offset, count, type, &max_vertex_accessed)) {
+ SetGLError(GL_INVALID_OPERATION);
+ }
+ }
+ return max_vertex_accessed;
+}
+
// Calls glShaderSource for the various versions of the ShaderSource command.
// Assumes that data / data_size points to a piece of memory that is in range
// of whatever context it came from (shared memory, immediate memory, bucket
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
index 01e9dd2..0bda376 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
@@ -2828,5 +2828,33 @@ error::Error GLES2DecoderImpl::HandleViewport(
return error::kNoError;
}
+error::Error GLES2DecoderImpl::HandleGetMaxValueInBuffer(
+ uint32 immediate_data_size, const gles2::GetMaxValueInBuffer& c) {
+ GLuint buffer_id;
+ if (!id_manager()->GetServiceId(c.buffer_id, &buffer_id)) {
+ SetGLError(GL_INVALID_VALUE);
+ return error::kNoError;
+ }
+ GLsizei count = static_cast<GLsizei>(c.count);
+ GLenum type = static_cast<GLenum>(c.type);
+ GLuint offset = static_cast<GLuint>(c.offset);
+ typedef GetMaxValueInBuffer::Result Result;
+ Result* result_dst = GetSharedMemoryAs<Result*>(
+ c.result_shm_id, c.result_shm_offset, sizeof(*result_dst));
+ if (!result_dst) {
+ return error::kOutOfBounds;
+ }
+ if (count < 0) {
+ SetGLError(GL_INVALID_VALUE);
+ return error::kNoError;
+ }
+ if (!ValidateGLenumIndexType(type)) {
+ SetGLError(GL_INVALID_ENUM);
+ return error::kNoError;
+ }
+ *result_dst = DoGetMaxValueInBuffer(buffer_id, count, type, offset);
+ return error::kNoError;
+}
+
#endif // GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_AUTOGEN_H_
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
index b916f8e..1f64b79 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
@@ -1885,6 +1885,61 @@ TEST_F(GLES2DecoderWithShaderTest, GetUniformLocationBucketInvalidArgs) {
EXPECT_NE(error::kNoError, ExecuteCmd(cmd));
}
+TEST_F(GLES2DecoderWithShaderTest, GetMaxValueInBuffer) {
+ SetupIndexBuffer();
+ GetMaxValueInBuffer::Result* result =
+ static_cast<GetMaxValueInBuffer::Result*>(shared_memory_address_);
+ *result = 0;
+
+ GetMaxValueInBuffer cmd;
+ cmd.Init(client_element_buffer_id_, kValidIndexRangeCount, GL_UNSIGNED_SHORT,
+ kValidIndexRangeStart * 2, kSharedMemoryId, kSharedMemoryOffset);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(7u, *result);
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+ cmd.Init(client_element_buffer_id_, kValidIndexRangeCount + 1,
+ GL_UNSIGNED_SHORT,
+ kValidIndexRangeStart * 2, kSharedMemoryId, kSharedMemoryOffset);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(100u, *result);
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ cmd.Init(kInvalidClientId, kValidIndexRangeCount,
+ GL_UNSIGNED_SHORT,
+ kValidIndexRangeStart * 2, kSharedMemoryId, kSharedMemoryOffset);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
+ cmd.Init(client_element_buffer_id_, kOutOfRangeIndexRangeEnd,
+ GL_UNSIGNED_SHORT,
+ kValidIndexRangeStart * 2, kSharedMemoryId, kSharedMemoryOffset);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+ cmd.Init(client_element_buffer_id_, kValidIndexRangeCount + 1,
+ GL_UNSIGNED_SHORT,
+ kOutOfRangeIndexRangeEnd * 2, kSharedMemoryId, kSharedMemoryOffset);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+ cmd.Init(client_element_buffer_id_, kValidIndexRangeCount + 1,
+ GL_UNSIGNED_SHORT,
+ kValidIndexRangeStart * 2, kSharedMemoryId, kSharedMemoryOffset);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ cmd.Init(client_buffer_id_, kValidIndexRangeCount + 1,
+ GL_UNSIGNED_SHORT,
+ kValidIndexRangeStart * 2, kSharedMemoryId, kSharedMemoryOffset);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
+ cmd.Init(client_element_buffer_id_, kValidIndexRangeCount + 1,
+ GL_UNSIGNED_SHORT,
+ kValidIndexRangeStart * 2,
+ kInvalidSharedMemoryId, kSharedMemoryOffset);
+ EXPECT_NE(error::kNoError, ExecuteCmd(cmd));
+ cmd.Init(client_element_buffer_id_, kValidIndexRangeCount + 1,
+ GL_UNSIGNED_SHORT,
+ kValidIndexRangeStart * 2,
+ kSharedMemoryId, kInvalidSharedMemoryOffset);
+ EXPECT_NE(error::kNoError, ExecuteCmd(cmd));
+}
+
// TODO(gman): BufferData
// TODO(gman): BufferDataImmediate
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h
index 203ec06..c91acde 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h
@@ -1851,5 +1851,7 @@ TEST_F(GLES2DecoderTest1, GetTexParameterivInvalidArgs2_1) {
}
// TODO(gman): GetUniformfv
+// TODO(gman): GetUniformiv
+
#endif // GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_1_AUTOGEN_H_
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2_autogen.h
index bdf34cf..64f1424 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2_autogen.h
@@ -8,8 +8,6 @@
#ifndef GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_2_AUTOGEN_H_
#define GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_2_AUTOGEN_H_
-// TODO(gman): GetUniformiv
-
// TODO(gman): GetUniformLocation
// TODO(gman): GetUniformLocationImmediate
@@ -1635,5 +1633,6 @@ TEST_F(GLES2DecoderTest2, ViewportInvalidArgs3_0) {
EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
}
// TODO(gman): SwapBuffers
+// TODO(gman): GetMaxValueInBuffer
#endif // GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_2_AUTOGEN_H_