diff options
author | gman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-05 05:04:28 +0000 |
---|---|---|
committer | gman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-05 05:04:28 +0000 |
commit | a6eb5232d2608cd0773bbe559757bd52c7f53670 (patch) | |
tree | a2ab7e129ab4a9105d70f067983bcd1c24bfa839 /gpu/command_buffer/service | |
parent | c620060371fe1832017570351139046ff8205fb4 (diff) | |
download | chromium_src-a6eb5232d2608cd0773bbe559757bd52c7f53670.zip chromium_src-a6eb5232d2608cd0773bbe559757bd52c7f53670.tar.gz chromium_src-a6eb5232d2608cd0773bbe559757bd52c7f53670.tar.bz2 |
Implements index validation for DrawElements.
(note: I also forgot to check in the changes to
build_gles2_cmd_buffer.py from my last CL so
that's in here as well)
TEST=various unit tests
BUG=26101
Review URL: http://codereview.chromium.org/668131
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@40713 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gpu/command_buffer/service')
-rw-r--r-- | gpu/command_buffer/service/buffer_manager.cc | 95 | ||||
-rw-r--r-- | gpu/command_buffer/service/buffer_manager.h | 79 | ||||
-rw-r--r-- | gpu/command_buffer/service/buffer_manager_unittest.cc | 83 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder.cc | 112 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder_autogen.h | 4 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc | 47 |
6 files changed, 339 insertions, 81 deletions
diff --git a/gpu/command_buffer/service/buffer_manager.cc b/gpu/command_buffer/service/buffer_manager.cc index 2dfbc15..9b58e42 100644 --- a/gpu/command_buffer/service/buffer_manager.cc +++ b/gpu/command_buffer/service/buffer_manager.cc @@ -4,6 +4,7 @@ #include "gpu/command_buffer/service/buffer_manager.h" #include "base/logging.h" +#include "gpu/command_buffer/common/gles2_cmd_utils.h" #include "gpu/command_buffer/service/gles2_cmd_decoder.h" namespace gpu { @@ -31,10 +32,96 @@ void BufferManager::RemoveBufferInfo(GLuint 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; +void BufferManager::BufferInfo::SetSize(GLsizeiptr size) { + DCHECK(!IsDeleted()); + if (size != size_) { + size_ = size; + ClearCache(); + if (target_ == GL_ELEMENT_ARRAY_BUFFER) { + shadow_.reset(new int8[size]); + memset(shadow_.get(), 0, size); + } + } +} + +bool BufferManager::BufferInfo::SetRange( + GLintptr offset, GLsizeiptr size, const GLvoid * data) { + DCHECK(!IsDeleted()); + if (offset + size < offset || + offset + size > size_) { + return false; + } + if (target_ == GL_ELEMENT_ARRAY_BUFFER) { + memcpy(shadow_.get() + offset, data, size); + ClearCache(); + } + return true; +} + +void BufferManager::BufferInfo::ClearCache() { + range_set_.clear(); +} + +template <typename T> +GLuint GetMaxValue(const void* data, GLuint offset, GLsizei count) { + GLuint max_value = 0; + const T* element = reinterpret_cast<const T*>( + static_cast<const int8*>(data) + offset); + const T* end = element + count; + for (; element < end; ++element) { + if (*element > max_value) { + max_value = *element; + } + } + return max_value; +} + +bool BufferManager::BufferInfo::GetMaxValueForRange( + GLuint offset, GLsizei count, GLenum type, GLuint* max_value) { + DCHECK_EQ(target_, static_cast<GLenum>(GL_ELEMENT_ARRAY_BUFFER)); + DCHECK(!IsDeleted()); + Range range(offset, count, type); + RangeToMaxValueMap::iterator it = range_set_.find(range); + if (it != range_set_.end()) { + *max_value = it->second; + return true; + } + + uint32 size; + if (!SafeMultiplyUint32( + count, GLES2Util::GetGLTypeSizeForTexturesAndBuffers(type), &size)) { + return false; + } + + if (!SafeAddUint32(offset, size, &size)) { + return false; + } + + if (size > static_cast<uint32>(size_)) { + return false; + } + + // Scan the range for the max value and store + GLuint max_v = 0; + switch (type) { + case GL_UNSIGNED_BYTE: + max_v = GetMaxValue<uint8>(shadow_.get(), offset, count); + break; + case GL_UNSIGNED_SHORT: + // Check we are not accessing an odd byte for a 2 byte value. + if ((offset & 1) != 0) { + return false; + } + max_v = GetMaxValue<uint16>(shadow_.get(), offset, count); + break; + default: + NOTREACHED(); // should never get here by validation. + break; + } + std::pair<RangeToMaxValueMap::iterator, bool> result = + range_set_.insert(std::make_pair(range, max_v)); + *max_value = max_v; + return true; } } // namespace gles2 diff --git a/gpu/command_buffer/service/buffer_manager.h b/gpu/command_buffer/service/buffer_manager.h index e278a85..4afdaa0 100644 --- a/gpu/command_buffer/service/buffer_manager.h +++ b/gpu/command_buffer/service/buffer_manager.h @@ -7,7 +7,9 @@ #include <map> #include "base/basictypes.h" +#include "base/logging.h" #include "base/ref_counted.h" +#include "base/scoped_ptr.h" #include "gpu/command_buffer/service/gl_utils.h" namespace gpu { @@ -27,6 +29,7 @@ class BufferManager { explicit BufferInfo(GLuint buffer_id) : buffer_id_(buffer_id), + target_(0), size_(0) { } @@ -34,17 +37,32 @@ class BufferManager { return buffer_id_; } + GLenum target() const { + return target_; + } + + void set_target(GLenum target) { + DCHECK_EQ(target_, 0u); // you can only set this once. + target_ = target; + } + GLsizeiptr size() const { return size_; } - void set_size(GLsizeiptr size) { - size_ = size; - } + void SetSize(GLsizeiptr size); + + // Sets a range of data for this buffer. Returns false if the offset or size + // is out of range. + bool SetRange( + GLintptr offset, GLsizeiptr size, const GLvoid * data); - // Returns the maximum value in the buffer for the given range - // interpreted as the given type. - GLuint GetMaxValueForRange(GLuint offset, GLsizei count, GLenum type); + // Gets the maximum value in the buffer for the given range interpreted as + // the given type. Returns false if offset and count are out of range. + // offset is in bytes. + // count is in elements of type. + bool GetMaxValueForRange(GLuint offset, GLsizei count, GLenum type, + GLuint* max_value); bool IsDeleted() { return buffer_id_ == 0; @@ -54,14 +72,63 @@ class BufferManager { friend class BufferManager; friend class base::RefCounted<BufferInfo>; + // Represents a range in a buffer. + class Range { + public: + Range(GLuint offset, GLsizei count, GLenum type) + : offset_(offset), + count_(count), + type_(type) { + } + + // A less functor provided for std::map so it can find ranges. + struct Less { + bool operator() (const Range& lhs, const Range& rhs) { + if (lhs.offset_ != rhs.offset_) { + return lhs.offset_ < rhs.offset_; + } + if (lhs.count_ != rhs.count_) { + return lhs.count_ < rhs.count_; + } + return lhs.type_ < rhs.type_; + } + }; + + private: + GLuint offset_; + GLsizei count_; + GLenum type_; + }; + ~BufferInfo() { } void MarkAsDeleted() { buffer_id_ = 0; + shadow_.reset(); + ClearCache(); } + // Clears any cache of index ranges. + void ClearCache(); + + // Service side buffer id. GLuint buffer_id_; + + // The type of buffer. 0 = unset, GL_BUFFER_ARRAY = vertex data, + // GL_ELEMENT_BUFFER_ARRAY = index data. + // Once set a buffer can not be used for something else. + GLenum target_; + + // Size of buffer. GLsizeiptr size_; + + // A copy of the data in the buffer. This data is only kept if the target + // is GL_ELEMENT_BUFFER_ARRAY + scoped_array<int8> shadow_; + + // A map of ranges to the highest value in that range of a certain type. + typedef std::map<Range, GLuint, Range::Less> RangeToMaxValueMap; + RangeToMaxValueMap range_set_; }; BufferManager() { } diff --git a/gpu/command_buffer/service/buffer_manager_unittest.cc b/gpu/command_buffer/service/buffer_manager_unittest.cc index 2b121d8..df58915 100644 --- a/gpu/command_buffer/service/buffer_manager_unittest.cc +++ b/gpu/command_buffer/service/buffer_manager_unittest.cc @@ -32,8 +32,14 @@ TEST_F(BufferManagerTest, Basic) { // Check buffer got created. BufferManager::BufferInfo* info1 = manager_.GetBufferInfo(kBuffer1Id); ASSERT_TRUE(info1 != NULL); + EXPECT_EQ(0u, info1->target()); + EXPECT_EQ(0, info1->size()); + EXPECT_FALSE(info1->IsDeleted()); + EXPECT_EQ(kBuffer1Id, info1->buffer_id()); + info1->set_target(GL_ELEMENT_ARRAY_BUFFER); + EXPECT_EQ(GL_ELEMENT_ARRAY_BUFFER, info1->target()); // Check we and set its size. - info1->set_size(kBuffer1Size); + info1->SetSize(kBuffer1Size); EXPECT_EQ(kBuffer1Size, info1->size()); // Check we get nothing for a non-existent buffer. EXPECT_TRUE(manager_.GetBufferInfo(kBuffer2Id) == NULL); @@ -44,7 +50,80 @@ TEST_F(BufferManagerTest, Basic) { EXPECT_TRUE(manager_.GetBufferInfo(kBuffer1Id) == NULL); } -// TODO(gman): Test GetMaxValueForRange. +TEST_F(BufferManagerTest, SetRange) { + const GLuint kBufferId = 1; + const uint8 data[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; + const uint8 new_data[] = {100, 120, 110}; + manager_.CreateBufferInfo(kBufferId); + BufferManager::BufferInfo* info = manager_.GetBufferInfo(kBufferId); + ASSERT_TRUE(info != NULL); + info->set_target(GL_ELEMENT_ARRAY_BUFFER); + info->SetSize(sizeof(data)); + EXPECT_TRUE(info->SetRange(0, sizeof(data), data)); + EXPECT_TRUE(info->SetRange(sizeof(data), 0, data)); + EXPECT_FALSE(info->SetRange(sizeof(data), 1, data)); + EXPECT_FALSE(info->SetRange(0, sizeof(data) + 1, data)); +} + +TEST_F(BufferManagerTest, GetMaxValueForRangeUint8) { + const GLuint kBufferId = 1; + const uint8 data[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; + const uint8 new_data[] = {100, 120, 110}; + manager_.CreateBufferInfo(kBufferId); + BufferManager::BufferInfo* info = manager_.GetBufferInfo(kBufferId); + ASSERT_TRUE(info != NULL); + info->set_target(GL_ELEMENT_ARRAY_BUFFER); + info->SetSize(sizeof(data)); + EXPECT_TRUE(info->SetRange(0, sizeof(data), data)); + GLuint max_value; + // Check entire range succeeds. + EXPECT_TRUE(info->GetMaxValueForRange(0, 10, GL_UNSIGNED_BYTE, &max_value)); + EXPECT_EQ(10u, max_value); + // Check sub range succeeds. + EXPECT_TRUE(info->GetMaxValueForRange(4, 3, GL_UNSIGNED_BYTE, &max_value)); + EXPECT_EQ(6u, max_value); + // Check changing sub range succeeds. + EXPECT_TRUE(info->SetRange(4, sizeof(new_data), new_data)); + EXPECT_TRUE(info->GetMaxValueForRange(4, 3, GL_UNSIGNED_BYTE, &max_value)); + EXPECT_EQ(120u, max_value); + max_value = 0; + EXPECT_TRUE(info->GetMaxValueForRange(0, 10, GL_UNSIGNED_BYTE, &max_value)); + EXPECT_EQ(120u, max_value); + // Check out of range fails. + EXPECT_FALSE(info->GetMaxValueForRange(0, 11, GL_UNSIGNED_BYTE, &max_value)); + EXPECT_FALSE(info->GetMaxValueForRange(10, 1, GL_UNSIGNED_BYTE, &max_value)); +} + +TEST_F(BufferManagerTest, GetMaxValueForRangeUint16) { + const GLuint kBufferId = 1; + const uint16 data[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; + const uint16 new_data[] = {100, 120, 110}; + manager_.CreateBufferInfo(kBufferId); + BufferManager::BufferInfo* info = manager_.GetBufferInfo(kBufferId); + ASSERT_TRUE(info != NULL); + info->set_target(GL_ELEMENT_ARRAY_BUFFER); + info->SetSize(sizeof(data)); + EXPECT_TRUE(info->SetRange(0, sizeof(data), data)); + GLuint max_value; + // Check entire range succeeds. + EXPECT_TRUE(info->GetMaxValueForRange(0, 10, GL_UNSIGNED_SHORT, &max_value)); + EXPECT_EQ(10u, max_value); + // Check odd offset fails for GL_UNSIGNED_SHORT. + EXPECT_FALSE(info->GetMaxValueForRange(1, 10, GL_UNSIGNED_SHORT, &max_value)); + // Check sub range succeeds. + EXPECT_TRUE(info->GetMaxValueForRange(8, 3, GL_UNSIGNED_SHORT, &max_value)); + EXPECT_EQ(6u, max_value); + // Check changing sub range succeeds. + EXPECT_TRUE(info->SetRange(8, sizeof(new_data), new_data)); + EXPECT_TRUE(info->GetMaxValueForRange(8, 3, GL_UNSIGNED_SHORT, &max_value)); + EXPECT_EQ(120u, max_value); + max_value = 0; + EXPECT_TRUE(info->GetMaxValueForRange(0, 10, GL_UNSIGNED_SHORT, &max_value)); + EXPECT_EQ(120u, max_value); + // Check out of range fails. + EXPECT_FALSE(info->GetMaxValueForRange(0, 11, GL_UNSIGNED_SHORT, &max_value)); + EXPECT_FALSE(info->GetMaxValueForRange(20, 1, GL_UNSIGNED_SHORT, &max_value)); +} } // namespace gles2 } // namespace gpu diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index 54cef89..dbb49e9 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc @@ -434,6 +434,14 @@ class GLES2DecoderImpl : public GLES2Decoder { // Wrapper for glBindTexture since we need to track the current targets. void DoBindTexture(GLenum target, GLuint texture); + // Wrapper for BufferData. + void DoBufferData( + GLenum target, GLsizeiptr size, const GLvoid * data, GLenum usage); + + // Wrapper for BufferSubData. + void DoBufferSubData( + GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid * data); + // Wrapper for glCompileShader. void DoCompileShader(GLuint shader); @@ -1503,10 +1511,15 @@ void GLES2DecoderImpl::DoBindBuffer(GLenum target, GLuint buffer) { BufferManager::BufferInfo* info = NULL; if (buffer) { info = GetBufferInfo(buffer); - if (!info) { + // Check the buffer exists + // Check that we are not trying to bind it to a different target. + if (!info || (info->target() != 0 && info->target() != target)) { SetGLError(GL_INVALID_OPERATION); return; } + if (info->target() == 0) { + info->set_target(target); + } } switch (target) { case GL_ARRAY_BUFFER: @@ -1919,28 +1932,19 @@ error::Error GLES2DecoderImpl::HandleDrawElements( !ValidateGLenumIndexType(type)) { SetGLError(GL_INVALID_ENUM); } else { - GLsizeiptr buffer_size = bound_element_array_buffer_->size(); - if (offset > buffer_size) { + GLuint max_vertex_accessed; + if (!bound_element_array_buffer_->GetMaxValueForRange( + offset, count, type, &max_vertex_accessed)) { SetGLError(GL_INVALID_OPERATION); } else { - GLsizei usable_size = buffer_size - offset; - GLsizei num_elements = - usable_size / GLES2Util::GetGLTypeSizeForTexturesAndBuffers(type); - if (count > num_elements) { - SetGLError(GL_INVALID_OPERATION); - } else { + if (IsDrawValid(max_vertex_accessed)) { + bool has_non_renderable_textures; + SetBlackTextureForNonRenderableTextures( + &has_non_renderable_textures); const GLvoid* indices = reinterpret_cast<const GLvoid*>(offset); - GLuint max_vertex_accessed = - bound_element_array_buffer_->GetMaxValueForRange( - offset, count, type); - if (IsDrawValid(max_vertex_accessed)) { - bool has_non_renderable_textures; - SetBlackTextureForNonRenderableTextures( - &has_non_renderable_textures); - glDrawElements(mode, count, type, indices); - if (has_non_renderable_textures) { - RestoreStateForNonRenderableTextures(); - } + glDrawElements(mode, count, type, indices); + if (has_non_renderable_textures) { + RestoreStateForNonRenderableTextures(); } } } @@ -2272,33 +2276,21 @@ error::Error GLES2DecoderImpl::HandleGetString( return error::kNoError; } -error::Error GLES2DecoderImpl::HandleBufferData( - uint32 immediate_data_size, const gles2::BufferData& c) { - GLenum target = static_cast<GLenum>(c.target); - GLsizeiptr size = static_cast<GLsizeiptr>(c.size); - uint32 data_shm_id = static_cast<uint32>(c.data_shm_id); - uint32 data_shm_offset = static_cast<uint32>(c.data_shm_offset); - GLenum usage = static_cast<GLenum>(c.usage); - const void* data = NULL; - if (data_shm_id != 0 || data_shm_offset != 0) { - data = GetSharedMemoryAs<const void*>(data_shm_id, data_shm_offset, size); - if (!data) { - return error::kOutOfBounds; - } - } +void GLES2DecoderImpl::DoBufferData( + GLenum target, GLsizeiptr size, const GLvoid * data, GLenum usage) { if (!ValidateGLenumBufferTarget(target) || !ValidateGLenumBufferUsage(usage)) { SetGLError(GL_INVALID_ENUM); - return error::kNoError; + return; } if (size < 0) { SetGLError(GL_INVALID_VALUE); - return error::kNoError; + DoBufferData(target, size, data, usage); } BufferManager::BufferInfo* info = GetBufferInfoForTarget(target); if (!info) { SetGLError(GL_INVALID_OPERATION); - return error::kNoError; + DoBufferData(target, size, data, usage); } // Clear the buffer to 0 if no initial data was passed in. scoped_array<int8> zero; @@ -2313,8 +2305,26 @@ error::Error GLES2DecoderImpl::HandleBufferData( if (error != GL_NO_ERROR) { SetGLError(error); } else { - info->set_size(size); + info->SetSize(size); + info->SetRange(0, size, data); } +} + +error::Error GLES2DecoderImpl::HandleBufferData( + uint32 immediate_data_size, const gles2::BufferData& c) { + GLenum target = static_cast<GLenum>(c.target); + GLsizeiptr size = static_cast<GLsizeiptr>(c.size); + uint32 data_shm_id = static_cast<uint32>(c.data_shm_id); + uint32 data_shm_offset = static_cast<uint32>(c.data_shm_offset); + GLenum usage = static_cast<GLenum>(c.usage); + const void* data = NULL; + if (data_shm_id != 0 || data_shm_offset != 0) { + data = GetSharedMemoryAs<const void*>(data_shm_id, data_shm_offset, size); + if (!data) { + return error::kOutOfBounds; + } + } + DoBufferData(target, size, data, usage); return error::kNoError; } @@ -2328,29 +2338,21 @@ error::Error GLES2DecoderImpl::HandleBufferDataImmediate( return error::kOutOfBounds; } GLenum usage = static_cast<GLenum>(c.usage); - if (!ValidateGLenumBufferTarget(target) || - !ValidateGLenumBufferUsage(usage)) { - SetGLError(GL_INVALID_ENUM); - return error::kNoError; - } - if (size < 0) { - SetGLError(GL_INVALID_VALUE); - return error::kNoError; - } + DoBufferData(target, size, data, usage); + return error::kNoError; +} + +void GLES2DecoderImpl::DoBufferSubData( + GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid * data) { BufferManager::BufferInfo* info = GetBufferInfoForTarget(target); if (!info) { SetGLError(GL_INVALID_OPERATION); - return error::kNoError; } - CopyRealGLErrorsToWrapper(); - glBufferData(target, size, data, usage); - GLenum error = glGetError(); - if (error != GL_NO_ERROR) { - SetGLError(error); + if (!info->SetRange(offset, size, data)) { + SetGLError(GL_INVALID_VALUE); } else { - info->set_size(size); + glBufferSubData(target, offset, size, data); } - return error::kNoError; } error::Error GLES2DecoderImpl::DoCompressedTexImage2D( diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h index 025e0b3..eb66e7f 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h @@ -231,7 +231,7 @@ error::Error GLES2DecoderImpl::HandleBufferSubData( if (data == NULL) { return error::kOutOfBounds; } - glBufferSubData(target, offset, size, data); + DoBufferSubData(target, offset, size, data); return error::kNoError; } @@ -254,7 +254,7 @@ error::Error GLES2DecoderImpl::HandleBufferSubDataImmediate( if (data == NULL) { return error::kOutOfBounds; } - glBufferSubData(target, offset, size, data); + DoBufferSubData(target, offset, size, data); return error::kNoError; } diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc index 5f96cd5..9228165 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc @@ -194,12 +194,12 @@ TEST_F(GLES2DecoderWithShaderTest, DrawElementsNoAttributesSucceeds) { SetupIndexBuffer(); EXPECT_CALL(*gl_, DrawElements(GL_TRIANGLES, kValidIndexRangeCount, GL_UNSIGNED_SHORT, - BufferOffset(kValidIndexRangeStart))) + BufferOffset(kValidIndexRangeStart * 2))) .Times(1) .RetiresOnSaturation(); DrawElements cmd; cmd.Init(GL_TRIANGLES, kValidIndexRangeCount, GL_UNSIGNED_SHORT, - kValidIndexRangeStart); + kValidIndexRangeStart * 2); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); } @@ -212,7 +212,7 @@ TEST_F(GLES2DecoderWithShaderTest, DrawElementsMissingAttributesFails) { .Times(0); DrawElements cmd; cmd.Init(GL_TRIANGLES, kValidIndexRangeCount, GL_UNSIGNED_SHORT, - kValidIndexRangeStart); + kValidIndexRangeStart * 2); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); } @@ -225,12 +225,12 @@ TEST_F(GLES2DecoderWithShaderTest, DrawElementsValidAttributesSucceeds) { EXPECT_CALL(*gl_, DrawElements(GL_TRIANGLES, kValidIndexRangeCount, GL_UNSIGNED_SHORT, - BufferOffset(kValidIndexRangeStart))) + BufferOffset(kValidIndexRangeStart * 2))) .Times(1) .RetiresOnSaturation(); DrawElements cmd; cmd.Init(GL_TRIANGLES, kValidIndexRangeCount, GL_UNSIGNED_SHORT, - kValidIndexRangeStart); + kValidIndexRangeStart * 2); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); } @@ -245,7 +245,7 @@ TEST_F(GLES2DecoderWithShaderTest, DrawElementsDeletedBufferFails) { .Times(0); DrawElements cmd; cmd.Init(GL_TRIANGLES, kValidIndexRangeCount, GL_UNSIGNED_SHORT, - kValidIndexRangeStart); + kValidIndexRangeStart * 2); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); } @@ -260,7 +260,7 @@ TEST_F(GLES2DecoderWithShaderTest, DrawElementsDeletedProgramSucceedsNoGLCall) { .Times(0); DrawElements cmd; cmd.Init(GL_TRIANGLES, kValidIndexRangeCount, GL_UNSIGNED_SHORT, - kValidIndexRangeStart); + kValidIndexRangeStart * 2); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); } @@ -274,7 +274,7 @@ TEST_F(GLES2DecoderWithShaderTest, DrawElementsWithInvalidModeFails) { .Times(0); DrawElements cmd; cmd.Init(GL_QUADS, kValidIndexRangeCount, GL_UNSIGNED_SHORT, - kValidIndexRangeStart); + kValidIndexRangeStart * 2); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_ENUM, GetGLError()); cmd.Init(GL_POLYGON, kValidIndexRangeCount, GL_UNSIGNED_SHORT, @@ -291,7 +291,7 @@ TEST_F(GLES2DecoderWithShaderTest, DrawElementsInvalidCountFails) { // Try start > 0 EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(0); DrawElements cmd; - cmd.Init(GL_TRIANGLES, kNumIndices, GL_UNSIGNED_SHORT, 1); + cmd.Init(GL_TRIANGLES, kNumIndices, GL_UNSIGNED_SHORT, 2); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); @@ -303,7 +303,6 @@ TEST_F(GLES2DecoderWithShaderTest, DrawElementsInvalidCountFails) { 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(); @@ -312,12 +311,24 @@ TEST_F(GLES2DecoderWithShaderTest, DrawElementsOutOfRangeIndicesFails) { EXPECT_CALL(*gl_, DrawElements(_, _, _, _)).Times(0); DrawElements cmd; cmd.Init(GL_TRIANGLES, kInvalidIndexRangeCount, GL_UNSIGNED_SHORT, - kInvalidIndexRangeStart); + kInvalidIndexRangeStart * 2); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); +} + +TEST_F(GLES2DecoderWithShaderTest, DrawElementsOddOffsetForUint16Fails) { + 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, 1); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); } -#endif TEST_F(GLES2DecoderWithShaderTest, GetVertexAttribPointervSucceeds) { const float dummy = 0; @@ -990,6 +1001,18 @@ TEST_F(GLES2DecoderWithShaderTest, Uniform1ivImmediateValidArgs) { ExecuteImmediateCmd(cmd, sizeof(temp))); } +TEST_F(GLES2DecoderWithShaderTest, BindBufferToDifferentTargetFails) { + // Bind the buffer to GL_ARRAY_BUFFER + DoBindBuffer(GL_ARRAY_BUFFER, client_buffer_id_, kServiceBufferId); + // Attempt to rebind to GL_ELEMENT_ARRAY_BUFFER + // NOTE: Real GLES2 does not have this restriction but WebGL and we do. + EXPECT_CALL(*gl_, BindBuffer(_, _)) + .Times(0); + BindBuffer cmd; + cmd.Init(GL_ELEMENT_ARRAY_BUFFER, client_buffer_id_); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); +} // TODO(gman): BindAttribLocation |