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