blob: 41e0850479c21bb49cd24fd16d0b521196d0079f [file] [log] [blame]
// Copyright 2022 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/embedder/software_surface_producer.h"
#include <lib/fdio/directory.h>
#include <lib/syslog/global.h>
#include <lib/zx/process.h>
#include <zircon/status.h>
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "src/embedder/engine/embedder.h"
#include "src/embedder/logging.h"
namespace embedder {
namespace {
/// Gets the current name of the Fuchsia process that
/// is running the embedder.
std::string GetCurrentProcessName() {
char name[ZX_MAX_NAME_LEN];
zx_status_t status = zx::process::self()->get_property(ZX_PROP_NAME, name, sizeof(name));
if (status != ZX_OK) {
FX_LOG(ERROR, kLogTag, "Failed to get process name for sysmem; using \"\".");
return std::string();
}
return std::string(name);
}
/// Gets the current process ID of the Fuchsia process that
/// is running the embedder.
zx_koid_t GetCurrentProcessId() {
zx_info_handle_basic_t info;
zx_status_t status =
zx::process::self()->get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info),
nullptr /* actual_count */, nullptr /* avail_count */);
if (status != ZX_OK) {
FX_LOG(ERROR, kLogTag, "Failed to get process ID for sysmem; using ZX_KOID_INVALID.");
return ZX_KOID_INVALID;
}
return info.koid;
}
} // namespace
SoftwareSurfaceProducer::SoftwareSurfaceProducer() {
zx_status_t status = fdio_service_connect("/svc/fuchsia.sysmem.Allocator",
sysmem_allocator_.NewRequest().TakeChannel().release());
sysmem_allocator_->SetDebugClientInfo(GetCurrentProcessName(), GetCurrentProcessId());
if (status != ZX_OK) {
FX_LOGF(ERROR, kLogTag, "Failed to connect to fuchsia.sysmem.Allocator: %s",
zx_status_get_string(status));
return;
}
status = fdio_service_connect("/svc/fuchsia.ui.composition.Allocator",
flatland_allocator_.NewRequest().TakeChannel().release());
if (status != ZX_OK) {
FX_LOGF(ERROR, kLogTag, "Failed to connect to fuchsia.ui.composition.Allocator: %s",
zx_status_get_string(status));
return;
}
valid_ = true;
}
SoftwareSurfaceProducer::~SoftwareSurfaceProducer() = default;
std::unique_ptr<SoftwareSurface> SoftwareSurfaceProducer::ProduceOffscreenSurface(const Size size) {
if (!IsValid()) {
FX_LOG(FATAL, kLogTag, "ProduceOffscreenSurface called with invalid state.");
return nullptr;
}
return CreateSurface(size);
}
std::unique_ptr<SoftwareSurface> SoftwareSurfaceProducer::ProduceSurface(const Size size) {
// TODO(akbiggs): Trace width and height here for parity with
// Github code.
FlutterEngineTraceEventInstant("SoftwareSurfaceProducer::ProduceSurface");
if (!IsValid()) {
FX_LOG(FATAL, kLogTag, "ProduceSurface called with invalid state.");
return nullptr;
}
std::unique_ptr<SoftwareSurface> surface;
auto exact_match_it = std::find_if(
available_surfaces_.begin(), available_surfaces_.end(), [&size](const auto& surface) {
auto surface_size = surface->GetSize();
return surface->IsValid() && surface_size.width == size.width &&
surface_size.height == size.height;
});
if (exact_match_it != available_surfaces_.end()) {
FlutterEngineTraceEventInstant("Exact match found");
surface = std::move(*exact_match_it);
available_surfaces_.erase(exact_match_it);
} else {
surface = CreateSurface(size);
}
if (surface == nullptr) {
FX_LOG(ERROR, kLogTag, "Could not acquire surface.");
return nullptr;
}
surface->ResetAge();
return surface;
}
void SoftwareSurfaceProducer::SubmitSurfaces(
std::vector<std::unique_ptr<SoftwareSurface>> surfaces) {
FlutterEngineTraceEventInstant("SoftwareSurfaceProducer::SubmitSurfaces");
if (!IsValid()) {
FX_LOG(FATAL, kLogTag, "SubmitSurfaces called with invalid state.");
return;
}
for (auto& surface : surfaces) {
SubmitSurface(std::move(surface));
}
AgeAndCollectOldBuffers();
}
void SoftwareSurfaceProducer::SubmitSurface(std::unique_ptr<SoftwareSurface> surface) {
FlutterEngineTraceEventInstant("SoftwareSurfacePool::SubmitSurface");
if (surface == nullptr) {
FX_LOG(ERROR, kLogTag, "Tried to submit null surface.");
return;
}
uintptr_t surface_key = reinterpret_cast<uintptr_t>(surface.get());
auto insert_iterator = pending_surfaces_.insert(std::make_pair(surface_key, // key
std::move(surface) // value
));
if (insert_iterator.second) {
insert_iterator.first->second->SignalWritesFinished(
std::bind(&SoftwareSurfaceProducer::RecyclePendingSurface, this, surface_key));
}
}
std::unique_ptr<SoftwareSurface> SoftwareSurfaceProducer::CreateSurface(const Size size) {
FlutterEngineTraceEventInstant("SoftwareSurfaceProducer::CreateSurface");
auto surface = std::make_unique<SoftwareSurface>(sysmem_allocator_, flatland_allocator_, size);
if (!surface->IsValid()) {
FX_LOG(ERROR, kLogTag, "Created surface is invalid.");
return nullptr;
}
return surface;
}
void SoftwareSurfaceProducer::RecycleSurface(std::unique_ptr<SoftwareSurface> surface) {
// The surface may have become invalid (for example if the fences could
// not be reset).
if (!surface->IsValid()) {
FX_LOG(ERROR, kLogTag, "Attempted to recycle invalid surface.");
return;
}
FlutterEngineTraceEventInstant("SoftwareSurfacePool::RecycleSurface");
// Recycle the buffer by putting it in the list of available surfaces if we
// have not reached the maximum amount of cached surfaces.
if (available_surfaces_.size() < kMaxSurfaces) {
available_surfaces_.push_back(std::move(surface));
} else {
FlutterEngineTraceEventInstant("Too many surfaces in pool, dropping");
}
// TODO(akbiggs): Trace stats here for parity with GitHub code.
}
void SoftwareSurfaceProducer::RecyclePendingSurface(uintptr_t surface_key) {
// Before we do anything, we must clear the surface from the collection of
// pending surfaces.
auto found_in_pending = pending_surfaces_.find(surface_key);
if (found_in_pending == pending_surfaces_.end()) {
FX_LOG(ERROR, kLogTag, "Attempted to recycle a surface that wasn't pending.");
return;
}
// Grab a hold of the surface to recycle and clear the entry in the pending
// surfaces collection.
auto surface_to_recycle = std::move(found_in_pending->second);
pending_surfaces_.erase(found_in_pending);
RecycleSurface(std::move(surface_to_recycle));
}
void SoftwareSurfaceProducer::AgeAndCollectOldBuffers() {
FlutterEngineTraceEventInstant("SoftwareSurfacePool::AgeAndCollectOldBuffers");
// Remove all surfaces that are no longer valid or are too old.
available_surfaces_.erase(std::remove_if(available_surfaces_.begin(), available_surfaces_.end(),
[&](auto& surface) {
return !surface->IsValid() ||
surface->AdvanceAndGetAge() >= kMaxSurfaceAge;
}),
available_surfaces_.end());
// TODO(akbiggs): Trace the aged surfaces here for parity with
// GitHub code.
}
bool SoftwareSurfaceProducer::IsValid() const { return valid_; }
} // namespace embedder