diff options
Diffstat (limited to 'gpu')
22 files changed, 835 insertions, 248 deletions
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py index e826abd..a408db7 100755 --- a/gpu/command_buffer/build_gles2_cmd_buffer.py +++ b/gpu/command_buffer/build_gles2_cmd_buffer.py @@ -1176,6 +1176,7 @@ _PEPPER_INTERFACES = [ # when they can not be automatically determined. # pepper_interface: The pepper interface that is used for this extension # invalid_test: False if no invalid test needed. +# shadowed: True = the value is shadowed so no glGetXXX call will be made. _FUNCTION_INFO = { 'ActiveTexture': { @@ -1563,7 +1564,13 @@ _FUNCTION_INFO = { 'decoder_func': 'DoGetBooleanv', 'gl_test_func': 'glGetBooleanv', }, - 'GetBufferParameteriv': {'type': 'GETn', 'result': ['SizedResult<GLint>']}, + 'GetBufferParameteriv': { + 'type': 'GETn', + 'result': ['SizedResult<GLint>'], + 'decoder_func': 'DoGetBufferParameteriv', + 'expectation': False, + 'shadowed': True, + }, 'GetError': { 'type': 'Is', 'decoder_func': 'GetGLError', @@ -4345,11 +4352,19 @@ class GETnHandler(TypeHandler): if (result->size != 0) { return error::kInvalidArguments; } - CopyRealGLErrorsToWrapper(); """ + shadowed = func.GetInfo('shadowed') + if not shadowed: + file.Write(" CopyRealGLErrorsToWrapper();\n"); file.Write(code) func.WriteHandlerImplementation(file) - code = """ GLenum error = glGetError(); + if shadowed: + code = """ result->SetNumResults(num_values); + return error::kNoError; +} +""" + else: + code = """ GLenum error = glGetError(); if (error == GL_NO_ERROR) { result->SetNumResults(num_values); } else { diff --git a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h index 8ef9a14..9e4c660 100644 --- a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h +++ b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h @@ -22,6 +22,7 @@ static GLES2Util::EnumToString enum_to_string_table[] = { { 0, "GL_FALSE", }, { 0x00400000, "GL_STENCIL_BUFFER_BIT6_QCOM", }, { 0x82E6, "GL_SAMPLER", }, + { 0x1E02, "GL_INCR", }, { 0x9130, "GL_SGX_PROGRAM_BINARY_IMG", }, { 0x9133, "GL_RENDERBUFFER_SAMPLES_IMG", }, { 0x82E0, "GL_BUFFER", }, @@ -60,7 +61,7 @@ static GLES2Util::EnumToString enum_to_string_table[] = { { 0x8B54, "GL_INT_VEC3", }, { 0x8DF4, "GL_MEDIUM_INT", }, { 0x8DF5, "GL_HIGH_INT", }, - { 0x8B51, "GL_FLOAT_VEC3", }, + { 0x8DF6, "GL_UNSIGNED_INT_10_10_10_2_OES", }, { 0x8B50, "GL_FLOAT_VEC2", }, { 0x806F, "GL_TEXTURE_3D_OES", }, { 0x92E0, "GL_DEBUG_OUTPUT", }, @@ -88,6 +89,7 @@ static GLES2Util::EnumToString enum_to_string_table[] = { { 0x0C10, "GL_SCISSOR_BOX", }, { 0x0C11, "GL_SCISSOR_TEST", }, { 0x80000000, "GL_MULTISAMPLE_BUFFER_BIT7_QCOM", }, + { 0x300E, "GL_CONTEXT_LOST", }, { 0x02000000, "GL_MULTISAMPLE_BUFFER_BIT1_QCOM", }, { 0x8C2F, "GL_ANY_SAMPLES_PASSED_EXT", }, { 0x8BD2, "GL_TEXTURE_WIDTH_QCOM", }, @@ -283,7 +285,7 @@ static GLES2Util::EnumToString enum_to_string_table[] = { { 0x8FB1, "GL_CPU_OPTIMIZED_QCOM", }, { 0x8B93, "GL_PALETTE4_RGBA4_OES", }, { 0x8B92, "GL_PALETTE4_R5_G6_B5_OES", }, - { 0x1E02, "GL_INCR", }, + { 0x8B91, "GL_PALETTE4_RGBA8_OES", }, { 0x8B90, "GL_PALETTE4_RGB8_OES", }, { 0x8B97, "GL_PALETTE8_R5_G6_B5_OES", }, { 0x8B96, "GL_PALETTE8_RGBA8_OES", }, @@ -339,7 +341,7 @@ static GLES2Util::EnumToString enum_to_string_table[] = { { 0x82E1, "GL_SHADER", }, { 0x8B52, "GL_FLOAT_VEC4", }, { 0x9240, "GL_UNPACK_FLIP_Y_CHROMIUM", }, - { 0x8DF6, "GL_UNSIGNED_INT_10_10_10_2_OES", }, + { 0x8B51, "GL_FLOAT_VEC3", }, { 0x8230, "GL_RG32F_EXT", }, { 0x8DF7, "GL_INT_10_10_10_2_OES", }, { 0x812F, "GL_CLAMP_TO_EDGE", }, @@ -372,7 +374,6 @@ static GLES2Util::EnumToString enum_to_string_table[] = { { 0x881C, "GL_ALPHA16F_EXT", }, { 0x8CD4, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_OES", }, { 0x8CD7, "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT", }, - { 0x08000000, "GL_MULTISAMPLE_BUFFER_BIT3_QCOM", }, { 0x9112, "GL_OBJECT_TYPE_APPLE", }, { 0x8038, "GL_POLYGON_OFFSET_FACTOR", }, { 0x851A, "GL_TEXTURE_CUBE_MAP_NEGATIVE_Z", }, @@ -461,7 +462,7 @@ static GLES2Util::EnumToString enum_to_string_table[] = { { 0x84D4, "GL_TEXTURE20", }, { 0x84D5, "GL_TEXTURE21", }, { 0x84D2, "GL_TEXTURE18", }, - { 0x8B91, "GL_PALETTE4_RGBA8_OES", }, + { 0x84D3, "GL_TEXTURE19", }, { 0x84D0, "GL_TEXTURE16", }, { 0x84D1, "GL_TEXTURE17", }, { 0x1E03, "GL_DECR", }, @@ -487,7 +488,7 @@ static GLES2Util::EnumToString enum_to_string_table[] = { { 0x8865, "GL_CURRENT_QUERY_EXT", }, { 0x8866, "GL_QUERY_RESULT_EXT", }, { 0x8867, "GL_QUERY_RESULT_AVAILABLE_EXT", }, - { 0x300E, "GL_CONTEXT_LOST", }, + { 0x08000000, "GL_MULTISAMPLE_BUFFER_BIT3_QCOM", }, { 0x87FA, "GL_3DC_XY_AMD", }, { 0x84C4, "GL_TEXTURE4", }, { 0x85B5, "GL_VERTEX_ARRAY_BINDING_OES", }, @@ -597,7 +598,6 @@ static GLES2Util::EnumToString enum_to_string_table[] = { { 0x8893, "GL_ELEMENT_ARRAY_BUFFER", }, { 0x8892, "GL_ARRAY_BUFFER", }, { 0x8BD8, "GL_TEXTURE_IMAGE_VALID_QCOM", }, - { 0x84D3, "GL_TEXTURE19", }, { 0x93BA, "GL_COMPRESSED_RGBA_ASTC_10x8_KHR", }, { 0x93BB, "GL_COMPRESSED_RGBA_ASTC_10x10_KHR", }, { 0x93BC, "GL_COMPRESSED_RGBA_ASTC_12x10_KHR", }, diff --git a/gpu/command_buffer/service/buffer_manager.cc b/gpu/command_buffer/service/buffer_manager.cc index cb3062a..207ae3b 100644 --- a/gpu/command_buffer/service/buffer_manager.cc +++ b/gpu/command_buffer/service/buffer_manager.cc @@ -7,18 +7,26 @@ #include "base/debug/trace_event.h" #include "base/logging.h" #include "gpu/command_buffer/common/gles2_cmd_utils.h" +#include "gpu/command_buffer/service/feature_info.h" #include "gpu/command_buffer/service/gles2_cmd_decoder.h" #include "gpu/command_buffer/service/memory_tracking.h" +#include "ui/gl/gl_bindings.h" namespace gpu { namespace gles2 { -BufferManager::BufferManager(MemoryTracker* memory_tracker) +BufferManager::BufferManager( + MemoryTracker* memory_tracker, + FeatureInfo* feature_info) : memory_tracker_( new MemoryTypeTracker(memory_tracker, MemoryTracker::kManaged)), + feature_info_(feature_info), allow_buffers_on_multiple_targets_(false), buffer_info_count_(0), - have_context_(true) { + have_context_(true), + use_client_side_arrays_for_stream_buffers_( + feature_info ? feature_info->workarounds( + ).use_client_side_arrays_for_stream_buffers : 0) { } BufferManager::~BufferManager() { @@ -70,7 +78,8 @@ Buffer::Buffer(BufferManager* manager, GLuint service_id) target_(0), size_(0), usage_(GL_STATIC_DRAW), - shadowed_(false) { + shadowed_(false), + is_client_side_array_(false) { manager_->StartTracking(this); } @@ -86,14 +95,24 @@ Buffer::~Buffer() { } void Buffer::SetInfo( - GLsizeiptr size, GLenum usage, bool shadow) { + GLsizeiptr size, GLenum usage, bool shadow, const GLvoid* data, + bool is_client_side_array) { usage_ = usage; + is_client_side_array_ = is_client_side_array; if (size != size_ || shadow != shadowed_) { shadowed_ = shadow; size_ = size; ClearCache(); if (shadowed_) { shadow_.reset(new int8[size]); + } else { + shadow_.reset(); + } + } + if (shadowed_) { + if (data) { + memcpy(shadow_.get(), data, size); + } else { memset(shadow_.get(), 0, size); } } @@ -217,17 +236,66 @@ bool BufferManager::GetClientId(GLuint service_id, GLuint* client_id) const { return false; } +bool BufferManager::IsUsageClientSideArray(GLenum usage) { + return usage == GL_STREAM_DRAW && use_client_side_arrays_for_stream_buffers_; +} + void BufferManager::SetInfo( - Buffer* info, GLsizeiptr size, GLenum usage) { + Buffer* info, GLsizeiptr size, GLenum usage, const GLvoid* data) { DCHECK(info); memory_tracker_->TrackMemFree(info->size()); - info->SetInfo(size, - usage, - info->target() == GL_ELEMENT_ARRAY_BUFFER || - allow_buffers_on_multiple_targets_); + bool is_client_side_array = IsUsageClientSideArray(usage); + bool shadow = info->target() == GL_ELEMENT_ARRAY_BUFFER || + allow_buffers_on_multiple_targets_ || + is_client_side_array; + info->SetInfo(size, usage, shadow, data, is_client_side_array); memory_tracker_->TrackMemAlloc(info->size()); } +void BufferManager::DoBufferData( + GLES2Decoder* decoder, + Buffer* buffer, + GLsizeiptr size, + GLenum usage, + const GLvoid* data) { + // Clear the buffer to 0 if no initial data was passed in. + scoped_array<int8> zero; + if (!data) { + zero.reset(new int8[size]); + memset(zero.get(), 0, size); + data = zero.get(); + } + + decoder->CopyRealGLErrorsToWrapper(); + if (IsUsageClientSideArray(usage)) { + glBufferData(buffer->target(), 0, NULL, usage); + } else { + glBufferData(buffer->target(), size, data, usage); + } + GLenum error = decoder->PeekGLError(); + if (error == GL_NO_ERROR) { + SetInfo(buffer, size, usage, data); + } else { + SetInfo(buffer, 0, usage, NULL); + } +} + +void BufferManager::DoBufferSubData( + GLES2Decoder* decoder, + Buffer* buffer, + GLintptr offset, + GLsizeiptr size, + const GLvoid* data) { + if (!buffer->SetRange(offset, size, data)) { + decoder->SetGLError(GL_INVALID_VALUE, "glBufferSubData", "out of range"); + return; + } + + if (!buffer->IsClientSideArray()) { + glBufferSubData(buffer->target(), offset, size, data); + } +} + bool BufferManager::SetTarget(Buffer* info, GLenum target) { // Check that we are not trying to bind it to a different target. if (info->target() != 0 && info->target() != target && diff --git a/gpu/command_buffer/service/buffer_manager.h b/gpu/command_buffer/service/buffer_manager.h index e9ea2f7..0d99e9b 100644 --- a/gpu/command_buffer/service/buffer_manager.h +++ b/gpu/command_buffer/service/buffer_manager.h @@ -19,6 +19,8 @@ namespace gpu { namespace gles2 { class BufferManager; +class FeatureInfo; +class GLES2Decoder; // Info about Buffers currently in the system. class GPU_EXPORT Buffer : public base::RefCounted<Buffer> { @@ -29,6 +31,10 @@ class GPU_EXPORT Buffer : public base::RefCounted<Buffer> { return service_id_; } + GLenum target() const { + return target_; + } + GLsizeiptr size() const { return size_; } @@ -37,11 +43,6 @@ class GPU_EXPORT Buffer : public base::RefCounted<Buffer> { return usage_; } - // 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); - // 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. @@ -60,6 +61,10 @@ class GPU_EXPORT Buffer : public base::RefCounted<Buffer> { return target() && !IsDeleted(); } + bool IsClientSideArray() const { + return is_client_side_array_; + } + private: friend class BufferManager; friend class BufferManagerTestBase; @@ -95,10 +100,6 @@ class GPU_EXPORT Buffer : public base::RefCounted<Buffer> { ~Buffer(); - GLenum target() const { - return target_; - } - void set_target(GLenum target) { DCHECK_EQ(target_, 0u); // you can only set this once. target_ = target; @@ -112,7 +113,16 @@ class GPU_EXPORT Buffer : public base::RefCounted<Buffer> { deleted_ = true; } - void SetInfo(GLsizeiptr size, GLenum usage, bool shadow); + // Sets the size, usage and initial data of a buffer. + // If shadow is true then if data is NULL buffer will be initialized to 0. + void SetInfo( + GLsizeiptr size, GLenum usage, bool shadow, const GLvoid* data, + bool is_client_side_array); + + // 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); // Clears any cache of index ranges. void ClearCache(); @@ -143,6 +153,10 @@ class GPU_EXPORT Buffer : public base::RefCounted<Buffer> { // Whether or not the data is shadowed. bool shadowed_; + // Whether or not this Buffer is not uploaded to the GPU but just + // sitting in local memory. + bool is_client_side_array_; + // A copy of the data in the buffer. This data is only kept if the target // is backed_ = true. scoped_array<int8> shadow_; @@ -159,7 +173,7 @@ class GPU_EXPORT Buffer : public base::RefCounted<Buffer> { // shared by multiple GLES2Decoders. class GPU_EXPORT BufferManager { public: - BufferManager(MemoryTracker* memory_tracker); + BufferManager(MemoryTracker* memory_tracker, FeatureInfo* feature_info); ~BufferManager(); // Must call before destruction. @@ -177,8 +191,22 @@ class GPU_EXPORT BufferManager { // Gets a client id for a given service id. bool GetClientId(GLuint service_id, GLuint* client_id) const; - // Sets the size and usage of a buffer. - void SetInfo(Buffer* info, GLsizeiptr size, GLenum usage); + // Does a glBufferData and updates the approprate accounting. Currently + // assume the values have already been validated. + void DoBufferData( + GLES2Decoder* decoder, + Buffer* buffer, + GLsizeiptr size, + GLenum usage, + const GLvoid* data); + + // Does a glBufferSubData and updates the approrate accounting. + void DoBufferSubData( + GLES2Decoder* decoder, + Buffer* buffer, + GLintptr offset, + GLsizeiptr size, + const GLvoid* data); // Sets the target of a buffer. Returns false if the target can not be set. bool SetTarget(Buffer* info, GLenum target); @@ -191,12 +219,20 @@ class GPU_EXPORT BufferManager { return memory_tracker_->GetMemRepresented(); } + // Tell's for a given usage if this would be a client side array. + bool IsUsageClientSideArray(GLenum usage); + private: friend class Buffer; void StartTracking(Buffer* info); void StopTracking(Buffer* info); + // Sets the size, usage and initial data of a buffer. + // If data is NULL buffer will be initialized to 0 if shadowed. + void SetInfo(Buffer* info, GLsizeiptr size, GLenum usage, const GLvoid* data); + scoped_ptr<MemoryTypeTracker> memory_tracker_; + scoped_refptr<FeatureInfo> feature_info_; // Info for each buffer in the system. typedef base::hash_map<GLuint, scoped_refptr<Buffer> > BufferInfoMap; @@ -210,6 +246,7 @@ class GPU_EXPORT BufferManager { unsigned int buffer_info_count_; bool have_context_; + bool use_client_side_arrays_for_stream_buffers_; DISALLOW_COPY_AND_ASSIGN(BufferManager); }; diff --git a/gpu/command_buffer/service/buffer_manager_unittest.cc b/gpu/command_buffer/service/buffer_manager_unittest.cc index e782e7f..65dd7ed 100644 --- a/gpu/command_buffer/service/buffer_manager_unittest.cc +++ b/gpu/command_buffer/service/buffer_manager_unittest.cc @@ -3,10 +3,15 @@ // found in the LICENSE file. #include "gpu/command_buffer/service/buffer_manager.h" +#include "gpu/command_buffer/service/feature_info.h" +#include "gpu/command_buffer/service/gles2_cmd_decoder_mock.h" #include "gpu/command_buffer/service/mocks.h" +#include "gpu/command_buffer/service/test_helper.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gl/gl_mock.h" +using ::testing::_; +using ::testing::Return; using ::testing::StrictMock; namespace gpu { @@ -14,32 +19,73 @@ namespace gles2 { class BufferManagerTestBase : public testing::Test { protected: - void SetUpBase(MemoryTracker* memory_tracker) { + void SetUpBase( + MemoryTracker* memory_tracker, + FeatureInfo* feature_info, + const char* extensions, + const char* vendor, + const char* renderer) { gl_.reset(new ::testing::StrictMock< ::gfx::MockGLInterface>()); ::gfx::GLInterface::SetGLInterface(gl_.get()); - manager_.reset(new BufferManager(memory_tracker)); + if (feature_info) { + TestHelper::SetupFeatureInfoInitExpectationsWithVendor( + gl_.get(), extensions, vendor, renderer); + feature_info->Initialize(NULL); + } + decoder_.reset(new MockGLES2Decoder()); + manager_.reset(new BufferManager(memory_tracker, feature_info)); } virtual void TearDown() { manager_->Destroy(false); manager_.reset(); ::gfx::GLInterface::SetGLInterface(NULL); + decoder_.reset(); gl_.reset(); } - GLenum GetTarget(const Buffer* info) const { - return info->target(); + GLenum GetTarget(const Buffer* buffer) const { + return buffer->target(); + } + + void DoBufferData( + Buffer* buffer, GLsizeiptr size, GLenum usage, const GLvoid* data, + GLenum error) { + TestHelper::DoBufferData( + gl_.get(), decoder_.get(), manager_.get(), + buffer, size, usage, data, error); + } + + bool DoBufferSubData( + Buffer* buffer, GLintptr offset, GLsizeiptr size, + const GLvoid* data) { + bool success = true; + if (!buffer->CheckRange(offset, size)) { + EXPECT_CALL(*decoder_, SetGLError(GL_INVALID_VALUE, _, _)) + .Times(1) + .RetiresOnSaturation(); + success = false; + } else if (!buffer->IsClientSideArray()) { + EXPECT_CALL(*gl_, BufferSubData( + buffer->target(), offset, size, _)) + .Times(1) + .RetiresOnSaturation(); + } + manager_->DoBufferSubData( + decoder_.get(), buffer, offset, size, data); + return success; } // Use StrictMock to make 100% sure we know how GL will be called. scoped_ptr< ::testing::StrictMock< ::gfx::MockGLInterface> > gl_; scoped_ptr<BufferManager> manager_; + scoped_ptr<MockGLES2Decoder> decoder_; }; class BufferManagerTest : public BufferManagerTestBase { protected: virtual void SetUp() { - SetUpBase(NULL); + SetUpBase(NULL, NULL, "", "", ""); } }; @@ -47,12 +93,22 @@ class BufferManagerMemoryTrackerTest : public BufferManagerTestBase { protected: virtual void SetUp() { mock_memory_tracker_ = new StrictMock<MockMemoryTracker>(); - SetUpBase(mock_memory_tracker_.get()); + SetUpBase(mock_memory_tracker_.get(), NULL, "", "", ""); } scoped_refptr<MockMemoryTracker> mock_memory_tracker_; }; +class BufferManagerClientSideArraysTest : public BufferManagerTestBase { + protected: + virtual void SetUp() { + feature_info_ = new FeatureInfo(); + SetUpBase(NULL, feature_info_.get(), "", "Imagination Technologies", ""); + } + + scoped_refptr<FeatureInfo> feature_info_; +}; + #define EXPECT_MEMORY_ALLOCATION_CHANGE(old_size, new_size, pool) \ EXPECT_CALL(*mock_memory_tracker_, \ TrackMemoryAllocatedChange(old_size, new_size, pool)) \ @@ -67,22 +123,23 @@ TEST_F(BufferManagerTest, Basic) { // Check we can create buffer. manager_->CreateBuffer(kClientBuffer1Id, kServiceBuffer1Id); // Check buffer got created. - Buffer* info1 = manager_->GetBuffer(kClientBuffer1Id); - ASSERT_TRUE(info1 != NULL); - EXPECT_EQ(0u, GetTarget(info1)); - EXPECT_EQ(0, info1->size()); - EXPECT_EQ(static_cast<GLenum>(GL_STATIC_DRAW), info1->usage()); - EXPECT_FALSE(info1->IsDeleted()); - EXPECT_EQ(kServiceBuffer1Id, info1->service_id()); + Buffer* buffer1 = manager_->GetBuffer(kClientBuffer1Id); + ASSERT_TRUE(buffer1 != NULL); + EXPECT_EQ(0u, GetTarget(buffer1)); + EXPECT_EQ(0, buffer1->size()); + EXPECT_EQ(static_cast<GLenum>(GL_STATIC_DRAW), buffer1->usage()); + EXPECT_FALSE(buffer1->IsDeleted()); + EXPECT_FALSE(buffer1->IsClientSideArray()); + EXPECT_EQ(kServiceBuffer1Id, buffer1->service_id()); GLuint client_id = 0; - EXPECT_TRUE(manager_->GetClientId(info1->service_id(), &client_id)); + EXPECT_TRUE(manager_->GetClientId(buffer1->service_id(), &client_id)); EXPECT_EQ(kClientBuffer1Id, client_id); - manager_->SetTarget(info1, GL_ELEMENT_ARRAY_BUFFER); - EXPECT_EQ(static_cast<GLenum>(GL_ELEMENT_ARRAY_BUFFER), GetTarget(info1)); + manager_->SetTarget(buffer1, GL_ELEMENT_ARRAY_BUFFER); + EXPECT_EQ(static_cast<GLenum>(GL_ELEMENT_ARRAY_BUFFER), GetTarget(buffer1)); // Check we and set its size. - manager_->SetInfo(info1, kBuffer1Size, GL_DYNAMIC_DRAW); - EXPECT_EQ(kBuffer1Size, info1->size()); - EXPECT_EQ(static_cast<GLenum>(GL_DYNAMIC_DRAW), info1->usage()); + DoBufferData(buffer1, kBuffer1Size, GL_DYNAMIC_DRAW, NULL, GL_NO_ERROR); + EXPECT_EQ(kBuffer1Size, buffer1->size()); + EXPECT_EQ(static_cast<GLenum>(GL_DYNAMIC_DRAW), buffer1->usage()); // Check we get nothing for a non-existent buffer. EXPECT_TRUE(manager_->GetBuffer(kClientBuffer2Id) == NULL); // Check trying to a remove non-existent buffers does not crash. @@ -105,15 +162,15 @@ TEST_F(BufferManagerMemoryTrackerTest, Basic) { EXPECT_MEMORY_ALLOCATION_CHANGE(0, 0, MemoryTracker::kManaged); manager_->CreateBuffer(kClientBuffer1Id, kServiceBuffer1Id); // Check buffer got created. - Buffer* info1 = manager_->GetBuffer(kClientBuffer1Id); - ASSERT_TRUE(info1 != NULL); - manager_->SetTarget(info1, GL_ELEMENT_ARRAY_BUFFER); + Buffer* buffer1 = manager_->GetBuffer(kClientBuffer1Id); + ASSERT_TRUE(buffer1 != NULL); + manager_->SetTarget(buffer1, GL_ELEMENT_ARRAY_BUFFER); // Check we and set its size. EXPECT_MEMORY_ALLOCATION_CHANGE(0, kBuffer1Size1, MemoryTracker::kManaged); - manager_->SetInfo(info1, kBuffer1Size1, GL_DYNAMIC_DRAW); + DoBufferData(buffer1, kBuffer1Size1, GL_DYNAMIC_DRAW, NULL, GL_NO_ERROR); EXPECT_MEMORY_ALLOCATION_CHANGE(kBuffer1Size1, 0, MemoryTracker::kManaged); EXPECT_MEMORY_ALLOCATION_CHANGE(0, kBuffer1Size2, MemoryTracker::kManaged); - manager_->SetInfo(info1, kBuffer1Size2, GL_DYNAMIC_DRAW); + DoBufferData(buffer1, kBuffer1Size2, GL_DYNAMIC_DRAW, NULL, GL_NO_ERROR); // On delete it will get freed. EXPECT_MEMORY_ALLOCATION_CHANGE(kBuffer1Size2, 0, MemoryTracker::kManaged); } @@ -124,38 +181,37 @@ TEST_F(BufferManagerTest, Destroy) { // Check we can create buffer. manager_->CreateBuffer(kClient1Id, kService1Id); // Check buffer got created. - Buffer* info1 = - manager_->GetBuffer(kClient1Id); - ASSERT_TRUE(info1 != NULL); + Buffer* buffer1 = manager_->GetBuffer(kClient1Id); + ASSERT_TRUE(buffer1 != NULL); EXPECT_CALL(*gl_, DeleteBuffersARB(1, ::testing::Pointee(kService1Id))) .Times(1) .RetiresOnSaturation(); manager_->Destroy(true); // Check the resources were released. - info1 = manager_->GetBuffer(kClient1Id); - ASSERT_TRUE(info1 == NULL); + buffer1 = manager_->GetBuffer(kClient1Id); + ASSERT_TRUE(buffer1 == NULL); } -TEST_F(BufferManagerTest, SetRange) { +TEST_F(BufferManagerTest, DoBufferSubData) { const GLuint kClientBufferId = 1; const GLuint kServiceBufferId = 11; const uint8 data[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; manager_->CreateBuffer(kClientBufferId, kServiceBufferId); - Buffer* info = manager_->GetBuffer(kClientBufferId); - ASSERT_TRUE(info != NULL); - manager_->SetTarget(info, GL_ELEMENT_ARRAY_BUFFER); - manager_->SetInfo(info, sizeof(data), GL_STATIC_DRAW); - 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)); - EXPECT_FALSE(info->SetRange(-1, sizeof(data), data)); - EXPECT_FALSE(info->SetRange(0, -1, data)); - manager_->SetInfo(info, 1, GL_STATIC_DRAW); + Buffer* buffer = manager_->GetBuffer(kClientBufferId); + ASSERT_TRUE(buffer != NULL); + manager_->SetTarget(buffer, GL_ELEMENT_ARRAY_BUFFER); + DoBufferData(buffer, sizeof(data), GL_STATIC_DRAW, NULL, GL_NO_ERROR); + EXPECT_TRUE(DoBufferSubData(buffer, 0, sizeof(data), data)); + EXPECT_TRUE(DoBufferSubData(buffer, sizeof(data), 0, data)); + EXPECT_FALSE(DoBufferSubData(buffer, sizeof(data), 1, data)); + EXPECT_FALSE(DoBufferSubData(buffer, 0, sizeof(data) + 1, data)); + EXPECT_FALSE(DoBufferSubData(buffer, -1, sizeof(data), data)); + EXPECT_FALSE(DoBufferSubData(buffer, 0, -1, data)); + DoBufferData(buffer, 1, GL_STATIC_DRAW, NULL, GL_NO_ERROR); const int size = 0x20000; scoped_array<uint8> temp(new uint8[size]); - EXPECT_FALSE(info->SetRange(0 - size, size, temp.get())); - EXPECT_FALSE(info->SetRange(1, size / 2, temp.get())); + EXPECT_FALSE(DoBufferSubData(buffer, 0 - size, size, temp.get())); + EXPECT_FALSE(DoBufferSubData(buffer, 1, size / 2, temp.get())); } TEST_F(BufferManagerTest, GetRange) { @@ -163,23 +219,24 @@ TEST_F(BufferManagerTest, GetRange) { const GLuint kServiceBufferId = 11; const uint8 data[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; manager_->CreateBuffer(kClientBufferId, kServiceBufferId); - Buffer* info = manager_->GetBuffer(kClientBufferId); - ASSERT_TRUE(info != NULL); - manager_->SetTarget(info, GL_ELEMENT_ARRAY_BUFFER); - manager_->SetInfo(info, sizeof(data), GL_STATIC_DRAW); - const char* buf = static_cast<const char*>(info->GetRange(0, sizeof(data))); + Buffer* buffer = manager_->GetBuffer(kClientBufferId); + ASSERT_TRUE(buffer != NULL); + manager_->SetTarget(buffer, GL_ELEMENT_ARRAY_BUFFER); + DoBufferData(buffer, sizeof(data), GL_STATIC_DRAW, NULL, GL_NO_ERROR); + const char* buf = + static_cast<const char*>(buffer->GetRange(0, sizeof(data))); ASSERT_TRUE(buf != NULL); const char* buf1 = - static_cast<const char*>(info->GetRange(1, sizeof(data) - 1)); + static_cast<const char*>(buffer->GetRange(1, sizeof(data) - 1)); EXPECT_EQ(buf + 1, buf1); - EXPECT_TRUE(info->GetRange(sizeof(data), 1) == NULL); - EXPECT_TRUE(info->GetRange(0, sizeof(data) + 1) == NULL); - EXPECT_TRUE(info->GetRange(-1, sizeof(data)) == NULL); - EXPECT_TRUE(info->GetRange(-0, -1) == NULL); + EXPECT_TRUE(buffer->GetRange(sizeof(data), 1) == NULL); + EXPECT_TRUE(buffer->GetRange(0, sizeof(data) + 1) == NULL); + EXPECT_TRUE(buffer->GetRange(-1, sizeof(data)) == NULL); + EXPECT_TRUE(buffer->GetRange(-0, -1) == NULL); const int size = 0x20000; - manager_->SetInfo(info, size / 2, GL_STATIC_DRAW); - EXPECT_TRUE(info->GetRange(0 - size, size) == NULL); - EXPECT_TRUE(info->GetRange(1, size / 2) == NULL); + DoBufferData(buffer, size / 2, GL_STATIC_DRAW, NULL, GL_NO_ERROR); + EXPECT_TRUE(buffer->GetRange(0 - size, size) == NULL); + EXPECT_TRUE(buffer->GetRange(1, size / 2) == NULL); } TEST_F(BufferManagerTest, GetMaxValueForRangeUint8) { @@ -188,28 +245,34 @@ TEST_F(BufferManagerTest, GetMaxValueForRangeUint8) { const uint8 data[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; const uint8 new_data[] = {100, 120, 110}; manager_->CreateBuffer(kClientBufferId, kServiceBufferId); - Buffer* info = manager_->GetBuffer(kClientBufferId); - ASSERT_TRUE(info != NULL); - manager_->SetTarget(info, GL_ELEMENT_ARRAY_BUFFER); - manager_->SetInfo(info, sizeof(data), GL_STATIC_DRAW); - EXPECT_TRUE(info->SetRange(0, sizeof(data), data)); + Buffer* buffer = manager_->GetBuffer(kClientBufferId); + ASSERT_TRUE(buffer != NULL); + manager_->SetTarget(buffer, GL_ELEMENT_ARRAY_BUFFER); + DoBufferData(buffer, sizeof(data), GL_STATIC_DRAW, NULL, GL_NO_ERROR); + EXPECT_TRUE(DoBufferSubData(buffer, 0, sizeof(data), data)); GLuint max_value; // Check entire range succeeds. - EXPECT_TRUE(info->GetMaxValueForRange(0, 10, GL_UNSIGNED_BYTE, &max_value)); + EXPECT_TRUE(buffer->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_TRUE(buffer->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_TRUE(DoBufferSubData(buffer, 4, sizeof(new_data), new_data)); + EXPECT_TRUE(buffer->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_TRUE(buffer->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)); + EXPECT_FALSE(buffer->GetMaxValueForRange( + 0, 11, GL_UNSIGNED_BYTE, &max_value)); + EXPECT_FALSE(buffer->GetMaxValueForRange( + 10, 1, GL_UNSIGNED_BYTE, &max_value)); } TEST_F(BufferManagerTest, GetMaxValueForRangeUint16) { @@ -218,30 +281,37 @@ TEST_F(BufferManagerTest, GetMaxValueForRangeUint16) { const uint16 data[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; const uint16 new_data[] = {100, 120, 110}; manager_->CreateBuffer(kClientBufferId, kServiceBufferId); - Buffer* info = manager_->GetBuffer(kClientBufferId); - ASSERT_TRUE(info != NULL); - manager_->SetTarget(info, GL_ELEMENT_ARRAY_BUFFER); - manager_->SetInfo(info, sizeof(data), GL_STATIC_DRAW); - EXPECT_TRUE(info->SetRange(0, sizeof(data), data)); + Buffer* buffer = manager_->GetBuffer(kClientBufferId); + ASSERT_TRUE(buffer != NULL); + manager_->SetTarget(buffer, GL_ELEMENT_ARRAY_BUFFER); + DoBufferData(buffer, sizeof(data), GL_STATIC_DRAW, NULL, GL_NO_ERROR); + EXPECT_TRUE(DoBufferSubData(buffer, 0, sizeof(data), data)); GLuint max_value; // Check entire range succeeds. - EXPECT_TRUE(info->GetMaxValueForRange(0, 10, GL_UNSIGNED_SHORT, &max_value)); + EXPECT_TRUE(buffer->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)); + EXPECT_FALSE(buffer->GetMaxValueForRange( + 1, 10, GL_UNSIGNED_SHORT, &max_value)); // Check sub range succeeds. - EXPECT_TRUE(info->GetMaxValueForRange(8, 3, GL_UNSIGNED_SHORT, &max_value)); + EXPECT_TRUE(buffer->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_TRUE(DoBufferSubData(buffer, 8, sizeof(new_data), new_data)); + EXPECT_TRUE(buffer->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_TRUE(buffer->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)); + EXPECT_FALSE(buffer->GetMaxValueForRange( + 0, 11, GL_UNSIGNED_SHORT, &max_value)); + EXPECT_FALSE(buffer->GetMaxValueForRange( + 20, 1, GL_UNSIGNED_SHORT, &max_value)); } TEST_F(BufferManagerTest, GetMaxValueForRangeUint32) { @@ -250,32 +320,38 @@ TEST_F(BufferManagerTest, GetMaxValueForRangeUint32) { const uint32 data[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; const uint32 new_data[] = {100, 120, 110}; manager_->CreateBuffer(kClientBufferId, kServiceBufferId); - Buffer* info = manager_->GetBuffer(kClientBufferId); - ASSERT_TRUE(info != NULL); - manager_->SetTarget(info, GL_ELEMENT_ARRAY_BUFFER); - manager_->SetInfo(info, sizeof(data), GL_STATIC_DRAW); - EXPECT_TRUE(info->SetRange(0, sizeof(data), data)); + Buffer* buffer = manager_->GetBuffer(kClientBufferId); + ASSERT_TRUE(buffer != NULL); + manager_->SetTarget(buffer, GL_ELEMENT_ARRAY_BUFFER); + DoBufferData(buffer, sizeof(data), GL_STATIC_DRAW, NULL, GL_NO_ERROR); + EXPECT_TRUE(DoBufferSubData(buffer, 0, sizeof(data), data)); GLuint max_value; // Check entire range succeeds. - EXPECT_TRUE(info->GetMaxValueForRange(0, 10, GL_UNSIGNED_INT, &max_value)); + EXPECT_TRUE( + buffer->GetMaxValueForRange(0, 10, GL_UNSIGNED_INT, &max_value)); EXPECT_EQ(10u, max_value); // Check non aligned offsets fails for GL_UNSIGNED_INT. - EXPECT_FALSE(info->GetMaxValueForRange(1, 10, GL_UNSIGNED_INT, &max_value)); - EXPECT_FALSE(info->GetMaxValueForRange(2, 10, GL_UNSIGNED_INT, &max_value)); - EXPECT_FALSE(info->GetMaxValueForRange(3, 10, GL_UNSIGNED_INT, &max_value)); + EXPECT_FALSE( + buffer->GetMaxValueForRange(1, 10, GL_UNSIGNED_INT, &max_value)); + EXPECT_FALSE( + buffer->GetMaxValueForRange(2, 10, GL_UNSIGNED_INT, &max_value)); + EXPECT_FALSE( + buffer->GetMaxValueForRange(3, 10, GL_UNSIGNED_INT, &max_value)); // Check sub range succeeds. - EXPECT_TRUE(info->GetMaxValueForRange(16, 3, GL_UNSIGNED_INT, &max_value)); + EXPECT_TRUE(buffer->GetMaxValueForRange(16, 3, GL_UNSIGNED_INT, &max_value)); EXPECT_EQ(6u, max_value); // Check changing sub range succeeds. - EXPECT_TRUE(info->SetRange(16, sizeof(new_data), new_data)); - EXPECT_TRUE(info->GetMaxValueForRange(16, 3, GL_UNSIGNED_INT, &max_value)); + EXPECT_TRUE(DoBufferSubData(buffer, 16, sizeof(new_data), new_data)); + EXPECT_TRUE(buffer->GetMaxValueForRange(16, 3, GL_UNSIGNED_INT, &max_value)); EXPECT_EQ(120u, max_value); max_value = 0; - EXPECT_TRUE(info->GetMaxValueForRange(0, 10, GL_UNSIGNED_INT, &max_value)); + EXPECT_TRUE(buffer->GetMaxValueForRange(0, 10, GL_UNSIGNED_INT, &max_value)); EXPECT_EQ(120u, max_value); // Check out of range fails. - EXPECT_FALSE(info->GetMaxValueForRange(0, 11, GL_UNSIGNED_INT, &max_value)); - EXPECT_FALSE(info->GetMaxValueForRange(40, 1, GL_UNSIGNED_INT, &max_value)); + EXPECT_FALSE( + buffer->GetMaxValueForRange(0, 11, GL_UNSIGNED_INT, &max_value)); + EXPECT_FALSE( + buffer->GetMaxValueForRange(40, 1, GL_UNSIGNED_INT, &max_value)); } TEST_F(BufferManagerTest, UseDeletedBuffer) { @@ -283,19 +359,34 @@ TEST_F(BufferManagerTest, UseDeletedBuffer) { const GLuint kServiceBufferId = 11; const uint32 data[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; manager_->CreateBuffer(kClientBufferId, kServiceBufferId); - scoped_refptr<Buffer> info = - manager_->GetBuffer(kClientBufferId); - ASSERT_TRUE(info != NULL); - manager_->SetTarget(info, GL_ARRAY_BUFFER); + scoped_refptr<Buffer> buffer = manager_->GetBuffer(kClientBufferId); + ASSERT_TRUE(buffer != NULL); + manager_->SetTarget(buffer, GL_ARRAY_BUFFER); // Remove buffer manager_->RemoveBuffer(kClientBufferId); // Use it after removing - manager_->SetInfo(info, sizeof(data), GL_STATIC_DRAW); + DoBufferData(buffer, sizeof(data), GL_STATIC_DRAW, NULL, GL_NO_ERROR); // Check that it gets deleted when the last reference is released. EXPECT_CALL(*gl_, DeleteBuffersARB(1, ::testing::Pointee(kServiceBufferId))) .Times(1) .RetiresOnSaturation(); - info = NULL; + buffer = NULL; +} + +// Test buffers get shadowed when they are supposed to be. +TEST_F(BufferManagerClientSideArraysTest, StreamBuffersAreShadowed) { + const GLuint kClientBufferId = 1; + const GLuint kServiceBufferId = 11; + static const uint32 data[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; + manager_->CreateBuffer(kClientBufferId, kServiceBufferId); + Buffer* buffer = manager_->GetBuffer(kClientBufferId); + ASSERT_TRUE(buffer != NULL); + manager_->SetTarget(buffer, GL_ARRAY_BUFFER); + DoBufferData(buffer, sizeof(data), GL_STREAM_DRAW, data, GL_NO_ERROR); + EXPECT_TRUE(buffer->IsClientSideArray()); + EXPECT_EQ(0, memcmp(data, buffer->GetRange(0, sizeof(data)), sizeof(data))); + DoBufferData(buffer, sizeof(data), GL_DYNAMIC_DRAW, data, GL_NO_ERROR); + EXPECT_FALSE(buffer->IsClientSideArray()); } } // namespace gles2 diff --git a/gpu/command_buffer/service/context_group.cc b/gpu/command_buffer/service/context_group.cc index abf262d..9f893e4 100644 --- a/gpu/command_buffer/service/context_group.cc +++ b/gpu/command_buffer/service/context_group.cc @@ -100,7 +100,8 @@ bool ContextGroup::Initialize( glGetIntegerv(GL_MAX_SAMPLES, &max_samples); } - buffer_manager_.reset(new BufferManager(memory_tracker_)); + buffer_manager_.reset(new BufferManager( + memory_tracker_, feature_info_.get())); framebuffer_manager_.reset(new FramebufferManager()); renderbuffer_manager_.reset(new RenderbufferManager(memory_tracker_, max_renderbuffer_size, diff --git a/gpu/command_buffer/service/feature_info.cc b/gpu/command_buffer/service/feature_info.cc index bde9f3e..3a60942 100644 --- a/gpu/command_buffer/service/feature_info.cc +++ b/gpu/command_buffer/service/feature_info.cc @@ -94,6 +94,7 @@ FeatureInfo::Workarounds::Workarounds() restore_scissor_on_fbo_change(false), flush_on_context_switch(false), delete_instead_of_resize_fbo(false), + use_client_side_arrays_for_stream_buffers(false), max_texture_size(0), max_cube_map_texture_size(0) { } @@ -168,6 +169,7 @@ void FeatureInfo::AddFeatures() { bool is_mesa = false; bool is_qualcomm = false; bool is_imagination = false; + bool is_arm = false; for (size_t ii = 0; ii < arraysize(string_ids); ++ii) { const char* str = reinterpret_cast<const char*>( glGetString(string_ids[ii])); @@ -180,6 +182,7 @@ void FeatureInfo::AddFeatures() { is_mesa |= string_set.Contains("mesa"); is_qualcomm |= string_set.Contains("qualcomm"); is_imagination |= string_set.Contains("imagination"); + is_arm |= string_set.Contains("arm"); } } @@ -191,7 +194,6 @@ void FeatureInfo::AddFeatures() { !CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableShaderNameHashing); - bool npot_ok = false; AddExtensionString("GL_ANGLE_translated_shader_source"); @@ -337,6 +339,19 @@ void FeatureInfo::AddFeatures() { feature_flags_.native_vertex_array_object = true; } + // If the driver doesn't like uploading lots of buffer data constantly + // work around it by using client side arrays. + if (is_arm || is_imagination) { + workarounds_.use_client_side_arrays_for_stream_buffers = true; + } + + // If we're using client_side_arrays we have to emulate + // vertex array objects since vertex array objects do not work + // with client side arrays. + if (workarounds_.use_client_side_arrays_for_stream_buffers) { + feature_flags_.native_vertex_array_object = false; + } + if (extensions.Contains("GL_OES_element_index_uint") || gfx::HasDesktopGLFeatures()) { AddExtensionString("GL_OES_element_index_uint"); diff --git a/gpu/command_buffer/service/feature_info.h b/gpu/command_buffer/service/feature_info.h index 2eec536..88f0671 100644 --- a/gpu/command_buffer/service/feature_info.h +++ b/gpu/command_buffer/service/feature_info.h @@ -54,6 +54,7 @@ class GPU_EXPORT FeatureInfo : public base::RefCounted<FeatureInfo> { bool restore_scissor_on_fbo_change; bool flush_on_context_switch; bool delete_instead_of_resize_fbo; + bool use_client_side_arrays_for_stream_buffers; // Note: 0 here means use driver limit. GLint max_texture_size; diff --git a/gpu/command_buffer/service/feature_info_unittest.cc b/gpu/command_buffer/service/feature_info_unittest.cc index 0edaa78..bf716ff 100644 --- a/gpu/command_buffer/service/feature_info_unittest.cc +++ b/gpu/command_buffer/service/feature_info_unittest.cc @@ -95,6 +95,7 @@ TEST_F(FeatureInfoTest, Basic) { EXPECT_FALSE(info_->workarounds().clear_alpha_in_readpixels); EXPECT_EQ(0, info_->workarounds().max_texture_size); EXPECT_EQ(0, info_->workarounds().max_cube_map_texture_size); + EXPECT_FALSE(info_->workarounds().use_client_side_arrays_for_stream_buffers); // Test good types. { @@ -799,5 +800,35 @@ TEST_F(FeatureInfoTest, InitializeOES_element_index_uint) { EXPECT_TRUE(info_->validators()->index_type.IsValid(GL_UNSIGNED_INT)); } +TEST_F(FeatureInfoTest, InitializeARM) { + SetupInitExpectationsWithVendor("", "ARM", "MAli-T604"); + info_->Initialize(NULL); + EXPECT_TRUE(info_->workarounds().use_client_side_arrays_for_stream_buffers); +} + +TEST_F(FeatureInfoTest, InitializeImagination) { + SetupInitExpectationsWithVendor( + "", "Imagination Techologies", "PowerVR SGX 540"); + info_->Initialize(NULL); + EXPECT_TRUE(info_->workarounds().use_client_side_arrays_for_stream_buffers); + EXPECT_FALSE(info_->feature_flags().native_vertex_array_object); +} + +TEST_F(FeatureInfoTest, InitializeARMVAOs) { + SetupInitExpectationsWithVendor( + "GL_OES_vertex_array_object", "ARM", "MAli-T604"); + info_->Initialize(NULL); + EXPECT_TRUE(info_->workarounds().use_client_side_arrays_for_stream_buffers); + EXPECT_FALSE(info_->feature_flags().native_vertex_array_object); +} + +TEST_F(FeatureInfoTest, InitializeImaginationVAOs) { + SetupInitExpectationsWithVendor( + "GL_OES_vertex_array_object", + "Imagination Techologies", "PowerVR SGX 540"); + info_->Initialize(NULL); + EXPECT_TRUE(info_->workarounds().use_client_side_arrays_for_stream_buffers); +} + } // 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 ed17725..8b183c0 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc @@ -1233,6 +1233,10 @@ class GLES2DecoderImpl : public GLES2Decoder { GLuint DoGetMaxValueInBufferCHROMIUM( GLuint buffer_id, GLsizei count, GLenum type, GLuint offset); + // Wrapper for glGetBufferParameteriv. + void DoGetBufferParameteriv( + GLenum target, GLenum pname, GLint* params); + // Wrapper for glGetProgramiv. void DoGetProgramiv( GLuint program_id, GLenum pname, GLint* params); @@ -4313,6 +4317,24 @@ void GLES2DecoderImpl::DoGetProgramiv( info->GetProgramiv(pname, params); } +void GLES2DecoderImpl::DoGetBufferParameteriv( + GLenum target, GLenum pname, GLint* params) { + Buffer* buffer = GetBufferInfoForTarget(target); + if (!buffer) { + return; + } + switch (pname) { + case GL_BUFFER_SIZE: + *params = buffer->size(); + break; + case GL_BUFFER_USAGE: + *params = buffer->usage(); + break; + default: + NOTREACHED(); + } +} + void GLES2DecoderImpl::DoBindAttribLocation( GLuint program, GLuint index, const char* name) { if (!StringIsValidForGLES(name)) { @@ -5737,54 +5759,13 @@ bool GLES2DecoderImpl::IsDrawValid( return false; } - // true if any enabled, used divisor is zero - bool divisor0 = false; - // Validate all attribs currently enabled. If they are used by the current - // program then check that they have enough elements to handle the draw call. - // If they are not used by the current program check that they have a buffer - // assigned. - const VertexAttribManager::VertexAttribInfoList& infos = - state_.vertex_attrib_manager->GetEnabledVertexAttribInfos(); - for (VertexAttribManager::VertexAttribInfoList::const_iterator it = - infos.begin(); it != infos.end(); ++it) { - const VertexAttrib* info = *it; - const Program::VertexAttrib* attrib_info = - state_.current_program->GetAttribInfoByLocation(info->index()); - if (attrib_info) { - divisor0 |= (info->divisor() == 0); - GLuint count = info->MaxVertexAccessed(primcount, max_vertex_accessed); - // This attrib is used in the current program. - if (!info->CanAccess(count)) { - SetGLError( - GL_INVALID_OPERATION, function_name, - (std::string( - "attempt to access out of range vertices in attribute ") + - base::IntToString(info->index())).c_str()); - return false; - } - } else { - // This attrib is not used in the current program. - if (!info->buffer()) { - SetGLError( - GL_INVALID_OPERATION, function_name, - (std::string( - "attempt to render with no buffer attached to " - "enabled attribute ") + - base::IntToString(info->index())).c_str()); - return false; - } - } - } - - if (primcount && !divisor0) { - SetGLError( - GL_INVALID_OPERATION, function_name, - "attempt instanced render with all attributes having " - "non-zero divisors"); - return false; - } - - return true; + return state_.vertex_attrib_manager->ValidateBindings( + function_name, + this, + feature_info_.get(), + state_.current_program, + max_vertex_accessed, + primcount); } bool GLES2DecoderImpl::SimulateAttrib0( @@ -6145,8 +6126,11 @@ error::Error GLES2DecoderImpl::DoDrawElements( } GLuint max_vertex_accessed; - if (!state_.vertex_attrib_manager->element_array_buffer()-> - GetMaxValueForRange(offset, count, type, &max_vertex_accessed)) { + Buffer* element_array_buffer = + state_.vertex_attrib_manager->element_array_buffer(); + + if (!element_array_buffer->GetMaxValueForRange( + offset, count, type, &max_vertex_accessed)) { SetGLError(GL_INVALID_OPERATION, function_name, "range out of bounds for buffer"); return error::kNoError; @@ -6168,12 +6152,27 @@ error::Error GLES2DecoderImpl::DoDrawElements( primcount)) { bool textures_set = SetBlackTextureForNonRenderableTextures(); ApplyDirtyState(); + // TODO(gman): Refactor to hide these details in BufferManager or + // VertexAttribManager. const GLvoid* indices = reinterpret_cast<const GLvoid*>(offset); + bool used_client_side_array = false; + if (element_array_buffer->IsClientSideArray()) { + used_client_side_array = true; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + indices = element_array_buffer->GetRange(offset, 0); + } + if (!instanced) { glDrawElements(mode, count, type, indices); } else { glDrawElementsInstancedANGLE(mode, count, type, indices, primcount); } + + if (used_client_side_array) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, + element_array_buffer->service_id()); + } + ProcessPendingQueries(); if (textures_set) { RestoreStateForNonRenderableTextures(); @@ -7191,8 +7190,8 @@ void GLES2DecoderImpl::DoBufferData( SetGLError(GL_INVALID_VALUE, "glBufferData", "size < 0"); return; } - Buffer* info = GetBufferInfoForTarget(target); - if (!info) { + Buffer* buffer = GetBufferInfoForTarget(target); + if (!buffer) { SetGLError(GL_INVALID_VALUE, "glBufferData", "unknown buffer"); return; } @@ -7202,23 +7201,7 @@ void GLES2DecoderImpl::DoBufferData( return; } - // Clear the buffer to 0 if no initial data was passed in. - scoped_array<int8> zero; - if (!data) { - zero.reset(new int8[size]); - memset(zero.get(), 0, size); - data = zero.get(); - } - - CopyRealGLErrorsToWrapper(); - glBufferData(target, size, data, usage); - GLenum error = PeekGLError(); - if (error == GL_NO_ERROR) { - buffer_manager()->SetInfo(info, size, usage); - info->SetRange(0, size, data); - } else { - buffer_manager()->SetInfo(info, 0, usage); - } + buffer_manager()->DoBufferData(this, buffer, size, usage, data); } error::Error GLES2DecoderImpl::HandleBufferData( @@ -7255,16 +7238,13 @@ error::Error GLES2DecoderImpl::HandleBufferDataImmediate( void GLES2DecoderImpl::DoBufferSubData( GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid * data) { - Buffer* info = GetBufferInfoForTarget(target); - if (!info) { + Buffer* buffer = GetBufferInfoForTarget(target); + if (!buffer) { SetGLError(GL_INVALID_VALUE, "glBufferSubData", "unknown buffer"); return; } - if (!info->SetRange(offset, size, data)) { - SetGLError(GL_INVALID_VALUE, "glBufferSubData", "out of range"); - return; - } - glBufferSubData(target, offset, size, data); + + buffer_manager()->DoBufferSubData(this, buffer, offset, size, data); } bool GLES2DecoderImpl::ClearLevel( diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h index 2263a7f..d06d652 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h @@ -975,11 +975,11 @@ error::Error GLES2DecoderImpl::HandleGetBooleanv( if (params == NULL) { return error::kOutOfBounds; } + CopyRealGLErrorsToWrapper(); // Check that the client initialized the result. if (result->size != 0) { return error::kInvalidArguments; } - CopyRealGLErrorsToWrapper(); DoGetBooleanv(pname, params); GLenum error = glGetError(); if (error == GL_NO_ERROR) { @@ -1015,17 +1015,10 @@ error::Error GLES2DecoderImpl::HandleGetBufferParameteriv( if (result->size != 0) { return error::kInvalidArguments; } - CopyRealGLErrorsToWrapper(); - glGetBufferParameteriv(target, pname, params); - GLenum error = glGetError(); - if (error == GL_NO_ERROR) { - result->SetNumResults(num_values); - } else { - SetGLError(error, "", ""); - } + DoGetBufferParameteriv(target, pname, params); + result->SetNumResults(num_values); return error::kNoError; } - error::Error GLES2DecoderImpl::HandleGetError( uint32 immediate_data_size, const gles2::cmds::GetError& c) { typedef cmds::GetError::Result Result; @@ -1054,11 +1047,11 @@ error::Error GLES2DecoderImpl::HandleGetFloatv( if (params == NULL) { return error::kOutOfBounds; } + CopyRealGLErrorsToWrapper(); // Check that the client initialized the result. if (result->size != 0) { return error::kInvalidArguments; } - CopyRealGLErrorsToWrapper(); DoGetFloatv(pname, params); GLenum error = glGetError(); if (error == GL_NO_ERROR) { @@ -1099,11 +1092,11 @@ error::Error GLES2DecoderImpl::HandleGetFramebufferAttachmentParameteriv( if (params == NULL) { return error::kOutOfBounds; } + CopyRealGLErrorsToWrapper(); // Check that the client initialized the result. if (result->size != 0) { return error::kInvalidArguments; } - CopyRealGLErrorsToWrapper(); DoGetFramebufferAttachmentParameteriv(target, attachment, pname, params); GLenum error = glGetError(); if (error == GL_NO_ERROR) { @@ -1130,11 +1123,11 @@ error::Error GLES2DecoderImpl::HandleGetIntegerv( if (params == NULL) { return error::kOutOfBounds; } + CopyRealGLErrorsToWrapper(); // Check that the client initialized the result. if (result->size != 0) { return error::kInvalidArguments; } - CopyRealGLErrorsToWrapper(); DoGetIntegerv(pname, params); GLenum error = glGetError(); if (error == GL_NO_ERROR) { @@ -1162,11 +1155,11 @@ error::Error GLES2DecoderImpl::HandleGetProgramiv( if (params == NULL) { return error::kOutOfBounds; } + CopyRealGLErrorsToWrapper(); // Check that the client initialized the result. if (result->size != 0) { return error::kInvalidArguments; } - CopyRealGLErrorsToWrapper(); DoGetProgramiv(program, pname, params); GLenum error = glGetError(); if (error == GL_NO_ERROR) { @@ -1199,11 +1192,11 @@ error::Error GLES2DecoderImpl::HandleGetRenderbufferParameteriv( if (params == NULL) { return error::kOutOfBounds; } + CopyRealGLErrorsToWrapper(); // Check that the client initialized the result. if (result->size != 0) { return error::kInvalidArguments; } - CopyRealGLErrorsToWrapper(); DoGetRenderbufferParameteriv(target, pname, params); GLenum error = glGetError(); if (error == GL_NO_ERROR) { @@ -1231,11 +1224,11 @@ error::Error GLES2DecoderImpl::HandleGetShaderiv( if (params == NULL) { return error::kOutOfBounds; } + CopyRealGLErrorsToWrapper(); // Check that the client initialized the result. if (result->size != 0) { return error::kInvalidArguments; } - CopyRealGLErrorsToWrapper(); DoGetShaderiv(shader, pname, params); GLenum error = glGetError(); if (error == GL_NO_ERROR) { @@ -1267,11 +1260,11 @@ error::Error GLES2DecoderImpl::HandleGetTexParameterfv( if (params == NULL) { return error::kOutOfBounds; } + CopyRealGLErrorsToWrapper(); // Check that the client initialized the result. if (result->size != 0) { return error::kInvalidArguments; } - CopyRealGLErrorsToWrapper(); glGetTexParameterfv(target, pname, params); GLenum error = glGetError(); if (error == GL_NO_ERROR) { @@ -1303,11 +1296,11 @@ error::Error GLES2DecoderImpl::HandleGetTexParameteriv( if (params == NULL) { return error::kOutOfBounds; } + CopyRealGLErrorsToWrapper(); // Check that the client initialized the result. if (result->size != 0) { return error::kInvalidArguments; } - CopyRealGLErrorsToWrapper(); glGetTexParameteriv(target, pname, params); GLenum error = glGetError(); if (error == GL_NO_ERROR) { @@ -1335,11 +1328,11 @@ error::Error GLES2DecoderImpl::HandleGetVertexAttribfv( if (params == NULL) { return error::kOutOfBounds; } + CopyRealGLErrorsToWrapper(); // Check that the client initialized the result. if (result->size != 0) { return error::kInvalidArguments; } - CopyRealGLErrorsToWrapper(); DoGetVertexAttribfv(index, pname, params); GLenum error = glGetError(); if (error == GL_NO_ERROR) { @@ -1367,11 +1360,11 @@ error::Error GLES2DecoderImpl::HandleGetVertexAttribiv( if (params == NULL) { return error::kOutOfBounds; } + CopyRealGLErrorsToWrapper(); // Check that the client initialized the result. if (result->size != 0) { return error::kInvalidArguments; } - CopyRealGLErrorsToWrapper(); DoGetVertexAttribiv(index, pname, params); GLenum error = glGetError(); if (error == GL_NO_ERROR) { 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 fc77e17..bb4b8c8 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 @@ -1172,16 +1172,9 @@ TEST_F(GLES2DecoderTest1, GetBooleanvInvalidArgs1_1) { } TEST_F(GLES2DecoderTest1, GetBufferParameterivValidArgs) { - EXPECT_CALL(*gl_, GetError()) - .WillOnce(Return(GL_NO_ERROR)) - .WillOnce(Return(GL_NO_ERROR)) - .RetiresOnSaturation(); SpecializedSetup<cmds::GetBufferParameteriv, 0>(true); typedef cmds::GetBufferParameteriv::Result Result; Result* result = static_cast<Result*>(shared_memory_address_); - EXPECT_CALL( - *gl_, GetBufferParameteriv( - GL_ARRAY_BUFFER, GL_BUFFER_SIZE, result->GetData())); result->size = 0; cmds::GetBufferParameteriv cmd; cmd.Init( diff --git a/gpu/command_buffer/service/query_manager_unittest.cc b/gpu/command_buffer/service/query_manager_unittest.cc index 5d273fa..28e6e08 100644 --- a/gpu/command_buffer/service/query_manager_unittest.cc +++ b/gpu/command_buffer/service/query_manager_unittest.cc @@ -98,7 +98,7 @@ class QueryManagerTest : public testing::Test { virtual ~MockCommandBufferEngine() { } - virtual Buffer GetSharedMemoryBuffer(int32 shm_id) OVERRIDE { + virtual gpu::Buffer GetSharedMemoryBuffer(int32 shm_id) OVERRIDE { return shm_id == kSharedMemoryId ? valid_buffer_ : invalid_buffer_; } @@ -129,8 +129,8 @@ class QueryManagerTest : public testing::Test { private: scoped_array<int8> data_; - Buffer valid_buffer_; - Buffer invalid_buffer_; + gpu::Buffer valid_buffer_; + gpu::Buffer invalid_buffer_; }; scoped_ptr<MockCommandBufferEngine> engine_; diff --git a/gpu/command_buffer/service/test_helper.cc b/gpu/command_buffer/service/test_helper.cc index 13cc469..2cbef21 100644 --- a/gpu/command_buffer/service/test_helper.cc +++ b/gpu/command_buffer/service/test_helper.cc @@ -10,7 +10,9 @@ #include "base/string_number_conversions.h" #include "base/strings/string_tokenizer.h" #include "gpu/command_buffer/common/types.h" +#include "gpu/command_buffer/service/buffer_manager.h" #include "gpu/command_buffer/service/gl_utils.h" +#include "gpu/command_buffer/service/gles2_cmd_decoder_mock.h" #include "gpu/command_buffer/service/program_manager.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gl/gl_mock.h" @@ -491,6 +493,30 @@ void TestHelper::SetupShader( gl, attribs, num_attribs, uniforms, num_uniforms, service_id); } +void TestHelper::DoBufferData( + ::gfx::MockGLInterface* gl, MockGLES2Decoder* decoder, + BufferManager* manager, Buffer* buffer, GLsizeiptr size, GLenum usage, + const GLvoid* data, GLenum error) { + EXPECT_CALL(*decoder, CopyRealGLErrorsToWrapper()) + .Times(1) + .RetiresOnSaturation(); + if (manager->IsUsageClientSideArray(usage)) { + EXPECT_CALL(*gl, BufferData( + buffer->target(), 0, _, usage)) + .Times(1) + .RetiresOnSaturation(); + } else { + EXPECT_CALL(*gl, BufferData( + buffer->target(), size, _, usage)) + .Times(1) + .RetiresOnSaturation(); + } + EXPECT_CALL(*decoder, PeekGLError()) + .WillOnce(Return(error)) + .RetiresOnSaturation(); + manager->DoBufferData(decoder, buffer, size, usage, data); +} + } // namespace gles2 } // namespace gpu diff --git a/gpu/command_buffer/service/test_helper.h b/gpu/command_buffer/service/test_helper.h index 86354c6..a97cf74 100644 --- a/gpu/command_buffer/service/test_helper.h +++ b/gpu/command_buffer/service/test_helper.h @@ -11,6 +11,9 @@ namespace gpu { namespace gles2 { struct DisallowedFeatures; +class Buffer; +class BufferManager; +class MockGLES2Decoder; class TestHelper { public: @@ -86,6 +89,11 @@ class TestHelper { UniformInfo* uniforms, size_t num_uniforms, GLuint service_id); + static void DoBufferData( + ::gfx::MockGLInterface* gl, MockGLES2Decoder* decoder, + BufferManager* manager, Buffer* buffer, GLsizeiptr size, GLenum usage, + const GLvoid* data, GLenum error); + private: static void SetupTextureInitializationExpectations( ::gfx::MockGLInterface* gl, GLenum target); diff --git a/gpu/command_buffer/service/vertex_attrib_manager.cc b/gpu/command_buffer/service/vertex_attrib_manager.cc index 50c2dec..fe346ab 100644 --- a/gpu/command_buffer/service/vertex_attrib_manager.cc +++ b/gpu/command_buffer/service/vertex_attrib_manager.cc @@ -9,13 +9,17 @@ #include "base/command_line.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "base/string_number_conversions.h" #include "build/build_config.h" #define GLES2_GPU_SERVICE 1 #include "gpu/command_buffer/common/gles2_cmd_format.h" #include "gpu/command_buffer/common/gles2_cmd_utils.h" #include "gpu/command_buffer/service/buffer_manager.h" +#include "gpu/command_buffer/service/feature_info.h" #include "gpu/command_buffer/service/gl_utils.h" +#include "gpu/command_buffer/service/gles2_cmd_decoder.h" #include "gpu/command_buffer/service/gpu_switches.h" +#include "gpu/command_buffer/service/program_manager.h" #include "gpu/command_buffer/service/vertex_array_manager.h" namespace gpu { @@ -31,6 +35,7 @@ VertexAttrib::VertexAttrib() gl_stride_(0), real_stride_(16), divisor_(0), + is_client_side_array_(false), list_(NULL) { } @@ -154,5 +159,114 @@ void VertexAttribManager::Unbind(Buffer* buffer) { } } +bool VertexAttribManager::ValidateBindings( + const char* function_name, + GLES2Decoder* decoder, + FeatureInfo* feature_info, + Program* current_program, + GLuint max_vertex_accessed, + GLsizei primcount) { + // true if any enabled, used divisor is zero + bool divisor0 = false; + const GLuint kInitialBufferId = 0xFFFFFFFFU; + GLuint current_buffer_id = kInitialBufferId; + bool use_client_side_arrays_for_stream_buffers = feature_info->workarounds( + ).use_client_side_arrays_for_stream_buffers; + // Validate all attribs currently enabled. If they are used by the current + // program then check that they have enough elements to handle the draw call. + // If they are not used by the current program check that they have a buffer + // assigned. + for (VertexAttribInfoList::iterator it = enabled_vertex_attribs_.begin(); + it != enabled_vertex_attribs_.end(); ++it) { + VertexAttrib* attrib = *it; + const Program::VertexAttrib* attrib_info = + current_program->GetAttribInfoByLocation(attrib->index()); + if (attrib_info) { + divisor0 |= (attrib->divisor() == 0); + GLuint count = attrib->MaxVertexAccessed(primcount, max_vertex_accessed); + // This attrib is used in the current program. + if (!attrib->CanAccess(count)) { + decoder->SetGLError( + GL_INVALID_OPERATION, function_name, + (std::string( + "attempt to access out of range vertices in attribute ") + + base::IntToString(attrib->index())).c_str()); + return false; + } + if (use_client_side_arrays_for_stream_buffers) { + Buffer* buffer = attrib->buffer(); + glEnableVertexAttribArray(attrib->index()); + if (buffer->IsClientSideArray()) { + if (current_buffer_id != 0) { + current_buffer_id = 0; + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + attrib->set_is_client_side_array(true); + const void* ptr = buffer->GetRange(attrib->offset(), 0); + DCHECK(ptr); + glVertexAttribPointer( + attrib->index(), + attrib->size(), + attrib->type(), + attrib->normalized(), + attrib->gl_stride(), + ptr); + } else if (attrib->is_client_side_array()) { + attrib->set_is_client_side_array(false); + GLuint new_buffer_id = buffer->service_id(); + if (new_buffer_id != current_buffer_id) { + current_buffer_id = new_buffer_id; + glBindBuffer(GL_ARRAY_BUFFER, current_buffer_id); + } + const void* ptr = reinterpret_cast<const void*>(attrib->offset()); + glVertexAttribPointer( + attrib->index(), + attrib->size(), + attrib->type(), + attrib->normalized(), + attrib->gl_stride(), + ptr); + } + } + } else { + // This attrib is not used in the current program. + if (!attrib->buffer()) { + decoder->SetGLError( + GL_INVALID_OPERATION, function_name, + (std::string( + "attempt to render with no buffer attached to " + "enabled attribute ") + + base::IntToString(attrib->index())).c_str()); + return false; + } else if (use_client_side_arrays_for_stream_buffers) { + Buffer* buffer = attrib->buffer(); + // Disable client side arrays for unused attributes else we'll + // read bad memory + if (buffer->IsClientSideArray()) { + // Don't disable attrib 0 since it's special. + if (attrib->index() > 0) { + glDisableVertexAttribArray(attrib->index()); + } + } + } + } + } + + if (primcount && !divisor0) { + decoder->SetGLError( + GL_INVALID_OPERATION, function_name, + "attempt instanced render with all attributes having " + "non-zero divisors"); + return false; + } + + if (current_buffer_id != kInitialBufferId) { + // Restore the buffer binding. + decoder->RestoreBufferBindings(); + } + + return true; +} + } // namespace gles2 } // namespace gpu diff --git a/gpu/command_buffer/service/vertex_attrib_manager.h b/gpu/command_buffer/service/vertex_attrib_manager.h index 76c75c8..ddd29be 100644 --- a/gpu/command_buffer/service/vertex_attrib_manager.h +++ b/gpu/command_buffer/service/vertex_attrib_manager.h @@ -17,6 +17,9 @@ namespace gpu { namespace gles2 { +class FeatureInfo; +class GLES2Decoder; +class Program; class VertexArrayManager; // Info about a Vertex Attribute. This is used to track what the user currently @@ -75,6 +78,14 @@ class GPU_EXPORT VertexAttrib { max_vertex_accessed; } + bool is_client_side_array() const { + return is_client_side_array_; + } + + void set_is_client_side_array(bool value) { + is_client_side_array_ = value; + } + private: friend class VertexAttribManager; @@ -139,6 +150,9 @@ class GPU_EXPORT VertexAttrib { GLsizei divisor_; + // Will be true if this was assigned to a client side array. + bool is_client_side_array_; + // The buffer bound to this attribute. scoped_refptr<Buffer> buffer_; @@ -231,6 +245,14 @@ class GPU_EXPORT VertexAttribManager : return vertex_attrib_infos_.size(); } + bool ValidateBindings( + const char* function_name, + GLES2Decoder* decoder, + FeatureInfo* feature_info, + Program* current_program, + GLuint max_vertex_accessed, + GLsizei primcount); + private: friend class VertexArrayManager; friend class VertexArrayManagerTest; diff --git a/gpu/command_buffer/service/vertex_attrib_manager_unittest.cc b/gpu/command_buffer/service/vertex_attrib_manager_unittest.cc index 3ce938c..71a694c 100644 --- a/gpu/command_buffer/service/vertex_attrib_manager_unittest.cc +++ b/gpu/command_buffer/service/vertex_attrib_manager_unittest.cc @@ -7,6 +7,7 @@ #include "base/memory/scoped_ptr.h" #include "gpu/command_buffer/service/buffer_manager.h" #include "gpu/command_buffer/service/feature_info.h" +#include "gpu/command_buffer/service/gles2_cmd_decoder_mock.h" #include "gpu/command_buffer/service/test_helper.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gl/gl_mock.h" @@ -109,7 +110,7 @@ TEST_F(VertexAttribManagerTest, Enable) { } TEST_F(VertexAttribManagerTest, SetAttribInfo) { - BufferManager buffer_manager(NULL); + BufferManager buffer_manager(NULL, NULL); buffer_manager.CreateBuffer(1, 2); Buffer* buffer = buffer_manager.GetBuffer(1); ASSERT_TRUE(buffer != NULL); @@ -145,7 +146,8 @@ TEST_F(VertexAttribManagerTest, HaveFixedAttribs) { } TEST_F(VertexAttribManagerTest, CanAccess) { - BufferManager buffer_manager(NULL); + MockGLES2Decoder decoder; + BufferManager buffer_manager(NULL, NULL); buffer_manager.CreateBuffer(1, 2); Buffer* buffer = buffer_manager.GetBuffer(1); ASSERT_TRUE(buffer != NULL); @@ -161,17 +163,23 @@ TEST_F(VertexAttribManagerTest, CanAccess) { EXPECT_FALSE(info->CanAccess(0)); EXPECT_TRUE(buffer_manager.SetTarget(buffer, GL_ARRAY_BUFFER)); - buffer_manager.SetInfo(buffer, 15, GL_STATIC_DRAW); + TestHelper::DoBufferData( + gl_.get(), &decoder, &buffer_manager, buffer, 15, GL_STATIC_DRAW, NULL, + GL_NO_ERROR); EXPECT_FALSE(info->CanAccess(0)); - buffer_manager.SetInfo(buffer, 16, GL_STATIC_DRAW); + TestHelper::DoBufferData( + gl_.get(), &decoder, &buffer_manager, buffer, 16, GL_STATIC_DRAW, NULL, + GL_NO_ERROR); EXPECT_TRUE(info->CanAccess(0)); EXPECT_FALSE(info->CanAccess(1)); manager_->SetAttribInfo(1, buffer, 4, GL_FLOAT, GL_FALSE, 0, 16, 1); EXPECT_FALSE(info->CanAccess(0)); - buffer_manager.SetInfo(buffer, 32, GL_STATIC_DRAW); + TestHelper::DoBufferData( + gl_.get(), &decoder, &buffer_manager, buffer, 32, GL_STATIC_DRAW, NULL, + GL_NO_ERROR); EXPECT_TRUE(info->CanAccess(0)); EXPECT_FALSE(info->CanAccess(1)); manager_->SetAttribInfo(1, buffer, 4, GL_FLOAT, GL_FALSE, 0, 16, 0); @@ -187,7 +195,7 @@ TEST_F(VertexAttribManagerTest, CanAccess) { } TEST_F(VertexAttribManagerTest, Unbind) { - BufferManager buffer_manager(NULL); + BufferManager buffer_manager(NULL, NULL); buffer_manager.CreateBuffer(1, 2); buffer_manager.CreateBuffer(3, 4); Buffer* buffer1 = buffer_manager.GetBuffer(1); @@ -223,6 +231,9 @@ TEST_F(VertexAttribManagerTest, Unbind) { buffer_manager.Destroy(false); } +// TODO(gman): Test ValidateBindings +// TODO(gman): Test ValidateBindings with client side arrays. + } // namespace gles2 } // namespace gpu diff --git a/gpu/command_buffer/tests/gl_stream_draw_unittests.cc b/gpu/command_buffer/tests/gl_stream_draw_unittests.cc new file mode 100644 index 0000000..3997016 --- /dev/null +++ b/gpu/command_buffer/tests/gl_stream_draw_unittests.cc @@ -0,0 +1,157 @@ +// Copyright (c) 2013 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. + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include "gpu/command_buffer/tests/gl_manager.h" +#include "gpu/command_buffer/tests/gl_test_utils.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + + +#define SHADER(Src) #Src + +namespace gpu { + +class GLStreamDrawTest : public testing::Test { + protected: + static const int kSize = 4; + + virtual void SetUp() { + GLManager::Options options; + options.size = gfx::Size(kSize, kSize); + gl_.Initialize(options); + } + + virtual void TearDown() { + gl_.Destroy(); + } + + GLManager gl_; +}; + +namespace { + +GLuint SetupProgram() { + static const char* v_shader_str = SHADER( + attribute vec4 a_position; + attribute vec4 a_color; + varying vec4 v_color; + void main() + { + gl_Position = a_position; + v_color = a_color; + } + ); + + static const char* f_shader_str = SHADER( + precision mediump float; + varying vec4 v_color; + void main() + { + gl_FragColor = v_color; + } + ); + + GLuint program = GLTestHelper::LoadProgram(v_shader_str, f_shader_str); + glUseProgram(program); + return program; +} + +} // anonymous namespace. + +TEST_F(GLStreamDrawTest, Basic) { + static GLfloat float_red[4] = { 1.0f, 0.0f, 0.0f, 1.0f, }; + static GLfloat float_green[4] = { 0.0f, 1.0f, 0.0f, 1.0f, }; + static uint8 expected_red[4] = {255, 0, 0, 255, }; + static uint8 expected_green[4] = {0, 255, 0, 255, }; + + GLuint program = SetupProgram(); + GLuint position_loc = glGetAttribLocation(program, "a_position"); + GLuint color_loc = glGetAttribLocation(program, "a_color"); + GLTestHelper::SetupUnitQuad(position_loc); + GLTestHelper::SetupColorsForUnitQuad(color_loc, float_red, GL_STREAM_DRAW); + glDrawArrays(GL_TRIANGLES, 0, 6); + EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, kSize, kSize, 0, expected_red)); + GLTestHelper::SetupColorsForUnitQuad(color_loc, float_green, GL_STATIC_DRAW); + glDrawArrays(GL_TRIANGLES, 0, 6); + EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, kSize, kSize, 0, expected_green)); + + GLTestHelper::CheckGLError("no errors", __LINE__); +} + +TEST_F(GLStreamDrawTest, DrawElements) { + static GLfloat float_red[4] = { 1.0f, 0.0f, 0.0f, 1.0f, }; + static GLfloat float_green[4] = { 0.0f, 1.0f, 0.0f, 1.0f, }; + static uint8 expected_red[4] = {255, 0, 0, 255, }; + static uint8 expected_green[4] = {0, 255, 0, 255, }; + + GLuint program = SetupProgram(); + GLuint position_loc = glGetAttribLocation(program, "a_position"); + GLuint color_loc = glGetAttribLocation(program, "a_color"); + GLTestHelper::SetupUnitQuad(position_loc); + GLTestHelper::SetupColorsForUnitQuad(color_loc, float_red, GL_STREAM_DRAW); + + GLuint index_buffer = 0; + glGenBuffers(1, &index_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer); + static GLubyte indices[] = { 0, 1, 2, 3, 4, 5, }; + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STREAM_DRAW); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, NULL); + EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, kSize, kSize, 0, expected_red)); + GLTestHelper::SetupColorsForUnitQuad(color_loc, float_green, GL_STATIC_DRAW); + + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, NULL); + EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, kSize, kSize, 0, expected_green)); + + GLTestHelper::CheckGLError("no errors", __LINE__); +} + +TEST_F(GLStreamDrawTest, VertexArrayObjects) { + if (!GLTestHelper::HasExtension("GL_OES_vertex_array_object")) { + return; + } + + static GLfloat float_red[4] = { 1.0f, 0.0f, 0.0f, 1.0f, }; + static GLfloat float_green[4] = { 0.0f, 1.0f, 0.0f, 1.0f, }; + static uint8 expected_red[4] = {255, 0, 0, 255, }; + static uint8 expected_green[4] = {0, 255, 0, 255, }; + + GLuint program = SetupProgram(); + GLuint position_loc = glGetAttribLocation(program, "a_position"); + GLuint color_loc = glGetAttribLocation(program, "a_color"); + + GLuint vaos[2]; + glGenVertexArraysOES(2, vaos); + + glBindVertexArrayOES(vaos[0]); + GLuint position_buffer = GLTestHelper::SetupUnitQuad(position_loc); + GLTestHelper::SetupColorsForUnitQuad(color_loc, float_red, GL_STREAM_DRAW); + + glBindVertexArrayOES(vaos[1]); + glBindBuffer(GL_ARRAY_BUFFER, position_buffer); + glEnableVertexAttribArray(position_loc); + glVertexAttribPointer(position_loc, 2, GL_FLOAT, GL_FALSE, 0, 0); + GLTestHelper::SetupColorsForUnitQuad(color_loc, float_green, GL_STATIC_DRAW); + + for (int ii = 0; ii < 2; ++ii) { + glBindVertexArrayOES(vaos[0]); + glDrawArrays(GL_TRIANGLES, 0, 6); + EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, kSize, kSize, 0, expected_red)); + + glBindVertexArrayOES(vaos[1]); + glDrawArrays(GL_TRIANGLES, 0, 6); + EXPECT_TRUE( + GLTestHelper::CheckPixels(0, 0, kSize, kSize, 0, expected_green)); + } + + GLTestHelper::CheckGLError("no errors", __LINE__); +} + +} // namespace gpu + diff --git a/gpu/command_buffer/tests/gl_test_utils.cc b/gpu/command_buffer/tests/gl_test_utils.cc index 1a3e7c2..4b87f1e 100644 --- a/gpu/command_buffer/tests/gl_test_utils.cc +++ b/gpu/command_buffer/tests/gl_test_utils.cc @@ -108,6 +108,24 @@ GLuint GLTestHelper::SetupUnitQuad(GLint position_location) { return vbo; } +GLuint GLTestHelper::SetupColorsForUnitQuad( + GLint location, const GLfloat color[4], GLenum usage) { + GLuint vbo = 0; + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + GLfloat vertices[6 * 4]; + for (int ii = 0; ii < 6; ++ii) { + for (int jj = 0; jj < 4; ++jj) { + vertices[ii * 4 + jj] = color[jj]; + } + } + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, usage); + glEnableVertexAttribArray(location); + glVertexAttribPointer(location, 4, GL_FLOAT, GL_FALSE, 0, 0); + + return vbo; +} + bool GLTestHelper::CheckPixels( GLint x, GLint y, GLsizei width, GLsizei height, GLint tolerance, const uint8* color) { diff --git a/gpu/command_buffer/tests/gl_test_utils.h b/gpu/command_buffer/tests/gl_test_utils.h index 31de7cd..972ea0a 100644 --- a/gpu/command_buffer/tests/gl_test_utils.h +++ b/gpu/command_buffer/tests/gl_test_utils.h @@ -35,6 +35,11 @@ class GLTestHelper { // Returns the created buffer. static GLuint SetupUnitQuad(GLint position_location); + // Make a 6 vertex colors. + // Returns the created buffer. + static GLuint SetupColorsForUnitQuad( + GLint location, const GLfloat color[4], GLenum usage); + // Checks an area of pixels for a color. static bool CheckPixels( GLint x, GLint y, GLsizei width, GLsizei height, GLint tolerance, diff --git a/gpu/gpu.gyp b/gpu/gpu.gyp index e6e9868..005573d 100644 --- a/gpu/gpu.gyp +++ b/gpu/gpu.gyp @@ -261,6 +261,7 @@ 'command_buffer/tests/gl_texture_storage_unittests.cc', 'command_buffer/tests/gl_unittests.cc', 'command_buffer/tests/gl_virtual_contexts_unittests.cc', + 'command_buffer/tests/gl_stream_draw_unittests.cc', 'command_buffer/tests/occlusion_query_unittests.cc', ], # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. |