|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | // A class to Manage a growing transfer buffer. | 
|  |  | 
|  | #include "gpu/command_buffer/client/transfer_buffer.h" | 
|  |  | 
|  | #include <stddef.h> | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include "base/bits.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/trace_event/trace_event.h" | 
|  | #include "gpu/command_buffer/client/cmd_buffer_helper.h" | 
|  |  | 
|  | namespace gpu { | 
|  |  | 
|  | TransferBuffer::TransferBuffer( | 
|  | CommandBufferHelper* helper) | 
|  | : helper_(helper), | 
|  | result_size_(0), | 
|  | default_buffer_size_(0), | 
|  | min_buffer_size_(0), | 
|  | max_buffer_size_(0), | 
|  | alignment_(0), | 
|  | size_to_flush_(0), | 
|  | bytes_since_last_flush_(0), | 
|  | buffer_id_(-1), | 
|  | result_buffer_(NULL), | 
|  | result_shm_offset_(0), | 
|  | usable_(true) { | 
|  | } | 
|  |  | 
|  | TransferBuffer::~TransferBuffer() { | 
|  | Free(); | 
|  | } | 
|  |  | 
|  | bool TransferBuffer::Initialize( | 
|  | unsigned int default_buffer_size, | 
|  | unsigned int result_size, | 
|  | unsigned int min_buffer_size, | 
|  | unsigned int max_buffer_size, | 
|  | unsigned int alignment, | 
|  | unsigned int size_to_flush) { | 
|  | result_size_ = result_size; | 
|  | default_buffer_size_ = default_buffer_size; | 
|  | min_buffer_size_ = min_buffer_size; | 
|  | max_buffer_size_ = max_buffer_size; | 
|  | alignment_ = alignment; | 
|  | size_to_flush_ = size_to_flush; | 
|  | ReallocateRingBuffer(default_buffer_size_ - result_size); | 
|  | return HaveBuffer(); | 
|  | } | 
|  |  | 
|  | void TransferBuffer::Free() { | 
|  | if (HaveBuffer()) { | 
|  | TRACE_EVENT0("gpu", "TransferBuffer::Free"); | 
|  | helper_->Finish(); | 
|  | helper_->command_buffer()->DestroyTransferBuffer(buffer_id_); | 
|  | buffer_id_ = -1; | 
|  | buffer_ = NULL; | 
|  | result_buffer_ = NULL; | 
|  | result_shm_offset_ = 0; | 
|  | ring_buffer_.reset(); | 
|  | bytes_since_last_flush_ = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool TransferBuffer::HaveBuffer() const { | 
|  | DCHECK(buffer_id_ == -1 || buffer_.get()); | 
|  | return buffer_id_ != -1; | 
|  | } | 
|  |  | 
|  | RingBuffer::Offset TransferBuffer::GetOffset(void* pointer) const { | 
|  | return ring_buffer_->GetOffset(pointer); | 
|  | } | 
|  |  | 
|  | void TransferBuffer::DiscardBlock(void* p) { | 
|  | ring_buffer_->DiscardBlock(p); | 
|  | } | 
|  |  | 
|  | void TransferBuffer::FreePendingToken(void* p, unsigned int token) { | 
|  | ring_buffer_->FreePendingToken(p, token); | 
|  | if (bytes_since_last_flush_ >= size_to_flush_ && size_to_flush_ > 0) { | 
|  | helper_->Flush(); | 
|  | bytes_since_last_flush_ = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | unsigned int TransferBuffer::GetSize() const { | 
|  | return HaveBuffer() ? ring_buffer_->GetLargestFreeOrPendingSize() : 0; | 
|  | } | 
|  |  | 
|  | unsigned int TransferBuffer::GetFreeSize() const { | 
|  | return HaveBuffer() ? ring_buffer_->GetTotalFreeSizeNoWaiting() : 0; | 
|  | } | 
|  |  | 
|  | void TransferBuffer::AllocateRingBuffer(unsigned int size) { | 
|  | for (;size >= min_buffer_size_; size /= 2) { | 
|  | int32_t id = -1; | 
|  | scoped_refptr<gpu::Buffer> buffer = | 
|  | helper_->command_buffer()->CreateTransferBuffer(size, &id); | 
|  | if (id != -1) { | 
|  | DCHECK(buffer.get()); | 
|  | buffer_ = buffer; | 
|  | ring_buffer_.reset(new RingBuffer( | 
|  | alignment_, | 
|  | result_size_, | 
|  | buffer_->size() - result_size_, | 
|  | helper_, | 
|  | static_cast<char*>(buffer_->memory()) + result_size_)); | 
|  | buffer_id_ = id; | 
|  | result_buffer_ = buffer_->memory(); | 
|  | result_shm_offset_ = 0; | 
|  | return; | 
|  | } | 
|  | // we failed so don't try larger than this. | 
|  | max_buffer_size_ = size / 2; | 
|  | } | 
|  | usable_ = false; | 
|  | } | 
|  |  | 
|  | static unsigned int ComputePOTSize(unsigned int dimension) { | 
|  | return (dimension == 0) ? 0 : 1 << base::bits::Log2Ceiling(dimension); | 
|  | } | 
|  |  | 
|  | void TransferBuffer::ReallocateRingBuffer(unsigned int size) { | 
|  | // What size buffer would we ask for if we needed a new one? | 
|  | unsigned int needed_buffer_size = ComputePOTSize(size + result_size_); | 
|  | needed_buffer_size = std::max(needed_buffer_size, min_buffer_size_); | 
|  | needed_buffer_size = std::max(needed_buffer_size, default_buffer_size_); | 
|  | needed_buffer_size = std::min(needed_buffer_size, max_buffer_size_); | 
|  |  | 
|  | if (usable_ && (!HaveBuffer() || needed_buffer_size > buffer_->size())) { | 
|  | if (HaveBuffer()) { | 
|  | Free(); | 
|  | } | 
|  | AllocateRingBuffer(needed_buffer_size); | 
|  | } | 
|  | } | 
|  |  | 
|  | void* TransferBuffer::AllocUpTo( | 
|  | unsigned int size, unsigned int* size_allocated) { | 
|  | DCHECK(size_allocated); | 
|  |  | 
|  | ReallocateRingBuffer(size); | 
|  |  | 
|  | if (!HaveBuffer()) { | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | unsigned int max_size = ring_buffer_->GetLargestFreeOrPendingSize(); | 
|  | *size_allocated = std::min(max_size, size); | 
|  | bytes_since_last_flush_ += *size_allocated; | 
|  | return ring_buffer_->Alloc(*size_allocated); | 
|  | } | 
|  |  | 
|  | void* TransferBuffer::Alloc(unsigned int size) { | 
|  | ReallocateRingBuffer(size); | 
|  |  | 
|  | if (!HaveBuffer()) { | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | unsigned int max_size = ring_buffer_->GetLargestFreeOrPendingSize(); | 
|  | if (size > max_size) { | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | bytes_since_last_flush_ += size; | 
|  | return ring_buffer_->Alloc(size); | 
|  | } | 
|  |  | 
|  | void* TransferBuffer::GetResultBuffer() { | 
|  | ReallocateRingBuffer(result_size_); | 
|  | return result_buffer_; | 
|  | } | 
|  |  | 
|  | int TransferBuffer::GetResultOffset() { | 
|  | ReallocateRingBuffer(result_size_); | 
|  | return result_shm_offset_; | 
|  | } | 
|  |  | 
|  | int TransferBuffer::GetShmId() { | 
|  | ReallocateRingBuffer(result_size_); | 
|  | return buffer_id_; | 
|  | } | 
|  |  | 
|  | unsigned int TransferBuffer::GetCurrentMaxAllocationWithoutRealloc() const { | 
|  | return HaveBuffer() ? ring_buffer_->GetLargestFreeOrPendingSize() : 0; | 
|  | } | 
|  |  | 
|  | unsigned int TransferBuffer::GetMaxAllocation() const { | 
|  | return HaveBuffer() ? max_buffer_size_ - result_size_ : 0; | 
|  | } | 
|  |  | 
|  | void ScopedTransferBufferPtr::Release() { | 
|  | if (buffer_) { | 
|  | transfer_buffer_->FreePendingToken(buffer_, helper_->InsertToken()); | 
|  | buffer_ = NULL; | 
|  | size_ = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | void ScopedTransferBufferPtr::Discard() { | 
|  | if (buffer_) { | 
|  | transfer_buffer_->DiscardBlock(buffer_); | 
|  | buffer_ = NULL; | 
|  | size_ = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | void ScopedTransferBufferPtr::Reset(unsigned int new_size) { | 
|  | Release(); | 
|  | // NOTE: we allocate buffers of size 0 so that HaveBuffer will be true, so | 
|  | // that address will return a pointer just like malloc, and so that GetShmId | 
|  | // will be valid. That has the side effect that we'll insert a token on free. | 
|  | // We could add code skip the token for a zero size buffer but it doesn't seem | 
|  | // worth the complication. | 
|  | buffer_ = transfer_buffer_->AllocUpTo(new_size, &size_); | 
|  | } | 
|  |  | 
|  | }  // namespace gpu |