Home | History | Annotate | Download | only in client
      1 // Copyright (c) 2012 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 // A class to Manage a growing transfer buffer.
      6 
      7 #include "gpu/command_buffer/client/transfer_buffer.h"
      8 #include "gpu/command_buffer/client/cmd_buffer_helper.h"
      9 
     10 namespace gpu {
     11 
     12 AlignedRingBuffer::~AlignedRingBuffer() {
     13 }
     14 
     15 TransferBuffer::TransferBuffer(
     16     CommandBufferHelper* helper)
     17     : helper_(helper),
     18       result_size_(0),
     19       default_buffer_size_(0),
     20       min_buffer_size_(0),
     21       max_buffer_size_(0),
     22       alignment_(0),
     23       size_to_flush_(0),
     24       bytes_since_last_flush_(0),
     25       buffer_id_(-1),
     26       result_buffer_(NULL),
     27       result_shm_offset_(0),
     28       usable_(true) {
     29 }
     30 
     31 TransferBuffer::~TransferBuffer() {
     32   Free();
     33 }
     34 
     35 bool TransferBuffer::Initialize(
     36     unsigned int default_buffer_size,
     37     unsigned int result_size,
     38     unsigned int min_buffer_size,
     39     unsigned int max_buffer_size,
     40     unsigned int alignment,
     41     unsigned int size_to_flush) {
     42   result_size_ = result_size;
     43   default_buffer_size_ = default_buffer_size;
     44   min_buffer_size_ = min_buffer_size;
     45   max_buffer_size_ = max_buffer_size;
     46   alignment_ = alignment;
     47   size_to_flush_ = size_to_flush;
     48   ReallocateRingBuffer(default_buffer_size_ - result_size);
     49   return HaveBuffer();
     50 }
     51 
     52 void TransferBuffer::Free() {
     53   if (HaveBuffer()) {
     54     helper_->Finish();
     55     helper_->command_buffer()->DestroyTransferBuffer(buffer_id_);
     56     buffer_id_ = -1;
     57     buffer_.ptr = NULL;
     58     buffer_.size = 0;
     59     result_buffer_ = NULL;
     60     result_shm_offset_ = 0;
     61     ring_buffer_.reset();
     62     bytes_since_last_flush_ = 0;
     63   }
     64 }
     65 
     66 bool TransferBuffer::HaveBuffer() const {
     67   return buffer_id_ != -1;
     68 }
     69 
     70 RingBuffer::Offset TransferBuffer::GetOffset(void* pointer) const {
     71   return ring_buffer_->GetOffset(pointer);
     72 }
     73 
     74 void TransferBuffer::FreePendingToken(void* p, unsigned int token) {
     75   ring_buffer_->FreePendingToken(p, token);
     76   if (bytes_since_last_flush_ >= size_to_flush_ && size_to_flush_ > 0) {
     77     helper_->Flush();
     78     bytes_since_last_flush_ = 0;
     79   }
     80 }
     81 
     82 void TransferBuffer::AllocateRingBuffer(unsigned int size) {
     83   for (;size >= min_buffer_size_; size /= 2) {
     84     int32 id = -1;
     85     gpu::Buffer buffer =
     86         helper_->command_buffer()->CreateTransferBuffer(size, &id);
     87     if (id != -1) {
     88       buffer_ = buffer;
     89       ring_buffer_.reset(new AlignedRingBuffer(
     90           alignment_,
     91           id,
     92           result_size_,
     93           buffer_.size - result_size_,
     94           helper_,
     95           static_cast<char*>(buffer_.ptr) + result_size_));
     96       buffer_id_ = id;
     97       result_buffer_ = buffer_.ptr;
     98       result_shm_offset_ = 0;
     99       return;
    100     }
    101     // we failed so don't try larger than this.
    102     max_buffer_size_ = size / 2;
    103   }
    104   usable_ = false;
    105 }
    106 
    107 // Returns the integer i such as 2^i <= n < 2^(i+1)
    108 static int Log2Floor(uint32 n) {
    109   if (n == 0)
    110     return -1;
    111   int log = 0;
    112   uint32 value = n;
    113   for (int i = 4; i >= 0; --i) {
    114     int shift = (1 << i);
    115     uint32 x = value >> shift;
    116     if (x != 0) {
    117       value = x;
    118       log += shift;
    119     }
    120   }
    121   GPU_DCHECK_EQ(value, 1u);
    122   return log;
    123 }
    124 
    125 // Returns the integer i such as 2^(i-1) < n <= 2^i
    126 static int Log2Ceiling(uint32 n) {
    127   if (n == 0) {
    128     return -1;
    129   } else {
    130     // Log2Floor returns -1 for 0, so the following works correctly for n=1.
    131     return 1 + Log2Floor(n - 1);
    132   }
    133 }
    134 
    135 static unsigned int ComputePOTSize(unsigned int dimension) {
    136   return (dimension == 0) ? 0 : 1 << Log2Ceiling(dimension);
    137 }
    138 
    139 void TransferBuffer::ReallocateRingBuffer(unsigned int size) {
    140   // What size buffer would we ask for if we needed a new one?
    141   unsigned int needed_buffer_size = ComputePOTSize(size + result_size_);
    142   needed_buffer_size = std::max(needed_buffer_size, min_buffer_size_);
    143   needed_buffer_size = std::max(needed_buffer_size, default_buffer_size_);
    144   needed_buffer_size = std::min(needed_buffer_size, max_buffer_size_);
    145 
    146   if (usable_ && (!HaveBuffer() || needed_buffer_size > buffer_.size)) {
    147     if (HaveBuffer()) {
    148       Free();
    149     }
    150     AllocateRingBuffer(needed_buffer_size);
    151   }
    152 }
    153 
    154 void* TransferBuffer::AllocUpTo(
    155     unsigned int size, unsigned int* size_allocated) {
    156   GPU_DCHECK(size_allocated);
    157 
    158   ReallocateRingBuffer(size);
    159 
    160   if (!HaveBuffer()) {
    161     return NULL;
    162   }
    163 
    164   unsigned int max_size = ring_buffer_->GetLargestFreeOrPendingSize();
    165   *size_allocated = std::min(max_size, size);
    166   bytes_since_last_flush_ += *size_allocated;
    167   return ring_buffer_->Alloc(*size_allocated);
    168 }
    169 
    170 void* TransferBuffer::Alloc(unsigned int size) {
    171   ReallocateRingBuffer(size);
    172 
    173   if (!HaveBuffer()) {
    174     return NULL;
    175   }
    176 
    177   unsigned int max_size = ring_buffer_->GetLargestFreeOrPendingSize();
    178   if (size > max_size) {
    179     return NULL;
    180   }
    181 
    182   bytes_since_last_flush_ += size;
    183   return ring_buffer_->Alloc(size);
    184 }
    185 
    186 void* TransferBuffer::GetResultBuffer() {
    187   ReallocateRingBuffer(result_size_);
    188   return result_buffer_;
    189 }
    190 
    191 int TransferBuffer::GetResultOffset() {
    192   ReallocateRingBuffer(result_size_);
    193   return result_shm_offset_;
    194 }
    195 
    196 int TransferBuffer::GetShmId() {
    197   ReallocateRingBuffer(result_size_);
    198   return buffer_id_;
    199 }
    200 
    201 unsigned int TransferBuffer::GetCurrentMaxAllocationWithoutRealloc() const {
    202   return HaveBuffer() ? ring_buffer_->GetLargestFreeOrPendingSize() : 0;
    203 }
    204 
    205 unsigned int TransferBuffer::GetMaxAllocation() const {
    206   return HaveBuffer() ? max_buffer_size_ - result_size_ : 0;
    207 }
    208 
    209 void ScopedTransferBufferPtr::Release() {
    210   if (buffer_) {
    211     transfer_buffer_->FreePendingToken(buffer_, helper_->InsertToken());
    212     buffer_ = NULL;
    213     size_ = 0;
    214   }
    215 }
    216 
    217 void ScopedTransferBufferPtr::Reset(unsigned int new_size) {
    218   Release();
    219   // NOTE: we allocate buffers of size 0 so that HaveBuffer will be true, so
    220   // that address will return a pointer just like malloc, and so that GetShmId
    221   // will be valid. That has the side effect that we'll insert a token on free.
    222   // We could add code skip the token for a zero size buffer but it doesn't seem
    223   // worth the complication.
    224   buffer_ = transfer_buffer_->AllocUpTo(new_size, &size_);
    225 }
    226 
    227 }  // namespace gpu
    228