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 #include <algorithm> 9 #include "gpu/command_buffer/client/cmd_buffer_helper.h" 10 11 namespace gpu { 12 13 RingBuffer::RingBuffer( 14 Offset base_offset, unsigned int size, CommandBufferHelper* helper) 15 : helper_(helper), 16 base_offset_(base_offset), 17 size_(size), 18 free_offset_(0), 19 in_use_offset_(0) { 20 } 21 22 RingBuffer::~RingBuffer() { 23 // Free blocks pending tokens. 24 while (!blocks_.empty()) { 25 FreeOldestBlock(); 26 } 27 } 28 29 void RingBuffer::FreeOldestBlock() { 30 GPU_DCHECK(!blocks_.empty()) << "no free blocks"; 31 Block& block = blocks_.front(); 32 GPU_DCHECK(block.state != IN_USE) 33 << "attempt to allocate more than maximum memory"; 34 if (block.state == FREE_PENDING_TOKEN) { 35 helper_->WaitForToken(block.token); 36 } 37 in_use_offset_ += block.size; 38 if (in_use_offset_ == size_) { 39 in_use_offset_ = 0; 40 } 41 // If they match then the entire buffer is free. 42 if (in_use_offset_ == free_offset_) { 43 in_use_offset_ = 0; 44 free_offset_ = 0; 45 } 46 blocks_.pop_front(); 47 } 48 49 RingBuffer::Offset RingBuffer::Alloc(unsigned int size) { 50 GPU_DCHECK_LE(size, size_) << "attempt to allocate more than maximum memory"; 51 GPU_DCHECK(blocks_.empty() || blocks_.back().state != IN_USE) 52 << "Attempt to alloc another block before freeing the previous."; 53 // Similarly to malloc, an allocation of 0 allocates at least 1 byte, to 54 // return different pointers every time. 55 if (size == 0) size = 1; 56 57 // Wait until there is enough room. 58 while (size > GetLargestFreeSizeNoWaiting()) { 59 FreeOldestBlock(); 60 } 61 62 if (size + free_offset_ > size_) { 63 // Add padding to fill space before wrapping around 64 blocks_.push_back(Block(free_offset_, size_ - free_offset_, PADDING)); 65 free_offset_ = 0; 66 } 67 68 Offset offset = free_offset_; 69 blocks_.push_back(Block(offset, size, IN_USE)); 70 free_offset_ += size; 71 if (free_offset_ == size_) { 72 free_offset_ = 0; 73 } 74 return offset + base_offset_; 75 } 76 77 void RingBuffer::FreePendingToken(RingBuffer::Offset offset, 78 unsigned int token) { 79 offset -= base_offset_; 80 GPU_DCHECK(!blocks_.empty()) << "no allocations to free"; 81 for (Container::reverse_iterator it = blocks_.rbegin(); 82 it != blocks_.rend(); 83 ++it) { 84 Block& block = *it; 85 if (block.offset == offset) { 86 GPU_DCHECK(block.state == IN_USE) 87 << "block that corresponds to offset already freed"; 88 block.token = token; 89 block.state = FREE_PENDING_TOKEN; 90 return; 91 } 92 } 93 GPU_NOTREACHED() << "attempt to free non-existant block"; 94 } 95 96 unsigned int RingBuffer::GetLargestFreeSizeNoWaiting() { 97 unsigned int last_token_read = helper_->last_token_read(); 98 while (!blocks_.empty()) { 99 Block& block = blocks_.front(); 100 if (block.token > last_token_read || block.state == IN_USE) break; 101 FreeOldestBlock(); 102 } 103 if (free_offset_ == in_use_offset_) { 104 if (blocks_.empty()) { 105 // The entire buffer is free. 106 GPU_DCHECK_EQ(free_offset_, 0u); 107 return size_; 108 } else { 109 // The entire buffer is in use. 110 return 0; 111 } 112 } else if (free_offset_ > in_use_offset_) { 113 // It's free from free_offset_ to size_ and from 0 to in_use_offset_ 114 return std::max(size_ - free_offset_, in_use_offset_); 115 } else { 116 // It's free from free_offset_ -> in_use_offset_; 117 return in_use_offset_ - free_offset_; 118 } 119 } 120 121 } // namespace gpu 122