blob: 4246ed5653727ef8efce4d471357d1b0dfcc5f6d [file] [log] [blame]
// Copyright 2013 The Flutter 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 "software_surface_producer.h"
#include <lib/fdio/directory.h>
#include <lib/zx/process.h>
#include <algorithm> // Foor std::remove_if
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "flutter/fml/logging.h"
#include "flutter/fml/trace_event.h"
namespace flutter_runner {
namespace {
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) {
FML_LOG(ERROR) << "Failed to get process name for sysmem; using \"\".";
return std::string();
}
return std::string(name);
}
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), /*actual_count*/ nullptr,
/*avail_count*/ nullptr);
if (status != ZX_OK) {
FML_LOG(ERROR) << "Failed to get process ID for sysmem; using 0.";
return ZX_KOID_INVALID;
}
return info.koid;
}
} // namespace
SoftwareSurfaceProducer::SoftwareSurfaceProducer(
scenic::Session* scenic_session)
: scenic_session_(scenic_session) {
zx_status_t status = fdio_service_connect(
"/svc/fuchsia.sysmem.Allocator",
sysmem_allocator_.NewRequest().TakeChannel().release());
sysmem_allocator_->SetDebugClientInfo(GetCurrentProcessName(),
GetCurrentProcessId());
FML_DCHECK(status == ZX_OK);
if (!scenic_session_) {
status = fdio_service_connect(
"/svc/fuchsia.ui.composition.Allocator",
flatland_allocator_.NewRequest().TakeChannel().release());
FML_DCHECK(status == ZX_OK);
}
valid_ = true;
}
SoftwareSurfaceProducer::~SoftwareSurfaceProducer() = default;
std::unique_ptr<SurfaceProducerSurface>
SoftwareSurfaceProducer::ProduceOffscreenSurface(const SkISize& size) {
FML_CHECK(valid_);
return CreateSurface(size);
}
std::unique_ptr<SurfaceProducerSurface> SoftwareSurfaceProducer::ProduceSurface(
const SkISize& size) {
TRACE_EVENT2("flutter", "SoftwareSurfacePool::ProduceSurface", "width",
size.width(), "height", size.height());
FML_CHECK(valid_);
std::unique_ptr<SurfaceProducerSurface> surface;
auto exact_match_it =
std::find_if(available_surfaces_.begin(), available_surfaces_.end(),
[&size](const auto& surface) {
return surface->IsValid() && surface->GetSize() == size;
});
if (exact_match_it != available_surfaces_.end()) {
TRACE_EVENT_INSTANT0("flutter", "Exact match found");
surface = std::move(*exact_match_it);
available_surfaces_.erase(exact_match_it);
} else {
surface = CreateSurface(size);
}
if (surface == nullptr) {
FML_LOG(ERROR) << "Could not acquire surface.";
return nullptr;
}
if (!surface->FlushSessionAcquireAndReleaseEvents()) {
FML_LOG(ERROR) << "Could not flush acquire/release events for buffer.";
return nullptr;
}
return surface;
}
void SoftwareSurfaceProducer::SubmitSurfaces(
std::vector<std::unique_ptr<SurfaceProducerSurface>> surfaces) {
TRACE_EVENT0("flutter", "SoftwareSurfaceProducer::SubmitSurfaces");
// Submit surface
for (auto& surface : surfaces) {
SubmitSurface(std::move(surface));
}
// Buffer management.
AgeAndCollectOldBuffers();
}
void SoftwareSurfaceProducer::SubmitSurface(
std::unique_ptr<SurfaceProducerSurface> surface) {
TRACE_EVENT0("flutter", "SoftwareSurfacePool::SubmitSurface");
FML_CHECK(valid_);
// This cast is safe because |SoftwareSurface| is the only implementation of
// |SurfaceProducerSurface| for Flutter on Fuchsia. Additionally, it is
// required, because we need to access |SoftwareSurface| specific information
// of the surface (such as the amount of memory it contains).
auto software_surface = std::unique_ptr<SoftwareSurface>(
static_cast<SoftwareSurface*>(surface.release()));
if (!software_surface) {
return;
}
uintptr_t surface_key = reinterpret_cast<uintptr_t>(software_surface.get());
auto insert_iterator = pending_surfaces_.insert(std::make_pair(
surface_key, // key
std::move(software_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 SkISize& size) {
TRACE_EVENT2("flutter", "SoftwareSurfacePool::CreateSurface", "width",
size.width(), "height", size.height());
auto surface = std::make_unique<SoftwareSurface>(
sysmem_allocator_, flatland_allocator_, scenic_session_, size);
if (!surface->IsValid()) {
FML_LOG(ERROR) << "Created surface is invalid.";
return nullptr;
}
trace_surfaces_created_++;
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()) {
FML_LOG(ERROR) << "Attempted to recycle invalid surface.";
return;
}
TRACE_EVENT0("flutter", "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 {
TRACE_EVENT_INSTANT0("flutter", "Too many surfaces in pool, dropping");
}
TraceStats();
}
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()) {
FML_LOG(ERROR) << "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() {
TRACE_EVENT0("flutter", "SoftwareSurfacePool::AgeAndCollectOldBuffers");
// Remove all surfaces that are no longer valid or are too old.
size_t size_before = available_surfaces_.size();
available_surfaces_.erase(
std::remove_if(available_surfaces_.begin(), available_surfaces_.end(),
[&](auto& surface) {
return !surface->IsValid() ||
surface->AdvanceAndGetAge() >= kMaxSurfaceAge;
}),
available_surfaces_.end());
TRACE_EVENT1("flutter", "AgeAndCollect", "aged surfaces",
(size_before - available_surfaces_.size()));
TraceStats();
}
void SoftwareSurfaceProducer::TraceStats() {
// Resources held in cached buffers.
size_t cached_surfaces_bytes = 0;
for (const auto& surface : available_surfaces_) {
cached_surfaces_bytes += surface->GetAllocationSize();
}
TRACE_COUNTER("flutter", "SurfacePoolCounts", 0u, "CachedCount",
available_surfaces_.size(), //
"Created", trace_surfaces_created_, //
"Reused", trace_surfaces_reused_, //
"PendingInCompositor", pending_surfaces_.size(), //
"Retained", 0 //
);
TRACE_COUNTER("flutter", "SurfacePoolBytes", 0u, //
"CachedBytes", cached_surfaces_bytes, //
"RetainedBytes", 0 //
);
// Reset per present/frame stats.
trace_surfaces_created_ = 0;
trace_surfaces_reused_ = 0;
}
} // namespace flutter_runner