blob: e355a4e28974dae8f559f255448afdc2c40c04ca [file] [log] [blame]
// 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 "src/media/playback/mediaplayer/graph/payloads/vmo_payload_allocator.h"
#include <lib/zx/vmar.h>
#include "src/lib/fxl/logging.h"
#include "src/lib/fxl/synchronization/thread_annotations.h"
#include "src/media/playback/mediaplayer/graph/formatting.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.
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_ =
std::make_unique<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 == 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