diff options
author | gman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-06 13:06:50 +0000 |
---|---|---|
committer | gman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-06 13:06:50 +0000 |
commit | 790aae08d87e585e3efe036f9e2e1fe7a5e501e4 (patch) | |
tree | 22d2be6f6f0813c8844e16c381ed4bf87bfc8666 /gpu/command_buffer | |
parent | 80d88e55fda2fe9546bc1b0d135f3c989afb91f4 (diff) | |
download | chromium_src-790aae08d87e585e3efe036f9e2e1fe7a5e501e4.zip chromium_src-790aae08d87e585e3efe036f9e2e1fe7a5e501e4.tar.gz chromium_src-790aae08d87e585e3efe036f9e2e1fe7a5e501e4.tar.bz2 |
Use client side arrays for GL_STREAM_DRAW attributes
Certain GPU/drivers are slow when using constantly changing
vertex buffers. They also run out of memory as the pipeline
the buffers so while a buffer is in used being drawn to they
can't delete it immediately when you upload new data to the
buffer.
This is an attempt to work around that issue seemlessly by
using client side arrays for buffers marked as GL_STREAM_DRAW
BUG=178093
Review URL: https://chromiumcodereview.appspot.com/12494005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@186416 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gpu/command_buffer')
21 files changed, 834 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, |