// Copyright 2017 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/ui/scenic/cpp/host_memory.h"
#include <lib/zx/vmar.h>
#include <lib/zx/vmo.h>
#include <zircon/assert.h>
#include <memory>
#include "lib/ui/scenic/cpp/commands.h"
namespace scenic {
namespace {
// Returns true if a memory object is of an appropriate size to recycle.
bool CanReuseMemory(const HostMemory& memory, size_t desired_size) {
return memory.data_size() >= desired_size &&
memory.data_size() <= desired_size * 2;
}
std::pair<zx::vmo, std::shared_ptr<HostData>> AllocateMemory(size_t size) {
// Create the vmo and map it into this process.
zx::vmo local_vmo;
zx_status_t status = zx::vmo::create(size, 0u, &local_vmo);
ZX_ASSERT_MSG(status == ZX_OK, "vmo create failed: status=%d", status);
auto data = std::make_shared<HostData>(local_vmo, 0u, size);
// Drop rights before we transfer the VMO to the session manager.
// TODO(MA-492): Now that host-local memory may be concurrently used as
// device-local memory on UMA platforms, we need to keep all permissions on
// the duplicated vmo handle, until Vulkan can import read-only memory.
zx::vmo remote_vmo;
status = local_vmo.replace(ZX_RIGHT_SAME_RIGHTS, &remote_vmo);
ZX_ASSERT_MSG(status == ZX_OK, "replace rights failed: status=%d", status);
return std::make_pair(std::move(remote_vmo), std::move(data));
}
} // namespace
HostData::HostData(const zx::vmo& vmo, off_t offset, size_t size)
: size_(size) {
static const uint32_t flags =
ZX_VM_FLAG_PERM_READ | ZX_VM_FLAG_PERM_WRITE | ZX_VM_FLAG_MAP_RANGE;
uintptr_t ptr;
zx_status_t status =
zx::vmar::root_self()->map(0, vmo, offset, size, flags, &ptr);
ZX_ASSERT_MSG(status == ZX_OK, "map failed: status=%d", status);
ptr_ = reinterpret_cast<void*>(ptr);
}
HostData::~HostData() {
zx_status_t status =
zx::vmar::root_self()->unmap(reinterpret_cast<uintptr_t>(ptr_), size_);
ZX_ASSERT_MSG(status == ZX_OK, "unmap failed: status=%d", status);
}
HostMemory::HostMemory(Session* session, size_t size)
: HostMemory(session, AllocateMemory(size)) {}
HostMemory::HostMemory(Session* session,
std::pair<zx::vmo, std::shared_ptr<HostData>> init)
: Memory(session, std::move(init.first), init.second->size(),
fuchsia::images::MemoryType::HOST_MEMORY),
data_(std::move(init.second)) {}
HostMemory::HostMemory(HostMemory&& moved)
: Memory(std::move(moved)), data_(std::move(moved.data_)) {}
HostMemory::~HostMemory() = default;
HostImage::HostImage(const HostMemory& memory, off_t memory_offset,
fuchsia::images::ImageInfo info)
: HostImage(memory.session(), memory.id(), memory_offset, memory.data(),
std::move(info)) {}
HostImage::HostImage(Session* session, uint32_t memory_id, off_t memory_offset,
std::shared_ptr<HostData> data,
fuchsia::images::ImageInfo info)
: Image(session, memory_id, memory_offset, std::move(info)),
data_(std::move(data)) {}
HostImage::HostImage(HostImage&& moved)
: Image(std::move(moved)), data_(std::move(moved.data_)) {}
HostImage::~HostImage() = default;
HostImagePool::HostImagePool(Session* session, uint32_t num_images)
: session_(session), image_ptrs_(num_images), memory_ptrs_(num_images) {}
HostImagePool::~HostImagePool() = default;
// TODO(mikejurka): Double-check these changes
bool HostImagePool::Configure(const fuchsia::images::ImageInfo* image_info) {
if (image_info) {
if (configured_ && ImageInfoEquals(*image_info, image_info_)) {
return false; // no change
}
configured_ = true;
image_info_ = *image_info;
} else {
if (!configured_) {
return false; // no change
}
configured_ = false;
}
for (uint32_t i = 0; i < num_images(); i++)
image_ptrs_[i].reset();
if (configured_) {
ZX_DEBUG_ASSERT(image_info_.width > 0);
ZX_DEBUG_ASSERT(image_info_.height > 0);
ZX_DEBUG_ASSERT(image_info_.stride > 0);
size_t desired_size = Image::ComputeSize(image_info_);
for (uint32_t i = 0; i < num_images(); i++) {
if (memory_ptrs_[i] && !CanReuseMemory(*memory_ptrs_[i], desired_size))
memory_ptrs_[i].reset();
}
}
return true;
}
const HostImage* HostImagePool::GetImage(uint32_t index) {
ZX_DEBUG_ASSERT(index < num_images());
if (image_ptrs_[index])
return image_ptrs_[index].get();
if (!configured_)
return nullptr;
if (!memory_ptrs_[index]) {
memory_ptrs_[index] =
std::make_unique<HostMemory>(session_, Image::ComputeSize(image_info_));
}
image_ptrs_[index] =
std::make_unique<HostImage>(*memory_ptrs_[index], 0u, image_info_);
return image_ptrs_[index].get();
}
void HostImagePool::DiscardImage(uint32_t index) {
ZX_DEBUG_ASSERT(index < num_images());
image_ptrs_[index].reset();
}
} // namespace scenic