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 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 25 RingBuffer::~RingBuffer() { 26 // Free blocks pending tokens. 27 while (!blocks_.empty()) { 28 FreeOldestBlock(); 29 } 30 } 31 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 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 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 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