diff options
author | jadahl@opera.com <jadahl@opera.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-31 09:07:02 +0000 |
---|---|---|
committer | jadahl@opera.com <jadahl@opera.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-31 09:07:02 +0000 |
commit | e3c4a9abad01205eed3e7556158dc05800051f7b (patch) | |
tree | 20dcee8098386d67ed3d9322f230b08e74b451fb /gpu/command_buffer/client | |
parent | 15770a4144bf8def5cd689351db58a842f2535a1 (diff) | |
download | chromium_src-e3c4a9abad01205eed3e7556158dc05800051f7b.zip chromium_src-e3c4a9abad01205eed3e7556158dc05800051f7b.tar.gz chromium_src-e3c4a9abad01205eed3e7556158dc05800051f7b.tar.bz2 |
gpu: Reuse transfer buffers more aggresively
By keeping track of transfer buffer usage (both sync and async), it is
possible to reuse an existing transfer buffer earlier than it is today.
For synchronous uploads, this is done by inserting tokens marking a
buffer's last usage. If such a token has passed, the buffer can be
reused.
For asynchronous uploads, this is done by adding an internal async
upload token to the GLES2Implementation and GLES2CmdDecoderImpl that
enumerates all asynchronous uploads. When an upload is completed, the
token is synchronized with the client which then can free the memory
associated with the async token.
The fenced allocator and mapped memory manager gets a callback that is
used to allow fenced allocator to make the user, in this case
GLES2Implementation, poll the current async upload token and free any
associated memory.
BUG=328808
Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=260177
Review URL: https://codereview.chromium.org/116863003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@260507 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gpu/command_buffer/client')
21 files changed, 500 insertions, 60 deletions
diff --git a/gpu/command_buffer/client/buffer_tracker.cc b/gpu/command_buffer/client/buffer_tracker.cc index 18cedb9..5887e52 100644 --- a/gpu/command_buffer/client/buffer_tracker.cc +++ b/gpu/command_buffer/client/buffer_tracker.cc @@ -60,8 +60,30 @@ void BufferTracker::FreePendingToken(Buffer* buffer, int32 token) { buffer->shm_id_ = 0; buffer->shm_offset_ = 0; buffer->address_ = NULL; + buffer->last_usage_token_ = 0; + buffer->last_async_upload_token_ = 0; } +void BufferTracker::Unmanage(Buffer* buffer) { + buffer->size_ = 0; + buffer->shm_id_ = 0; + buffer->shm_offset_ = 0; + buffer->address_ = NULL; + buffer->last_usage_token_ = 0; + buffer->last_async_upload_token_ = 0; +} + +void BufferTracker::Free(Buffer* buffer) { + if (buffer->address_) + mapped_memory_->Free(buffer->address_); + + buffer->size_ = 0; + buffer->shm_id_ = 0; + buffer->shm_offset_ = 0; + buffer->address_ = NULL; + buffer->last_usage_token_ = 0; + buffer->last_async_upload_token_ = 0; +} } // namespace gles2 } // namespace gpu diff --git a/gpu/command_buffer/client/buffer_tracker.h b/gpu/command_buffer/client/buffer_tracker.h index 3e50364..33bd94b 100644 --- a/gpu/command_buffer/client/buffer_tracker.h +++ b/gpu/command_buffer/client/buffer_tracker.h @@ -35,7 +35,8 @@ class GLES2_IMPL_EXPORT BufferTracker { shm_offset_(shm_offset), address_(address), mapped_(false), - transfer_ready_token_(0) { + last_usage_token_(0), + last_async_upload_token_(0) { } GLenum id() const { @@ -66,12 +67,20 @@ class GLES2_IMPL_EXPORT BufferTracker { return mapped_; } - void set_transfer_ready_token(int token) { - transfer_ready_token_ = token; + void set_last_usage_token(int token) { + last_usage_token_ = token; } - uint32 transfer_ready_token() const { - return transfer_ready_token_; + int last_usage_token() const { + return last_usage_token_; + } + + void set_last_async_upload_token(uint32 async_token) { + last_async_upload_token_ = async_token; + } + + GLuint last_async_upload_token() const { + return last_async_upload_token_; } private: @@ -84,7 +93,8 @@ class GLES2_IMPL_EXPORT BufferTracker { uint32 shm_offset_; void* address_; bool mapped_; - int32 transfer_ready_token_; + int32 last_usage_token_; + GLuint last_async_upload_token_; }; BufferTracker(MappedMemoryManager* manager); @@ -96,7 +106,9 @@ class GLES2_IMPL_EXPORT BufferTracker { // Frees the block of memory associated with buffer, pending the passage // of a token. - void FreePendingToken(Buffer*, int32 token); + void FreePendingToken(Buffer* buffer, int32 token); + void Unmanage(Buffer* buffer); + void Free(Buffer* buffer); private: typedef base::hash_map<GLuint, Buffer*> BufferMap; diff --git a/gpu/command_buffer/client/buffer_tracker_unittest.cc b/gpu/command_buffer/client/buffer_tracker_unittest.cc index a298844..f6174c0 100644 --- a/gpu/command_buffer/client/buffer_tracker_unittest.cc +++ b/gpu/command_buffer/client/buffer_tracker_unittest.cc @@ -42,6 +42,11 @@ class MockClientCommandBufferImpl : public MockClientCommandBuffer { bool context_lost_; }; +namespace { +void EmptyPoll() { +} +} + class BufferTrackerTest : public testing::Test { protected: static const int32 kNumCommandEntries = 400; @@ -53,7 +58,7 @@ class BufferTrackerTest : public testing::Test { helper_.reset(new GLES2CmdHelper(command_buffer_.get())); helper_->Initialize(kCommandBufferSizeBytes); mapped_memory_.reset(new MappedMemoryManager( - helper_.get(), MappedMemoryManager::kNoLimit)); + helper_.get(), base::Bind(&EmptyPoll), MappedMemoryManager::kNoLimit)); buffer_tracker_.reset(new BufferTracker(mapped_memory_.get())); } @@ -127,5 +132,22 @@ TEST_F(BufferTrackerTest, LostContext) { buffer_tracker_->RemoveBuffer(kId); } +TEST_F(BufferTrackerTest, Unmanage) { + const GLuint kId = 123; + const GLsizeiptr size = 64; + + BufferTracker::Buffer* buffer = buffer_tracker_->CreateBuffer(kId, size); + ASSERT_TRUE(buffer != NULL); + EXPECT_EQ(mapped_memory_->bytes_in_use(), static_cast<size_t>(size)); + + void* mem = buffer->address(); + buffer_tracker_->Unmanage(buffer); + buffer_tracker_->RemoveBuffer(kId); + EXPECT_EQ(mapped_memory_->bytes_in_use(), static_cast<size_t>(size)); + + mapped_memory_->Free(mem); + EXPECT_EQ(mapped_memory_->bytes_in_use(), static_cast<size_t>(0)); +} + } // namespace gles2 } // namespace gpu diff --git a/gpu/command_buffer/client/cmd_buffer_helper.h b/gpu/command_buffer/client/cmd_buffer_helper.h index 49230b4..a2de5ac 100644 --- a/gpu/command_buffer/client/cmd_buffer_helper.h +++ b/gpu/command_buffer/client/cmd_buffer_helper.h @@ -84,6 +84,15 @@ class GPU_EXPORT CommandBufferHelper { // shutdown. int32 InsertToken(); + // Returns true if the token has passed. + // Parameters: + // the value of the token to check whether it has passed + bool HasTokenPassed(int32 token) const { + if (token > token_) + return true; // we wrapped + return last_token_read() >= token; + } + // Waits until the token of a particular value has passed through the command // stream (i.e. commands inserted before that token have been executed). // NOTE: This will call Flush if it needs to block. diff --git a/gpu/command_buffer/client/fenced_allocator.cc b/gpu/command_buffer/client/fenced_allocator.cc index 0e90bf3..4e405e9 100644 --- a/gpu/command_buffer/client/fenced_allocator.cc +++ b/gpu/command_buffer/client/fenced_allocator.cc @@ -34,8 +34,10 @@ const FencedAllocator::Offset FencedAllocator::kInvalidOffset; #endif FencedAllocator::FencedAllocator(unsigned int size, - CommandBufferHelper *helper) + CommandBufferHelper* helper, + const base::Closure& poll_callback) : helper_(helper), + poll_callback_(poll_callback), bytes_in_use_(0) { Block block = { FREE, 0, RoundDown(size), kUnusedToken }; blocks_.push_back(block); @@ -203,10 +205,13 @@ FencedAllocator::BlockIndex FencedAllocator::WaitForTokenAndFreeBlock( // Frees any blocks pending a token for which the token has been read. void FencedAllocator::FreeUnused() { - int32 last_token_read = helper_->last_token_read(); + // Free any potential blocks that has its lifetime handled outside. + poll_callback_.Run(); + for (unsigned int i = 0; i < blocks_.size();) { Block& block = blocks_[i]; - if (block.state == FREE_PENDING_TOKEN && block.token <= last_token_read) { + if (block.state == FREE_PENDING_TOKEN && + helper_->HasTokenPassed(block.token)) { block.state = FREE; i = CollapseFreeBlock(i); } else { diff --git a/gpu/command_buffer/client/fenced_allocator.h b/gpu/command_buffer/client/fenced_allocator.h index bb5c551..77fadc3 100644 --- a/gpu/command_buffer/client/fenced_allocator.h +++ b/gpu/command_buffer/client/fenced_allocator.h @@ -9,6 +9,7 @@ #include <vector> +#include "base/bind.h" #include "base/logging.h" #include "gpu/command_buffer/common/types.h" #include "gpu/gpu_export.h" @@ -35,7 +36,8 @@ class GPU_EXPORT FencedAllocator { // Creates a FencedAllocator. Note that the size of the buffer is passed, but // not its base address: everything is handled as offsets into the buffer. FencedAllocator(unsigned int size, - CommandBufferHelper *helper); + CommandBufferHelper *helper, + const base::Closure& poll_callback); ~FencedAllocator(); @@ -136,6 +138,7 @@ class GPU_EXPORT FencedAllocator { Offset AllocInBlock(BlockIndex index, unsigned int size); CommandBufferHelper *helper_; + base::Closure poll_callback_; Container blocks_; size_t bytes_in_use_; @@ -148,8 +151,9 @@ class FencedAllocatorWrapper { public: FencedAllocatorWrapper(unsigned int size, CommandBufferHelper* helper, + const base::Closure& poll_callback, void* base) - : allocator_(size, helper), + : allocator_(size, helper, poll_callback), base_(base) { } // Allocates a block of memory. If the buffer is out of directly available diff --git a/gpu/command_buffer/client/fenced_allocator_test.cc b/gpu/command_buffer/client/fenced_allocator_test.cc index f213535..2db1328 100644 --- a/gpu/command_buffer/client/fenced_allocator_test.cc +++ b/gpu/command_buffer/client/fenced_allocator_test.cc @@ -29,6 +29,7 @@ using testing::Truly; using testing::Sequence; using testing::DoAll; using testing::Invoke; +using testing::InvokeWithoutArgs; using testing::_; class BaseFencedAllocatorTest : public testing::Test { @@ -88,6 +89,11 @@ class BaseFencedAllocatorTest : public testing::Test { const unsigned int BaseFencedAllocatorTest::kBufferSize; #endif +namespace { +void EmptyPoll() { +} +} + // Test fixture for FencedAllocator test - Creates a FencedAllocator, using a // CommandBufferHelper with a mock AsyncAPIInterface for its interface (calling // it directly, not through the RPC mechanism), making sure Noops are ignored @@ -96,7 +102,9 @@ class FencedAllocatorTest : public BaseFencedAllocatorTest { protected: virtual void SetUp() { BaseFencedAllocatorTest::SetUp(); - allocator_.reset(new FencedAllocator(kBufferSize, helper_.get())); + allocator_.reset(new FencedAllocator(kBufferSize, + helper_.get(), + base::Bind(&EmptyPoll))); } virtual void TearDown() { @@ -391,6 +399,63 @@ TEST_F(FencedAllocatorTest, TestGetLargestFreeOrPendingSize) { EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeSize()); } +class FencedAllocatorPollTest : public BaseFencedAllocatorTest { + public: + static const unsigned int kAllocSize = 128; + + MOCK_METHOD0(MockedPoll, void()); + + protected: + virtual void TearDown() { + // If the GpuScheduler posts any tasks, this forces them to run. + base::MessageLoop::current()->RunUntilIdle(); + + BaseFencedAllocatorTest::TearDown(); + } +}; + +TEST_F(FencedAllocatorPollTest, TestPoll) { + scoped_ptr<FencedAllocator> allocator( + new FencedAllocator(kBufferSize, + helper_.get(), + base::Bind(&FencedAllocatorPollTest::MockedPoll, + base::Unretained(this)))); + + FencedAllocator::Offset mem1 = allocator->Alloc(kAllocSize); + FencedAllocator::Offset mem2 = allocator->Alloc(kAllocSize); + EXPECT_NE(mem1, FencedAllocator::kInvalidOffset); + EXPECT_NE(mem2, FencedAllocator::kInvalidOffset); + EXPECT_TRUE(allocator->CheckConsistency()); + EXPECT_EQ(allocator->bytes_in_use(), kAllocSize * 2); + + // Check that no-op Poll doesn't affect the state. + EXPECT_CALL(*this, MockedPoll()).RetiresOnSaturation(); + allocator->FreeUnused(); + EXPECT_TRUE(allocator->CheckConsistency()); + EXPECT_EQ(allocator->bytes_in_use(), kAllocSize * 2); + + // Check that freeing in Poll works. + base::Closure free_mem1_closure = + base::Bind(&FencedAllocator::Free, + base::Unretained(allocator.get()), + mem1); + EXPECT_CALL(*this, MockedPoll()) + .WillOnce(InvokeWithoutArgs(&free_mem1_closure, &base::Closure::Run)) + .RetiresOnSaturation(); + allocator->FreeUnused(); + EXPECT_TRUE(allocator->CheckConsistency()); + EXPECT_EQ(allocator->bytes_in_use(), kAllocSize * 1); + + // Check that freeing still works. + EXPECT_CALL(*this, MockedPoll()).RetiresOnSaturation(); + allocator->Free(mem2); + allocator->FreeUnused(); + EXPECT_TRUE(allocator->CheckConsistency()); + EXPECT_EQ(allocator->bytes_in_use(), 0u); + + allocator.reset(); +} + // Test fixture for FencedAllocatorWrapper test - Creates a // FencedAllocatorWrapper, using a CommandBufferHelper with a mock // AsyncAPIInterface for its interface (calling it directly, not through the @@ -406,7 +471,9 @@ class FencedAllocatorWrapperTest : public BaseFencedAllocatorTest { // something. buffer_.reset(static_cast<char*>(base::AlignedAlloc( kBufferSize, kAllocAlignment))); - allocator_.reset(new FencedAllocatorWrapper(kBufferSize, helper_.get(), + allocator_.reset(new FencedAllocatorWrapper(kBufferSize, + helper_.get(), + base::Bind(&EmptyPoll), buffer_.get())); } diff --git a/gpu/command_buffer/client/gles2_c_lib_autogen.h b/gpu/command_buffer/client/gles2_c_lib_autogen.h index ee380a6..6aab61e 100644 --- a/gpu/command_buffer/client/gles2_c_lib_autogen.h +++ b/gpu/command_buffer/client/gles2_c_lib_autogen.h @@ -881,6 +881,9 @@ void GLES2AsyncTexImage2DCHROMIUM(GLenum target, void GLES2WaitAsyncTexImage2DCHROMIUM(GLenum target) { gles2::GetGLContext()->WaitAsyncTexImage2DCHROMIUM(target); } +void GLES2WaitAllAsyncTexImage2DCHROMIUM() { + gles2::GetGLContext()->WaitAllAsyncTexImage2DCHROMIUM(); +} void GLES2DiscardFramebufferEXT(GLenum target, GLsizei count, const GLenum* attachments) { @@ -1259,6 +1262,9 @@ extern const NameToFunc g_gles2_function_table[] = { reinterpret_cast<GLES2FunctionPointer>(glAsyncTexImage2DCHROMIUM), }, {"glWaitAsyncTexImage2DCHROMIUM", reinterpret_cast<GLES2FunctionPointer>(glWaitAsyncTexImage2DCHROMIUM), }, + {"glWaitAllAsyncTexImage2DCHROMIUM", + reinterpret_cast<GLES2FunctionPointer>( + glWaitAllAsyncTexImage2DCHROMIUM), }, {"glDiscardFramebufferEXT", reinterpret_cast<GLES2FunctionPointer>(glDiscardFramebufferEXT), }, {"glLoseContextCHROMIUM", diff --git a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h index ffe9e54..8150d64 100644 --- a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h +++ b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h @@ -2097,7 +2097,10 @@ void AsyncTexSubImage2DCHROMIUM(GLenum target, GLenum format, GLenum type, uint32 data_shm_id, - uint32 data_shm_offset) { + uint32 data_shm_offset, + uint32 async_upload_token, + uint32 sync_data_shm_id, + uint32 sync_data_shm_offset) { gles2::cmds::AsyncTexSubImage2DCHROMIUM* c = GetCmdSpace<gles2::cmds::AsyncTexSubImage2DCHROMIUM>(); if (c) { @@ -2110,7 +2113,10 @@ void AsyncTexSubImage2DCHROMIUM(GLenum target, format, type, data_shm_id, - data_shm_offset); + data_shm_offset, + async_upload_token, + sync_data_shm_id, + sync_data_shm_offset); } } @@ -2123,7 +2129,10 @@ void AsyncTexImage2DCHROMIUM(GLenum target, GLenum format, GLenum type, uint32 pixels_shm_id, - uint32 pixels_shm_offset) { + uint32 pixels_shm_offset, + uint32 async_upload_token, + uint32 sync_data_shm_id, + uint32 sync_data_shm_offset) { gles2::cmds::AsyncTexImage2DCHROMIUM* c = GetCmdSpace<gles2::cmds::AsyncTexImage2DCHROMIUM>(); if (c) { @@ -2136,7 +2145,10 @@ void AsyncTexImage2DCHROMIUM(GLenum target, format, type, pixels_shm_id, - pixels_shm_offset); + pixels_shm_offset, + async_upload_token, + sync_data_shm_id, + sync_data_shm_offset); } } @@ -2148,6 +2160,14 @@ void WaitAsyncTexImage2DCHROMIUM(GLenum target) { } } +void WaitAllAsyncTexImage2DCHROMIUM() { + gles2::cmds::WaitAllAsyncTexImage2DCHROMIUM* c = + GetCmdSpace<gles2::cmds::WaitAllAsyncTexImage2DCHROMIUM>(); + if (c) { + c->Init(); + } +} + void DiscardFramebufferEXT(GLenum target, GLsizei count, uint32 attachments_shm_id, diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc index 147714a..0dd5161 100644 --- a/gpu/command_buffer/client/gles2_implementation.cc +++ b/gpu/command_buffer/client/gles2_implementation.cc @@ -15,6 +15,7 @@ #include <string> #include <GLES2/gl2ext.h> #include <GLES2/gl2extchromium.h> +#include "base/bind.h" #include "gpu/command_buffer/client/buffer_tracker.h" #include "gpu/command_buffer/client/gpu_memory_buffer_tracker.h" #include "gpu/command_buffer/client/program_info_manager.h" @@ -106,6 +107,10 @@ GLES2Implementation::GLES2Implementation( bound_array_buffer_id_(0), bound_pixel_pack_transfer_buffer_id_(0), bound_pixel_unpack_transfer_buffer_id_(0), + async_upload_token_(0), + async_upload_sync_(NULL), + async_upload_sync_shm_id_(0), + async_upload_sync_shm_offset_(0), error_bits_(0), debug_(false), use_count_(0), @@ -151,7 +156,15 @@ bool GLES2Implementation::Initialize( return false; } - mapped_memory_.reset(new MappedMemoryManager(helper_, mapped_memory_limit)); + mapped_memory_.reset( + new MappedMemoryManager( + helper_, + base::Bind(&GLES2Implementation::PollAsyncUploads, + // The mapped memory manager is owned by |this| here, and + // since its destroyed before before we destroy ourselves + // we don't need extra safety measures for this closure. + base::Unretained(this)), + mapped_memory_limit)); unsigned chunk_size = 2 * 1024 * 1024; if (mapped_memory_limit != kNoLimit) { @@ -278,6 +291,13 @@ GLES2Implementation::~GLES2Implementation() { buffer_tracker_.reset(); + FreeAllAsyncUploadBuffers(); + + if (async_upload_sync_) { + mapped_memory_->Free(async_upload_sync_); + async_upload_sync_ = NULL; + } + // Make sure the commands make it the service. WaitForCmd(); } @@ -307,6 +327,7 @@ void GLES2Implementation::FreeUnusedSharedMemory() { } void GLES2Implementation::FreeEverything() { + FreeAllAsyncUploadBuffers(); WaitForCmd(); query_tracker_->Shrink(); FreeUnusedSharedMemory(); @@ -1364,13 +1385,8 @@ void GLES2Implementation::BufferDataHelper( } BufferTracker::Buffer* buffer = buffer_tracker_->GetBuffer(buffer_id); - if (buffer) { - // Free buffer memory, pending the passage of a token. - buffer_tracker_->FreePendingToken(buffer, helper_->InsertToken()); - - // Remove old buffer. - buffer_tracker_->RemoveBuffer(buffer_id); - } + if (buffer) + RemoveTransferBuffer(buffer); // Create new buffer. buffer = buffer_tracker_->CreateBuffer(buffer_id, size); @@ -1498,6 +1514,30 @@ void GLES2Implementation::BufferSubData( CheckGLError(); } +void GLES2Implementation::RemoveTransferBuffer(BufferTracker::Buffer* buffer) { + int32 token = buffer->last_usage_token(); + uint32 async_token = buffer->last_async_upload_token(); + + if (async_token) { + if (HasAsyncUploadTokenPassed(async_token)) { + buffer_tracker_->Free(buffer); + } else { + detached_async_upload_memory_.push_back( + std::make_pair(buffer->address(), async_token)); + buffer_tracker_->Unmanage(buffer); + } + } else if (token) { + if (helper_->HasTokenPassed(token)) + buffer_tracker_->Free(buffer); + else + buffer_tracker_->FreePendingToken(buffer, token); + } else { + buffer_tracker_->Free(buffer); + } + + buffer_tracker_->RemoveBuffer(buffer->id()); +} + bool GLES2Implementation::GetBoundPixelTransferBuffer( GLenum target, const char* function_name, @@ -1573,7 +1613,7 @@ void GLES2Implementation::CompressedTexImage2D( helper_->CompressedTexImage2D( target, level, internalformat, width, height, border, image_size, buffer->shm_id(), buffer->shm_offset() + offset); - buffer->set_transfer_ready_token(helper_->InsertToken()); + buffer->set_last_usage_token(helper_->InsertToken()); } return; } @@ -1614,7 +1654,7 @@ void GLES2Implementation::CompressedTexSubImage2D( helper_->CompressedTexSubImage2D( target, level, xoffset, yoffset, width, height, format, image_size, buffer->shm_id(), buffer->shm_offset() + offset); - buffer->set_transfer_ready_token(helper_->InsertToken()); + buffer->set_last_usage_token(helper_->InsertToken()); CheckGLError(); } return; @@ -1701,7 +1741,7 @@ void GLES2Implementation::TexImage2D( helper_->TexImage2D( target, level, internalformat, width, height, border, format, type, buffer->shm_id(), buffer->shm_offset() + offset); - buffer->set_transfer_ready_token(helper_->InsertToken()); + buffer->set_last_usage_token(helper_->InsertToken()); CheckGLError(); } return; @@ -1807,7 +1847,7 @@ void GLES2Implementation::TexSubImage2D( helper_->TexSubImage2D( target, level, xoffset, yoffset, width, height, format, type, buffer->shm_id(), buffer->shm_offset() + offset, false); - buffer->set_transfer_ready_token(helper_->InsertToken()); + buffer->set_last_usage_token(helper_->InsertToken()); CheckGLError(); } return; @@ -2390,24 +2430,24 @@ void GLES2Implementation::GenQueriesEXTHelper( // deleted the resource. bool GLES2Implementation::BindBufferHelper( - GLenum target, GLuint buffer) { + GLenum target, GLuint buffer_id) { // TODO(gman): See note #1 above. bool changed = false; switch (target) { case GL_ARRAY_BUFFER: - if (bound_array_buffer_id_ != buffer) { - bound_array_buffer_id_ = buffer; + if (bound_array_buffer_id_ != buffer_id) { + bound_array_buffer_id_ = buffer_id; changed = true; } break; case GL_ELEMENT_ARRAY_BUFFER: - changed = vertex_array_object_manager_->BindElementArray(buffer); + changed = vertex_array_object_manager_->BindElementArray(buffer_id); break; case GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM: - bound_pixel_pack_transfer_buffer_id_ = buffer; + bound_pixel_pack_transfer_buffer_id_ = buffer_id; break; case GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM: - bound_pixel_unpack_transfer_buffer_id_ = buffer; + bound_pixel_unpack_transfer_buffer_id_ = buffer_id; break; default: changed = true; @@ -2415,7 +2455,7 @@ bool GLES2Implementation::BindBufferHelper( } // TODO(gman): There's a bug here. If the target is invalid the ID will not be // used even though it's marked it as used here. - GetIdHandler(id_namespaces::kBuffers)->MarkAsUsedForBind(buffer); + GetIdHandler(id_namespaces::kBuffers)->MarkAsUsedForBind(buffer_id); return changed; } @@ -2558,13 +2598,11 @@ void GLES2Implementation::DeleteBuffersHelper( bound_array_buffer_id_ = 0; } vertex_array_object_manager_->UnbindBuffer(buffers[ii]); + BufferTracker::Buffer* buffer = buffer_tracker_->GetBuffer(buffers[ii]); - if (buffer) { - // Free buffer memory, pending the passage of a token. - buffer_tracker_->FreePendingToken(buffer, helper_->InsertToken()); - // Remove buffer. - buffer_tracker_->RemoveBuffer(buffers[ii]); - } + if (buffer) + RemoveTransferBuffer(buffer); + if (buffers[ii] == bound_pixel_unpack_transfer_buffer_id_) { bound_pixel_unpack_transfer_buffer_id_ = 0; } @@ -3616,9 +3654,9 @@ void* GLES2Implementation::MapBufferCHROMIUM(GLuint target, GLenum access) { // with this method of synchronization. Until this is fixed, // MapBufferCHROMIUM will not block even if the transfer is not ready // for these calls. - if (buffer->transfer_ready_token()) { - helper_->WaitForToken(buffer->transfer_ready_token()); - buffer->set_transfer_ready_token(0); + if (buffer->last_usage_token()) { + helper_->WaitForToken(buffer->last_usage_token()); + buffer->set_last_usage_token(0); } buffer->set_mapped(true); @@ -3652,6 +3690,71 @@ GLboolean GLES2Implementation::UnmapBufferCHROMIUM(GLuint target) { return true; } +bool GLES2Implementation::EnsureAsyncUploadSync() { + if (async_upload_sync_) + return true; + + int32 shm_id; + unsigned int shm_offset; + void* mem = mapped_memory_->Alloc(sizeof(AsyncUploadSync), + &shm_id, + &shm_offset); + if (!mem) + return false; + + async_upload_sync_shm_id_ = shm_id; + async_upload_sync_shm_offset_ = shm_offset; + async_upload_sync_ = static_cast<AsyncUploadSync*>(mem); + async_upload_sync_->Reset(); + + return true; +} + +uint32 GLES2Implementation::NextAsyncUploadToken() { + async_upload_token_++; + if (async_upload_token_ == 0) + async_upload_token_++; + return async_upload_token_; +} + +void GLES2Implementation::PollAsyncUploads() { + if (!async_upload_sync_) + return; + + if (helper_->IsContextLost()) { + DetachedAsyncUploadMemoryList::iterator it = + detached_async_upload_memory_.begin(); + while (it != detached_async_upload_memory_.end()) { + mapped_memory_->Free(it->first); + it = detached_async_upload_memory_.erase(it); + } + return; + } + + DetachedAsyncUploadMemoryList::iterator it = + detached_async_upload_memory_.begin(); + while (it != detached_async_upload_memory_.end()) { + if (HasAsyncUploadTokenPassed(it->second)) { + mapped_memory_->Free(it->first); + it = detached_async_upload_memory_.erase(it); + } else { + break; + } + } +} + +void GLES2Implementation::FreeAllAsyncUploadBuffers() { + // Free all completed unmanaged async uploads buffers. + PollAsyncUploads(); + + // Synchronously free rest of the unmanaged async upload buffers. + if (!detached_async_upload_memory_.empty()) { + WaitAllAsyncTexImage2DCHROMIUM(); + WaitForCmd(); + PollAsyncUploads(); + } +} + void GLES2Implementation::AsyncTexImage2DCHROMIUM( GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, @@ -3683,7 +3786,12 @@ void GLES2Implementation::AsyncTexImage2DCHROMIUM( if (!pixels && !bound_pixel_unpack_transfer_buffer_id_) { helper_->AsyncTexImage2DCHROMIUM( target, level, internalformat, width, height, border, format, type, - 0, 0); + 0, 0, 0, 0, 0); + return; + } + + if (!EnsureAsyncUploadSync()) { + SetGLError(GL_OUT_OF_MEMORY, "glTexImage2D", "out of memory"); return; } @@ -3696,9 +3804,13 @@ void GLES2Implementation::AsyncTexImage2DCHROMIUM( bound_pixel_unpack_transfer_buffer_id_, "glAsyncTexImage2DCHROMIUM", offset, size); if (buffer && buffer->shm_id() != -1) { + uint32 async_token = NextAsyncUploadToken(); + buffer->set_last_async_upload_token(async_token); helper_->AsyncTexImage2DCHROMIUM( target, level, internalformat, width, height, border, format, type, - buffer->shm_id(), buffer->shm_offset() + offset); + buffer->shm_id(), buffer->shm_offset() + offset, + async_token, + async_upload_sync_shm_id_, async_upload_sync_shm_offset_); } } @@ -3731,6 +3843,11 @@ void GLES2Implementation::AsyncTexSubImage2DCHROMIUM( return; } + if (!EnsureAsyncUploadSync()) { + SetGLError(GL_OUT_OF_MEMORY, "glTexImage2D", "out of memory"); + return; + } + // Async uploads require a transfer buffer to be bound. // TODO(hubbe): Make MapBufferCHROMIUM block if someone tries to re-use // the buffer before the transfer is finished. (Currently such @@ -3740,9 +3857,13 @@ void GLES2Implementation::AsyncTexSubImage2DCHROMIUM( bound_pixel_unpack_transfer_buffer_id_, "glAsyncTexSubImage2DCHROMIUM", offset, size); if (buffer && buffer->shm_id() != -1) { + uint32 async_token = NextAsyncUploadToken(); + buffer->set_last_async_upload_token(async_token); helper_->AsyncTexSubImage2DCHROMIUM( target, level, xoffset, yoffset, width, height, format, type, - buffer->shm_id(), buffer->shm_offset() + offset); + buffer->shm_id(), buffer->shm_offset() + offset, + async_token, + async_upload_sync_shm_id_, async_upload_sync_shm_offset_); } } @@ -3754,6 +3875,14 @@ void GLES2Implementation::WaitAsyncTexImage2DCHROMIUM(GLenum target) { CheckGLError(); } +void GLES2Implementation::WaitAllAsyncTexImage2DCHROMIUM() { + GPU_CLIENT_SINGLE_THREAD_CHECK(); + GPU_CLIENT_LOG("[" << GetLogPrefix() + << "] glWaitAllAsyncTexImage2DCHROMIUM()"); + helper_->WaitAllAsyncTexImage2DCHROMIUM(); + CheckGLError(); +} + GLuint GLES2Implementation::InsertSyncPointCHROMIUM() { GPU_CLIENT_SINGLE_THREAD_CHECK(); GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glInsertSyncPointCHROMIUM"); diff --git a/gpu/command_buffer/client/gles2_implementation.h b/gpu/command_buffer/client/gles2_implementation.h index bd5ee04..b120a62 100644 --- a/gpu/command_buffer/client/gles2_implementation.h +++ b/gpu/command_buffer/client/gles2_implementation.h @@ -601,6 +601,38 @@ class GLES2_IMPL_EXPORT GLES2Implementation void OnSwapBuffersComplete(); + // Remove the transfer buffer from the buffer tracker. For buffers used + // asynchronously the memory is free:ed if the upload has completed. For + // other buffers, the memory is either free:ed immediately or free:ed pending + // a token. + void RemoveTransferBuffer(BufferTracker::Buffer* buffer); + + // Returns true if the async upload token has passed. + // + // NOTE: This will detect wrapped async tokens by checking if the most + // significant bit of async token to check is 1 but the last read is 0, i.e. + // the uint32 wrapped. + bool HasAsyncUploadTokenPassed(uint32 token) const { + return async_upload_sync_->HasAsyncUploadTokenPassed(token); + } + + // Get the next async upload token. + uint32 NextAsyncUploadToken(); + + // Ensure that the shared memory used for synchronizing async upload tokens + // has been mapped. + // + // Returns false on error, true on success. + bool EnsureAsyncUploadSync(); + + // Checks the last read asynchronously upload token and frees any unmanaged + // transfer buffer that has its async token passed. + void PollAsyncUploads(); + + // Free every async upload buffer. If some async upload buffer is still in use + // wait for them to finish before freeing. + void FreeAllAsyncUploadBuffers(); + bool GetBoundPixelTransferBuffer( GLenum target, const char* function_name, GLuint* buffer_id); BufferTracker::Buffer* GetBoundPixelUnpackTransferBufferIfValid( @@ -673,6 +705,18 @@ class GLES2_IMPL_EXPORT GLES2Implementation GLuint bound_pixel_pack_transfer_buffer_id_; GLuint bound_pixel_unpack_transfer_buffer_id_; + // The current asynchronous pixel buffer upload token. + uint32 async_upload_token_; + + // The shared memory used for synchronizing asynchronous upload tokens. + AsyncUploadSync* async_upload_sync_; + int32 async_upload_sync_shm_id_; + unsigned int async_upload_sync_shm_offset_; + + // Unmanaged pixel transfer buffer memory pending asynchronous upload token. + typedef std::list<std::pair<void*, uint32> > DetachedAsyncUploadMemoryList; + DetachedAsyncUploadMemoryList detached_async_upload_memory_; + // Client side management for vertex array objects. Needed to correctly // track client side arrays. scoped_ptr<VertexArrayObjectManager> vertex_array_object_manager_; diff --git a/gpu/command_buffer/client/gles2_implementation_autogen.h b/gpu/command_buffer/client/gles2_implementation_autogen.h index 7301f50..03ed808 100644 --- a/gpu/command_buffer/client/gles2_implementation_autogen.h +++ b/gpu/command_buffer/client/gles2_implementation_autogen.h @@ -696,6 +696,8 @@ virtual void AsyncTexImage2DCHROMIUM(GLenum target, virtual void WaitAsyncTexImage2DCHROMIUM(GLenum target) OVERRIDE; +virtual void WaitAllAsyncTexImage2DCHROMIUM() OVERRIDE; + virtual void DiscardFramebufferEXT(GLenum target, GLsizei count, const GLenum* attachments) OVERRIDE; diff --git a/gpu/command_buffer/client/gles2_interface_autogen.h b/gpu/command_buffer/client/gles2_interface_autogen.h index 4da19dc..c3c3f90 100644 --- a/gpu/command_buffer/client/gles2_interface_autogen.h +++ b/gpu/command_buffer/client/gles2_interface_autogen.h @@ -478,6 +478,7 @@ virtual void AsyncTexImage2DCHROMIUM(GLenum target, GLenum type, const void* pixels) = 0; virtual void WaitAsyncTexImage2DCHROMIUM(GLenum target) = 0; +virtual void WaitAllAsyncTexImage2DCHROMIUM() = 0; virtual void DiscardFramebufferEXT(GLenum target, GLsizei count, const GLenum* attachments) = 0; diff --git a/gpu/command_buffer/client/gles2_interface_stub_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_autogen.h index e1db978..4d7e1f9 100644 --- a/gpu/command_buffer/client/gles2_interface_stub_autogen.h +++ b/gpu/command_buffer/client/gles2_interface_stub_autogen.h @@ -502,6 +502,7 @@ virtual void AsyncTexImage2DCHROMIUM(GLenum target, GLenum type, const void* pixels) OVERRIDE; virtual void WaitAsyncTexImage2DCHROMIUM(GLenum target) OVERRIDE; +virtual void WaitAllAsyncTexImage2DCHROMIUM() OVERRIDE; virtual void DiscardFramebufferEXT(GLenum target, GLsizei count, const GLenum* attachments) OVERRIDE; diff --git a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h index 61c81ee..639cadd 100644 --- a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h +++ b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h @@ -620,6 +620,7 @@ void GLES2InterfaceStub::AsyncTexImage2DCHROMIUM(GLenum /* target */, GLenum /* type */, const void* /* pixels */) {} void GLES2InterfaceStub::WaitAsyncTexImage2DCHROMIUM(GLenum /* target */) {} +void GLES2InterfaceStub::WaitAllAsyncTexImage2DCHROMIUM() {} void GLES2InterfaceStub::DiscardFramebufferEXT( GLenum /* target */, GLsizei /* count */, diff --git a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h index 43cc090d..89bb479 100644 --- a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h +++ b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h @@ -502,6 +502,7 @@ virtual void AsyncTexImage2DCHROMIUM(GLenum target, GLenum type, const void* pixels) OVERRIDE; virtual void WaitAsyncTexImage2DCHROMIUM(GLenum target) OVERRIDE; +virtual void WaitAllAsyncTexImage2DCHROMIUM() OVERRIDE; virtual void DiscardFramebufferEXT(GLenum target, GLsizei count, const GLenum* attachments) OVERRIDE; diff --git a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h index e915d7b..0734ab1 100644 --- a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h +++ b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h @@ -1441,6 +1441,12 @@ void GLES2TraceImplementation::WaitAsyncTexImage2DCHROMIUM(GLenum target) { gl_->WaitAsyncTexImage2DCHROMIUM(target); } +void GLES2TraceImplementation::WaitAllAsyncTexImage2DCHROMIUM() { + TRACE_EVENT_BINARY_EFFICIENT0("gpu", + "GLES2Trace::WaitAllAsyncTexImage2DCHROMIUM"); + gl_->WaitAllAsyncTexImage2DCHROMIUM(); +} + void GLES2TraceImplementation::DiscardFramebufferEXT( GLenum target, GLsizei count, diff --git a/gpu/command_buffer/client/mapped_memory.cc b/gpu/command_buffer/client/mapped_memory.cc index aeab080..b62ca27 100644 --- a/gpu/command_buffer/client/mapped_memory.cc +++ b/gpu/command_buffer/client/mapped_memory.cc @@ -15,17 +15,20 @@ namespace gpu { MemoryChunk::MemoryChunk(int32 shm_id, scoped_refptr<gpu::Buffer> shm, - CommandBufferHelper* helper) + CommandBufferHelper* helper, + const base::Closure& poll_callback) : shm_id_(shm_id), shm_(shm), - allocator_(shm->size(), helper, shm->memory()) {} + allocator_(shm->size(), helper, poll_callback, shm->memory()) {} MemoryChunk::~MemoryChunk() {} MappedMemoryManager::MappedMemoryManager(CommandBufferHelper* helper, + const base::Closure& poll_callback, size_t unused_memory_reclaim_limit) : chunk_size_multiple_(1), helper_(helper), + poll_callback_(poll_callback), allocated_memory_(0), max_free_bytes_(unused_memory_reclaim_limit) { } @@ -88,7 +91,7 @@ void* MappedMemoryManager::Alloc( cmd_buf->CreateTransferBuffer(chunk_size, &id); if (id < 0) return NULL; - MemoryChunk* mc = new MemoryChunk(id, shm, helper_); + MemoryChunk* mc = new MemoryChunk(id, shm, helper_, poll_callback_); allocated_memory_ += mc->GetSize(); chunks_.push_back(mc); void* mem = mc->Alloc(size); diff --git a/gpu/command_buffer/client/mapped_memory.h b/gpu/command_buffer/client/mapped_memory.h index 00251e8..e7f62ff 100644 --- a/gpu/command_buffer/client/mapped_memory.h +++ b/gpu/command_buffer/client/mapped_memory.h @@ -5,6 +5,7 @@ #ifndef GPU_COMMAND_BUFFER_CLIENT_MAPPED_MEMORY_H_ #define GPU_COMMAND_BUFFER_CLIENT_MAPPED_MEMORY_H_ +#include "base/bind.h" #include "base/memory/scoped_vector.h" #include "gpu/command_buffer/client/fenced_allocator.h" #include "gpu/command_buffer/common/buffer.h" @@ -20,7 +21,8 @@ class GPU_EXPORT MemoryChunk { public: MemoryChunk(int32 shm_id, scoped_refptr<gpu::Buffer> shm, - CommandBufferHelper* helper); + CommandBufferHelper* helper, + const base::Closure& poll_callback); ~MemoryChunk(); // Gets the size of the largest free block that is available without waiting. @@ -121,6 +123,7 @@ class GPU_EXPORT MappedMemoryManager { // |unused_memory_reclaim_limit|: When exceeded this causes pending memory // to be reclaimed before allocating more memory. MappedMemoryManager(CommandBufferHelper* helper, + const base::Closure& poll_callback, size_t unused_memory_reclaim_limit); ~MappedMemoryManager(); @@ -165,6 +168,15 @@ class GPU_EXPORT MappedMemoryManager { return chunks_.size(); } + size_t bytes_in_use() const { + size_t bytes_in_use = 0; + for (size_t ii = 0; ii < chunks_.size(); ++ii) { + MemoryChunk* chunk = chunks_[ii]; + bytes_in_use += chunk->bytes_in_use(); + } + return bytes_in_use; + } + // Used for testing size_t allocated_memory() const { return allocated_memory_; @@ -176,6 +188,7 @@ class GPU_EXPORT MappedMemoryManager { // size a chunk is rounded up to. unsigned int chunk_size_multiple_; CommandBufferHelper* helper_; + base::Closure poll_callback_; MemoryChunkVector chunks_; size_t allocated_memory_; size_t max_free_bytes_; diff --git a/gpu/command_buffer/client/mapped_memory_unittest.cc b/gpu/command_buffer/client/mapped_memory_unittest.cc index 3e174fa..d853119 100644 --- a/gpu/command_buffer/client/mapped_memory_unittest.cc +++ b/gpu/command_buffer/client/mapped_memory_unittest.cc @@ -4,6 +4,7 @@ #include "gpu/command_buffer/client/mapped_memory.h" +#include <list> #include "base/bind.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" @@ -85,6 +86,11 @@ class MappedMemoryTestBase : public testing::Test { const unsigned int MappedMemoryTestBase::kBufferSize; #endif +namespace { +void EmptyPoll() { +} +} + // Test fixture for MemoryChunk test - Creates a MemoryChunk, using a // CommandBufferHelper with a mock AsyncAPIInterface for its interface (calling // it directly, not through the RPC mechanism), making sure Noops are ignored @@ -97,7 +103,10 @@ class MemoryChunkTest : public MappedMemoryTestBase { scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory()); shared_memory->CreateAndMapAnonymous(kBufferSize); buffer_ = new gpu::Buffer(shared_memory.Pass(), kBufferSize); - chunk_.reset(new MemoryChunk(kShmId, buffer_, helper_.get())); + chunk_.reset(new MemoryChunk(kShmId, + buffer_, + helper_.get(), + base::Bind(&EmptyPoll))); } virtual void TearDown() { @@ -148,11 +157,16 @@ TEST_F(MemoryChunkTest, Basic) { } class MappedMemoryManagerTest : public MappedMemoryTestBase { + public: + MappedMemoryManager* manager() const { + return manager_.get(); + } + protected: virtual void SetUp() { MappedMemoryTestBase::SetUp(); manager_.reset(new MappedMemoryManager( - helper_.get(), MappedMemoryManager::kNoLimit)); + helper_.get(), base::Bind(&EmptyPoll), MappedMemoryManager::kNoLimit)); } virtual void TearDown() { @@ -312,7 +326,8 @@ TEST_F(MappedMemoryManagerTest, ChunkSizeMultiple) { TEST_F(MappedMemoryManagerTest, UnusedMemoryLimit) { const unsigned int kChunkSize = 2048; // Reset the manager with a memory limit. - manager_.reset(new MappedMemoryManager(helper_.get(), kChunkSize)); + manager_.reset(new MappedMemoryManager( + helper_.get(), base::Bind(&EmptyPoll), kChunkSize)); manager_->set_chunk_size_multiple(kChunkSize); // Allocate one chunk worth of memory. @@ -340,7 +355,8 @@ TEST_F(MappedMemoryManagerTest, UnusedMemoryLimit) { TEST_F(MappedMemoryManagerTest, MemoryLimitWithReuse) { const unsigned int kSize = 1024; // Reset the manager with a memory limit. - manager_.reset(new MappedMemoryManager(helper_.get(), kSize)); + manager_.reset(new MappedMemoryManager( + helper_.get(), base::Bind(&EmptyPoll), kSize)); const unsigned int kChunkSize = 2 * 1024; manager_->set_chunk_size_multiple(kChunkSize); @@ -386,4 +402,55 @@ TEST_F(MappedMemoryManagerTest, MemoryLimitWithReuse) { EXPECT_EQ(1 * kChunkSize, manager_->allocated_memory()); } +namespace { +void Poll(MappedMemoryManagerTest *test, std::list<void*>* list) { + std::list<void*>::iterator it = list->begin(); + while (it != list->end()) { + void* address = *it; + test->manager()->Free(address); + it = list->erase(it); + } +} +} + +TEST_F(MappedMemoryManagerTest, Poll) { + std::list<void*> unmanaged_memory_list; + + const unsigned int kSize = 1024; + // Reset the manager with a memory limit. + manager_.reset(new MappedMemoryManager( + helper_.get(), + base::Bind(&Poll, this, &unmanaged_memory_list), + kSize)); + + // Allocate kSize bytes. Don't add the address to + // the unmanaged memory list, so that it won't be free:ed just yet. + int32 id1; + unsigned int offset1; + void* mem1 = manager_->Alloc(kSize, &id1, &offset1); + EXPECT_EQ(manager_->bytes_in_use(), kSize); + + // Allocate kSize more bytes, and make sure we grew. + int32 id2; + unsigned int offset2; + void* mem2 = manager_->Alloc(kSize, &id2, &offset2); + EXPECT_EQ(manager_->bytes_in_use(), kSize * 2); + + // Make the unmanaged buffer be released next time FreeUnused() is called + // in MappedMemoryManager/FencedAllocator. This happens for example when + // allocating new memory. + unmanaged_memory_list.push_back(mem1); + + // Allocate kSize more bytes. This should poll unmanaged memory, which now + // should free the previously allocated unmanaged memory. + int32 id3; + unsigned int offset3; + void* mem3 = manager_->Alloc(kSize, &id3, &offset3); + EXPECT_EQ(manager_->bytes_in_use(), kSize * 2); + + manager_->Free(mem2); + manager_->Free(mem3); + EXPECT_EQ(manager_->bytes_in_use(), static_cast<size_t>(0)); +} + } // namespace gpu diff --git a/gpu/command_buffer/client/query_tracker_unittest.cc b/gpu/command_buffer/client/query_tracker_unittest.cc index ce299f0..0820a99 100644 --- a/gpu/command_buffer/client/query_tracker_unittest.cc +++ b/gpu/command_buffer/client/query_tracker_unittest.cc @@ -18,6 +18,11 @@ namespace gpu { namespace gles2 { +namespace { +void EmptyPoll() { +} +} + class QuerySyncManagerTest : public testing::Test { protected: static const int32 kNumCommandEntries = 400; @@ -29,7 +34,7 @@ class QuerySyncManagerTest : public testing::Test { helper_.reset(new GLES2CmdHelper(command_buffer_.get())); helper_->Initialize(kCommandBufferSizeBytes); mapped_memory_.reset(new MappedMemoryManager( - helper_.get(), MappedMemoryManager::kNoLimit)); + helper_.get(), base::Bind(&EmptyPoll), MappedMemoryManager::kNoLimit)); sync_manager_.reset(new QuerySyncManager(mapped_memory_.get())); } @@ -83,7 +88,7 @@ class QueryTrackerTest : public testing::Test { helper_.reset(new GLES2CmdHelper(command_buffer_.get())); helper_->Initialize(kCommandBufferSizeBytes); mapped_memory_.reset(new MappedMemoryManager( - helper_.get(), MappedMemoryManager::kNoLimit)); + helper_.get(), base::Bind(&EmptyPoll), MappedMemoryManager::kNoLimit)); query_tracker_.reset(new QueryTracker(mapped_memory_.get())); } |