diff options
author | gman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-13 22:06:46 +0000 |
---|---|---|
committer | gman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-13 22:06:46 +0000 |
commit | 1d32bc88813cca868952dcb927c541f27331b4b3 (patch) | |
tree | 387acc0eca1c6aaa6e8e38c8775636afc4edc8d2 /gpu/command_buffer | |
parent | 138ac9c942f20d2d3e5b48abc52be524d9559b11 (diff) | |
download | chromium_src-1d32bc88813cca868952dcb927c541f27331b4b3.zip chromium_src-1d32bc88813cca868952dcb927c541f27331b4b3.tar.gz chromium_src-1d32bc88813cca868952dcb927c541f27331b4b3.tar.bz2 |
Adds unit tests for glDrawArrays and glDrawBuffers
in the GLES2Decoder class of command buffers.
Also fixes a few bugs related to validating buffer
access and reporting GL errors
I also separated out the id mapping, buffer tracking
and program tracking into separate classes as
different decoders will need to share instances of
those classes once we allow sharing of resources.
TEST=none
BUG=none
Review URL: http://codereview.chromium.org/546026
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@36178 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gpu/command_buffer')
6 files changed, 778 insertions, 194 deletions
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py index 4687c4d..170d4cf 100755 --- a/gpu/command_buffer/build_gles2_cmd_buffer.py +++ b/gpu/command_buffer/build_gles2_cmd_buffer.py @@ -137,7 +137,7 @@ GL_APICALL void GL_APIENTRY glStencilMask (GLuint mask); GL_APICALL void GL_APIENTRY glStencilMaskSeparate (GLenumFaceType face, GLuint mask); GL_APICALL void GL_APIENTRY glStencilOp (GLenumStencilOp fail, GLenumStencilOp zfail, GLenumStencilOp zpass); GL_APICALL void GL_APIENTRY glStencilOpSeparate (GLenumFaceType face, GLenumStencilOp fail, GLenumStencilOp zfail, GLenumStencilOp zpass); -GL_APICALL void GL_APIENTRY glTexImage2D (GLenumTextureTarget target, GLint level, GLintTextureFormat internalformat, GLsizei width, GLsizei height, GLint border, GLenumTextureFormat format, GLenumPixelType type, const void* pixels); +GL_APICALL void GL_APIENTRY glTexImage2D (GLenumTextureTarget target, GLint level, GLintTextureFormat internalformat, GLsizei width, GLsizei height, GLintTextureBorder border, GLenumTextureFormat format, GLenumPixelType type, const void* pixels); GL_APICALL void GL_APIENTRY glTexParameterf (GLenumTextureBindTarget target, GLenumTextureParameter pname, GLfloat param); GL_APICALL void GL_APIENTRY glTexParameterfv (GLenumTextureBindTarget target, GLenumTextureParameter pname, const GLfloat* params); GL_APICALL void GL_APIENTRY glTexParameteri (GLenumTextureBindTarget target, GLenumTextureParameter pname, GLint param); @@ -784,6 +784,15 @@ _ENUM_LISTS = { 'GL_DOUBLE', ], }, + 'TextureBorder': { + 'type': 'GLint', + 'valid': [ + '0', + ], + 'invalid': [ + '1', + ], + }, 'VertexAttribSize': { 'type': 'GLint', 'valid': [ @@ -3305,7 +3314,7 @@ class ResourceIdArgument(Argument): def WriteGetCode(self, file): """Overridden from Argument.""" file.Write(" %s %s;\n" % (self.type, self.name)) - file.Write(" if (!id_map_.GetServiceId(c.%s, &%s)) {\n" % + file.Write(" if (!id_manager_->GetServiceId(c.%s, &%s)) {\n" % (self.name, self.name)) file.Write(" SetGLError(GL_INVALID_VALUE);\n") file.Write(" return parse_error::kParseNoError;\n") diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index 282fb99..9243d3d 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc @@ -100,11 +100,11 @@ const CommandInfo g_command_info[] = { namespace GLErrorBit { enum GLErrorBit { kNoError = 0, - kInvalidEnum, - kInvalidValue, - kInvalidOperation, - kOutOfMemory, - kInvalidFrameBufferOperation, + kInvalidEnum = (1 << 0), + kInvalidValue = (1 << 1), + kInvalidOperation = (1 << 2), + kOutOfMemory = (1 << 3), + kInvalidFrameBufferOperation = (1 << 4), }; } @@ -164,7 +164,10 @@ GLES2Decoder::GLES2Decoder() } // This class maps one set of ids to another. -class IdMap { +// +// NOTE: To support shared resources an instance of this class will +// need to be shared by multiple GLES2Decoders. +class IdManager { public: // Maps a client_id to a service_id. Return false if the client_id or // service_id are already mapped to something else. @@ -187,13 +190,13 @@ class IdMap { MapType id_map_; }; -bool IdMap::AddMapping(GLuint client_id, GLuint service_id) { +bool IdManager::AddMapping(GLuint client_id, GLuint service_id) { std::pair<MapType::iterator, bool> result = id_map_.insert( std::make_pair(client_id, service_id)); return result.second; } -bool IdMap::RemoveMapping(GLuint client_id, GLuint service_id) { +bool IdManager::RemoveMapping(GLuint client_id, GLuint service_id) { MapType::iterator iter = id_map_.find(client_id); if (iter != id_map_.end() && iter->second == service_id) { id_map_.erase(iter); @@ -202,7 +205,7 @@ bool IdMap::RemoveMapping(GLuint client_id, GLuint service_id) { return false; } -bool IdMap::GetServiceId(GLuint client_id, GLuint* service_id) { +bool IdManager::GetServiceId(GLuint client_id, GLuint* service_id) { DCHECK(service_id); MapType::iterator iter = id_map_.find(client_id); if (iter != id_map_.end()) { @@ -212,7 +215,7 @@ bool IdMap::GetServiceId(GLuint client_id, GLuint* service_id) { return false; } -bool IdMap::GetClientId(GLuint service_id, GLuint* client_id) { +bool IdManager::GetClientId(GLuint service_id, GLuint* client_id) { DCHECK(client_id); MapType::iterator end(id_map_.end()); for (MapType::iterator iter(id_map_.begin()); @@ -226,6 +229,160 @@ bool IdMap::GetClientId(GLuint service_id, GLuint* client_id) { return false; } +// This class keeps track of the buffers and their sizes so we can do +// bounds checking. +// +// NOTE: To support shared resources an instance of this class will need to be +// shared by multiple GLES2Decoders. +class BufferManager { + public: + // Info about Buffers currently in the system. + class BufferInfo { + public: + BufferInfo() + : size_(0) { + } + + explicit BufferInfo(GLsizeiptr size) + : size_(size) { + } + + GLsizeiptr size() const { + return size_; + } + + // Returns the maximum value in the buffer for the given range + // interpreted as the given type. + GLuint GetMaxValueForRange(GLuint offset, GLsizei count, GLenum type); + + private: + GLsizeiptr size_; + }; + + // Removes any buffers in the VertexAtrribInfos and BufferInfos. This is used + // on glDeleteBuffers so we can make sure the user does not try to render + // with deleted buffers. + void RemoveBufferInfo(GLuint buffer_id); + + // Gets the buffer info for the given buffer. + BufferInfo* GetBufferInfo(GLuint buffer); + + // Sets the info for a buffer. + void SetBufferInfo(GLuint buffer, GLsizeiptr size); + + private: + // Info for each buffer in the system. + // TODO(gman): Choose a faster container. + typedef std::map<GLuint, BufferInfo> BufferInfoMap; + BufferInfoMap buffer_infos_; +}; + +BufferManager::BufferInfo* BufferManager::GetBufferInfo( + GLuint buffer) { + BufferInfoMap::iterator it = buffer_infos_.find(buffer); + return it != buffer_infos_.end() ? &it->second : NULL; +} + +void BufferManager::SetBufferInfo(GLuint buffer, GLsizeiptr size) { + buffer_infos_[buffer] = BufferInfo(size); +} + +void BufferManager::RemoveBufferInfo(GLuint buffer_id) { + buffer_infos_.erase(buffer_id); +} + +GLuint BufferManager::BufferInfo::GetMaxValueForRange( + GLuint offset, GLsizei count, GLenum type) { + // TODO(gman): Scan the values in the given range and cache their results. + return 0u; +} + +// Tracks the Programs. +// +// NOTE: To support shared resources an instance of this class will +// need to be shared by multiple GLES2Decoders. +class ProgramManager { + public: + // This is used to track which attributes a particular program needs + // so we can verify at glDrawXXX time that every attribute is either disabled + // or if enabled that it points to a valid source. + class ProgramInfo { + public: + typedef std::vector<GLuint> AttribLocationVector; + + ProgramInfo() { + } + + void SetNumAttributes(int num_attribs) { + attrib_locations_.resize(num_attribs); + } + + void SetAttributeLocation(GLuint index, int location) { + DCHECK(index < attrib_locations_.size()); + attrib_locations_[index] = location; + } + + const AttribLocationVector& GetAttribLocations() const { + return attrib_locations_; + } + private: + AttribLocationVector attrib_locations_; + }; + + ProgramInfo* GetProgramInfo(GLuint program); + + // Updates the program info for the given program. + void UpdateProgramInfo(GLuint program); + + // Deletes the program info for the given program. + void RemoveProgramInfo(GLuint program); + + private: + // Info for each "successfully linked" program by service side program Id. + // TODO(gman): Choose a faster container. + typedef std::map<GLuint, ProgramInfo> ProgramInfoMap; + ProgramInfoMap program_infos_; +}; + +ProgramManager::ProgramInfo* ProgramManager::GetProgramInfo(GLuint program) { + ProgramInfoMap::iterator it = program_infos_.find(program); + return it != program_infos_.end() ? &it->second : NULL; +} + +void ProgramManager::UpdateProgramInfo(GLuint program) { + ProgramInfo* info = GetProgramInfo(program); + if (!info) { + std::pair<ProgramInfoMap::iterator, bool> result = + program_infos_.insert(std::make_pair(program, ProgramInfo())); + DCHECK(result.second); + info = &result.first->second; + } + GLint num_attribs = 0; + GLint max_len = 0; + glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &num_attribs); + info->SetNumAttributes(num_attribs); + glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_len); + // TODO(gman): Should we check for error? + scoped_array<char> name_buffer(new char[max_len + 1]); + for (GLint ii = 0; ii < num_attribs; ++ii) { + GLsizei length; + GLsizei size; + GLenum type; + glGetActiveAttrib( + program, ii, max_len + 1, &length, &size, &type, name_buffer.get()); + // TODO(gman): Should we check for error? + GLint location = glGetAttribLocation(program, name_buffer.get()); + info->SetAttributeLocation(ii, location); + } +} + +void ProgramManager::RemoveProgramInfo(GLuint program) { + ProgramInfoMap::iterator it = program_infos_.find(program); + if (it != program_infos_.end()) { + program_infos_.erase(it); + } +} + // This class implements GLES2Decoder so we don't have to expose all the GLES2 // cmd stuff to outside this class. class GLES2DecoderImpl : public GLES2Decoder { @@ -266,9 +423,10 @@ class GLES2DecoderImpl : public GLES2Decoder { if (offset_ > buffer_size || real_stride_ == 0) { num_elements_ = 0; } else { - uint32 size = buffer_size - offset_; - num_elements_ = size / real_stride_ + - (size % real_stride_ >= GetGLTypeSize(type_) ? 1 : 0); + uint32 usable_size = buffer_size - offset_; + num_elements_ = usable_size / real_stride_ + + ((usable_size % real_stride_) >= + (GetGLTypeSize(type_) * size_) ? 1 : 0); } } @@ -316,45 +474,6 @@ class GLES2DecoderImpl : public GLES2Decoder { GLuint num_elements_; }; - // Info about Buffers currently in the system. - struct BufferInfo { - BufferInfo() - : size(0) { - } - - explicit BufferInfo(GLsizeiptr _size) - : size(_size) { - } - - GLsizeiptr size; - }; - - // This is used to track which attributes a particular program needs - // so we can verify at glDrawXXX time that every attribute is either disabled - // or if enabled that it points to a valid source. - class ProgramInfo { - public: - typedef std::vector<GLuint> AttribLocationVector; - - ProgramInfo() { - } - - void SetNumAttributes(int num_attribs) { - attrib_locations_.resize(num_attribs); - } - - void SetAttributeLocation(GLuint index, int location) { - DCHECK(index < attrib_locations_.size()); - attrib_locations_[index] = location; - } - - const AttribLocationVector& GetAttribLocations() const { - return attrib_locations_; - } - private: - AttribLocationVector attrib_locations_; - }; - GLES2DecoderImpl(); // Overridden from AsyncAPIInterface. @@ -421,16 +540,24 @@ class GLES2DecoderImpl : public GLES2Decoder { // Gets the program info for the given program. Returns NULL if none exists. // Programs that have no had glLinkProgram succesfully called on them will // not exist. - ProgramInfo* GetProgramInfo(GLuint program); + ProgramManager::ProgramInfo* GetProgramInfo(GLuint program) { + return program_manager_->GetProgramInfo(program); + } // Updates the program info for the given program. - void UpdateProgramInfo(GLuint program); + void UpdateProgramInfo(GLuint program) { + program_manager_->UpdateProgramInfo(program); + } // Deletes the program info for the given program. - void RemoveProgramInfo(GLuint program); + void RemoveProgramInfo(GLuint program) { + program_manager_->RemoveProgramInfo(program); + } // Gets the buffer info for the given buffer. - const BufferInfo* GetBufferInfo(GLuint buffer); + BufferManager::BufferInfo* GetBufferInfo(GLuint buffer) { + return buffer_manager_->GetBufferInfo(buffer); + } // Sets the info for a buffer. void SetBufferInfo(GLuint buffer, GLsizeiptr size); @@ -499,7 +626,7 @@ class GLES2DecoderImpl : public GLES2Decoder { uint32 error_bits_; // Map of client ids to GL ids. - IdMap id_map_; + scoped_ptr<IdManager> id_manager_; // Util to help with GL. GLES2Util util_; @@ -525,18 +652,12 @@ class GLES2DecoderImpl : public GLES2Decoder { // if it is safe to draw. scoped_array<VertexAttribInfo> vertex_attrib_infos_; - // Info for each buffer in the system. - // TODO(gman): Choose a faster container. - typedef std::map<GLuint, BufferInfo> BufferInfoMap; - BufferInfoMap buffer_infos_; + scoped_ptr<BufferManager> buffer_manager_; - // Info for each "successfully linked" program by service side program Id. - // TODO(gman): Choose a faster container. - typedef std::map<GLuint, ProgramInfo> ProgramInfoMap; - ProgramInfoMap program_infos_; + scoped_ptr<ProgramManager> program_manager_; - // The program in current use through glUseProgram. - ProgramInfo* current_program_info_; + // The program in use by glUseProgram + GLuint current_program_; #if defined(UNIT_TEST) #elif defined(OS_WIN) @@ -562,7 +683,7 @@ GLES2DecoderImpl::GLES2DecoderImpl() bound_array_buffer_(0), bound_element_array_buffer_(0), max_vertex_attribs_(0), - current_program_info_(NULL), + current_program_(0), #if defined(UNIT_TEST) #elif defined(OS_WIN) device_context_(NULL), @@ -572,6 +693,10 @@ GLES2DecoderImpl::GLES2DecoderImpl() } bool GLES2DecoderImpl::Initialize() { + id_manager_.reset(new IdManager()); + buffer_manager_.reset(new BufferManager()); + program_manager_.reset(new ProgramManager()); + if (!InitPlatformSpecific()) return false; if (!InitGlew()) @@ -815,7 +940,7 @@ void GLDeleteTexturesHelper( uint32 GLES2DecoderImpl::GetServiceIdForTesting(uint32 client_id) { #if defined(UNIT_TEST) GLuint service_id; - bool result = id_map_.GetServiceId(client_id, &service_id); + bool result = id_manager_->GetServiceId(client_id, &service_id); return result ? service_id : 0u; #else DCHECK(false); @@ -827,7 +952,7 @@ bool GLES2DecoderImpl::ValidateIdsAreUnused( GLsizei n, const GLuint* client_ids) { for (GLsizei ii = 0; ii < n; ++ii) { GLuint service_id; - if (id_map_.GetServiceId(client_ids[ii], &service_id)) { + if (id_manager_->GetServiceId(client_ids[ii], &service_id)) { return false; } } @@ -837,7 +962,7 @@ bool GLES2DecoderImpl::ValidateIdsAreUnused( bool GLES2DecoderImpl::RegisterObjects( GLsizei n, const GLuint* client_ids, const GLuint* service_ids) { for (GLsizei ii = 0; ii < n; ++ii) { - if (!id_map_.AddMapping(client_ids[ii], service_ids[ii])) { + if (!id_manager_->AddMapping(client_ids[ii], service_ids[ii])) { NOTREACHED(); return false; } @@ -848,23 +973,14 @@ bool GLES2DecoderImpl::RegisterObjects( void GLES2DecoderImpl::UnregisterObjects( GLsizei n, const GLuint* client_ids, GLuint* service_ids) { for (GLsizei ii = 0; ii < n; ++ii) { - if (id_map_.GetServiceId(client_ids[ii], &service_ids[ii])) { - id_map_.RemoveMapping(client_ids[ii], service_ids[ii]); + if (id_manager_->GetServiceId(client_ids[ii], &service_ids[ii])) { + id_manager_->RemoveMapping(client_ids[ii], service_ids[ii]); } else { service_ids[ii] = 0; } } } -void GLES2DecoderImpl::RemoveBufferInfo(GLuint buffer_id) { - for (GLuint ii = 0; ii < max_vertex_attribs_; ++ii) { - if (vertex_attrib_infos_[ii].buffer() == buffer_id) { - vertex_attrib_infos_[ii].Clear(); - } - } - buffer_infos_.erase(buffer_id); -} - bool GLES2DecoderImpl::InitPlatformSpecific() { #if defined(UNIT_TEST) #elif defined(OS_WIN) @@ -1030,7 +1146,7 @@ void GLES2DecoderImpl::CreateProgramHelper(GLuint client_id) { // TODO(gman): verify client_id is unused. GLuint service_id = glCreateProgram(); if (service_id) { - id_map_.AddMapping(client_id, service_id); + id_manager_->AddMapping(client_id, service_id); } } @@ -1038,7 +1154,7 @@ void GLES2DecoderImpl::CreateShaderHelper(GLenum type, GLuint client_id) { // TODO(gman): verify client_id is unused. GLuint service_id = glCreateShader(type); if (service_id) { - id_map_.AddMapping(client_id, service_id); + id_manager_->AddMapping(client_id, service_id); } } @@ -1079,12 +1195,12 @@ parse_error::ParseError GLES2DecoderImpl::HandleDeleteShader( uint32 immediate_data_size, const gles2::DeleteShader& c) { GLuint shader = c.shader; GLuint service_id; - if (!id_map_.GetServiceId(shader, &service_id)) { + if (!id_manager_->GetServiceId(shader, &service_id)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } glDeleteShader(service_id); - id_map_.RemoveMapping(shader, service_id); + id_manager_->RemoveMapping(shader, service_id); return parse_error::kParseNoError; } @@ -1092,13 +1208,13 @@ parse_error::ParseError GLES2DecoderImpl::HandleDeleteProgram( uint32 immediate_data_size, const gles2::DeleteProgram& c) { GLuint program = c.program; GLuint service_id; - if (!id_map_.GetServiceId(program, &service_id)) { + if (!id_manager_->GetServiceId(program, &service_id)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } - RemoveProgramInfo(program); + RemoveProgramInfo(service_id); glDeleteProgram(service_id); - id_map_.RemoveMapping(program, service_id); + id_manager_->RemoveMapping(program, service_id); return parse_error::kParseNoError; } @@ -1134,12 +1250,12 @@ void GLES2DecoderImpl::DoSwapBuffers() { } void GLES2DecoderImpl::DoUseProgram(GLuint program) { - ProgramInfo* info = GetProgramInfo(program); + ProgramManager::ProgramInfo* info = GetProgramInfo(program); if (!info) { // Program was not linked successfully. (ie, glLinkProgram) SetGLError(GL_INVALID_OPERATION); } else { - current_program_info_ = info; + current_program_ = program; glUseProgram(program); } } @@ -1174,17 +1290,12 @@ void GLES2DecoderImpl::CopyRealGLErrorsToWrapper() { } } -const GLES2DecoderImpl::BufferInfo* GLES2DecoderImpl::GetBufferInfo( - GLuint buffer) { - BufferInfoMap::iterator it = buffer_infos_.find(buffer); - return it != buffer_infos_.end() ? &it->second : NULL; -} - void GLES2DecoderImpl::SetBufferInfo(GLuint buffer, GLsizeiptr size) { - buffer_infos_[buffer] = BufferInfo(size); + buffer_manager_->SetBufferInfo(buffer, size); // Also go through VertexAttribInfo and update any info that references // the same buffer. + // TODO(gman): This code needs to change for shared resources. for (GLuint ii = 0; ii < max_vertex_attribs_; ++ii) { if (vertex_attrib_infos_[ii].buffer() == buffer) { vertex_attrib_infos_[ii].SetBufferSize(size); @@ -1192,47 +1303,14 @@ void GLES2DecoderImpl::SetBufferInfo(GLuint buffer, GLsizeiptr size) { } } -GLES2DecoderImpl::ProgramInfo* GLES2DecoderImpl::GetProgramInfo( - GLuint program) { - ProgramInfoMap::iterator it = program_infos_.find(program); - return it != program_infos_.end() ? &it->second : NULL; -} - -void GLES2DecoderImpl::UpdateProgramInfo(GLuint program) { - ProgramInfo* info = GetProgramInfo(program); - if (!info) { - std::pair<ProgramInfoMap::iterator, bool> result = - program_infos_.insert(std::make_pair(program, ProgramInfo())); - DCHECK(result.second); - info = &result.first->second; - } - GLint num_attribs = 0; - GLint max_len = 0; - glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &num_attribs); - info->SetNumAttributes(num_attribs); - glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_len); - // TODO(gman): Should we check for error? - scoped_array<char> name_buffer(new char[max_len + 1]); - for (GLint ii = 0; ii < num_attribs; ++ii) { - GLsizei length; - GLsizei size; - GLenum type; - glGetActiveAttrib( - program, ii, max_len + 1, &length, &size, &type, name_buffer.get()); - // TODO(gman): Should we check for error? - GLint location = glGetAttribLocation(program, name_buffer.get()); - info->SetAttributeLocation(ii, location); - } -} - -void GLES2DecoderImpl::RemoveProgramInfo(GLuint program) { - ProgramInfoMap::iterator it = program_infos_.find(program); - if (it != program_infos_.end()) { - if (current_program_info_ == &it->second) { - current_program_info_ = NULL; +void GLES2DecoderImpl::RemoveBufferInfo(GLuint buffer_id) { + // TODO(gman): This code needs to change for shared resources. + for (GLuint ii = 0; ii < max_vertex_attribs_; ++ii) { + if (vertex_attrib_infos_[ii].buffer() == buffer_id) { + vertex_attrib_infos_[ii].Clear(); } - program_infos_.erase(it); } + buffer_manager_->RemoveBufferInfo(buffer_id); } bool GLES2DecoderImpl::VertexAttribInfo::CanAccess(GLuint index) { @@ -1240,15 +1318,22 @@ bool GLES2DecoderImpl::VertexAttribInfo::CanAccess(GLuint index) { } bool GLES2DecoderImpl::IsDrawValid(GLuint max_vertex_accessed) { - if (current_program_info_) { + if (current_program_) { + ProgramManager::ProgramInfo* info = GetProgramInfo(current_program_); + if (!info) { + // The program does not exist. + SetGLError(GL_INVALID_OPERATION); + return false; + } // Validate that all attribs current program needs are setup correctly. - const ProgramInfo::AttribLocationVector& locations = - current_program_info_->GetAttribLocations(); + const ProgramManager::ProgramInfo::AttribLocationVector& locations = + info->GetAttribLocations(); for (size_t ii = 0; ii < locations.size(); ++ii) { GLuint location = locations[ii]; DCHECK_LT(location, max_vertex_attribs_); if (!vertex_attrib_infos_[location].CanAccess(max_vertex_accessed)) { SetGLError(GL_INVALID_OPERATION); + return false; } } return true; @@ -1264,19 +1349,39 @@ parse_error::ParseError GLES2DecoderImpl::HandleDrawElements( GLenum mode = c.mode; GLsizei count = c.count; GLenum type = c.type; + int32 offset = c.index_offset; if (!ValidateGLenumDrawMode(mode) || !ValidateGLenumIndexType(type)) { - SetGLError(GL_INVALID_VALUE); + SetGLError(GL_INVALID_ENUM); } else { - const GLvoid* indices = reinterpret_cast<const GLvoid*>(c.index_offset); - // TODO(gman): Validate indices. Get maximum index. - // - // This value should be computed by walking the index buffer from 0 to - // count and finding the maximum vertex accessed. - // For now we'll special case 0 to not check. - GLuint max_vertex_accessed = 0; - if (IsDrawValid(max_vertex_accessed)) { - glDrawElements(mode, count, type, indices); + // TODO(gman): We could cache this lookup in glBindBuffer. + BufferManager::BufferInfo* info = + GetBufferInfo(bound_element_array_buffer_); + if (!info) { + SetGLError(GL_INVALID_OPERATION); + } else { + GLsizeiptr buffer_size = info->size(); + if (offset > buffer_size) { + SetGLError(GL_INVALID_OPERATION); + } else { + GLsizei usable_size = buffer_size - offset; + GLsizei num_elements = usable_size / GetGLTypeSize(type); + if (count > num_elements) { + SetGLError(GL_INVALID_OPERATION); + } else { + const GLvoid* indices = reinterpret_cast<const GLvoid*>(offset); + // TODO(gman): Validate indices. Get maximum index. + // + // This value should be computed by walking the index buffer from 0 + // to count and finding the maximum vertex accessed. For now we'll + // pass 0 so it should always pass. + GLuint max_vertex_accessed = info->GetMaxValueForRange( + offset, count, type); + if (IsDrawValid(max_vertex_accessed)) { + glDrawElements(mode, count, type, indices); + } + } + } } } } else { @@ -1319,7 +1424,7 @@ parse_error::ParseError ShaderSourceHelper( parse_error::ParseError GLES2DecoderImpl::HandleShaderSource( uint32 immediate_data_size, const gles2::ShaderSource& c) { GLuint shader; - if (!id_map_.GetServiceId(c.shader, &shader)) { + if (!id_manager_->GetServiceId(c.shader, &shader)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -1337,7 +1442,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleShaderSource( parse_error::ParseError GLES2DecoderImpl::HandleShaderSourceImmediate( uint32 immediate_data_size, const gles2::ShaderSourceImmediate& c) { GLuint shader; - if (!id_map_.GetServiceId(c.shader, &shader)) { + if (!id_manager_->GetServiceId(c.shader, &shader)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -1369,7 +1474,8 @@ parse_error::ParseError GLES2DecoderImpl::HandleVertexAttribPointer( SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } - const BufferInfo* buffer_info = GetBufferInfo(bound_array_buffer_); + const BufferManager::BufferInfo* buffer_info = + GetBufferInfo(bound_array_buffer_); GLsizei component_size = GetGLTypeSize(type); GLsizei real_stride = stride != 0 ? stride : component_size * size; if (offset % component_size > 0) { @@ -1378,7 +1484,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleVertexAttribPointer( } vertex_attrib_infos_[indx].SetInfo( bound_array_buffer_, - buffer_info ? buffer_info->size : 0, + buffer_info ? buffer_info->size() : 0, size, type, real_stride, @@ -1442,7 +1548,7 @@ parse_error::ParseError GLES2DecoderImpl::HandlePixelStorei( parse_error::ParseError GLES2DecoderImpl::HandleGetAttribLocation( uint32 immediate_data_size, const gles2::GetAttribLocation& c) { GLuint program; - if (!id_map_.GetServiceId(c.program, &program)) { + if (!id_manager_->GetServiceId(c.program, &program)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -1462,7 +1568,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleGetAttribLocation( parse_error::ParseError GLES2DecoderImpl::HandleGetAttribLocationImmediate( uint32 immediate_data_size, const gles2::GetAttribLocationImmediate& c) { GLuint program; - if (!id_map_.GetServiceId(c.program, &program)) { + if (!id_manager_->GetServiceId(c.program, &program)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -1482,7 +1588,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleGetAttribLocationImmediate( parse_error::ParseError GLES2DecoderImpl::HandleGetUniformLocation( uint32 immediate_data_size, const gles2::GetUniformLocation& c) { GLuint program; - if (!id_map_.GetServiceId(c.program, &program)) { + if (!id_manager_->GetServiceId(c.program, &program)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -1502,7 +1608,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleGetUniformLocation( parse_error::ParseError GLES2DecoderImpl::HandleGetUniformLocationImmediate( uint32 immediate_data_size, const gles2::GetUniformLocationImmediate& c) { GLuint program; - if (!id_map_.GetServiceId(c.program, &program)) { + if (!id_manager_->GetServiceId(c.program, &program)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h index c87ac9e..5338e58 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h @@ -18,12 +18,12 @@ parse_error::ParseError GLES2DecoderImpl::HandleActiveTexture( parse_error::ParseError GLES2DecoderImpl::HandleAttachShader( uint32 immediate_data_size, const gles2::AttachShader& c) { GLuint program; - if (!id_map_.GetServiceId(c.program, &program)) { + if (!id_manager_->GetServiceId(c.program, &program)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } GLuint shader; - if (!id_map_.GetServiceId(c.shader, &shader)) { + if (!id_manager_->GetServiceId(c.shader, &shader)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -34,7 +34,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleAttachShader( parse_error::ParseError GLES2DecoderImpl::HandleBindAttribLocation( uint32 immediate_data_size, const gles2::BindAttribLocation& c) { GLuint program; - if (!id_map_.GetServiceId(c.program, &program)) { + if (!id_manager_->GetServiceId(c.program, &program)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -53,7 +53,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleBindAttribLocation( parse_error::ParseError GLES2DecoderImpl::HandleBindAttribLocationImmediate( uint32 immediate_data_size, const gles2::BindAttribLocationImmediate& c) { GLuint program; - if (!id_map_.GetServiceId(c.program, &program)) { + if (!id_manager_->GetServiceId(c.program, &program)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -73,7 +73,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleBindBuffer( uint32 immediate_data_size, const gles2::BindBuffer& c) { GLenum target = static_cast<GLenum>(c.target); GLuint buffer; - if (!id_map_.GetServiceId(c.buffer, &buffer)) { + if (!id_manager_->GetServiceId(c.buffer, &buffer)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -89,7 +89,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleBindFramebuffer( uint32 immediate_data_size, const gles2::BindFramebuffer& c) { GLenum target = static_cast<GLenum>(c.target); GLuint framebuffer; - if (!id_map_.GetServiceId(c.framebuffer, &framebuffer)) { + if (!id_manager_->GetServiceId(c.framebuffer, &framebuffer)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -105,7 +105,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleBindRenderbuffer( uint32 immediate_data_size, const gles2::BindRenderbuffer& c) { GLenum target = static_cast<GLenum>(c.target); GLuint renderbuffer; - if (!id_map_.GetServiceId(c.renderbuffer, &renderbuffer)) { + if (!id_manager_->GetServiceId(c.renderbuffer, &renderbuffer)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -121,7 +121,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleBindTexture( uint32 immediate_data_size, const gles2::BindTexture& c) { GLenum target = static_cast<GLenum>(c.target); GLuint texture; - if (!id_map_.GetServiceId(c.texture, &texture)) { + if (!id_manager_->GetServiceId(c.texture, &texture)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -305,7 +305,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleColorMask( parse_error::ParseError GLES2DecoderImpl::HandleCompileShader( uint32 immediate_data_size, const gles2::CompileShader& c) { GLuint shader; - if (!id_map_.GetServiceId(c.shader, &shader)) { + if (!id_manager_->GetServiceId(c.shader, &shader)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -563,12 +563,12 @@ parse_error::ParseError GLES2DecoderImpl::HandleDepthRangef( parse_error::ParseError GLES2DecoderImpl::HandleDetachShader( uint32 immediate_data_size, const gles2::DetachShader& c) { GLuint program; - if (!id_map_.GetServiceId(c.program, &program)) { + if (!id_manager_->GetServiceId(c.program, &program)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } GLuint shader; - if (!id_map_.GetServiceId(c.shader, &shader)) { + if (!id_manager_->GetServiceId(c.shader, &shader)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -643,7 +643,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleFramebufferRenderbuffer( GLenum attachment = static_cast<GLenum>(c.attachment); GLenum renderbuffertarget = static_cast<GLenum>(c.renderbuffertarget); GLuint renderbuffer; - if (!id_map_.GetServiceId(c.renderbuffer, &renderbuffer)) { + if (!id_manager_->GetServiceId(c.renderbuffer, &renderbuffer)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -670,7 +670,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleFramebufferTexture2D( GLenum attachment = static_cast<GLenum>(c.attachment); GLenum textarget = static_cast<GLenum>(c.textarget); GLuint texture; - if (!id_map_.GetServiceId(c.texture, &texture)) { + if (!id_manager_->GetServiceId(c.texture, &texture)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -943,7 +943,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleGetIntegerv( parse_error::ParseError GLES2DecoderImpl::HandleGetProgramiv( uint32 immediate_data_size, const gles2::GetProgramiv& c) { GLuint program; - if (!id_map_.GetServiceId(c.program, &program)) { + if (!id_manager_->GetServiceId(c.program, &program)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -967,7 +967,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleGetProgramiv( parse_error::ParseError GLES2DecoderImpl::HandleGetProgramInfoLog( uint32 immediate_data_size, const gles2::GetProgramInfoLog& c) { GLuint program; - if (!id_map_.GetServiceId(c.program, &program)) { + if (!id_manager_->GetServiceId(c.program, &program)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -1018,7 +1018,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleGetRenderbufferParameteriv( parse_error::ParseError GLES2DecoderImpl::HandleGetShaderiv( uint32 immediate_data_size, const gles2::GetShaderiv& c) { GLuint shader; - if (!id_map_.GetServiceId(c.shader, &shader)) { + if (!id_manager_->GetServiceId(c.shader, &shader)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -1042,7 +1042,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleGetShaderiv( parse_error::ParseError GLES2DecoderImpl::HandleGetShaderInfoLog( uint32 immediate_data_size, const gles2::GetShaderInfoLog& c) { GLuint shader; - if (!id_map_.GetServiceId(c.shader, &shader)) { + if (!id_manager_->GetServiceId(c.shader, &shader)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -1069,7 +1069,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleGetShaderInfoLog( parse_error::ParseError GLES2DecoderImpl::HandleGetShaderSource( uint32 immediate_data_size, const gles2::GetShaderSource& c) { GLuint shader; - if (!id_map_.GetServiceId(c.shader, &shader)) { + if (!id_manager_->GetServiceId(c.shader, &shader)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -1211,7 +1211,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleHint( parse_error::ParseError GLES2DecoderImpl::HandleIsBuffer( uint32 immediate_data_size, const gles2::IsBuffer& c) { GLuint buffer; - if (!id_map_.GetServiceId(c.buffer, &buffer)) { + if (!id_manager_->GetServiceId(c.buffer, &buffer)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -1237,7 +1237,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleIsEnabled( parse_error::ParseError GLES2DecoderImpl::HandleIsFramebuffer( uint32 immediate_data_size, const gles2::IsFramebuffer& c) { GLuint framebuffer; - if (!id_map_.GetServiceId(c.framebuffer, &framebuffer)) { + if (!id_manager_->GetServiceId(c.framebuffer, &framebuffer)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -1250,7 +1250,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleIsFramebuffer( parse_error::ParseError GLES2DecoderImpl::HandleIsProgram( uint32 immediate_data_size, const gles2::IsProgram& c) { GLuint program; - if (!id_map_.GetServiceId(c.program, &program)) { + if (!id_manager_->GetServiceId(c.program, &program)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -1263,7 +1263,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleIsProgram( parse_error::ParseError GLES2DecoderImpl::HandleIsRenderbuffer( uint32 immediate_data_size, const gles2::IsRenderbuffer& c) { GLuint renderbuffer; - if (!id_map_.GetServiceId(c.renderbuffer, &renderbuffer)) { + if (!id_manager_->GetServiceId(c.renderbuffer, &renderbuffer)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -1276,7 +1276,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleIsRenderbuffer( parse_error::ParseError GLES2DecoderImpl::HandleIsShader( uint32 immediate_data_size, const gles2::IsShader& c) { GLuint shader; - if (!id_map_.GetServiceId(c.shader, &shader)) { + if (!id_manager_->GetServiceId(c.shader, &shader)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -1289,7 +1289,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleIsShader( parse_error::ParseError GLES2DecoderImpl::HandleIsTexture( uint32 immediate_data_size, const gles2::IsTexture& c) { GLuint texture; - if (!id_map_.GetServiceId(c.texture, &texture)) { + if (!id_manager_->GetServiceId(c.texture, &texture)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -1309,7 +1309,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleLineWidth( parse_error::ParseError GLES2DecoderImpl::HandleLinkProgram( uint32 immediate_data_size, const gles2::LinkProgram& c) { GLuint program; - if (!id_map_.GetServiceId(c.program, &program)) { + if (!id_manager_->GetServiceId(c.program, &program)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -2091,7 +2091,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleUniformMatrix4fvImmediate( parse_error::ParseError GLES2DecoderImpl::HandleUseProgram( uint32 immediate_data_size, const gles2::UseProgram& c) { GLuint program; - if (!id_map_.GetServiceId(c.program, &program)) { + if (!id_manager_->GetServiceId(c.program, &program)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } @@ -2102,7 +2102,7 @@ parse_error::ParseError GLES2DecoderImpl::HandleUseProgram( parse_error::ParseError GLES2DecoderImpl::HandleValidateProgram( uint32 immediate_data_size, const gles2::ValidateProgram& c) { GLuint program; - if (!id_map_.GetServiceId(c.program, &program)) { + if (!id_manager_->GetServiceId(c.program, &program)) { SetGLError(GL_INVALID_VALUE); return parse_error::kParseNoError; } diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc index 4a740d5..1f8771a 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc @@ -9,10 +9,14 @@ #include "testing/gtest/include/gtest/gtest.h" using ::testing::_; -using ::testing::Return; -using ::testing::SetArgumentPointee; +using ::testing::DoAll; using ::testing::InSequence; +using ::testing::MatcherCast; using ::testing::Pointee; +using ::testing::Return; +using ::testing::SetArrayArgument; +using ::testing::SetArgumentPointee; +using ::testing::StrEq; namespace gpu { namespace gles2 { @@ -25,7 +29,8 @@ class GLES2DecoderTest : public testing::Test { client_program_id_(102), client_renderbuffer_id_(103), client_shader_id_(104), - client_texture_id_(105) { + client_texture_id_(105), + client_element_buffer_id_(106) { memset(immediate_buffer_, 0xEE, sizeof(immediate_buffer_)); } @@ -38,6 +43,7 @@ class GLES2DecoderTest : public testing::Test { static const GLuint kServiceTextureId = 304; static const GLuint kServiceProgramId = 305; static const GLuint kServiceShaderId = 306; + static const GLuint kServiceElementBufferId = 307; static const int32 kSharedMemoryId = 401; static const size_t kSharedBufferSize = 2048; @@ -99,20 +105,23 @@ class GLES2DecoderTest : public testing::Test { EXPECT_CALL(*gl_, GenBuffersARB(_, _)) .WillOnce(SetArgumentPointee<1>(kServiceBufferId)) .RetiresOnSaturation(); + GenHelper<GenBuffersImmediate>(client_buffer_id_); EXPECT_CALL(*gl_, GenFramebuffersEXT(_, _)) .WillOnce(SetArgumentPointee<1>(kServiceFramebufferId)) .RetiresOnSaturation(); + GenHelper<GenFramebuffersImmediate>(client_framebuffer_id_); EXPECT_CALL(*gl_, GenRenderbuffersEXT(_, _)) .WillOnce(SetArgumentPointee<1>(kServiceRenderbufferId)) .RetiresOnSaturation(); + GenHelper<GenRenderbuffersImmediate>(client_renderbuffer_id_); EXPECT_CALL(*gl_, GenTextures(_, _)) .WillOnce(SetArgumentPointee<1>(kServiceTextureId)) .RetiresOnSaturation(); - - GenHelper<GenBuffersImmediate>(client_buffer_id_); - GenHelper<GenFramebuffersImmediate>(client_framebuffer_id_); - GenHelper<GenRenderbuffersImmediate>(client_renderbuffer_id_); GenHelper<GenTexturesImmediate>(client_texture_id_); + EXPECT_CALL(*gl_, GenBuffersARB(_, _)) + .WillOnce(SetArgumentPointee<1>(kServiceElementBufferId)) + .RetiresOnSaturation(); + GenHelper<GenBuffersImmediate>(client_element_buffer_id_); { EXPECT_CALL(*gl_, CreateProgram()) @@ -133,6 +142,8 @@ class GLES2DecoderTest : public testing::Test { cmd.Init(GL_VERTEX_SHADER, client_shader_id_); EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); } + + EXPECT_EQ(GL_NO_ERROR, GetGLError()); } virtual void TearDown() { @@ -168,6 +179,16 @@ class GLES2DecoderTest : public testing::Test { return decoder_->GetServiceIdForTesting(client_id); } + GLenum GetGLError() { + EXPECT_CALL(*gl_, GetError()) + .WillOnce(Return(GL_NO_ERROR)) + .RetiresOnSaturation(); + GetError cmd; + cmd.Init(shared_memory_id_, shared_memory_offset_); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + return *GetSharedMemoryAs<GLenum*>(); + } + scoped_ptr<::gles2::MockGLInterface> gl_; scoped_ptr<GLES2Decoder> decoder_; @@ -177,6 +198,7 @@ class GLES2DecoderTest : public testing::Test { GLuint client_renderbuffer_id_; GLuint client_shader_id_; GLuint client_texture_id_; + GLuint client_element_buffer_id_; uint32 shared_memory_id_; uint32 shared_memory_offset_; @@ -224,6 +246,443 @@ void GLES2DecoderTest::SpecializedSetup<LinkProgram, 0>() { .WillOnce(SetArgumentPointee<2>(0)); }; + +class GLES2DecoderWithShaderTest : public GLES2DecoderTest { + public: + GLES2DecoderWithShaderTest() + : GLES2DecoderTest() { + } + + static const GLint kNumAttribs = 3; + static const GLint kMaxAttribLength = 10; + static const GLsizei kNumVertices = 100; + static const GLsizei kNumIndices = 10; + static const int kValidIndexRangeStart = 1; + static const int kValidIndexRangeCount = 7; + static const int kInvalidIndexRangeStart = 0; + static const int kInvalidIndexRangeCount = 7; + static const int kOutOfRangeIndexRangeEnd = 10; + static const char* kAttrib1Name; + static const char* kAttrib2Name; + static const char* kAttrib3Name; + + protected: + virtual void SetUp() { + GLES2DecoderTest::SetUp(); + + { + struct AttribInfo { + const char* name; + GLint size; + GLenum type; + GLint location; + }; + static AttribInfo attribs[] = { + { kAttrib1Name, 1, GL_FLOAT_VEC4, 0, }, + { kAttrib2Name, 1, GL_FLOAT_VEC2, 1, }, + { kAttrib3Name, 1, GL_FLOAT_VEC3, 2, }, + }; + LinkProgram cmd; + cmd.Init(client_program_id_); + + EXPECT_CALL(*gl_, LinkProgram(kServiceProgramId)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, GetError()) + .WillOnce(Return(GL_NO_ERROR)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, + GetProgramiv(kServiceProgramId, GL_ACTIVE_ATTRIBUTES, _)) + .WillOnce(SetArgumentPointee<2>(kNumAttribs)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, + GetProgramiv(kServiceProgramId, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, _)) + .WillOnce(SetArgumentPointee<2>(kMaxAttribLength)) + .RetiresOnSaturation(); + { + InSequence s; + for (GLint ii = 0; ii < kNumAttribs; ++ii) { + const AttribInfo& info = attribs[ii]; + EXPECT_CALL(*gl_, + GetActiveAttrib(kServiceProgramId, ii, + kMaxAttribLength + 1, _, _, _, _)) + .WillOnce(DoAll( + SetArgumentPointee<3>(strlen(info.name)), + SetArgumentPointee<4>(info.size), + SetArgumentPointee<5>(info.type), + SetArrayArgument<6>(info.name, + info.name + strlen(info.name) + 1))) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, GetAttribLocation(kServiceProgramId, + StrEq(info.name))) + .WillOnce(Return(info.location)) + .RetiresOnSaturation(); + } + } + + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + } + + { + EXPECT_CALL(*gl_, UseProgram(kServiceProgramId)) + .Times(1) + .RetiresOnSaturation(); + UseProgram cmd; + cmd.Init(client_program_id_); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + } + } + + virtual void TearDown() { + GLES2DecoderTest::TearDown(); + } + + inline GLvoid* BufferOffset(unsigned i) { + return static_cast<int8 *>(NULL)+(i); + } + + void DoEnableVertexAttribArray(GLint index) { + EXPECT_CALL(*gl_, EnableVertexAttribArray(index)) + .Times(1) + .RetiresOnSaturation(); + EnableVertexAttribArray cmd; + cmd.Init(index); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + } + + void DoBindBuffer(GLenum target, GLuint client_id, GLuint service_id) { + EXPECT_CALL(*gl_, BindBuffer(target, service_id)) + .Times(1) + .RetiresOnSaturation(); + BindBuffer cmd; + cmd.Init(target, client_id); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + } + + void DoBufferData(GLenum target, GLsizei size) { + EXPECT_CALL(*gl_, BufferData(target, size, _, GL_STREAM_DRAW)) + .Times(1) + .RetiresOnSaturation(); + BufferData cmd; + cmd.Init(target, size, 0, 0, GL_STREAM_DRAW); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + } + + void DoBufferSubData( + GLenum target, GLint offset, GLsizei size, const void* data) { + EXPECT_CALL(*gl_, BufferSubData(target, offset, size, + shared_memory_address_)) + .Times(1) + .RetiresOnSaturation(); + memcpy(shared_memory_address_, data, size); + BufferSubData cmd; + cmd.Init(target, offset, size, shared_memory_id_, shared_memory_offset_); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + } + + void DoDeleteBuffer(GLuint client_id, GLuint service_id) { + EXPECT_CALL(*gl_, DeleteBuffersARB(1, Pointee(service_id))) + .Times(1) + .RetiresOnSaturation(); + DeleteBuffers cmd; + cmd.Init(1, shared_memory_id_, shared_memory_offset_); + memcpy(shared_memory_address_, &client_id, sizeof(client_id)); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + } + + void DoDeleteProgram(GLuint client_id, GLuint service_id) { + EXPECT_CALL(*gl_, DeleteProgram(service_id)) + .Times(1) + .RetiresOnSaturation(); + DeleteProgram cmd; + cmd.Init(client_id); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + } + + void DoVertexAttribPointer( + GLuint index, GLint size, GLenum type, GLsizei stride, GLuint offset) { + EXPECT_CALL(*gl_, + VertexAttribPointer(index, size, type, GL_FALSE, stride, + BufferOffset(offset))) + .Times(1) + .RetiresOnSaturation(); + VertexAttribPointer cmd; + cmd.Init(index, size, GL_FLOAT, GL_FALSE, stride, offset); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + } + + void SetupVertexBuffer() { + DoEnableVertexAttribArray(1); + DoBindBuffer(GL_ARRAY_BUFFER, client_buffer_id_, kServiceBufferId); + GLfloat f = 0; + DoBufferData(GL_ARRAY_BUFFER, kNumVertices * 2 * sizeof(f)); + } + + void SetupIndexBuffer() { + DoBindBuffer(GL_ELEMENT_ARRAY_BUFFER, + client_element_buffer_id_, + kServiceElementBufferId); + static const GLshort indices[] = {100, 1, 2, 3, 4, 5, 6, 7, 100, 9}; + COMPILE_ASSERT(arraysize(indices) == kNumIndices, Indices_is_not_10); + DoBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices)); + DoBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(indices), indices); + } + + void DeleteVertexBuffer() { + DoDeleteBuffer(client_buffer_id_, kServiceBufferId); + } + + void DeleteIndexBuffer() { + DoDeleteBuffer(client_element_buffer_id_, kServiceElementBufferId); + } +}; + +const char* GLES2DecoderWithShaderTest::kAttrib1Name = "attrib1"; +const char* GLES2DecoderWithShaderTest::kAttrib2Name = "attrib2"; +const char* GLES2DecoderWithShaderTest::kAttrib3Name = "attrib3"; + +TEST_F(GLES2DecoderWithShaderTest, DrawArraysNoAttributesSucceeds) { + EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices)) + .Times(1) + .RetiresOnSaturation(); + DrawArrays cmd; + cmd.Init(GL_TRIANGLES, 0, kNumVertices); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); +} + +TEST_F(GLES2DecoderWithShaderTest, DrawArraysMissingAttributesFails) { + DoEnableVertexAttribArray(1); + + EXPECT_CALL(*gl_, DrawArrays(_, _, _)) + .Times(0); + DrawArrays cmd; + cmd.Init(GL_TRIANGLES, 0, kNumVertices); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); +} + +TEST_F(GLES2DecoderWithShaderTest, DrawArraysValidAttributesSucceeds) { + SetupVertexBuffer(); + DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0); + + EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices)) + .Times(1) + .RetiresOnSaturation(); + DrawArrays cmd; + cmd.Init(GL_TRIANGLES, 0, kNumVertices); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); +} + +TEST_F(GLES2DecoderWithShaderTest, DrawArraysDeletedBufferFails) { + SetupVertexBuffer(); + DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0); + DeleteVertexBuffer(); + + EXPECT_CALL(*gl_, DrawArrays(_, _, _)) + .Times(0); + DrawArrays cmd; + cmd.Init(GL_TRIANGLES, 0, kNumVertices); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); +} + +TEST_F(GLES2DecoderWithShaderTest, DrawArraysDeletedProgramFails) { + SetupVertexBuffer(); + DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0); + DoDeleteProgram(client_program_id_, kServiceProgramId); + + EXPECT_CALL(*gl_, DrawArrays(_, _, _)) + .Times(0); + DrawArrays cmd; + cmd.Init(GL_TRIANGLES, 0, kNumVertices); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); +} + +TEST_F(GLES2DecoderWithShaderTest, DrawArraysWithInvalidModeFails) { + SetupVertexBuffer(); + DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0); + + EXPECT_CALL(*gl_, DrawArrays(_, _, _)) + .Times(0); + DrawArrays cmd; + cmd.Init(GL_QUADS, 0, 1); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_ENUM, GetGLError()); + cmd.Init(GL_POLYGON, 0, 1); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_ENUM, GetGLError()); +} + +TEST_F(GLES2DecoderWithShaderTest, DrawArraysInvalidCountFails) { + SetupVertexBuffer(); + DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0); + + // Try start > 0 + EXPECT_CALL(*gl_, DrawArrays(_, _, _)).Times(0); + DrawArrays cmd; + cmd.Init(GL_TRIANGLES, 1, kNumVertices); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); + + // Try with count > size + cmd.Init(GL_TRIANGLES, 0, kNumVertices + 1); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); + + // Try with attrib offset > 0 + cmd.Init(GL_TRIANGLES, 0, kNumVertices); + DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 4); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); + + // Try with size > 2 (ie, vec3 instead of vec2) + DoVertexAttribPointer(1, 3, GL_FLOAT, 0, 0); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); + + // Try with stride > 8 (vec2 + vec2 byte) + GLfloat f; + DoVertexAttribPointer(1, 2, GL_FLOAT, sizeof(f) * 2 + 1, 0); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); +} + +TEST_F(GLES2DecoderWithShaderTest, DrawElementsNoAttributesSucceeds) { + SetupIndexBuffer(); + EXPECT_CALL(*gl_, DrawElements(GL_TRIANGLES, kValidIndexRangeCount, + GL_UNSIGNED_SHORT, + BufferOffset(kValidIndexRangeStart))) + .Times(1) + .RetiresOnSaturation(); + DrawElements cmd; + cmd.Init(GL_TRIANGLES, kValidIndexRangeCount, GL_UNSIGNED_SHORT, + kValidIndexRangeStart); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); +} + +TEST_F(GLES2DecoderWithShaderTest, DrawElementsMissingAttributesFails) { + SetupIndexBuffer(); + DoEnableVertexAttribArray(1); + + EXPECT_CALL(*gl_, DrawElements(_, _, _, _)) + .Times(0); + DrawElements cmd; + cmd.Init(GL_TRIANGLES, kValidIndexRangeCount, GL_UNSIGNED_SHORT, + kValidIndexRangeStart); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); +} + +TEST_F(GLES2DecoderWithShaderTest, DrawElementsValidAttributesSucceeds) { + SetupVertexBuffer(); + SetupIndexBuffer(); + DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0); + + EXPECT_CALL(*gl_, DrawElements(GL_TRIANGLES, kValidIndexRangeCount, + GL_UNSIGNED_SHORT, + BufferOffset(kValidIndexRangeStart))) + .Times(1) + .RetiresOnSaturation(); + DrawElements cmd; + cmd.Init(GL_TRIANGLES, kValidIndexRangeCount, GL_UNSIGNED_SHORT, + kValidIndexRangeStart); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); +} + +TEST_F(GLES2DecoderWithShaderTest, DrawElementsDeletedBufferFails) { + SetupVertexBuffer(); + SetupIndexBuffer(); + DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0); + DeleteIndexBuffer(); + + EXPECT_CALL(*gl_, DrawElements(_, _, _, _)) + .Times(0); + DrawElements cmd; + cmd.Init(GL_TRIANGLES, kValidIndexRangeCount, GL_UNSIGNED_SHORT, + kValidIndexRangeStart); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); +} + +TEST_F(GLES2DecoderWithShaderTest, DrawElementsDeleteProgramFails) { + SetupVertexBuffer(); + SetupIndexBuffer(); + DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0); + DoDeleteProgram(client_program_id_, kServiceProgramId); + + EXPECT_CALL(*gl_, DrawElements(_, _, _, _)) + .Times(0); + DrawElements cmd; + cmd.Init(GL_TRIANGLES, kValidIndexRangeCount, GL_UNSIGNED_SHORT, + kValidIndexRangeStart); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); +} + +TEST_F(GLES2DecoderWithShaderTest, DrawElementsWithInvalidModeFails) { + SetupVertexBuffer(); + SetupIndexBuffer(); + DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0); + + EXPECT_CALL(*gl_, DrawElements(_, _, _, _)) + .Times(0); + DrawElements cmd; + cmd.Init(GL_QUADS, kValidIndexRangeCount, GL_UNSIGNED_SHORT, + kValidIndexRangeStart); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_ENUM, GetGLError()); + cmd.Init(GL_POLYGON, kValidIndexRangeCount, GL_UNSIGNED_SHORT, + kValidIndexRangeStart); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_ENUM, GetGLError()); +} + +TEST_F(GLES2DecoderWithShaderTest, DrawElementsInvalidCountFails) { + SetupVertexBuffer(); + SetupIndexBuffer(); + DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0); + + // Try start > 0 + EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(0); + DrawElements cmd; + cmd.Init(GL_TRIANGLES, kNumIndices, GL_UNSIGNED_SHORT, 1); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); + + // Try with count > size + cmd.Init(GL_TRIANGLES, kNumIndices + 1, GL_UNSIGNED_SHORT, 0); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); +} + +#if 0 // TODO(gman): Turn on this test once buffer validation is in +TEST_F(GLES2DecoderWithShaderTest, DrawElementsOutOfRangeIndicesFails) { + SetupVertexBuffer(); + SetupIndexBuffer(); + DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0); + + EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(0); + DrawElements cmd; + cmd.Init(GL_TRIANGLES, kInvalidIndexRangeCount, GL_UNSIGNED_SHORT, + kInvalidIndexRangeStart); + EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); +} +#endif + #include "gpu/command_buffer/service/gles2_cmd_decoder_unittest_autogen.h" } // namespace gles2 diff --git a/gpu/command_buffer/service/gles2_cmd_validation_autogen.h b/gpu/command_buffer/service/gles2_cmd_validation_autogen.h index fe1ca29..7520b58 100644 --- a/gpu/command_buffer/service/gles2_cmd_validation_autogen.h +++ b/gpu/command_buffer/service/gles2_cmd_validation_autogen.h @@ -30,6 +30,7 @@ bool ValidateGLenumHintTarget(GLenum value); bool ValidateGLenumHintMode(GLenum value); bool ValidateGLenumTextureFormat(GLenum value); bool ValidateGLenumStringType(GLenum value); +bool ValidateGLintTextureBorder(GLenum value); bool ValidateGLenumFrameBufferTarget(GLenum value); bool ValidateGLbooleanFalse(GLenum value); bool ValidateGLintPixelStoreAlignment(GLenum value); diff --git a/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h b/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h index e2bcd97..20a0f84 100644 --- a/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h +++ b/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h @@ -300,6 +300,15 @@ bool ValidateGLenumStringType(GLenum value) { } } +bool ValidateGLintTextureBorder(GLenum value) { + switch (value) { + case 0: + return true; + default: + return false; + } +} + bool ValidateGLenumFrameBufferTarget(GLenum value) { switch (value) { case GL_FRAMEBUFFER: |