// 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