1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // This file contains the implementation of the RingBuffer class.
7 #include "gpu/command_buffer/client/ring_buffer.h"
11 #include "base/logging.h"
12 #include "gpu/command_buffer/client/cmd_buffer_helper.h"
16 RingBuffer::RingBuffer(
17 Offset base_offset, unsigned int size, CommandBufferHelper* helper)
19 base_offset_(base_offset),
25 RingBuffer::~RingBuffer() {
26 // Free blocks pending tokens.
27 while (!blocks_.empty()) {
32 void RingBuffer::FreeOldestBlock() {
33 DCHECK(!blocks_.empty()) << "no free blocks";
34 Block& block = blocks_.front();
35 DCHECK(block.state != IN_USE)
36 << "attempt to allocate more than maximum memory";
37 if (block.state == FREE_PENDING_TOKEN) {
38 helper_->WaitForToken(block.token);
40 in_use_offset_ += block.size;
41 if (in_use_offset_ == size_) {
44 // If they match then the entire buffer is free.
45 if (in_use_offset_ == free_offset_) {
52 RingBuffer::Offset RingBuffer::Alloc(unsigned int size) {
53 DCHECK_LE(size, size_) << "attempt to allocate more than maximum memory";
54 DCHECK(blocks_.empty() || blocks_.back().state != IN_USE)
55 << "Attempt to alloc another block before freeing the previous.";
56 // Similarly to malloc, an allocation of 0 allocates at least 1 byte, to
57 // return different pointers every time.
58 if (size == 0) size = 1;
60 // Wait until there is enough room.
61 while (size > GetLargestFreeSizeNoWaiting()) {
65 if (size + free_offset_ > size_) {
66 // Add padding to fill space before wrapping around
67 blocks_.push_back(Block(free_offset_, size_ - free_offset_, PADDING));
71 Offset offset = free_offset_;
72 blocks_.push_back(Block(offset, size, IN_USE));
74 if (free_offset_ == size_) {
77 return offset + base_offset_;
80 void RingBuffer::FreePendingToken(RingBuffer::Offset offset,
82 offset -= base_offset_;
83 DCHECK(!blocks_.empty()) << "no allocations to free";
84 for (Container::reverse_iterator it = blocks_.rbegin();
88 if (block.offset == offset) {
89 DCHECK(block.state == IN_USE)
90 << "block that corresponds to offset already freed";
92 block.state = FREE_PENDING_TOKEN;
96 NOTREACHED() << "attempt to free non-existant block";
99 unsigned int RingBuffer::GetLargestFreeSizeNoWaiting() {
100 unsigned int last_token_read = helper_->last_token_read();
101 while (!blocks_.empty()) {
102 Block& block = blocks_.front();
103 if (block.token > last_token_read || block.state == IN_USE) break;
106 if (free_offset_ == in_use_offset_) {
107 if (blocks_.empty()) {
108 // The entire buffer is free.
109 DCHECK_EQ(free_offset_, 0u);
112 // The entire buffer is in use.
115 } else if (free_offset_ > in_use_offset_) {
116 // It's free from free_offset_ to size_ and from 0 to in_use_offset_
117 return std::max(size_ - free_offset_, in_use_offset_);
119 // It's free from free_offset_ -> in_use_offset_;
120 return in_use_offset_ - free_offset_;