diff options
Diffstat (limited to 'gpu/command_buffer/client/transfer_buffer.cc')
-rw-r--r-- | gpu/command_buffer/client/transfer_buffer.cc | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/gpu/command_buffer/client/transfer_buffer.cc b/gpu/command_buffer/client/transfer_buffer.cc new file mode 100644 index 0000000..28bb0e6 --- /dev/null +++ b/gpu/command_buffer/client/transfer_buffer.cc @@ -0,0 +1,211 @@ +// 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 "../client/transfer_buffer.h" +#include "../client/cmd_buffer_helper.h" + +namespace gpu { + +AlignedRingBuffer::~AlignedRingBuffer() { +} + +TransferBuffer::TransferBuffer( + CommandBufferHelper* helper) + : helper_(helper), + result_size_(0), + min_buffer_size_(0), + max_buffer_size_(0), + alignment_(), + buffer_id_(-1), + result_buffer_(NULL), + result_shm_offset_(0), + usable_(true) { +} + +TransferBuffer::~TransferBuffer() { + Free(); +} + +bool TransferBuffer::Initialize( + unsigned int starting_buffer_size, + unsigned int result_size, + unsigned int min_buffer_size, + unsigned int max_buffer_size, + unsigned int alignment) { + result_size_ = result_size; + min_buffer_size_ = min_buffer_size; + max_buffer_size_ = max_buffer_size; + alignment_ = alignment; + ReallocateRingBuffer(starting_buffer_size - result_size); + return HaveBuffer(); +} + +void TransferBuffer::Free() { + if (HaveBuffer()) { + helper_->Finish(); + helper_->command_buffer()->DestroyTransferBuffer(buffer_id_); + buffer_id_ = -1; + buffer_.ptr = NULL; + buffer_.size = 0; + result_buffer_ = NULL; + result_shm_offset_ = 0; + ring_buffer_.reset(); + } +} + +bool TransferBuffer::HaveBuffer() const { + return buffer_id_ != -1; +} + +RingBuffer::Offset TransferBuffer::GetOffset(void* pointer) const { + return ring_buffer_->GetOffset(pointer); +} + +void TransferBuffer::FreePendingToken(void* p, unsigned int token) { + ring_buffer_->FreePendingToken(p, token); +} + +void TransferBuffer::AllocateRingBuffer(unsigned int size) { + for (;size >= min_buffer_size_; size /= 2) { + int32 id = helper_->command_buffer()->CreateTransferBuffer(size, -1); + if (id != -1) { + buffer_ = helper_->command_buffer()->GetTransferBuffer(id); + ring_buffer_.reset(new AlignedRingBuffer( + alignment_, + id, + result_size_, + buffer_.size - result_size_, + helper_, + static_cast<char*>(buffer_.ptr) + result_size_)); + buffer_id_ = id; + result_buffer_ = buffer_.ptr; + result_shm_offset_ = 0; + return; + } + // we failed so don't try larger than this. + max_buffer_size_ = size / 2; + } + usable_ = false; +} + +// Returns the integer i such as 2^i <= n < 2^(i+1) +static int Log2Floor(uint32 n) { + if (n == 0) + return -1; + int log = 0; + uint32 value = n; + for (int i = 4; i >= 0; --i) { + int shift = (1 << i); + uint32 x = value >> shift; + if (x != 0) { + value = x; + log += shift; + } + } + GPU_DCHECK_EQ(value, 1u); + return log; +} + +// Returns the integer i such as 2^(i-1) < n <= 2^i +static int Log2Ceiling(uint32 n) { + if (n == 0) { + return -1; + } else { + // Log2Floor returns -1 for 0, so the following works correctly for n=1. + return 1 + Log2Floor(n - 1); + } +} + +static unsigned int ComputePOTSize(unsigned int dimension) { + return (dimension == 0) ? 0 : 1 << 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::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) { + GPU_DCHECK(size_allocated); + + ReallocateRingBuffer(size); + + if (!HaveBuffer()) { + return NULL; + } + + unsigned int max_size = ring_buffer_->GetLargestFreeOrPendingSize(); + *size_allocated = std::min(max_size, size); + 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; + } + + 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::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 |