diff options
Diffstat (limited to 'chromium/gpu/command_buffer/client/ring_buffer.cc')
-rw-r--r-- | chromium/gpu/command_buffer/client/ring_buffer.cc | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/chromium/gpu/command_buffer/client/ring_buffer.cc b/chromium/gpu/command_buffer/client/ring_buffer.cc new file mode 100644 index 00000000000..42e09bd3e1f --- /dev/null +++ b/chromium/gpu/command_buffer/client/ring_buffer.cc @@ -0,0 +1,121 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains the implementation of the RingBuffer class. + +#include "gpu/command_buffer/client/ring_buffer.h" +#include <algorithm> +#include "gpu/command_buffer/client/cmd_buffer_helper.h" + +namespace gpu { + +RingBuffer::RingBuffer( + Offset base_offset, unsigned int size, CommandBufferHelper* helper) + : helper_(helper), + base_offset_(base_offset), + size_(size), + free_offset_(0), + in_use_offset_(0) { +} + +RingBuffer::~RingBuffer() { + // Free blocks pending tokens. + while (!blocks_.empty()) { + FreeOldestBlock(); + } +} + +void RingBuffer::FreeOldestBlock() { + GPU_DCHECK(!blocks_.empty()) << "no free blocks"; + Block& block = blocks_.front(); + GPU_DCHECK(block.state != IN_USE) + << "attempt to allocate more than maximum memory"; + if (block.state == FREE_PENDING_TOKEN) { + helper_->WaitForToken(block.token); + } + in_use_offset_ += block.size; + if (in_use_offset_ == size_) { + in_use_offset_ = 0; + } + // If they match then the entire buffer is free. + if (in_use_offset_ == free_offset_) { + in_use_offset_ = 0; + free_offset_ = 0; + } + blocks_.pop_front(); +} + +RingBuffer::Offset RingBuffer::Alloc(unsigned int size) { + GPU_DCHECK_LE(size, size_) << "attempt to allocate more than maximum memory"; + GPU_DCHECK(blocks_.empty() || blocks_.back().state != IN_USE) + << "Attempt to alloc another block before freeing the previous."; + // Similarly to malloc, an allocation of 0 allocates at least 1 byte, to + // return different pointers every time. + if (size == 0) size = 1; + + // Wait until there is enough room. + while (size > GetLargestFreeSizeNoWaiting()) { + FreeOldestBlock(); + } + + if (size + free_offset_ > size_) { + // Add padding to fill space before wrapping around + blocks_.push_back(Block(free_offset_, size_ - free_offset_, PADDING)); + free_offset_ = 0; + } + + Offset offset = free_offset_; + blocks_.push_back(Block(offset, size, IN_USE)); + free_offset_ += size; + if (free_offset_ == size_) { + free_offset_ = 0; + } + return offset + base_offset_; +} + +void RingBuffer::FreePendingToken(RingBuffer::Offset offset, + unsigned int token) { + offset -= base_offset_; + GPU_DCHECK(!blocks_.empty()) << "no allocations to free"; + for (Container::reverse_iterator it = blocks_.rbegin(); + it != blocks_.rend(); + ++it) { + Block& block = *it; + if (block.offset == offset) { + GPU_DCHECK(block.state == IN_USE) + << "block that corresponds to offset already freed"; + block.token = token; + block.state = FREE_PENDING_TOKEN; + return; + } + } + GPU_NOTREACHED() << "attempt to free non-existant block"; +} + +unsigned int RingBuffer::GetLargestFreeSizeNoWaiting() { + unsigned int last_token_read = helper_->last_token_read(); + while (!blocks_.empty()) { + Block& block = blocks_.front(); + if (block.token > last_token_read || block.state == IN_USE) break; + FreeOldestBlock(); + } + if (free_offset_ == in_use_offset_) { + if (blocks_.empty()) { + // The entire buffer is free. + GPU_DCHECK_EQ(free_offset_, 0u); + return size_; + } else { + // The entire buffer is in use. + return 0; + } + } else if (free_offset_ > in_use_offset_) { + // It's free from free_offset_ to size_ and from 0 to in_use_offset_ + return std::max(size_ - free_offset_, in_use_offset_); + } else { + // It's free from free_offset_ -> in_use_offset_; + return in_use_offset_ - free_offset_; + } +} + +} // namespace gpu |