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.
4
5 // This file contains the implementation of the RingBuffer class.
6
7 #include "gpu/command_buffer/client/ring_buffer.h"
8
9 #include <algorithm>
10
11 #include "base/logging.h"
12 #include "gpu/command_buffer/client/cmd_buffer_helper.h"
13
14 namespace gpu {
15
RingBuffer(Offset base_offset,unsigned int size,CommandBufferHelper * helper)16 RingBuffer::RingBuffer(
17 Offset base_offset, unsigned int size, CommandBufferHelper* helper)
18 : helper_(helper),
19 base_offset_(base_offset),
20 size_(size),
21 free_offset_(0),
22 in_use_offset_(0) {
23 }
24
~RingBuffer()25 RingBuffer::~RingBuffer() {
26 // Free blocks pending tokens.
27 while (!blocks_.empty()) {
28 FreeOldestBlock();
29 }
30 }
31
FreeOldestBlock()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);
39 }
40 in_use_offset_ += block.size;
41 if (in_use_offset_ == size_) {
42 in_use_offset_ = 0;
43 }
44 // If they match then the entire buffer is free.
45 if (in_use_offset_ == free_offset_) {
46 in_use_offset_ = 0;
47 free_offset_ = 0;
48 }
49 blocks_.pop_front();
50 }
51
Alloc(unsigned int size)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;
59
60 // Wait until there is enough room.
61 while (size > GetLargestFreeSizeNoWaiting()) {
62 FreeOldestBlock();
63 }
64
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));
68 free_offset_ = 0;
69 }
70
71 Offset offset = free_offset_;
72 blocks_.push_back(Block(offset, size, IN_USE));
73 free_offset_ += size;
74 if (free_offset_ == size_) {
75 free_offset_ = 0;
76 }
77 return offset + base_offset_;
78 }
79
FreePendingToken(RingBuffer::Offset offset,unsigned int token)80 void RingBuffer::FreePendingToken(RingBuffer::Offset offset,
81 unsigned int token) {
82 offset -= base_offset_;
83 DCHECK(!blocks_.empty()) << "no allocations to free";
84 for (Container::reverse_iterator it = blocks_.rbegin();
85 it != blocks_.rend();
86 ++it) {
87 Block& block = *it;
88 if (block.offset == offset) {
89 DCHECK(block.state == IN_USE)
90 << "block that corresponds to offset already freed";
91 block.token = token;
92 block.state = FREE_PENDING_TOKEN;
93 return;
94 }
95 }
96 NOTREACHED() << "attempt to free non-existant block";
97 }
98
GetLargestFreeSizeNoWaiting()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;
104 FreeOldestBlock();
105 }
106 if (free_offset_ == in_use_offset_) {
107 if (blocks_.empty()) {
108 // The entire buffer is free.
109 DCHECK_EQ(free_offset_, 0u);
110 return size_;
111 } else {
112 // The entire buffer is in use.
113 return 0;
114 }
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_);
118 } else {
119 // It's free from free_offset_ -> in_use_offset_;
120 return in_use_offset_ - free_offset_;
121 }
122 }
123
124 } // namespace gpu
125