Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / gpu / command_buffer / client / ring_buffer.cc
blob002d529c6d7e9313ebbc43b98738fb15d2858db3
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"
9 #include <algorithm>
11 #include "base/logging.h"
12 #include "gpu/command_buffer/client/cmd_buffer_helper.h"
14 namespace gpu {
16 RingBuffer::RingBuffer(unsigned int alignment, Offset base_offset,
17 unsigned int size, CommandBufferHelper* helper,
18 void* base)
19 : helper_(helper),
20 base_offset_(base_offset),
21 size_(size),
22 free_offset_(0),
23 in_use_offset_(0),
24 alignment_(alignment),
25 base_(static_cast<int8*>(base) - base_offset) {
28 RingBuffer::~RingBuffer() {
29 // Free blocks pending tokens.
30 while (!blocks_.empty()) {
31 FreeOldestBlock();
35 void RingBuffer::FreeOldestBlock() {
36 DCHECK(!blocks_.empty()) << "no free blocks";
37 Block& block = blocks_.front();
38 DCHECK(block.state != IN_USE)
39 << "attempt to allocate more than maximum memory";
40 if (block.state == FREE_PENDING_TOKEN) {
41 helper_->WaitForToken(block.token);
43 in_use_offset_ += block.size;
44 if (in_use_offset_ == size_) {
45 in_use_offset_ = 0;
47 // If they match then the entire buffer is free.
48 if (in_use_offset_ == free_offset_) {
49 in_use_offset_ = 0;
50 free_offset_ = 0;
52 blocks_.pop_front();
55 void* RingBuffer::Alloc(unsigned int size) {
56 DCHECK_LE(size, size_) << "attempt to allocate more than maximum memory";
57 DCHECK(blocks_.empty() || blocks_.back().state != IN_USE)
58 << "Attempt to alloc another block before freeing the previous.";
59 // Similarly to malloc, an allocation of 0 allocates at least 1 byte, to
60 // return different pointers every time.
61 if (size == 0) size = 1;
62 // Allocate rounded to alignment size so that the offsets are always
63 // memory-aligned.
64 size = RoundToAlignment(size);
66 // Wait until there is enough room.
67 while (size > GetLargestFreeSizeNoWaiting()) {
68 FreeOldestBlock();
71 if (size + free_offset_ > size_) {
72 // Add padding to fill space before wrapping around
73 blocks_.push_back(Block(free_offset_, size_ - free_offset_, PADDING));
74 free_offset_ = 0;
77 Offset offset = free_offset_;
78 blocks_.push_back(Block(offset, size, IN_USE));
79 free_offset_ += size;
80 if (free_offset_ == size_) {
81 free_offset_ = 0;
83 return GetPointer(offset + base_offset_);
86 void RingBuffer::FreePendingToken(void* pointer,
87 unsigned int token) {
88 Offset offset = GetOffset(pointer);
89 offset -= base_offset_;
90 DCHECK(!blocks_.empty()) << "no allocations to free";
91 for (Container::reverse_iterator it = blocks_.rbegin();
92 it != blocks_.rend();
93 ++it) {
94 Block& block = *it;
95 if (block.offset == offset) {
96 DCHECK(block.state == IN_USE)
97 << "block that corresponds to offset already freed";
98 block.token = token;
99 block.state = FREE_PENDING_TOKEN;
100 return;
103 NOTREACHED() << "attempt to free non-existant block";
106 void RingBuffer::DiscardBlock(void* pointer) {
107 Offset offset = GetOffset(pointer);
108 offset -= base_offset_;
109 DCHECK(!blocks_.empty()) << "no allocations to discard";
110 for (Container::reverse_iterator it = blocks_.rbegin();
111 it != blocks_.rend();
112 ++it) {
113 Block& block = *it;
114 if (block.offset == offset) {
115 DCHECK(block.state != PADDING)
116 << "block that corresponds to offset already discarded";
117 block.state = PADDING;
119 // Remove block if it were in the back along with any extra padding.
120 while (!blocks_.empty() && blocks_.back().state == PADDING) {
121 free_offset_= blocks_.back().offset;
122 blocks_.pop_back();
125 // Remove blocks if it were in the front along with extra padding.
126 while (!blocks_.empty() && blocks_.front().state == PADDING) {
127 blocks_.pop_front();
128 if (blocks_.empty())
129 break;
131 in_use_offset_ = blocks_.front().offset;
134 // In the special case when there are no blocks, we should be reset it.
135 if (blocks_.empty()) {
136 in_use_offset_ = 0;
137 free_offset_ = 0;
139 return;
142 NOTREACHED() << "attempt to discard non-existant block";
145 unsigned int RingBuffer::GetLargestFreeSizeNoWaiting() {
146 unsigned int last_token_read = helper_->last_token_read();
147 while (!blocks_.empty()) {
148 Block& block = blocks_.front();
149 if (block.token > last_token_read || block.state == IN_USE) break;
150 FreeOldestBlock();
152 if (free_offset_ == in_use_offset_) {
153 if (blocks_.empty()) {
154 // The entire buffer is free.
155 DCHECK_EQ(free_offset_, 0u);
156 return size_;
157 } else {
158 // The entire buffer is in use.
159 return 0;
161 } else if (free_offset_ > in_use_offset_) {
162 // It's free from free_offset_ to size_ and from 0 to in_use_offset_
163 return std::max(size_ - free_offset_, in_use_offset_);
164 } else {
165 // It's free from free_offset_ -> in_use_offset_;
166 return in_use_offset_ - free_offset_;
170 unsigned int RingBuffer::GetTotalFreeSizeNoWaiting() {
171 unsigned int largest_free_size = GetLargestFreeSizeNoWaiting();
172 if (free_offset_ > in_use_offset_) {
173 // It's free from free_offset_ to size_ and from 0 to in_use_offset_.
174 return size_ - free_offset_ + in_use_offset_;
175 } else {
176 return largest_free_size;
180 } // namespace gpu