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