blob: 5d14b53cf8399ab90aebc65fae65c778776fb5a9 [file] [log] [blame]
Mohsen Izadi0b9fbb62018-08-30 20:30:001// Copyright 2018 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/host/gpu_host_impl.h"
6
7#include <utility>
8
9#include "base/bind_helpers.h"
10#include "base/command_line.h"
11#include "base/feature_list.h"
12#include "base/metrics/histogram_macros.h"
13#include "base/no_destructor.h"
14#include "base/threading/thread_checker.h"
15#include "base/trace_event/trace_event.h"
16#include "build/build_config.h"
17#include "components/viz/common/features.h"
18#include "gpu/config/gpu_driver_bug_workaround_type.h"
19#include "gpu/config/gpu_feature_info.h"
20#include "gpu/config/gpu_finch_features.h"
21#include "gpu/config/gpu_info.h"
22#include "gpu/ipc/common/gpu_client_ids.h"
23#include "gpu/ipc/host/shader_disk_cache.h"
24#include "ipc/ipc_channel.h"
25#include "ui/gfx/font_render_params.h"
26
27#if defined(OS_ANDROID)
28#include "base/android/build_info.h"
29#endif
30
31#if defined(OS_WIN)
32#include "ui/gfx/win/rendering_window_manager.h"
33#endif
34
35namespace viz {
36namespace {
37
38// A wrapper around gfx::FontRenderParams that checks it is set and accessed on
39// the same thread.
40class FontRenderParams {
41 public:
42 void Set(const gfx::FontRenderParams& params);
43 const base::Optional<gfx::FontRenderParams>& Get();
44
45 private:
46 friend class base::NoDestructor<FontRenderParams>;
47
48 FontRenderParams();
49 ~FontRenderParams();
50
51 THREAD_CHECKER(thread_checker_);
52 base::Optional<gfx::FontRenderParams> params_;
53
54 DISALLOW_COPY_AND_ASSIGN(FontRenderParams);
55};
56
57void FontRenderParams::Set(const gfx::FontRenderParams& params) {
58 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
59 params_ = params;
60}
61
62const base::Optional<gfx::FontRenderParams>& FontRenderParams::Get() {
63 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
64 return params_;
65}
66
67FontRenderParams::FontRenderParams() = default;
68
69FontRenderParams::~FontRenderParams() {
70 NOTREACHED();
71}
72
73FontRenderParams& GetFontRenderParams() {
74 static base::NoDestructor<FontRenderParams> instance;
75 return *instance;
76}
77
78} // namespace
79
80GpuHostImpl::InitParams::InitParams() = default;
81
82GpuHostImpl::InitParams::InitParams(InitParams&&) = default;
83
84GpuHostImpl::InitParams::~InitParams() = default;
85
86GpuHostImpl::GpuHostImpl(Delegate* delegate,
87 IPC::Channel* channel,
88 InitParams params)
89 : delegate_(delegate),
90 channel_(channel),
91 params_(std::move(params)),
92 gpu_host_binding_(this),
93 weak_ptr_factory_(this) {
94 DCHECK(delegate_);
95 DCHECK(channel_);
96 channel_->GetAssociatedInterfaceSupport()->GetRemoteAssociatedInterface(
97 &viz_main_ptr_);
98 mojom::GpuHostPtr host_proxy;
99 gpu_host_binding_.Bind(mojo::MakeRequest(&host_proxy));
100
101 discardable_memory::mojom::DiscardableSharedMemoryManagerPtr
102 discardable_manager_ptr;
103 auto discardable_request = mojo::MakeRequest(&discardable_manager_ptr);
104 delegate_->BindDiscardableMemoryRequest(std::move(discardable_request));
105
106 DCHECK(GetFontRenderParams().Get());
107 viz_main_ptr_->CreateGpuService(
108 mojo::MakeRequest(&gpu_service_ptr_), std::move(host_proxy),
109 std::move(discardable_manager_ptr), activity_flags_.CloneHandle(),
110 GetFontRenderParams().Get()->subpixel_rendering);
111}
112
113GpuHostImpl::~GpuHostImpl() = default;
114
115// static
116void GpuHostImpl::InitFontRenderParams(const gfx::FontRenderParams& params) {
117 DCHECK(!GetFontRenderParams().Get());
118 GetFontRenderParams().Set(params);
119}
120
121void GpuHostImpl::OnProcessLaunched(base::ProcessId pid) {
122 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
123 DCHECK_EQ(base::kNullProcessId, pid_);
124 DCHECK_NE(base::kNullProcessId, pid);
125 pid_ = pid;
126}
127
128void GpuHostImpl::OnProcessCrashed() {
129 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
130
131 // If the GPU process crashed while compiling a shader, we may have invalid
132 // cached binaries. Completely clear the shader cache to force shader binaries
133 // to be re-created.
134 if (activity_flags_.IsFlagSet(
135 gpu::ActivityFlagsBase::FLAG_LOADING_PROGRAM_BINARY)) {
136 auto* shader_cache_factory = delegate_->GetShaderCacheFactory();
137 for (auto cache_key : client_id_to_shader_cache_) {
138 // This call will temporarily extend the lifetime of the cache (kept
139 // alive in the factory), and may drop loads of cached shader binaries if
140 // it takes a while to complete. As we are intentionally dropping all
141 // binaries, this behavior is fine.
142 shader_cache_factory->ClearByClientId(
143 cache_key.first, base::Time(), base::Time::Max(), base::DoNothing());
144 }
145 }
146}
147
Mohsen Izadibabed2b2018-08-31 01:37:39148void GpuHostImpl::AddConnectionErrorHandler(base::OnceClosure handler) {
149 connection_error_handlers_.push_back(std::move(handler));
150}
151
Mohsen Izadi0b9fbb62018-08-30 20:30:00152void GpuHostImpl::BlockLiveOffscreenContexts() {
153 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
154
155 for (auto iter = urls_with_live_offscreen_contexts_.begin();
156 iter != urls_with_live_offscreen_contexts_.end(); ++iter) {
157 delegate_->BlockDomainFrom3DAPIs(*iter, Delegate::DomainGuilt::kUnknown);
158 }
159}
160
161void GpuHostImpl::ConnectFrameSinkManager(
162 mojom::FrameSinkManagerRequest request,
163 mojom::FrameSinkManagerClientPtrInfo client) {
164 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
165 TRACE_EVENT0("gpu", "GpuHostImpl::ConnectFrameSinkManager");
166
167 mojom::FrameSinkManagerParamsPtr params =
168 mojom::FrameSinkManagerParams::New();
169 params->restart_id = params_.restart_id;
170 params->use_activation_deadline =
171 params_.deadline_to_synchronize_surfaces.has_value();
172 params->activation_deadline_in_frames =
173 params_.deadline_to_synchronize_surfaces.value_or(0u);
174 params->frame_sink_manager = std::move(request);
175 params->frame_sink_manager_client = std::move(client);
176 viz_main_ptr_->CreateFrameSinkManager(std::move(params));
177}
178
179void GpuHostImpl::EstablishGpuChannel(int client_id,
180 uint64_t client_tracing_id,
181 bool is_gpu_host,
182 EstablishChannelCallback callback) {
183 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
184 TRACE_EVENT0("gpu", "GpuHostImpl::EstablishGpuChannel");
185
186 // If GPU features are already blacklisted, no need to establish the channel.
187 if (!delegate_->GpuAccessAllowed()) {
188 DVLOG(1) << "GPU blacklisted, refusing to open a GPU channel.";
189 std::move(callback).Run(mojo::ScopedMessagePipeHandle(), gpu::GPUInfo(),
190 gpu::GpuFeatureInfo(),
191 EstablishChannelStatus::kGpuAccessDenied);
192 return;
193 }
194
195 if (gpu::IsReservedClientId(client_id)) {
196 // The display-compositor/GrShaderCache in the gpu process uses these
197 // special client ids.
198 std::move(callback).Run(mojo::ScopedMessagePipeHandle(), gpu::GPUInfo(),
199 gpu::GpuFeatureInfo(),
200 EstablishChannelStatus::kGpuAccessDenied);
201 return;
202 }
203
204 bool cache_shaders_on_disk =
205 delegate_->GetShaderCacheFactory()->Get(client_id) != nullptr;
206
207 channel_requests_.push(std::move(callback));
208 gpu_service_ptr_->EstablishGpuChannel(
209 client_id, client_tracing_id, is_gpu_host, cache_shaders_on_disk,
210 base::BindOnce(&GpuHostImpl::OnChannelEstablished,
211 weak_ptr_factory_.GetWeakPtr(), client_id));
212
213 if (!params_.disable_gpu_shader_disk_cache) {
214 CreateChannelCache(client_id);
215
216 bool oopd_enabled =
217 base::FeatureList::IsEnabled(features::kVizDisplayCompositor);
218 if (oopd_enabled)
219 CreateChannelCache(gpu::kInProcessCommandBufferClientId);
220
221 bool oopr_enabled =
222 base::FeatureList::IsEnabled(features::kDefaultEnableOopRasterization);
223 if (oopr_enabled)
224 CreateChannelCache(gpu::kGrShaderCacheClientId);
225 }
226}
227
228void GpuHostImpl::SendOutstandingReplies() {
229 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
230
Mohsen Izadibabed2b2018-08-31 01:37:39231 for (auto& handler : connection_error_handlers_)
232 std::move(handler).Run();
233 connection_error_handlers_.clear();
234
Mohsen Izadi0b9fbb62018-08-30 20:30:00235 // Send empty channel handles for all EstablishChannel requests.
236 while (!channel_requests_.empty()) {
237 auto callback = std::move(channel_requests_.front());
238 channel_requests_.pop();
239 std::move(callback).Run(mojo::ScopedMessagePipeHandle(), gpu::GPUInfo(),
240 gpu::GpuFeatureInfo(),
241 EstablishChannelStatus::kGpuHostInvalid);
242 }
243}
244
245mojom::GpuService* GpuHostImpl::gpu_service() {
246 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
247 DCHECK(gpu_service_ptr_.is_bound());
248 return gpu_service_ptr_.get();
249}
250
251std::string GpuHostImpl::GetShaderPrefixKey() {
252 if (shader_prefix_key_.empty()) {
253 const gpu::GPUInfo& info = delegate_->GetGPUInfo();
254 const gpu::GPUInfo::GPUDevice& active_gpu = info.active_gpu();
255
256 shader_prefix_key_ = params_.product + "-" + info.gl_vendor + "-" +
257 info.gl_renderer + "-" + active_gpu.driver_version +
258 "-" + active_gpu.driver_vendor;
259
260#if defined(OS_ANDROID)
261 std::string build_fp =
262 base::android::BuildInfo::GetInstance()->android_build_fp();
263 // TODO(ericrk): Remove this after it's up for a few days.
264 // https://crbug.com/699122.
265 CHECK(!build_fp.empty());
266 shader_prefix_key_ += "-" + build_fp;
267#endif
268 }
269
270 return shader_prefix_key_;
271}
272
273void GpuHostImpl::LoadedShader(int32_t client_id,
274 const std::string& key,
275 const std::string& data) {
276 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
277
278 std::string prefix = GetShaderPrefixKey();
279 bool prefix_ok = !key.compare(0, prefix.length(), prefix);
280 UMA_HISTOGRAM_BOOLEAN("GPU.ShaderLoadPrefixOK", prefix_ok);
281 if (prefix_ok) {
282 // Remove the prefix from the key before load.
283 std::string key_no_prefix = key.substr(prefix.length() + 1);
284 gpu_service_ptr_->LoadedShader(client_id, key_no_prefix, data);
285 }
286}
287
288void GpuHostImpl::CreateChannelCache(int32_t client_id) {
289 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
290 TRACE_EVENT0("gpu", "GpuHostImpl::CreateChannelCache");
291
292 scoped_refptr<gpu::ShaderDiskCache> cache =
293 delegate_->GetShaderCacheFactory()->Get(client_id);
294 if (!cache)
295 return;
296
297 cache->set_shader_loaded_callback(base::BindRepeating(
298 &GpuHostImpl::LoadedShader, weak_ptr_factory_.GetWeakPtr(), client_id));
299
300 client_id_to_shader_cache_[client_id] = cache;
301}
302
303void GpuHostImpl::OnChannelEstablished(
304 int client_id,
305 mojo::ScopedMessagePipeHandle channel_handle) {
306 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
307 TRACE_EVENT0("gpu", "GpuHostImpl::OnChannelEstablished");
308
309 DCHECK(!channel_requests_.empty());
310 auto callback = std::move(channel_requests_.front());
311 channel_requests_.pop();
312
313 // Currently if any of the GPU features are blacklisted, we don't establish a
314 // GPU channel.
315 if (channel_handle.is_valid() && !delegate_->GpuAccessAllowed()) {
316 gpu_service_ptr_->CloseChannel(client_id);
317 std::move(callback).Run(mojo::ScopedMessagePipeHandle(), gpu::GPUInfo(),
318 gpu::GpuFeatureInfo(),
319 EstablishChannelStatus::kGpuAccessDenied);
320 RecordLogMessage(logging::LOG_WARNING, "WARNING",
321 "Hardware acceleration is unavailable.");
322 return;
323 }
324
325 std::move(callback).Run(std::move(channel_handle), delegate_->GetGPUInfo(),
326 delegate_->GetGpuFeatureInfo(),
327 EstablishChannelStatus::kSuccess);
328}
329
330void GpuHostImpl::DidInitialize(
331 const gpu::GPUInfo& gpu_info,
332 const gpu::GpuFeatureInfo& gpu_feature_info,
333 const base::Optional<gpu::GPUInfo>& gpu_info_for_hardware_gpu,
334 const base::Optional<gpu::GpuFeatureInfo>&
335 gpu_feature_info_for_hardware_gpu) {
336 UMA_HISTOGRAM_BOOLEAN("GPU.GPUProcessInitialized", true);
337 initialized_ = true;
338
339 // Set GPU driver bug workaround flags that are checked on the browser side.
340 wake_up_gpu_before_drawing_ =
341 gpu_feature_info.IsWorkaroundEnabled(gpu::WAKE_UP_GPU_BEFORE_DRAWING);
342 dont_disable_webgl_when_compositor_context_lost_ =
343 gpu_feature_info.IsWorkaroundEnabled(
344 gpu::DONT_DISABLE_WEBGL_WHEN_COMPOSITOR_CONTEXT_LOST);
345
346 delegate_->UpdateGpuInfo(gpu_info, gpu_feature_info,
347 gpu_info_for_hardware_gpu,
348 gpu_feature_info_for_hardware_gpu);
349}
350
351void GpuHostImpl::DidFailInitialize() {
352 UMA_HISTOGRAM_BOOLEAN("GPU.GPUProcessInitialized", false);
353 delegate_->DidFailInitialize();
354}
355
356void GpuHostImpl::DidCreateContextSuccessfully() {
357 delegate_->DidCreateContextSuccessfully();
358}
359
360void GpuHostImpl::DidCreateOffscreenContext(const GURL& url) {
361 urls_with_live_offscreen_contexts_.insert(url);
362}
363
364void GpuHostImpl::DidDestroyOffscreenContext(const GURL& url) {
365 // We only want to remove *one* of the entries in the multiset for this
366 // particular URL, so can't use the erase method taking a key.
367 auto candidate = urls_with_live_offscreen_contexts_.find(url);
368 if (candidate != urls_with_live_offscreen_contexts_.end())
369 urls_with_live_offscreen_contexts_.erase(candidate);
370}
371
372void GpuHostImpl::DidDestroyChannel(int32_t client_id) {
373 TRACE_EVENT0("gpu", "GpuHostImpl::DidDestroyChannel");
374 client_id_to_shader_cache_.erase(client_id);
375}
376
377void GpuHostImpl::DidLoseContext(bool offscreen,
378 gpu::error::ContextLostReason reason,
379 const GURL& active_url) {
380 // TODO(kbr): would be nice to see the "offscreen" flag too.
381 TRACE_EVENT2("gpu", "GpuHostImpl::DidLoseContext", "reason", reason, "url",
382 active_url.possibly_invalid_spec());
383
384 if (!offscreen || active_url.is_empty()) {
385 // Assume that the loss of the compositor's or accelerated canvas'
386 // context is a serious event and blame the loss on all live
387 // offscreen contexts. This more robustly handles situations where
388 // the GPU process may not actually detect the context loss in the
389 // offscreen context. However, situations have been seen where the
390 // compositor's context can be lost due to driver bugs (as of this
391 // writing, on Android), so allow that possibility.
392 if (!dont_disable_webgl_when_compositor_context_lost_)
393 BlockLiveOffscreenContexts();
394 return;
395 }
396
397 Delegate::DomainGuilt guilt = Delegate::DomainGuilt::kUnknown;
398 switch (reason) {
399 case gpu::error::kGuilty:
400 guilt = Delegate::DomainGuilt::kKnown;
401 break;
402 // Treat most other error codes as though they had unknown provenance.
403 // In practice this doesn't affect the user experience. A lost context
404 // of either known or unknown guilt still causes user-level 3D APIs
405 // (e.g. WebGL) to be blocked on that domain until the user manually
406 // reenables them.
407 case gpu::error::kUnknown:
408 case gpu::error::kOutOfMemory:
409 case gpu::error::kMakeCurrentFailed:
410 case gpu::error::kGpuChannelLost:
411 case gpu::error::kInvalidGpuMessage:
412 break;
413 case gpu::error::kInnocent:
414 return;
415 }
416
417 delegate_->BlockDomainFrom3DAPIs(active_url, guilt);
418}
419
420void GpuHostImpl::DisableGpuCompositing() {
421 delegate_->DisableGpuCompositing();
422}
423
424void GpuHostImpl::SetChildSurface(gpu::SurfaceHandle parent,
425 gpu::SurfaceHandle child) {
426#if defined(OS_WIN)
427 constexpr char kBadMessageError[] = "Bad parenting request from gpu process.";
428 if (!params_.in_process) {
429 DWORD parent_process_id = 0;
430 DWORD parent_thread_id =
431 ::GetWindowThreadProcessId(parent, &parent_process_id);
432 if (!parent_thread_id || parent_process_id != ::GetCurrentProcessId()) {
433 LOG(ERROR) << kBadMessageError;
434 return;
435 }
436
437 DWORD child_process_id = 0;
438 DWORD child_thread_id =
439 ::GetWindowThreadProcessId(child, &child_process_id);
440 if (!child_thread_id || child_process_id != pid_) {
441 LOG(ERROR) << kBadMessageError;
442 return;
443 }
444 }
445
446 if (!gfx::RenderingWindowManager::GetInstance()->RegisterChild(parent,
447 child)) {
448 LOG(ERROR) << kBadMessageError;
449 }
450#else
451 NOTREACHED();
452#endif
453}
454
455void GpuHostImpl::StoreShaderToDisk(int32_t client_id,
456 const std::string& key,
457 const std::string& shader) {
458 TRACE_EVENT0("gpu", "GpuHostImpl::StoreShaderToDisk");
459 auto iter = client_id_to_shader_cache_.find(client_id);
460 // If the cache doesn't exist then this is an off the record profile.
461 if (iter == client_id_to_shader_cache_.end())
462 return;
463 std::string prefix = GetShaderPrefixKey();
464 iter->second->Cache(prefix + ":" + key, shader);
465}
466
467void GpuHostImpl::RecordLogMessage(int32_t severity,
468 const std::string& header,
469 const std::string& message) {
470 delegate_->RecordLogMessage(severity, header, message);
471}
472
473} // namespace viz