// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/renderer_host/compositor_dependencies_android.h"
#include <utility>
#include "base/allocator/partition_alloc_support.h"
#include "base/features.h"
#include "base/functional/bind.h"
#include "base/no_destructor.h"
#include "base/system/sys_info.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "components/viz/client/frame_eviction_manager.h"
#include "content/browser/browser_main_loop.h"
#include "content/browser/gpu/browser_gpu_channel_host_factory.h"
#include "content/browser/gpu/gpu_data_manager_impl.h"
#include "content/browser/gpu/gpu_process_host.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/content_features.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
namespace content {
namespace {
// The client_id used here should not conflict with the client_id generated
// from RenderWidgetHostImpl.
constexpr uint32_t kDefaultClientId = 0u;
void BrowserGpuChannelHostFactorySetApplicationVisible(bool is_visible) {
// This code relies on the browser's GpuChannelEstablishFactory being the
// BrowserGpuChannelHostFactory.
DCHECK_EQ(BrowserMainLoop::GetInstance()->gpu_channel_establish_factory(),
BrowserGpuChannelHostFactory::instance());
BrowserGpuChannelHostFactory::instance()->SetApplicationVisible(is_visible);
}
// These functions are called based on application visibility status.
void SendOnBackgroundedToGpuService() {
content::GpuProcessHost::CallOnUI(
FROM_HERE, content::GPU_PROCESS_KIND_SANDBOXED, false /* force_create */,
base::BindOnce([](content::GpuProcessHost* host) {
// This is not necessarily the most logical place to notify the
// allocator, but it matches the call made on the GPU process side.
base::allocator::PartitionAllocSupport::Get()->OnBackgrounded();
if (host) {
host->gpu_service()->OnBackgrounded();
}
}));
}
void SendOnForegroundedToGpuService() {
content::GpuProcessHost::CallOnUI(
FROM_HERE, content::GPU_PROCESS_KIND_SANDBOXED, false /* force_create */,
base::BindOnce([](content::GpuProcessHost* host) {
base::allocator::PartitionAllocSupport::Get()->OnForegrounded();
if (host) {
host->gpu_service()->OnForegrounded();
}
}));
}
} // namespace
// static
CompositorDependenciesAndroid& CompositorDependenciesAndroid::Get() {
static base::NoDestructor<CompositorDependenciesAndroid> instance;
return *instance;
}
CompositorDependenciesAndroid::CompositorDependenciesAndroid()
: frame_sink_id_allocator_(kDefaultClientId) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Set up a callback to automatically re-connect if we lose our connection.
// Unretained is safe due to base::NoDestructor.
host_frame_sink_manager_.SetConnectionLostCallback(base::BindRepeating(
&CompositorDependenciesAndroid::CreateVizFrameSinkManager,
base::Unretained(this)));
CreateVizFrameSinkManager();
}
CompositorDependenciesAndroid::~CompositorDependenciesAndroid() = default;
void CompositorDependenciesAndroid::CreateVizFrameSinkManager() {
mojo::PendingRemote<viz::mojom::FrameSinkManager> frame_sink_manager;
mojo::PendingReceiver<viz::mojom::FrameSinkManager>
frame_sink_manager_receiver =
frame_sink_manager.InitWithNewPipeAndPassReceiver();
mojo::PendingRemote<viz::mojom::FrameSinkManagerClient>
frame_sink_manager_client;
mojo::PendingReceiver<viz::mojom::FrameSinkManagerClient>
frame_sink_manager_client_receiver =
frame_sink_manager_client.InitWithNewPipeAndPassReceiver();
// Setup HostFrameSinkManager with interface endpoints.
host_frame_sink_manager_.BindAndSetManager(
std::move(frame_sink_manager_client_receiver),
base::SingleThreadTaskRunner::GetCurrentDefault(),
std::move(frame_sink_manager));
// Set up a pending request which will be run once we've successfully
// connected to the GPU process.
pending_connect_viz_on_main_thread_ = base::BindOnce(
&CompositorDependenciesAndroid::ConnectVizFrameSinkManagerOnMainThread,
std::move(frame_sink_manager_receiver),
std::move(frame_sink_manager_client),
host_frame_sink_manager_.debug_renderer_settings());
}
viz::FrameSinkId CompositorDependenciesAndroid::AllocateFrameSinkId() {
return frame_sink_id_allocator_.NextFrameSinkId();
}
void CompositorDependenciesAndroid::TryEstablishVizConnectionIfNeeded() {
if (!pending_connect_viz_on_main_thread_)
return;
std::move(pending_connect_viz_on_main_thread_).Run();
}
// Called on the GpuProcessHost thread, after a GPU connection has already been
// established. |gpu_process_host| should only be invalid if a channel has been
// established and lost. In this case the ConnectionLost callback will be
// re-run when the request is deleted (goes out of scope).
// static
void CompositorDependenciesAndroid::ConnectVizFrameSinkManagerOnMainThread(
mojo::PendingReceiver<viz::mojom::FrameSinkManager> receiver,
mojo::PendingRemote<viz::mojom::FrameSinkManagerClient> client,
const viz::DebugRendererSettings& debug_renderer_settings) {
auto* gpu_process_host = GpuProcessHost::Get();
if (!gpu_process_host)
return;
gpu_process_host->gpu_host()->ConnectFrameSinkManager(
std::move(receiver), std::move(client), debug_renderer_settings);
}
void CompositorDependenciesAndroid::EnqueueLowEndBackgroundCleanup() {
if (base::SysInfo::IsLowEndDevice()) {
low_end_background_cleanup_task_.Reset(base::BindOnce(
&CompositorDependenciesAndroid::DoLowEndBackgroundCleanup,
base::Unretained(this)));
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, low_end_background_cleanup_task_.callback(),
base::Seconds(5));
}
}
void CompositorDependenciesAndroid::DoLowEndBackgroundCleanup() {
// When we become visible, we immediately cancel the callback that runs this
// code. First, evict all unlocked frames, allowing resources to be
// reclaimed.
viz::FrameEvictionManager::GetInstance()->PurgeAllUnlockedFrames();
// Next, notify the GPU process to do background processing, which will
// lose all renderer contexts.
auto* host = GpuProcessHost::Get();
if (host) {
host->gpu_service()->OnBackgroundCleanup();
}
}
void CompositorDependenciesAndroid::OnCompositorVisible(
CompositorImpl* compositor) {
CHECK(!visible_synchronous_compositors_);
bool element_inserted = visible_compositors_.insert(compositor).second;
DCHECK(element_inserted);
if (visible_compositors_.size() == 1)
OnVisibilityChanged();
}
void CompositorDependenciesAndroid::OnCompositorHidden(
CompositorImpl* compositor) {
CHECK(!visible_synchronous_compositors_);
size_t elements_removed = visible_compositors_.erase(compositor);
DCHECK_EQ(1u, elements_removed);
if (visible_compositors_.size() == 0)
OnVisibilityChanged();
}
void CompositorDependenciesAndroid::OnSynchronousCompositorVisible() {
CHECK(visible_compositors_.empty());
visible_synchronous_compositors_++;
if (visible_synchronous_compositors_ == 1u) {
OnVisibilityChanged();
}
}
void CompositorDependenciesAndroid::OnSynchronousCompositorHidden() {
CHECK(visible_compositors_.empty());
CHECK_GT(visible_synchronous_compositors_, 0u);
visible_synchronous_compositors_--;
if (visible_synchronous_compositors_ == 0u) {
OnVisibilityChanged();
}
}
// This function runs when our first CompositorImpl becomes visible or when
// our last Compositormpl is hidden.
void CompositorDependenciesAndroid::OnVisibilityChanged() {
if (visible_compositors_.size() > 0 ||
visible_synchronous_compositors_ > 0u) {
GpuDataManagerImpl::GetInstance()->SetApplicationVisible(true);
BrowserGpuChannelHostFactorySetApplicationVisible(true);
SendOnForegroundedToGpuService();
low_end_background_cleanup_task_.Cancel();
} else {
GpuDataManagerImpl::GetInstance()->SetApplicationVisible(false);
BrowserGpuChannelHostFactorySetApplicationVisible(false);
SendOnBackgroundedToGpuService();
EnqueueLowEndBackgroundCleanup();
}
}
} // namespace content