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 14namespace gpu { 15 16RingBuffer::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 28RingBuffer::~RingBuffer() { 29 // Free blocks pending tokens. 30 while (!blocks_.empty()) { 31 FreeOldestBlock(); 32 } 33} 34 35void 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 55void* 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 86void 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 106unsigned 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