• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(unsigned int alignment,Offset base_offset,unsigned int size,CommandBufferHelper * helper,void * base)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) {
26 }
27 
~RingBuffer()28 RingBuffer::~RingBuffer() {
29   // Free blocks pending tokens.
30   while (!blocks_.empty()) {
31     FreeOldestBlock();
32   }
33 }
34 
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);
42   }
43   in_use_offset_ += block.size;
44   if (in_use_offset_ == size_) {
45     in_use_offset_ = 0;
46   }
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;
51   }
52   blocks_.pop_front();
53 }
54 
Alloc(unsigned int size)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);
65 
66   // Wait until there is enough room.
67   while (size > GetLargestFreeSizeNoWaiting()) {
68     FreeOldestBlock();
69   }
70 
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;
75   }
76 
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;
82   }
83   return GetPointer(offset + base_offset_);
84 }
85 
FreePendingToken(void * pointer,unsigned int token)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;
101     }
102   }
103   NOTREACHED() << "attempt to free non-existant block";
104 }
105 
GetLargestFreeSizeNoWaiting()106 unsigned int RingBuffer::GetLargestFreeSizeNoWaiting() {
107   unsigned int last_token_read = helper_->last_token_read();
108   while (!blocks_.empty()) {
109     Block& block = blocks_.front();
110     if (block.token > last_token_read || block.state == IN_USE) break;
111     FreeOldestBlock();
112   }
113   if (free_offset_ == in_use_offset_) {
114     if (blocks_.empty()) {
115       // The entire buffer is free.
116       DCHECK_EQ(free_offset_, 0u);
117       return size_;
118     } else {
119       // The entire buffer is in use.
120       return 0;
121     }
122   } else if (free_offset_ > in_use_offset_) {
123     // It's free from free_offset_ to size_ and from 0 to in_use_offset_
124     return std::max(size_ - free_offset_, in_use_offset_);
125   } else {
126     // It's free from free_offset_ -> in_use_offset_;
127     return in_use_offset_ - free_offset_;
128   }
129 }
130 
131 }  // namespace gpu
132