summaryrefslogtreecommitdiffstats
path: root/gpu/command_buffer/client
diff options
context:
space:
mode:
Diffstat (limited to 'gpu/command_buffer/client')
-rw-r--r--gpu/command_buffer/client/buffer_tracker.cc22
-rw-r--r--gpu/command_buffer/client/buffer_tracker.h26
-rw-r--r--gpu/command_buffer/client/buffer_tracker_unittest.cc24
-rw-r--r--gpu/command_buffer/client/cmd_buffer_helper.h9
-rw-r--r--gpu/command_buffer/client/fenced_allocator.cc11
-rw-r--r--gpu/command_buffer/client/fenced_allocator.h8
-rw-r--r--gpu/command_buffer/client/fenced_allocator_test.cc71
-rw-r--r--gpu/command_buffer/client/gles2_c_lib_autogen.h6
-rw-r--r--gpu/command_buffer/client/gles2_cmd_helper_autogen.h28
-rw-r--r--gpu/command_buffer/client/gles2_implementation.cc191
-rw-r--r--gpu/command_buffer/client/gles2_implementation.h44
-rw-r--r--gpu/command_buffer/client/gles2_implementation_autogen.h2
-rw-r--r--gpu/command_buffer/client/gles2_interface_autogen.h1
-rw-r--r--gpu/command_buffer/client/gles2_interface_stub_autogen.h1
-rw-r--r--gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h1
-rw-r--r--gpu/command_buffer/client/gles2_trace_implementation_autogen.h1
-rw-r--r--gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h6
-rw-r--r--gpu/command_buffer/client/mapped_memory.cc9
-rw-r--r--gpu/command_buffer/client/mapped_memory.h15
-rw-r--r--gpu/command_buffer/client/mapped_memory_unittest.cc75
-rw-r--r--gpu/command_buffer/client/query_tracker_unittest.cc9
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()));
}