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 #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