diff options
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())); } |