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