blob: 2734fa5bd8574b79c5e646277f777514dc2f5da3 [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 "lib/media/transport/shared_buffer_set_allocator.h"
namespace media {
SharedBufferSetAllocator::SharedBufferSetAllocator(uint32_t local_map_flags,
zx_rights_t remote_rights)
: SharedBufferSet(local_map_flags), remote_rights_(remote_rights) {}
SharedBufferSetAllocator::~SharedBufferSetAllocator() {}
void SharedBufferSetAllocator::Reset() {
std::lock_guard<std::mutex> locker(mutex_);
use_fixed_buffer_ = false;
buffers_.clear();
free_whole_buffer_ids_by_size_.clear();
active_sliced_buffer_id_ = kNullBufferId;
while (!buffer_updates_.empty()) {
buffer_updates_.pop();
}
SharedBufferSet::Reset();
}
bool SharedBufferSetAllocator::SetFixedBufferSize(uint64_t size) {
FXL_DCHECK(size != 0);
std::lock_guard<std::mutex> locker(mutex_);
FXL_DCHECK(!use_fixed_buffer_) << "SetFixedBufferSize called more than once";
FXL_DCHECK(buffers_.empty())
<< "SetFixedBufferSize called after AllocateRegion";
FXL_DCHECK(active_sliced_buffer_id_ == kNullBufferId);
active_sliced_buffer_id_ = CreateBuffer(false, size);
if (active_sliced_buffer_id_ == kNullBufferId) {
return false;
}
use_fixed_buffer_ = true;
return true;
}
void* SharedBufferSetAllocator::AllocateRegion(uint64_t size) {
FXL_DCHECK(size != 0);
std::lock_guard<std::mutex> locker(mutex_);
Locator locator;
if (size >= kWholeRegionMinimumSize && !use_fixed_buffer_) {
locator = AllocateWholeRegion(size);
} else {
locator = AllocateSlicedRegion(size);
}
return PtrFromLocator(locator);
}
void SharedBufferSetAllocator::ReleaseRegion(void* ptr) {
FXL_DCHECK(ptr != nullptr);
std::lock_guard<std::mutex> locker(mutex_);
if (buffers_.empty()) {
// Freeing after |Reset|.
return;
}
Locator locator = LocatorFromPtr(ptr);
if (!locator) {
return;
}
FXL_DCHECK(locator.buffer_id() < buffers_.size());
if (buffers_[locator.buffer_id()].whole()) {
ReleaseWholeRegion(locator);
} else {
ReleaseSlicedRegion(locator);
}
}
bool SharedBufferSetAllocator::PollForBufferUpdate(uint32_t* buffer_id_out,
zx::vmo* handle_out) {
FXL_DCHECK(buffer_id_out != nullptr);
FXL_DCHECK(handle_out != nullptr);
std::lock_guard<std::mutex> locker(mutex_);
if (buffer_updates_.empty()) {
return false;
}
*buffer_id_out = buffer_updates_.front().buffer_id_;
*handle_out = std::move(buffer_updates_.front().vmo_);
buffer_updates_.pop();
return true;
}
SharedBufferSet::Locator SharedBufferSetAllocator::AllocateWholeRegion(
uint64_t size) {
auto lower_bound = free_whole_buffer_ids_by_size_.lower_bound(size);
for (auto iter = free_whole_buffer_ids_by_size_.begin(); iter != lower_bound;
++iter) {
DeleteBuffer(iter->second);
}
free_whole_buffer_ids_by_size_.erase(free_whole_buffer_ids_by_size_.begin(),
lower_bound);
FXL_DCHECK(lower_bound == free_whole_buffer_ids_by_size_.begin());
if (lower_bound != free_whole_buffer_ids_by_size_.end()) {
// Found a free buffer that's large enough. Use it.
uint32_t buffer_id = lower_bound->second;
free_whole_buffer_ids_by_size_.erase(lower_bound);
return Locator(buffer_id, 0);
}
// Didn't find a large enough buffer. Create one.
uint32_t buffer_id = CreateBuffer(true, size);
if (buffer_id == kNullBufferId) {
return Locator::Null();
}
return Locator(buffer_id, 0);
}
void SharedBufferSetAllocator::ReleaseWholeRegion(const Locator& locator) {
FXL_DCHECK(locator);
FXL_DCHECK(locator.buffer_id() < buffers_.size());
FXL_DCHECK(!buffers_[locator.buffer_id()].allocator_);
free_whole_buffer_ids_by_size_.insert(
std::make_pair(buffers_[locator.buffer_id()].size_, locator.buffer_id()));
}
SharedBufferSet::Locator SharedBufferSetAllocator::AllocateSlicedRegion(
uint64_t size) {
if (active_sliced_buffer_id_ == kNullBufferId) {
// No buffer has been established for allocating sliced buffers. Create one.
FXL_DCHECK(!use_fixed_buffer_);
active_sliced_buffer_id_ =
CreateBuffer(false, size * kSlicedBufferInitialSizeMultiplier);
if (active_sliced_buffer_id_ == kNullBufferId) {
return Locator::Null();
}
}
// Try allocating from the buffer.
FXL_DCHECK(buffers_[active_sliced_buffer_id_].allocator_);
uint64_t offset =
buffers_[active_sliced_buffer_id_].allocator_->AllocateRegion(size);
if (offset != FifoAllocator::kNullOffset) {
// Allocation succeeded.
return Locator(active_sliced_buffer_id_, offset);
}
if (use_fixed_buffer_) {
// Allocation failed, and we're using a fixed buffer. Fail the request.
return Locator::Null();
}
// Allocation failed - we need a bigger buffer. We either grow the buffer size
// by a factor of kSlicedBufferGrowMultiplier or use the initial buffer size
// calculation based on this allocation request, whichever produces the
// larger buffer.
//
// The old buffer will be deleted once all the regions that were allocated
// from it are released.
uint64_t new_buffer_size = std::max(
size * kSlicedBufferInitialSizeMultiplier,
buffers_[active_sliced_buffer_id_].size_ * kSlicedBufferGrowMultiplier);
uint32_t buffer_id = CreateBuffer(false, new_buffer_size);
if (buffer_id == kNullBufferId) {
return Locator::Null();
}
MaybeDeleteSlicedBuffer(active_sliced_buffer_id_);
active_sliced_buffer_id_ = buffer_id;
FXL_DCHECK(buffers_[active_sliced_buffer_id_].allocator_);
offset = buffers_[active_sliced_buffer_id_].allocator_->AllocateRegion(size);
// The allocation must succeed since the new buffer is at least
// kSlicedBufferInitialSizeMultiplier times larger than size.
FXL_DCHECK(offset != FifoAllocator::kNullOffset);
return Locator(active_sliced_buffer_id_, offset);
}
void SharedBufferSetAllocator::ReleaseSlicedRegion(const Locator& locator) {
FXL_DCHECK(locator);
FXL_DCHECK(locator.buffer_id() < buffers_.size());
FXL_DCHECK(buffers_[locator.buffer_id()].allocator_);
buffers_[locator.buffer_id()].allocator_->ReleaseRegion(locator.offset());
// Delete the buffer if it's no longer the active one, and it's fully
// released.
if (locator.buffer_id() != active_sliced_buffer_id_) {
MaybeDeleteSlicedBuffer(locator.buffer_id());
}
}
uint32_t SharedBufferSetAllocator::CreateBuffer(bool whole, uint64_t size) {
uint32_t buffer_id;
zx::vmo vmo;
zx_status_t status = CreateNewBuffer(size, &buffer_id, remote_rights_, &vmo);
if (status != ZX_OK) {
return kNullBufferId;
}
if (buffers_.size() <= buffer_id) {
buffers_.resize(buffer_id + 1);
}
Buffer& buffer = buffers_[buffer_id];
buffer.size_ = size;
if (!whole) {
buffer.allocator_.reset(new FifoAllocator(size));
}
buffer_updates_.emplace(buffer_id, std::move(vmo));
return buffer_id;
}
void SharedBufferSetAllocator::DeleteBuffer(uint32_t id) {
FXL_DCHECK(buffers_.size() > id);
RemoveBuffer(id);
buffers_[id].size_ = 0;
buffers_[id].allocator_.reset();
buffer_updates_.emplace(id);
}
void SharedBufferSetAllocator::MaybeDeleteSlicedBuffer(uint32_t id) {
FXL_DCHECK(buffers_[id].allocator_);
if (!buffers_[id].allocator_->AnyCurrentAllocatedRegions()) {
DeleteBuffer(id);
}
}
SharedBufferSetAllocator::Buffer::Buffer() {}
SharedBufferSetAllocator::Buffer::~Buffer() {}
SharedBufferSetAllocator::BufferUpdate::BufferUpdate(uint32_t buffer_id,
zx::vmo vmo)
: buffer_id_(buffer_id), vmo_(std::move(vmo)) {}
SharedBufferSetAllocator::BufferUpdate::BufferUpdate(uint32_t buffer_id)
: buffer_id_(buffer_id) {}
SharedBufferSetAllocator::BufferUpdate::~BufferUpdate() {}
} // namespace media