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(unsigned int alignment
, Offset base_offset
,
17 unsigned int size
, CommandBufferHelper
* helper
,
20 base_offset_(base_offset
),
24 alignment_(alignment
),
25 base_(static_cast<int8
*>(base
) - base_offset
) {
28 RingBuffer::~RingBuffer() {
29 // Free blocks pending tokens.
30 while (!blocks_
.empty()) {
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_
) {
47 // If they match then the entire buffer is free.
48 if (in_use_offset_
== free_offset_
) {
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
64 size
= RoundToAlignment(size
);
66 // Wait until there is enough room.
67 while (size
> GetLargestFreeSizeNoWaiting()) {
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
));
77 Offset offset
= free_offset_
;
78 blocks_
.push_back(Block(offset
, size
, IN_USE
));
80 if (free_offset_
== size_
) {
83 return GetPointer(offset
+ base_offset_
);
86 void RingBuffer::FreePendingToken(void* pointer
,
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();
95 if (block
.offset
== offset
) {
96 DCHECK(block
.state
== IN_USE
)
97 << "block that corresponds to offset already freed";
99 block
.state
= FREE_PENDING_TOKEN
;
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();
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
;
125 // Remove blocks if it were in the front along with extra padding.
126 while (!blocks_
.empty() && blocks_
.front().state
== PADDING
) {
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()) {
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;
152 if (free_offset_
== in_use_offset_
) {
153 if (blocks_
.empty()) {
154 // The entire buffer is free.
155 DCHECK_EQ(free_offset_
, 0u);
158 // The entire buffer is in use.
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_
);
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_
;
176 return largest_free_size
;