| // 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() { |
| 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); |
| |
| 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_, 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 |