diff options
Diffstat (limited to 'gpu/command_buffer')
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, ¤t[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(¤t_attrib, ¤t, 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_ |