Home | History | Annotate | Download | only in client
      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