blob: 9962f0735557da4fe3c19e2dd9407f0228aba4fd [file] [log] [blame]
// Copyright 2016 The Chromium 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 "components/viz/service/main/viz_main_impl.h"
#include <memory>
#include <utility>
#include "base/message_loop/message_loop.h"
#include "base/power_monitor/power_monitor_device_source.h"
#include "base/single_thread_task_runner.h"
#include "base/trace_event/memory_dump_manager.h"
#include "build/build_config.h"
#include "components/viz/service/gl/gpu_service_impl.h"
#include "gpu/command_buffer/common/activity_flags.h"
#include "gpu/ipc/gpu_in_process_thread_service.h"
#include "gpu/ipc/service/gpu_init.h"
#include "gpu/ipc/service/gpu_watchdog_thread.h"
#include "media/gpu/buildflags.h"
#include "services/metrics/public/cpp/delegating_ukm_recorder.h"
#include "services/metrics/public/cpp/mojo_ukm_recorder.h"
#include "services/metrics/public/mojom/constants.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
#include "third_party/skia/include/core/SkFontLCDConfig.h"
#if defined(USE_OZONE)
#include "ui/ozone/public/ozone_platform.h"
#endif
namespace {
std::unique_ptr<base::Thread> CreateAndStartIOThread() {
// TODO(sad): We do not need the IO thread once gpu has a separate process.
// It should be possible to use |main_task_runner_| for doing IO tasks.
base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0);
thread_options.priority = base::ThreadPriority::NORMAL;
#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
// TODO(reveman): Remove this in favor of setting it explicitly for each
// type of process.
thread_options.priority = base::ThreadPriority::DISPLAY;
#endif
auto io_thread = std::make_unique<base::Thread>("GpuIOThread");
CHECK(io_thread->StartWithOptions(thread_options));
return io_thread;
}
} // namespace
namespace viz {
VizMainImpl::ExternalDependencies::ExternalDependencies() = default;
VizMainImpl::ExternalDependencies::~ExternalDependencies() = default;
VizMainImpl::ExternalDependencies::ExternalDependencies(
ExternalDependencies&& other) = default;
VizMainImpl::ExternalDependencies& VizMainImpl::ExternalDependencies::operator=(
ExternalDependencies&& other) = default;
VizMainImpl::VizMainImpl(Delegate* delegate,
ExternalDependencies dependencies,
std::unique_ptr<gpu::GpuInit> gpu_init)
: delegate_(delegate),
dependencies_(std::move(dependencies)),
gpu_init_(std::move(gpu_init)),
gpu_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
binding_(this),
associated_binding_(this) {
DCHECK(gpu_init_);
// TODO(crbug.com/609317): Remove this when Mus Window Server and GPU are
// split into separate processes. Until then this is necessary to be able to
// run Mushrome (chrome with mus) with Mus running in the browser process.
if (!base::PowerMonitor::Get()) {
power_monitor_ = std::make_unique<base::PowerMonitor>(
std::make_unique<base::PowerMonitorDeviceSource>());
}
if (!dependencies_.io_thread_task_runner)
io_thread_ = CreateAndStartIOThread();
if (dependencies_.create_display_compositor) {
viz_compositor_thread_runner_ =
std::make_unique<VizCompositorThreadRunner>();
if (delegate_) {
delegate_->PostCompositorThreadCreated(
viz_compositor_thread_runner_->task_runner());
}
}
CreateUkmRecorderIfNeeded(dependencies.connector);
gpu_service_ = std::make_unique<GpuServiceImpl>(
gpu_init_->gpu_info(), gpu_init_->TakeWatchdogThread(), io_task_runner(),
gpu_init_->gpu_feature_info(), gpu_init_->gpu_preferences(),
gpu_init_->gpu_info_for_hardware_gpu(),
gpu_init_->gpu_feature_info_for_hardware_gpu(),
gpu_init_->vulkan_implementation(),
base::BindOnce(&VizMainImpl::ExitProcess, base::Unretained(this)));
if (dependencies_.create_display_compositor)
gpu_service_->set_oopd_enabled();
}
VizMainImpl::~VizMainImpl() {
DCHECK(gpu_thread_task_runner_->BelongsToCurrentThread());
// The compositor holds on to some resources from gpu service. So destroy the
// compositor first, before destroying the gpu service. However, before the
// compositor is destroyed, close the binding, so that the gpu service doesn't
// need to process commands from the host as it is shutting down.
binding_.Close();
associated_binding_.Close();
// If the VizCompositorThread was started then this will block until the
// thread has been shutdown. All RootCompositorFrameSinks must be destroyed
// before now, otherwise the compositor thread will deadlock waiting for a
// response from the blocked GPU thread.
viz_compositor_thread_runner_.reset();
if (ukm_recorder_)
ukm::DelegatingUkmRecorder::Get()->RemoveDelegate(ukm_recorder_.get());
}
void VizMainImpl::SetLogMessagesForHost(LogMessages log_messages) {
log_messages_ = std::move(log_messages);
}
void VizMainImpl::Bind(mojom::VizMainRequest request) {
binding_.Bind(std::move(request));
}
void VizMainImpl::BindAssociated(mojom::VizMainAssociatedRequest request) {
associated_binding_.Bind(std::move(request));
}
void VizMainImpl::CreateGpuService(
mojom::GpuServiceRequest request,
mojom::GpuHostPtr gpu_host,
discardable_memory::mojom::DiscardableSharedMemoryManagerPtr
discardable_memory_manager,
mojo::ScopedSharedBufferHandle activity_flags,
gfx::FontRenderParams::SubpixelRendering subpixel_rendering) {
DCHECK(gpu_thread_task_runner_->BelongsToCurrentThread());
// If GL is disabled then don't try to collect GPUInfo, we're not using GPU.
if (gl::GetGLImplementation() != gl::kGLImplementationDisabled)
gpu_service_->UpdateGPUInfo();
for (const LogMessage& log : log_messages_)
gpu_host->RecordLogMessage(log.severity, log.header, log.message);
log_messages_.clear();
if (!gpu_init_->init_successful()) {
LOG(ERROR) << "Exiting GPU process due to errors during initialization";
gpu_service_.reset();
gpu_host->DidFailInitialize();
if (delegate_)
delegate_->OnInitializationFailed();
return;
}
if (!gpu_init_->gpu_info().in_process_gpu) {
// If the GPU is running in the browser process, discardable memory manager
// has already been initialized.
discardable_shared_memory_manager_ = std::make_unique<
discardable_memory::ClientDiscardableSharedMemoryManager>(
std::move(discardable_memory_manager), io_task_runner());
base::DiscardableMemoryAllocator::SetInstance(
discardable_shared_memory_manager_.get());
}
SkFontLCDConfig::SetSubpixelOrder(
gfx::FontRenderParams::SubpixelRenderingToSkiaLCDOrder(
subpixel_rendering));
SkFontLCDConfig::SetSubpixelOrientation(
gfx::FontRenderParams::SubpixelRenderingToSkiaLCDOrientation(
subpixel_rendering));
gpu_service_->Bind(std::move(request));
gpu_service_->InitializeWithHost(
std::move(gpu_host),
gpu::GpuProcessActivityFlags(std::move(activity_flags)),
gpu_init_->TakeDefaultOffscreenSurface(),
dependencies_.sync_point_manager, dependencies_.shutdown_event);
if (!pending_frame_sink_manager_params_.is_null()) {
CreateFrameSinkManagerInternal(
std::move(pending_frame_sink_manager_params_));
pending_frame_sink_manager_params_.reset();
}
if (delegate_)
delegate_->OnGpuServiceConnection(gpu_service_.get());
}
void VizMainImpl::CreateUkmRecorderIfNeeded(
service_manager::Connector* connector) {
// If GPU is running in the browser process, we can use browser's UKMRecorder.
if (gpu_init_->gpu_info().in_process_gpu)
return;
DCHECK(connector) << "Unable to initialize UKMRecorder in the GPU process - "
<< "no valid connector.";
ukm_recorder_ = ukm::MojoUkmRecorder::Create(connector);
ukm::DelegatingUkmRecorder::Get()->AddDelegate(ukm_recorder_->GetWeakPtr());
}
void VizMainImpl::CreateFrameSinkManager(
mojom::FrameSinkManagerParamsPtr params) {
DCHECK(viz_compositor_thread_runner_);
DCHECK(gpu_thread_task_runner_->BelongsToCurrentThread());
if (!gpu_service_ || !gpu_service_->is_initialized()) {
DCHECK(pending_frame_sink_manager_params_.is_null());
pending_frame_sink_manager_params_ = std::move(params);
return;
}
CreateFrameSinkManagerInternal(std::move(params));
}
void VizMainImpl::CreateFrameSinkManagerInternal(
mojom::FrameSinkManagerParamsPtr params) {
DCHECK(gpu_service_);
DCHECK(gpu_thread_task_runner_->BelongsToCurrentThread());
gl::GLSurfaceFormat format;
// If we are running a SW Viz process, we may not have a default offscreen
// surface.
if (auto* offscreen_surface =
gpu_service_->gpu_channel_manager()->default_offscreen_surface()) {
format = offscreen_surface->GetFormat();
} else {
DCHECK_EQ(gl::GetGLImplementation(), gl::kGLImplementationDisabled);
}
task_executor_ = base::MakeRefCounted<gpu::GpuInProcessThreadService>(
gpu_thread_task_runner_, gpu_service_->scheduler(),
gpu_service_->sync_point_manager(), gpu_service_->mailbox_manager(),
gpu_service_->share_group(), format, gpu_service_->gpu_feature_info(),
gpu_service_->gpu_channel_manager()->gpu_preferences());
viz_compositor_thread_runner_->CreateFrameSinkManager(
std::move(params), task_executor_, gpu_service_.get());
}
void VizMainImpl::CreateVizDevTools(mojom::VizDevToolsParamsPtr params) {
#if defined(USE_VIZ_DEVTOOLS)
viz_compositor_thread_runner_->CreateVizDevTools(std::move(params));
#endif
}
void VizMainImpl::ExitProcess() {
DCHECK(gpu_thread_task_runner_->BelongsToCurrentThread());
// Close mojom::VizMain bindings first so the browser can't try to reconnect.
binding_.Close();
associated_binding_.Close();
if (viz_compositor_thread_runner_) {
// OOP-D requires destroying RootCompositorFrameSinkImpls on the compositor
// thread while the GPU thread is still running to avoid deadlock. Quit GPU
// thread TaskRunner after cleanup on compositor thread is finished.
viz_compositor_thread_runner_->CleanupForShutdown(base::BindOnce(
&Delegate::QuitMainMessageLoop, base::Unretained(delegate_)));
} else {
delegate_->QuitMainMessageLoop();
}
}
} // namespace viz