1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/viz/service/main/viz_main_impl.h"
6 
7 #include <memory>
8 #include <utility>
9 
10 #include "base/bind.h"
11 #include "base/feature_list.h"
12 #include "base/message_loop/message_pump_type.h"
13 #include "base/power_monitor/power_monitor.h"
14 #include "base/power_monitor/power_monitor_source.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/trace_event/memory_dump_manager.h"
17 #include "build/build_config.h"
18 #include "components/ui_devtools/buildflags.h"
19 #include "gpu/command_buffer/common/activity_flags.h"
20 #include "gpu/config/gpu_finch_features.h"
21 #include "gpu/ipc/service/gpu_init.h"
22 #include "gpu/ipc/service/gpu_watchdog_thread.h"
23 #include "media/gpu/buildflags.h"
24 #include "services/metrics/public/cpp/delegating_ukm_recorder.h"
25 #include "services/metrics/public/cpp/mojo_ukm_recorder.h"
26 #include "skia/ext/legacy_display_globals.h"
27 
28 namespace {
29 
CreateAndStartIOThread()30 std::unique_ptr<base::Thread> CreateAndStartIOThread() {
31   // TODO(sad): We do not need the IO thread once gpu has a separate process.
32   // It should be possible to use |main_task_runner_| for doing IO tasks.
33   base::Thread::Options thread_options(base::MessagePumpType::IO, 0);
34   // TODO(reveman): Remove this in favor of setting it explicitly for each
35   // type of process.
36   if (base::FeatureList::IsEnabled(features::kGpuUseDisplayThreadPriority))
37     thread_options.priority = base::ThreadPriority::DISPLAY;
38   auto io_thread = std::make_unique<base::Thread>("GpuIOThread");
39   CHECK(io_thread->StartWithOptions(thread_options));
40   return io_thread;
41 }
42 
43 }  // namespace
44 
45 namespace viz {
46 
47 VizMainImpl::ExternalDependencies::ExternalDependencies() = default;
48 
49 VizMainImpl::ExternalDependencies::~ExternalDependencies() = default;
50 
51 VizMainImpl::ExternalDependencies::ExternalDependencies(
52     ExternalDependencies&& other) = default;
53 
54 VizMainImpl::ExternalDependencies& VizMainImpl::ExternalDependencies::operator=(
55     ExternalDependencies&& other) = default;
56 
VizMainImpl(Delegate * delegate,ExternalDependencies dependencies,std::unique_ptr<gpu::GpuInit> gpu_init)57 VizMainImpl::VizMainImpl(Delegate* delegate,
58                          ExternalDependencies dependencies,
59                          std::unique_ptr<gpu::GpuInit> gpu_init)
60     : delegate_(delegate),
61       dependencies_(std::move(dependencies)),
62       gpu_init_(std::move(gpu_init)),
63       gpu_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
64   DCHECK(gpu_init_);
65 
66   // TODO(crbug.com/609317): Remove this when Mus Window Server and GPU are
67   // split into separate processes. Until then this is necessary to be able to
68   // run Mushrome (chrome with mus) with Mus running in the browser process.
69   if (dependencies_.power_monitor_source) {
70     base::PowerMonitor::Initialize(
71         std::move(dependencies_.power_monitor_source));
72   }
73 
74   if (!dependencies_.io_thread_task_runner)
75     io_thread_ = CreateAndStartIOThread();
76 
77   if (dependencies_.viz_compositor_thread_runner) {
78     viz_compositor_thread_runner_ = dependencies_.viz_compositor_thread_runner;
79   } else {
80     viz_compositor_thread_runner_impl_ =
81         std::make_unique<VizCompositorThreadRunnerImpl>();
82     viz_compositor_thread_runner_ = viz_compositor_thread_runner_impl_.get();
83   }
84   if (delegate_) {
85     delegate_->PostCompositorThreadCreated(
86         viz_compositor_thread_runner_->task_runner());
87   }
88 
89   if (!gpu_init_->gpu_info().in_process_gpu && dependencies_.ukm_recorder) {
90     // NOTE: If the GPU is running in the browser process, we can use the
91     // browser's UKMRecorder.
92     ukm::DelegatingUkmRecorder::Get()->AddDelegate(
93         dependencies_.ukm_recorder->GetWeakPtr());
94   }
95 
96   gpu_service_ = std::make_unique<GpuServiceImpl>(
97       gpu_init_->gpu_info(), gpu_init_->TakeWatchdogThread(), io_task_runner(),
98       gpu_init_->gpu_feature_info(), gpu_init_->gpu_preferences(),
99       gpu_init_->gpu_info_for_hardware_gpu(),
100       gpu_init_->gpu_feature_info_for_hardware_gpu(),
101       gpu_init_->gpu_extra_info(), gpu_init_->vulkan_implementation(),
102       base::BindOnce(&VizMainImpl::ExitProcess, base::Unretained(this)));
103 }
104 
~VizMainImpl()105 VizMainImpl::~VizMainImpl() {
106   DCHECK(gpu_thread_task_runner_->BelongsToCurrentThread());
107 
108   // The compositor holds on to some resources from gpu service. So destroy the
109   // compositor first, before destroying the gpu service. However, before the
110   // compositor is destroyed, close the binding, so that the gpu service doesn't
111   // need to process commands from the host as it is shutting down.
112   receiver_.reset();
113 
114   // If the VizCompositorThread was started and owned by VizMainImpl, then this
115   // will block until the thread has been shutdown. All RootCompositorFrameSinks
116   // must be destroyed before now, otherwise the compositor thread will deadlock
117   // waiting for a response from the blocked GPU thread.
118   // For the non-owned case for Android WebView, Viz does not communicate with
119   // this thread so there is no need to shutdown viz first.
120   viz_compositor_thread_runner_ = nullptr;
121   viz_compositor_thread_runner_impl_.reset();
122 
123   if (dependencies_.ukm_recorder)
124     ukm::DelegatingUkmRecorder::Get()->RemoveDelegate(
125         dependencies_.ukm_recorder.get());
126 }
127 
BindAssociated(mojo::PendingAssociatedReceiver<mojom::VizMain> pending_receiver)128 void VizMainImpl::BindAssociated(
129     mojo::PendingAssociatedReceiver<mojom::VizMain> pending_receiver) {
130   receiver_.Bind(std::move(pending_receiver));
131 }
132 
CreateGpuService(mojo::PendingReceiver<mojom::GpuService> pending_receiver,mojo::PendingRemote<mojom::GpuHost> pending_gpu_host,mojo::PendingRemote<discardable_memory::mojom::DiscardableSharedMemoryManager> discardable_memory_manager,mojo::ScopedSharedBufferHandle activity_flags,gfx::FontRenderParams::SubpixelRendering subpixel_rendering)133 void VizMainImpl::CreateGpuService(
134     mojo::PendingReceiver<mojom::GpuService> pending_receiver,
135     mojo::PendingRemote<mojom::GpuHost> pending_gpu_host,
136     mojo::PendingRemote<
137         discardable_memory::mojom::DiscardableSharedMemoryManager>
138         discardable_memory_manager,
139     mojo::ScopedSharedBufferHandle activity_flags,
140     gfx::FontRenderParams::SubpixelRendering subpixel_rendering) {
141   DCHECK(gpu_thread_task_runner_->BelongsToCurrentThread());
142 
143   mojo::Remote<mojom::GpuHost> gpu_host(std::move(pending_gpu_host));
144 
145   // If GL is disabled then don't try to collect GPUInfo, we're not using GPU.
146   if (gl::GetGLImplementation() != gl::kGLImplementationDisabled)
147     gpu_service_->UpdateGPUInfo();
148 
149   if (!gpu_init_->init_successful()) {
150     LOG(ERROR) << "Exiting GPU process due to errors during initialization";
151     GpuServiceImpl::FlushPreInitializeLogMessages(gpu_host.get());
152     gpu_service_.reset();
153     gpu_host->DidFailInitialize();
154     if (delegate_)
155       delegate_->OnInitializationFailed();
156     return;
157   }
158 
159   if (!gpu_init_->gpu_info().in_process_gpu) {
160     // If the GPU is running in the browser process, discardable memory manager
161     // has already been initialized.
162     discardable_shared_memory_manager_ = base::MakeRefCounted<
163         discardable_memory::ClientDiscardableSharedMemoryManager>(
164         std::move(discardable_memory_manager), io_task_runner());
165     base::DiscardableMemoryAllocator::SetInstance(
166         discardable_shared_memory_manager_.get());
167   }
168 
169   skia::LegacyDisplayGlobals::SetCachedPixelGeometry(
170       gfx::FontRenderParams::SubpixelRenderingToSkiaPixelGeometry(
171           subpixel_rendering));
172 
173   gpu_service_->Bind(std::move(pending_receiver));
174   gpu_service_->InitializeWithHost(
175       gpu_host.Unbind(),
176       gpu::GpuProcessActivityFlags(std::move(activity_flags)),
177       gpu_init_->TakeDefaultOffscreenSurface(),
178       dependencies_.sync_point_manager, dependencies_.shared_image_manager,
179       dependencies_.shutdown_event);
180 
181   if (!pending_frame_sink_manager_params_.is_null()) {
182     CreateFrameSinkManagerInternal(
183         std::move(pending_frame_sink_manager_params_));
184     pending_frame_sink_manager_params_.reset();
185   }
186   if (delegate_)
187     delegate_->OnGpuServiceConnection(gpu_service_.get());
188 }
189 
190 #if defined(OS_WIN)
CreateInfoCollectionGpuService(mojo::PendingReceiver<mojom::InfoCollectionGpuService> pending_receiver)191 void VizMainImpl::CreateInfoCollectionGpuService(
192     mojo::PendingReceiver<mojom::InfoCollectionGpuService> pending_receiver) {
193   DCHECK(gpu_thread_task_runner_->BelongsToCurrentThread());
194   DCHECK(!info_collection_gpu_service_);
195   DCHECK(gpu_init_->device_perf_info().has_value());
196 
197   info_collection_gpu_service_ = std::make_unique<InfoCollectionGpuServiceImpl>(
198       gpu_thread_task_runner_, io_task_runner(),
199       gpu_init_->device_perf_info().value(), gpu_init_->gpu_info().active_gpu(),
200       std::move(pending_receiver));
201 }
202 #endif
203 
CreateFrameSinkManager(mojom::FrameSinkManagerParamsPtr params)204 void VizMainImpl::CreateFrameSinkManager(
205     mojom::FrameSinkManagerParamsPtr params) {
206   DCHECK(viz_compositor_thread_runner_);
207   DCHECK(gpu_thread_task_runner_->BelongsToCurrentThread());
208   if (!gpu_service_ || !gpu_service_->is_initialized()) {
209     DCHECK(pending_frame_sink_manager_params_.is_null());
210     pending_frame_sink_manager_params_ = std::move(params);
211     return;
212   }
213   CreateFrameSinkManagerInternal(std::move(params));
214 }
215 
CreateFrameSinkManagerInternal(mojom::FrameSinkManagerParamsPtr params)216 void VizMainImpl::CreateFrameSinkManagerInternal(
217     mojom::FrameSinkManagerParamsPtr params) {
218   DCHECK(gpu_service_);
219   DCHECK(gpu_thread_task_runner_->BelongsToCurrentThread());
220 
221   gl::GLSurfaceFormat format;
222   // If we are running a SW Viz process, we may not have a default offscreen
223   // surface.
224   if (auto* offscreen_surface =
225           gpu_service_->gpu_channel_manager()->default_offscreen_surface()) {
226     format = offscreen_surface->GetFormat();
227   } else {
228     DCHECK_EQ(gl::GetGLImplementation(), gl::kGLImplementationDisabled);
229   }
230 
231   // When the host loses its connection to the viz process, it assumes the
232   // process has crashed and tries to reinitialize it. However, it is possible
233   // to have lost the connection for other reasons (e.g. deserialization
234   // errors) and the viz process is already set up. We cannot recreate
235   // FrameSinkManagerImpl, so just do a hard CHECK rather than crashing down the
236   // road so that all crash reports caused by this issue look the same and have
237   // the same signature. https://crbug.com/928845
238   CHECK(!task_executor_);
239 
240   task_executor_ = std::make_unique<gpu::GpuInProcessThreadService>(
241       this, gpu_thread_task_runner_, gpu_service_->GetGpuScheduler(),
242       gpu_service_->sync_point_manager(), gpu_service_->mailbox_manager(),
243       format, gpu_service_->gpu_feature_info(),
244       gpu_service_->gpu_channel_manager()->gpu_preferences(),
245       gpu_service_->shared_image_manager(),
246       gpu_service_->gpu_channel_manager()->program_cache());
247 
248   viz_compositor_thread_runner_->CreateFrameSinkManager(
249       std::move(params), task_executor_.get(), gpu_service_.get());
250 }
251 
CreateVizDevTools(mojom::VizDevToolsParamsPtr params)252 void VizMainImpl::CreateVizDevTools(mojom::VizDevToolsParamsPtr params) {
253 #if BUILDFLAG(USE_VIZ_DEVTOOLS)
254   viz_compositor_thread_runner_->CreateVizDevTools(std::move(params));
255 #endif
256 }
257 
GetSharedContextState()258 scoped_refptr<gpu::SharedContextState> VizMainImpl::GetSharedContextState() {
259   return gpu_service_->GetContextState();
260 }
261 
GetShareGroup()262 scoped_refptr<gl::GLShareGroup> VizMainImpl::GetShareGroup() {
263   return gpu_service_->share_group();
264 }
265 
ExitProcess(base::Optional<ExitCode> immediate_exit_code)266 void VizMainImpl::ExitProcess(base::Optional<ExitCode> immediate_exit_code) {
267   DCHECK(gpu_thread_task_runner_->BelongsToCurrentThread());
268 
269   if (!gpu_init_->gpu_info().in_process_gpu && immediate_exit_code) {
270     // Atomically shut down GPU process to make it faster and simpler.
271     base::Process::TerminateCurrentProcessImmediately(
272         static_cast<int>(immediate_exit_code.value()));
273     return;
274   }
275 
276   // Close mojom::VizMain bindings first so the browser can't try to reconnect.
277   receiver_.reset();
278 
279   if (viz_compositor_thread_runner_) {
280     // Destroy RootCompositorFrameSinkImpls on the compositor while the GPU
281     // thread is still running to avoid deadlock. Quit GPU thread TaskRunner
282     // after cleanup on compositor thread is finished.
283     viz_compositor_thread_runner_->CleanupForShutdown(base::BindOnce(
284         &Delegate::QuitMainMessageLoop, base::Unretained(delegate_)));
285   } else {
286     delegate_->QuitMainMessageLoop();
287   }
288 }
289 
290 }  // namespace viz
291