diff --git a/gpu/command_buffer/client/client_test_helper.cc b/gpu/command_buffer/client/client_test_helper.cc index 2acf562751c2df..3034dd032893da 100644 --- a/gpu/command_buffer/client/client_test_helper.cc +++ b/gpu/command_buffer/client/client_test_helper.cc @@ -43,11 +43,10 @@ int32_t FakeCommandBufferServiceBase::GetNextFreeTransferBufferId() { return -1; } -void FakeCommandBufferServiceBase::SetGetBufferHelper(int transfer_buffer_id, - int32_t token) { +void FakeCommandBufferServiceBase::SetGetBufferHelper(int transfer_buffer_id) { ++state_.set_get_buffer_count; state_.get_offset = 0; - state_.token = token; + state_.token = 10000; // All token checks in the tests should pass. } scoped_refptr @@ -132,7 +131,7 @@ CommandBuffer::State MockClientCommandBuffer::WaitForGetOffsetInRange( } void MockClientCommandBuffer::SetGetBuffer(int transfer_buffer_id) { - SetGetBufferHelper(transfer_buffer_id, token_); + SetGetBufferHelper(transfer_buffer_id); } scoped_refptr MockClientCommandBuffer::CreateTransferBuffer( diff --git a/gpu/command_buffer/client/client_test_helper.h b/gpu/command_buffer/client/client_test_helper.h index 9008bde72bf9c9..e401b248783b52 100644 --- a/gpu/command_buffer/client/client_test_helper.h +++ b/gpu/command_buffer/client/client_test_helper.h @@ -44,7 +44,7 @@ class FakeCommandBufferServiceBase : public CommandBufferServiceBase { int32_t GetNextFreeTransferBufferId(); void FlushHelper(int32_t put_offset); - void SetGetBufferHelper(int transfer_buffer_id, int32_t token); + void SetGetBufferHelper(int transfer_buffer_id); scoped_refptr CreateTransferBufferHelper(size_t size, int32_t* id); void DestroyTransferBufferHelper(int32_t id); @@ -80,11 +80,8 @@ class MockClientCommandBuffer : public CommandBuffer, int32_t GetServicePutOffset() { return put_offset_; } - void SetTokenForSetGetBuffer(int32_t token) { token_ = token; } - private: int32_t put_offset_ = 0; - int32_t token_ = 10000; // All token checks in the tests should pass. }; class MockClientCommandBufferMockFlush : public MockClientCommandBuffer { diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc index 03c0960b42d161..5dae3bc1e6707e 100644 --- a/gpu/command_buffer/client/gles2_implementation.cc +++ b/gpu/command_buffer/client/gles2_implementation.cc @@ -1546,12 +1546,12 @@ void GLES2Implementation::DeleteSyncStub(GLsizei n, const GLuint* syncs) { GLint GLES2Implementation::GetAttribLocationHelper( GLuint program, const char* name) { typedef cmds::GetAttribLocation::Result Result; - SetBucketAsCString(kResultBucketId, name); Result* result = GetResultAs(); if (!result) { return -1; } *result = -1; + SetBucketAsCString(kResultBucketId, name); helper_->GetAttribLocation( program, kResultBucketId, GetResultShmId(), GetResultShmOffset()); WaitForCmd(); @@ -1575,12 +1575,12 @@ GLint GLES2Implementation::GetAttribLocation( GLint GLES2Implementation::GetUniformLocationHelper( GLuint program, const char* name) { typedef cmds::GetUniformLocation::Result Result; - SetBucketAsCString(kResultBucketId, name); Result* result = GetResultAs(); if (!result) { return -1; } *result = -1; + SetBucketAsCString(kResultBucketId, name); helper_->GetUniformLocation(program, kResultBucketId, GetResultShmId(), GetResultShmOffset()); WaitForCmd(); @@ -1662,12 +1662,12 @@ bool GLES2Implementation::GetProgramivHelper( GLint GLES2Implementation::GetFragDataIndexEXTHelper(GLuint program, const char* name) { typedef cmds::GetFragDataIndexEXT::Result Result; - SetBucketAsCString(kResultBucketId, name); Result* result = GetResultAs(); if (!result) { return -1; } *result = -1; + SetBucketAsCString(kResultBucketId, name); helper_->GetFragDataIndexEXT(program, kResultBucketId, GetResultShmId(), GetResultShmOffset()); WaitForCmd(); @@ -1691,12 +1691,12 @@ GLint GLES2Implementation::GetFragDataIndexEXT(GLuint program, GLint GLES2Implementation::GetFragDataLocationHelper( GLuint program, const char* name) { typedef cmds::GetFragDataLocation::Result Result; - SetBucketAsCString(kResultBucketId, name); Result* result = GetResultAs(); if (!result) { return -1; } *result = -1; + SetBucketAsCString(kResultBucketId, name); helper_->GetFragDataLocation( program, kResultBucketId, GetResultShmId(), GetResultShmOffset()); WaitForCmd(); @@ -1720,12 +1720,12 @@ GLint GLES2Implementation::GetFragDataLocation( GLuint GLES2Implementation::GetUniformBlockIndexHelper( GLuint program, const char* name) { typedef cmds::GetUniformBlockIndex::Result Result; - SetBucketAsCString(kResultBucketId, name); Result* result = GetResultAs(); if (!result) { return GL_INVALID_INDEX; } *result = GL_INVALID_INDEX; + SetBucketAsCString(kResultBucketId, name); helper_->GetUniformBlockIndex( program, kResultBucketId, GetResultShmId(), GetResultShmOffset()); WaitForCmd(); @@ -4956,12 +4956,12 @@ GLboolean GLES2Implementation::EnableFeatureCHROMIUM( << feature << ")"); TRACE_EVENT0("gpu", "GLES2::EnableFeatureCHROMIUM"); typedef cmds::EnableFeatureCHROMIUM::Result Result; - SetBucketAsCString(kResultBucketId, feature); Result* result = GetResultAs(); if (!result) { return false; } *result = 0; + SetBucketAsCString(kResultBucketId, feature); helper_->EnableFeatureCHROMIUM( kResultBucketId, GetResultShmId(), GetResultShmOffset()); WaitForCmd(); diff --git a/gpu/command_buffer/client/implementation_base.cc b/gpu/command_buffer/client/implementation_base.cc index ce8dfcd0cf08f0..508cf1297f0a38 100644 --- a/gpu/command_buffer/client/implementation_base.cc +++ b/gpu/command_buffer/client/implementation_base.cc @@ -156,7 +156,7 @@ gpu::ContextResult ImplementationBase::Initialize( if (!transfer_buffer_->Initialize( limits.start_transfer_buffer_size, kStartingOffset, limits.min_transfer_buffer_size, limits.max_transfer_buffer_size, - kAlignment)) { + kAlignment, kSizeToFlush)) { // TransferBuffer::Initialize doesn't fail for transient reasons such as if // the context was lost. See http://crrev.com/c/720269 LOG(ERROR) << "ContextResult::kFatalFailure: " diff --git a/gpu/command_buffer/client/implementation_base.h b/gpu/command_buffer/client/implementation_base.h index b38fab99ad57de..376ed9836bcedb 100644 --- a/gpu/command_buffer/client/implementation_base.h +++ b/gpu/command_buffer/client/implementation_base.h @@ -51,6 +51,9 @@ class GLES2_IMPL_EXPORT ImplementationBase // used for testing only. If more things are reseved add them here. static const unsigned int kStartingOffset = kMaxSizeOfSimpleResult; + // Size in bytes to issue async flush for transfer buffer. + static const unsigned int kSizeToFlush = 256 * 1024; + // Alignment of allocations. static const unsigned int kAlignment = 16; diff --git a/gpu/command_buffer/client/mock_transfer_buffer.cc b/gpu/command_buffer/client/mock_transfer_buffer.cc index 221e39cf101648..f371c257c6df66 100644 --- a/gpu/command_buffer/client/mock_transfer_buffer.cc +++ b/gpu/command_buffer/client/mock_transfer_buffer.cc @@ -44,7 +44,8 @@ bool MockTransferBuffer::Initialize(unsigned int starting_buffer_size, unsigned int result_size, unsigned int /* min_buffer_size */, unsigned int /* max_buffer_size */, - unsigned int alignment) { + unsigned int alignment, + unsigned int /* size_to_flush */) { // Just check they match. return size_ == starting_buffer_size && result_size_ == result_size && alignment_ == alignment && !initialize_fail_; diff --git a/gpu/command_buffer/client/mock_transfer_buffer.h b/gpu/command_buffer/client/mock_transfer_buffer.h index 5709e9ea25a0c6..a07e00f2398aa2 100644 --- a/gpu/command_buffer/client/mock_transfer_buffer.h +++ b/gpu/command_buffer/client/mock_transfer_buffer.h @@ -35,7 +35,8 @@ class MockTransferBuffer : public TransferBufferInterface { unsigned int result_size, unsigned int /* min_buffer_size */, unsigned int /* max_buffer_size */, - unsigned int alignment) override; + unsigned int alignment, + unsigned int size_to_flush) override; int GetShmId() override; void* GetResultBuffer() override; int GetResultOffset() override; diff --git a/gpu/command_buffer/client/ring_buffer.h b/gpu/command_buffer/client/ring_buffer.h index 3b09125b72fdac..0018cde75343b8 100644 --- a/gpu/command_buffer/client/ring_buffer.h +++ b/gpu/command_buffer/client/ring_buffer.h @@ -80,9 +80,6 @@ class GPU_EXPORT RingBuffer { return size_ - size_ % alignment_; } - // Total size minus usable size. - unsigned int GetUsedSize() { return size_ - GetLargestFreeSizeNoWaiting(); } - // Gets a pointer to a memory block given the base memory and the offset. void* GetPointer(RingBuffer::Offset offset) const { return static_cast(base_) + offset; diff --git a/gpu/command_buffer/client/shared_memory_limits.h b/gpu/command_buffer/client/shared_memory_limits.h index 0d76ad52d97d8e..16da712c7e4ef8 100644 --- a/gpu/command_buffer/client/shared_memory_limits.h +++ b/gpu/command_buffer/client/shared_memory_limits.h @@ -28,17 +28,17 @@ struct SharedMemoryLimits { // On memory constrained devices, switch to lower limits. if (base::SysInfo::AmountOfPhysicalMemoryMB() <= 512) { command_buffer_size = 512 * 1024; - start_transfer_buffer_size = 32 * 1024; - min_transfer_buffer_size = 32 * 1024; + start_transfer_buffer_size = 256 * 1024; + min_transfer_buffer_size = 128 * 1024; mapped_memory_chunk_size = 256 * 1024; } #endif } int32_t command_buffer_size = 1024 * 1024; - uint32_t start_transfer_buffer_size = 64 * 1024; - uint32_t min_transfer_buffer_size = 64 * 1024; - uint32_t max_transfer_buffer_size = 64 * 1024 * 1024; + uint32_t start_transfer_buffer_size = 1024 * 1024; + uint32_t min_transfer_buffer_size = 256 * 1024; + uint32_t max_transfer_buffer_size = 16 * 1024 * 1024; static constexpr uint32_t kNoLimit = 0; uint32_t mapped_memory_reclaim_limit = kNoLimit; diff --git a/gpu/command_buffer/client/transfer_buffer.cc b/gpu/command_buffer/client/transfer_buffer.cc index 926ae8b64353eb..d8b1feb9983d19 100644 --- a/gpu/command_buffer/client/transfer_buffer.cc +++ b/gpu/command_buffer/client/transfer_buffer.cc @@ -17,17 +17,21 @@ namespace gpu { -TransferBuffer::TransferBuffer(CommandBufferHelper* helper) +TransferBuffer::TransferBuffer( + CommandBufferHelper* helper) : helper_(helper), result_size_(0), default_buffer_size_(0), min_buffer_size_(0), max_buffer_size_(0), alignment_(0), + size_to_flush_(0), + bytes_since_last_flush_(0), buffer_id_(-1), result_buffer_(NULL), result_shm_offset_(0), - usable_(true) {} + usable_(true) { +} TransferBuffer::~TransferBuffer() { Free(); @@ -41,16 +45,19 @@ base::SharedMemoryHandle TransferBuffer::shared_memory_handle() const { return buffer_->backing()->shared_memory_handle(); } -bool TransferBuffer::Initialize(unsigned int default_buffer_size, - unsigned int result_size, - unsigned int min_buffer_size, - unsigned int max_buffer_size, - unsigned int alignment) { +bool TransferBuffer::Initialize( + unsigned int default_buffer_size, + unsigned int result_size, + unsigned int min_buffer_size, + unsigned int max_buffer_size, + unsigned int alignment, + unsigned int size_to_flush) { result_size_ = result_size; default_buffer_size_ = default_buffer_size; min_buffer_size_ = min_buffer_size; max_buffer_size_ = max_buffer_size; alignment_ = alignment; + size_to_flush_ = size_to_flush; ReallocateRingBuffer(default_buffer_size_ - result_size); return HaveBuffer(); } @@ -64,10 +71,8 @@ void TransferBuffer::Free() { buffer_ = NULL; result_buffer_ = NULL; result_shm_offset_ = 0; - previous_ring_buffers_.push_back(std::move(ring_buffer_)); - last_allocated_size_ = 0; - high_water_mark_ = GetPreviousRingBufferUsedBytes(); - bytes_since_last_shrink_ = 0; + ring_buffer_.reset(); + bytes_since_last_flush_ = 0; } } @@ -86,6 +91,10 @@ void TransferBuffer::DiscardBlock(void* p) { void TransferBuffer::FreePendingToken(void* p, unsigned int token) { ring_buffer_->FreePendingToken(p, token); + if (bytes_since_last_flush_ >= size_to_flush_ && size_to_flush_ > 0) { + helper_->Flush(); + bytes_since_last_flush_ = 0; + } } unsigned int TransferBuffer::GetSize() const { @@ -110,7 +119,6 @@ void TransferBuffer::AllocateRingBuffer(unsigned int size) { scoped_refptr buffer = helper_->command_buffer()->CreateTransferBuffer(size, &id); if (id != -1) { - last_allocated_size_ = size; DCHECK(buffer.get()); buffer_ = buffer; ring_buffer_ = std::make_unique( @@ -119,7 +127,6 @@ void TransferBuffer::AllocateRingBuffer(unsigned int size) { buffer_id_ = id; result_buffer_ = buffer_->memory(); result_shm_offset_ = 0; - bytes_since_last_shrink_ = 0; return; } // we failed so don't try larger than this. @@ -132,7 +139,7 @@ static unsigned int ComputePOTSize(unsigned int dimension) { return (dimension == 0) ? 0 : 1 << base::bits::Log2Ceiling(dimension); } -void TransferBuffer::ReallocateRingBuffer(unsigned int size, bool shrink) { +void TransferBuffer::ReallocateRingBuffer(unsigned int size) { // What size buffer would we ask for if we needed a new one? unsigned int needed_buffer_size = ComputePOTSize(size + result_size_); DCHECK_EQ(needed_buffer_size % alignment_, 0u) @@ -141,11 +148,7 @@ void TransferBuffer::ReallocateRingBuffer(unsigned int size, bool shrink) { needed_buffer_size = std::max(needed_buffer_size, default_buffer_size_); needed_buffer_size = std::min(needed_buffer_size, max_buffer_size_); - unsigned int current_size = HaveBuffer() ? buffer_->size() : 0; - if (current_size == needed_buffer_size) - return; - - if (usable_ && (shrink || needed_buffer_size > current_size)) { + if (usable_ && (!HaveBuffer() || needed_buffer_size > buffer_->size())) { if (HaveBuffer()) { Free(); } @@ -153,49 +156,11 @@ void TransferBuffer::ReallocateRingBuffer(unsigned int size, bool shrink) { } } -unsigned int TransferBuffer::GetPreviousRingBufferUsedBytes() { - while (!previous_ring_buffers_.empty() && - previous_ring_buffers_.front()->GetUsedSize() == 0) { - previous_ring_buffers_.pop_front(); - } - unsigned int total = 0; - for (auto& buffer : previous_ring_buffers_) { - total += buffer->GetUsedSize(); - } - return total; -} - -void TransferBuffer::ShrinkOrExpandRingBufferIfNecessary( - unsigned int size_to_allocate) { - unsigned int available_size = GetFreeSize(); - high_water_mark_ = - std::max(high_water_mark_, last_allocated_size_ - available_size + - size_to_allocate + - GetPreviousRingBufferUsedBytes()); - if (size_to_allocate > available_size) { - // Try to expand the ring buffer. - ReallocateRingBuffer(high_water_mark_); - } else if (bytes_since_last_shrink_ > high_water_mark_ * kShrinkThreshold) { - // The intent of the above check is to limit the frequency of buffer shrink - // attempts. Unfortunately if an application uploads a large amount of data - // once and from then on uploads only a small amount per frame, it will be a - // very long time before we attempt to shrink (or forever, if no data is - // uploaded). - // TODO(jdarpinian): Change this heuristic to be based on frame number - // instead, and consider shrinking at the end of each frame (for clients - // that have a notion of frames). - bytes_since_last_shrink_ = 0; - ReallocateRingBuffer(high_water_mark_ + high_water_mark_ / 4, - true /* shrink */); - high_water_mark_ = size_to_allocate + GetPreviousRingBufferUsedBytes(); - } -} - void* TransferBuffer::AllocUpTo( unsigned int size, unsigned int* size_allocated) { DCHECK(size_allocated); - ShrinkOrExpandRingBufferIfNecessary(size); + ReallocateRingBuffer(size); if (!HaveBuffer()) { return NULL; @@ -203,12 +168,12 @@ void* TransferBuffer::AllocUpTo( unsigned int max_size = ring_buffer_->GetLargestFreeOrPendingSize(); *size_allocated = std::min(max_size, size); - bytes_since_last_shrink_ += *size_allocated; + bytes_since_last_flush_ += *size_allocated; return ring_buffer_->Alloc(*size_allocated); } void* TransferBuffer::Alloc(unsigned int size) { - ShrinkOrExpandRingBufferIfNecessary(size); + ReallocateRingBuffer(size); if (!HaveBuffer()) { return NULL; @@ -218,7 +183,8 @@ void* TransferBuffer::Alloc(unsigned int size) { if (size > max_size) { return NULL; } - bytes_since_last_shrink_ += size; + + bytes_since_last_flush_ += size; return ring_buffer_->Alloc(size); } diff --git a/gpu/command_buffer/client/transfer_buffer.h b/gpu/command_buffer/client/transfer_buffer.h index 317998452d2d0d..e5d0d332633e09 100644 --- a/gpu/command_buffer/client/transfer_buffer.h +++ b/gpu/command_buffer/client/transfer_buffer.h @@ -11,7 +11,6 @@ #include #include "base/compiler_specific.h" -#include "base/containers/circular_deque.h" #include "base/macros.h" #include "gpu/command_buffer/client/ring_buffer.h" #include "gpu/command_buffer/common/buffer.h" @@ -37,11 +36,13 @@ class GPU_EXPORT TransferBufferInterface { // Otherwise, this returns an invalid handle. virtual base::SharedMemoryHandle shared_memory_handle() const = 0; - virtual bool Initialize(unsigned int buffer_size, - unsigned int result_size, - unsigned int min_buffer_size, - unsigned int max_buffer_size, - unsigned int alignment) = 0; + virtual bool Initialize( + unsigned int buffer_size, + unsigned int result_size, + unsigned int min_buffer_size, + unsigned int max_buffer_size, + unsigned int alignment, + unsigned int size_to_flush) = 0; virtual int GetShmId() = 0; virtual void* GetResultBuffer() = 0; @@ -85,7 +86,8 @@ class GPU_EXPORT TransferBuffer : public TransferBufferInterface { unsigned int result_size, unsigned int min_buffer_size, unsigned int max_buffer_size, - unsigned int alignment) override; + unsigned int alignment, + unsigned int size_to_flush) override; int GetShmId() override; void* GetResultBuffer() override; int GetResultOffset() override; @@ -105,25 +107,14 @@ class GPU_EXPORT TransferBuffer : public TransferBufferInterface { unsigned int GetCurrentMaxAllocationWithoutRealloc() const; unsigned int GetMaxAllocation() const; - // We will attempt to shrink the ring buffer once the number of bytes - // allocated reaches this threshold times the high water mark. - static const int kShrinkThreshold = 120; - private: // Tries to reallocate the ring buffer if it's not large enough for size. - void ReallocateRingBuffer(unsigned int size, bool shrink = false); + void ReallocateRingBuffer(unsigned int size); void AllocateRingBuffer(unsigned int size); - void ShrinkOrExpandRingBufferIfNecessary(unsigned int size); - - // Returns the number of bytes that are still in use in ring buffers that we - // previously freed. - unsigned int GetPreviousRingBufferUsedBytes(); - CommandBufferHelper* helper_; std::unique_ptr ring_buffer_; - base::circular_deque> previous_ring_buffers_; // size reserved for results unsigned int result_size_; @@ -137,17 +128,14 @@ class GPU_EXPORT TransferBuffer : public TransferBufferInterface { // max size we'll let the buffer grow unsigned int max_buffer_size_; - // Size of the currently allocated ring buffer. - unsigned int last_allocated_size_ = 0; - - // The size to shrink the ring buffer to next time shrinking happens. - unsigned int high_water_mark_ = 0; - // alignment for allocations unsigned int alignment_; - // Number of bytes since we last attempted to shrink the ring buffer. - unsigned int bytes_since_last_shrink_ = 0; + // Size at which to do an async flush. 0 = never. + unsigned int size_to_flush_; + + // Number of bytes since we last flushed. + unsigned int bytes_since_last_flush_; // the current buffer. scoped_refptr buffer_; diff --git a/gpu/command_buffer/client/transfer_buffer_unittest.cc b/gpu/command_buffer/client/transfer_buffer_unittest.cc index 2bc0f5fa30d4ae..92a0d3bc39650c 100644 --- a/gpu/command_buffer/client/transfer_buffer_unittest.cc +++ b/gpu/command_buffer/client/transfer_buffer_unittest.cc @@ -44,10 +44,14 @@ class TransferBufferTest : public testing::Test { void SetUp() override; void TearDown() override; - virtual void Initialize() { + virtual void Initialize(unsigned int size_to_flush) { ASSERT_TRUE(transfer_buffer_->Initialize( - kTransferBufferSize, kStartingOffset, kTransferBufferSize, - kTransferBufferSize, kAlignment)); + kTransferBufferSize, + kStartingOffset, + kTransferBufferSize, + kTransferBufferSize, + kAlignment, + size_to_flush)); } MockClientCommandBufferMockFlush* command_buffer() const { @@ -97,7 +101,7 @@ const size_t TransferBufferTest::kTransferBufferSize; #endif TEST_F(TransferBufferTest, Basic) { - Initialize(); + Initialize(0); EXPECT_TRUE(transfer_buffer_->HaveBuffer()); EXPECT_EQ(transfer_buffer_id_, transfer_buffer_->GetShmId()); EXPECT_EQ( @@ -108,7 +112,7 @@ TEST_F(TransferBufferTest, Basic) { } TEST_F(TransferBufferTest, Free) { - Initialize(); + Initialize(0); EXPECT_TRUE(transfer_buffer_->HaveBuffer()); EXPECT_EQ(transfer_buffer_id_, transfer_buffer_->GetShmId()); EXPECT_NE(base::UnguessableToken(), @@ -210,7 +214,7 @@ TEST_F(TransferBufferTest, Free) { } TEST_F(TransferBufferTest, TooLargeAllocation) { - Initialize(); + Initialize(0); // Check that we can't allocate large than max size. void* ptr = transfer_buffer_->Alloc(kTransferBufferSize + 1); EXPECT_TRUE(ptr == NULL); @@ -224,7 +228,7 @@ TEST_F(TransferBufferTest, TooLargeAllocation) { } TEST_F(TransferBufferTest, MemoryAlignmentAfterZeroAllocation) { - Initialize(); + Initialize(32u); void* ptr = transfer_buffer_->Alloc(0); EXPECT_EQ((reinterpret_cast(ptr) & (kAlignment - 1)), 0u); transfer_buffer_->FreePendingToken(ptr, helper_->InsertToken()); @@ -234,6 +238,32 @@ TEST_F(TransferBufferTest, MemoryAlignmentAfterZeroAllocation) { transfer_buffer_->FreePendingToken(ptr, helper_->InsertToken()); } +TEST_F(TransferBufferTest, Flush) { + Initialize(16u); + unsigned int size_allocated = 0; + for (int i = 0; i < 8; ++i) { + void* ptr = transfer_buffer_->AllocUpTo(8u, &size_allocated); + ASSERT_TRUE(ptr != NULL); + EXPECT_EQ(8u, size_allocated); + if (i % 2) { + EXPECT_CALL(*command_buffer(), Flush(_)) + .Times(1) + .RetiresOnSaturation(); + } + transfer_buffer_->FreePendingToken(ptr, helper_->InsertToken()); + } + for (int i = 0; i < 8; ++i) { + void* ptr = transfer_buffer_->Alloc(8u); + ASSERT_TRUE(ptr != NULL); + if (i % 2) { + EXPECT_CALL(*command_buffer(), Flush(_)) + .Times(1) + .RetiresOnSaturation(); + } + transfer_buffer_->FreePendingToken(ptr, helper_->InsertToken()); + } +} + class MockClientCommandBufferCanFail : public MockClientCommandBufferMockFlush { public: MockClientCommandBufferCanFail() = default; @@ -278,7 +308,6 @@ class TransferBufferExpandContractTest : public testing::Test { void TransferBufferExpandContractTest::SetUp() { command_buffer_.reset(new StrictMock()); - command_buffer_->SetTokenForSetGetBuffer(0); EXPECT_CALL(*command_buffer(), CreateTransferBuffer(kCommandBufferSizeBytes, _)) @@ -302,8 +331,12 @@ void TransferBufferExpandContractTest::SetUp() { transfer_buffer_.reset(new TransferBuffer(helper_.get())); ASSERT_TRUE(transfer_buffer_->Initialize( - kStartTransferBufferSize, kStartingOffset, kMinTransferBufferSize, - kMaxTransferBufferSize, kAlignment)); + kStartTransferBufferSize, + kStartingOffset, + kMinTransferBufferSize, + kMaxTransferBufferSize, + kAlignment, + 0)); } void TransferBufferExpandContractTest::TearDown() { @@ -337,153 +370,62 @@ const size_t TransferBufferExpandContractTest::kMaxTransferBufferSize; const size_t TransferBufferExpandContractTest::kMinTransferBufferSize; #endif -TEST_F(TransferBufferExpandContractTest, ExpandWithSmallAllocations) { - int32_t token = helper_->InsertToken(); - EXPECT_FALSE(helper_->HasTokenPassed(token)); - - auto ExpectCreateTransferBuffer = [&](int size) { - EXPECT_CALL(*command_buffer(), DestroyTransferBuffer(_)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*command_buffer(), OrderingBarrier(_)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*command_buffer(), CreateTransferBuffer(size, _)) - .WillOnce( - Invoke(command_buffer(), - &MockClientCommandBufferCanFail::RealCreateTransferBuffer)) - .RetiresOnSaturation(); - }; - +TEST_F(TransferBufferExpandContractTest, Expand) { // Check it starts at starting size. EXPECT_EQ( kStartTransferBufferSize - kStartingOffset, transfer_buffer_->GetCurrentMaxAllocationWithoutRealloc()); - // Fill the free space. - unsigned int size_allocated = 0; - void* ptr = transfer_buffer_->AllocUpTo(transfer_buffer_->GetFreeSize(), - &size_allocated); - transfer_buffer_->FreePendingToken(ptr, token); - - // Allocate one more byte to force expansion. - ExpectCreateTransferBuffer(kStartTransferBufferSize * 2); - ptr = transfer_buffer_->AllocUpTo(1, &size_allocated); - ASSERT_TRUE(ptr != NULL); - EXPECT_EQ(1u, size_allocated); - transfer_buffer_->FreePendingToken(ptr, token); - - // Fill free space and expand again. - ptr = transfer_buffer_->AllocUpTo(transfer_buffer_->GetFreeSize(), - &size_allocated); - transfer_buffer_->FreePendingToken(ptr, token); - ExpectCreateTransferBuffer(kStartTransferBufferSize * 4); - ptr = transfer_buffer_->AllocUpTo(1, &size_allocated); - ASSERT_TRUE(ptr != NULL); - EXPECT_EQ(1u, size_allocated); - transfer_buffer_->FreePendingToken(ptr, token); - - // Try to expand again, no expansion should occur because we are at max. - ptr = transfer_buffer_->AllocUpTo(transfer_buffer_->GetFreeSize(), - &size_allocated); - transfer_buffer_->FreePendingToken(ptr, token); - EXPECT_CALL(*command_buffer(), Flush(_)).Times(1).RetiresOnSaturation(); - ptr = transfer_buffer_->AllocUpTo(1, &size_allocated); - ASSERT_TRUE(ptr != NULL); - EXPECT_EQ(1u, size_allocated); - transfer_buffer_->FreePendingToken(ptr, token); - EXPECT_EQ(kMaxTransferBufferSize - kStartingOffset, - transfer_buffer_->GetCurrentMaxAllocationWithoutRealloc()); -} - -TEST_F(TransferBufferExpandContractTest, ExpandWithLargeAllocations) { - int32_t token = helper_->InsertToken(); - EXPECT_FALSE(helper_->HasTokenPassed(token)); - - auto ExpectCreateTransferBuffer = [&](int size) { - EXPECT_CALL(*command_buffer(), DestroyTransferBuffer(_)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*command_buffer(), OrderingBarrier(_)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*command_buffer(), CreateTransferBuffer(size, _)) - .WillOnce( - Invoke(command_buffer(), - &MockClientCommandBufferCanFail::RealCreateTransferBuffer)) - .RetiresOnSaturation(); - }; - - // Check it starts at starting size. - EXPECT_EQ(kStartTransferBufferSize - kStartingOffset, - transfer_buffer_->GetCurrentMaxAllocationWithoutRealloc()); + EXPECT_CALL(*command_buffer(), DestroyTransferBuffer(_)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*command_buffer(), OrderingBarrier(_)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*command_buffer(), + CreateTransferBuffer(kStartTransferBufferSize * 2, _)) + .WillOnce(Invoke( + command_buffer(), + &MockClientCommandBufferCanFail::RealCreateTransferBuffer)) + .RetiresOnSaturation(); - // Allocate one byte more than the free space to force expansion. + // Try next power of 2. + const size_t kSize1 = 512 - kStartingOffset; unsigned int size_allocated = 0; - ExpectCreateTransferBuffer(kStartTransferBufferSize * 2); - void* ptr = transfer_buffer_->AllocUpTo(transfer_buffer_->GetFreeSize() + 1, - &size_allocated); - transfer_buffer_->FreePendingToken(ptr, token); - - // Expand again. - ExpectCreateTransferBuffer(kStartTransferBufferSize * 4); - unsigned int size_requested = transfer_buffer_->GetFreeSize() + 1; - ptr = transfer_buffer_->AllocUpTo(size_requested, &size_allocated); + void* ptr = transfer_buffer_->AllocUpTo(kSize1, &size_allocated); ASSERT_TRUE(ptr != NULL); - EXPECT_EQ(size_requested, size_allocated); - transfer_buffer_->FreePendingToken(ptr, token); + EXPECT_EQ(kSize1, size_allocated); + EXPECT_EQ(kSize1, transfer_buffer_->GetCurrentMaxAllocationWithoutRealloc()); + transfer_buffer_->FreePendingToken(ptr, 1); - // Try to expand again, no expansion should occur because we are at max. - EXPECT_CALL(*command_buffer(), Flush(_)).Times(1).RetiresOnSaturation(); - size_requested = - transfer_buffer_->GetCurrentMaxAllocationWithoutRealloc() + 1; - ptr = transfer_buffer_->AllocUpTo(size_requested, &size_allocated); - EXPECT_LT(size_allocated, size_requested); - transfer_buffer_->FreePendingToken(ptr, token); - EXPECT_EQ(kMaxTransferBufferSize - kStartingOffset, - transfer_buffer_->GetCurrentMaxAllocationWithoutRealloc()); -} + EXPECT_CALL(*command_buffer(), DestroyTransferBuffer(_)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*command_buffer(), OrderingBarrier(_)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*command_buffer(), + CreateTransferBuffer(kMaxTransferBufferSize, _)) + .WillOnce(Invoke( + command_buffer(), + &MockClientCommandBufferCanFail::RealCreateTransferBuffer)) + .RetiresOnSaturation(); -TEST_F(TransferBufferExpandContractTest, ShrinkRingBuffer) { - int32_t token = helper_->InsertToken(); - // For this test we want all allocations to be freed immediately. - command_buffer_->SetToken(token); - EXPECT_TRUE(helper_->HasTokenPassed(token)); + // Try next power of 2. + const size_t kSize2 = 1024 - kStartingOffset; + ptr = transfer_buffer_->AllocUpTo(kSize2, &size_allocated); + ASSERT_TRUE(ptr != NULL); + EXPECT_EQ(kSize2, size_allocated); + EXPECT_EQ(kSize2, transfer_buffer_->GetCurrentMaxAllocationWithoutRealloc()); + transfer_buffer_->FreePendingToken(ptr, 1); - auto ExpectCreateTransferBuffer = [&](int size) { - EXPECT_CALL(*command_buffer(), DestroyTransferBuffer(_)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*command_buffer(), OrderingBarrier(_)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*command_buffer(), CreateTransferBuffer(size, _)) - .WillOnce( - Invoke(command_buffer(), - &MockClientCommandBufferCanFail::RealCreateTransferBuffer)) - .RetiresOnSaturation(); - }; - - // Expand the ring buffer to the maximum size. - ExpectCreateTransferBuffer(kMaxTransferBufferSize); - void* ptr = transfer_buffer_->Alloc(kMaxTransferBufferSize - kStartingOffset); - EXPECT_TRUE(ptr != NULL); - transfer_buffer_->FreePendingToken(ptr, token); - - // We shouldn't shrink before we reach the allocation threshold. - for (size_t allocated = kMaxTransferBufferSize - kStartingOffset; - allocated < (kStartTransferBufferSize + kStartingOffset) * - (TransferBuffer::kShrinkThreshold);) { - ptr = transfer_buffer_->Alloc(kStartTransferBufferSize); - EXPECT_TRUE(ptr != NULL); - transfer_buffer_->FreePendingToken(ptr, token); - allocated += kStartTransferBufferSize; - } - // The next allocation should trip the threshold and shrink. - ExpectCreateTransferBuffer(kStartTransferBufferSize * 2); - ptr = transfer_buffer_->Alloc(1); - EXPECT_TRUE(ptr != NULL); - transfer_buffer_->FreePendingToken(ptr, token); + // Try next one more. Should not go past max. + size_allocated = 0; + const size_t kSize3 = kSize2 + 1; + ptr = transfer_buffer_->AllocUpTo(kSize3, &size_allocated); + EXPECT_EQ(kSize2, size_allocated); + EXPECT_EQ(kSize2, transfer_buffer_->GetCurrentMaxAllocationWithoutRealloc()); + transfer_buffer_->FreePendingToken(ptr, 1); } TEST_F(TransferBufferExpandContractTest, Contract) {