| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "garnet/bin/mediaplayer/graph/payloads/vmo_payload_allocator.h" |
| |
| #include <lib/zx/vmar.h> |
| #include "garnet/bin/mediaplayer/graph/formatting.h" |
| #include "lib/fxl/logging.h" |
| #include "lib/fxl/synchronization/thread_annotations.h" |
| |
| namespace media_player { |
| |
| // static |
| fbl::RefPtr<VmoPayloadAllocator> VmoPayloadAllocator::Create() { |
| return fbl::MakeRefCounted<VmoPayloadAllocator>(); |
| } |
| |
| void VmoPayloadAllocator::Dump(std::ostream& os) const { |
| std::lock_guard<std::mutex> locker(mutex_); |
| |
| os << fostr::Indent; |
| os << fostr::NewLine << "vmo allocation: " << vmo_allocation_; |
| os << fostr::NewLine << "payload vmos: " << payload_vmos_; |
| os << fostr::Outdent; |
| } |
| |
| void VmoPayloadAllocator::SetVmoAllocation(VmoAllocation vmo_allocation) { |
| FXL_DCHECK(vmo_allocation != VmoAllocation::kNotApplicable); |
| std::lock_guard<std::mutex> locker(mutex_); |
| FXL_DCHECK(vmo_allocation_ == VmoAllocation::kNotApplicable) |
| << "SetVmoAllocation may only be called once."; |
| vmo_allocation_ = vmo_allocation; |
| } |
| |
| fbl::RefPtr<PayloadBuffer> VmoPayloadAllocator::AllocatePayloadBuffer( |
| uint64_t size) { |
| std::lock_guard<std::mutex> locker(mutex_); |
| FXL_DCHECK(vmo_allocation_ != VmoAllocation::kNotApplicable) |
| << "SetVmoAllocation must be called before AllocatePayloadBuffer."; |
| FXL_DCHECK(!payload_vmos_.empty()); |
| FXL_DCHECK((vmo_allocation_ != VmoAllocation::kSingleVmo) || |
| (payload_vmos_.size() == 1)); |
| |
| fbl::RefPtr<PayloadBuffer> payload_buffer; |
| size_t vmo_index = suggested_allocation_vmo_; |
| |
| while (true) { |
| payload_buffer = TryAllocateFromVmo(payload_vmos_[vmo_index], size); |
| if (payload_buffer) { |
| break; |
| } |
| |
| vmo_index = (vmo_index + 1) % payload_vmos_.size(); |
| if (vmo_index == suggested_allocation_vmo_) { |
| // Payload memory exhausted. |
| FXL_LOG(ERROR) << "Couldn't allocate buffer of size " << size << "."; |
| return nullptr; |
| } |
| } |
| |
| if (vmo_allocation_ == VmoAllocation::kVmoPerBuffer) { |
| // We've used the entire VMO, so it's not a good suggestion. |
| suggested_allocation_vmo_ = (vmo_index + 1) % payload_vmos_.size(); |
| } |
| |
| return payload_buffer; |
| } |
| |
| std::vector<fbl::RefPtr<PayloadVmo>> VmoPayloadAllocator::GetVmos() const { |
| std::lock_guard<std::mutex> locker(mutex_); |
| return payload_vmos_; |
| } |
| |
| void VmoPayloadAllocator::AddVmo(fbl::RefPtr<PayloadVmo> payload_vmo) { |
| FXL_DCHECK(payload_vmo); |
| |
| std::lock_guard<std::mutex> locker(mutex_); |
| FXL_DCHECK(payload_vmos_.empty() || |
| vmo_allocation_ != VmoAllocation::kSingleVmo) |
| << "Attempt to add more than one VMO to single-vmo allocator."; |
| |
| payload_vmo->SetIndex(payload_vmos_.size()); |
| |
| payload_vmos_.push_back(payload_vmo); |
| if (vmo_allocation_ != VmoAllocation::kVmoPerBuffer) { |
| payload_vmo->allocator_ = |
| fbl::make_unique<media::FifoAllocator>(payload_vmo->size()); |
| } |
| } |
| |
| void VmoPayloadAllocator::RemoveVmo(fbl::RefPtr<PayloadVmo> payload_vmo) { |
| std::lock_guard<std::mutex> locker(mutex_); |
| |
| for (auto iter = payload_vmos_.begin(); iter != payload_vmos_.end(); ++iter) { |
| if (*iter == payload_vmo) { |
| payload_vmos_.erase(iter); |
| |
| if (suggested_allocation_vmo_ >= payload_vmos_.size()) { |
| suggested_allocation_vmo_ = 0; |
| } |
| return; |
| } |
| } |
| |
| FXL_LOG(WARNING) << "VMO not found in RemoveVmo."; |
| } |
| |
| fbl::RefPtr<PayloadBuffer> VmoPayloadAllocator::TryAllocateFromVmo( |
| fbl::RefPtr<PayloadVmo> payload_vmo, uint64_t size) { |
| FXL_DCHECK(payload_vmo); |
| |
| if (vmo_allocation_ == VmoAllocation::kVmoPerBuffer) { |
| // Try to allocate the entire VMO. |
| |
| if (payload_vmo->allocated_) { |
| // It's already allocated. |
| return nullptr; |
| } |
| |
| if (payload_vmo->size() < size) { |
| FXL_LOG(ERROR) << "VMO is too small (" << payload_vmo->size() |
| << " bytes) for allocation (" << size << " bytes)."; |
| return nullptr; |
| } |
| |
| payload_vmo->allocated_ = true; |
| |
| // Success! |
| return PayloadBuffer::Create(size, payload_vmo->start(), payload_vmo, 0, |
| [this, this_refptr = fbl::WrapRefPtr(this)]( |
| PayloadBuffer* payload_buffer) { |
| FXL_DCHECK(payload_buffer->vmo()); |
| // Take the |VmoPayloadAllocator|'s mutex to |
| // serialize access to the |allocated_| field |
| // of the |PayloadVmo|. |
| std::lock_guard<std::mutex> locker(mutex_); |
| payload_buffer->vmo()->allocated_ = false; |
| }); |
| } |
| |
| // Try to allocate a region from the VMO. |
| FXL_DCHECK(payload_vmo->allocator_); |
| |
| // The region allocated at the top of the VMO will be aligned to 4096 bytes. |
| // We ensure that subsequent allocations will be |kByteAlignment|-aligned by |
| // aligning-up all the sizes. |
| uint64_t offset = |
| payload_vmo->allocator_->AllocateRegion(PayloadBuffer::AlignUp(size)); |
| if (offset == media::FifoAllocator::kNullOffset) { |
| // Can't allocate. |
| return nullptr; |
| } |
| |
| // Success! |
| return PayloadBuffer::Create( |
| size, reinterpret_cast<uint8_t*>(payload_vmo->start()) + offset, |
| payload_vmo, offset, |
| [this, this_refptr = fbl::WrapRefPtr(this), |
| offset](PayloadBuffer* payload_buffer) { |
| FXL_DCHECK(payload_buffer->vmo()); |
| FXL_DCHECK(payload_buffer->vmo()->allocator_); |
| // Take the |VmoPayloadAllocator|'s mutex to serialize access to the |
| // |FifoAllocator|. |
| std::lock_guard<std::mutex> locker(mutex_); |
| payload_buffer->vmo()->allocator_->ReleaseRegion(offset); |
| }); |
| } |
| |
| } // namespace media_player |