gpu_data_manager_impl_private.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
1// Copyright (c) 2013 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 "content/browser/gpu/gpu_data_manager_impl_private.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/command_line.h"
10#include "base/debug/trace_event.h"
11#include "base/metrics/field_trial.h"
12#include "base/metrics/histogram.h"
13#include "base/metrics/sparse_histogram.h"
14#include "base/strings/string_number_conversions.h"
15#include "base/strings/stringprintf.h"
16#include "base/sys_info.h"
17#include "base/version.h"
18#include "cc/base/switches.h"
19#include "content/browser/gpu/gpu_process_host.h"
20#include "content/common/gpu/gpu_messages.h"
21#include "content/public/browser/browser_thread.h"
22#include "content/public/browser/gpu_data_manager_observer.h"
23#include "content/public/common/content_client.h"
24#include "content/public/common/content_constants.h"
25#include "content/public/common/content_switches.h"
26#include "content/public/common/web_preferences.h"
27#include "gpu/command_buffer/service/gpu_switches.h"
28#include "gpu/config/gpu_control_list_jsons.h"
29#include "gpu/config/gpu_driver_bug_workaround_type.h"
30#include "gpu/config/gpu_feature_type.h"
31#include "gpu/config/gpu_info_collector.h"
32#include "gpu/config/gpu_util.h"
33#include "ui/base/ui_base_switches.h"
34#include "ui/gl/gl_implementation.h"
35#include "ui/gl/gl_switches.h"
36#include "ui/gl/gpu_switching_manager.h"
37
38#if defined(OS_MACOSX)
39#include <ApplicationServices/ApplicationServices.h>
40#endif  // OS_MACOSX
41#if defined(OS_WIN)
42#include "base/win/windows_version.h"
43#endif  // OS_WIN
44#if defined(OS_ANDROID)
45#include "ui/gfx/android/device_display_info.h"
46#endif  // OS_ANDROID
47
48namespace content {
49
50namespace {
51
52enum GpuFeatureStatus {
53    kGpuFeatureEnabled = 0,
54    kGpuFeatureBlacklisted = 1,
55    kGpuFeatureDisabled = 2,  // disabled by user but not blacklisted
56    kGpuFeatureNumStatus
57};
58
59#if defined(OS_WIN)
60
61enum WinSubVersion {
62  kWinOthers = 0,
63  kWinXP,
64  kWinVista,
65  kWin7,
66  kWin8,
67  kNumWinSubVersions
68};
69
70int GetGpuBlacklistHistogramValueWin(GpuFeatureStatus status) {
71  static WinSubVersion sub_version = kNumWinSubVersions;
72  if (sub_version == kNumWinSubVersions) {
73    sub_version = kWinOthers;
74    std::string version_str = base::SysInfo::OperatingSystemVersion();
75    size_t pos = version_str.find_first_not_of("0123456789.");
76    if (pos != std::string::npos)
77      version_str = version_str.substr(0, pos);
78    Version os_version(version_str);
79    if (os_version.IsValid() && os_version.components().size() >= 2) {
80      const std::vector<uint16>& version_numbers = os_version.components();
81      if (version_numbers[0] == 5)
82        sub_version = kWinXP;
83      else if (version_numbers[0] == 6 && version_numbers[1] == 0)
84        sub_version = kWinVista;
85      else if (version_numbers[0] == 6 && version_numbers[1] == 1)
86        sub_version = kWin7;
87      else if (version_numbers[0] == 6 && version_numbers[1] == 2)
88        sub_version = kWin8;
89    }
90  }
91  int entry_index = static_cast<int>(sub_version) * kGpuFeatureNumStatus;
92  switch (status) {
93    case kGpuFeatureEnabled:
94      break;
95    case kGpuFeatureBlacklisted:
96      entry_index++;
97      break;
98    case kGpuFeatureDisabled:
99      entry_index += 2;
100      break;
101  }
102  return entry_index;
103}
104#endif  // OS_WIN
105
106// Send UMA histograms about the enabled features and GPU properties.
107void UpdateStats(const gpu::GPUInfo& gpu_info,
108                 const gpu::GpuBlacklist* blacklist,
109                 const std::set<int>& blacklisted_features) {
110  uint32 max_entry_id = blacklist->max_entry_id();
111  if (max_entry_id == 0) {
112    // GPU Blacklist was not loaded.  No need to go further.
113    return;
114  }
115
116  const base::CommandLine& command_line =
117      *base::CommandLine::ForCurrentProcess();
118  bool disabled = false;
119
120  // Use entry 0 to capture the total number of times that data
121  // was recorded in this histogram in order to have a convenient
122  // denominator to compute blacklist percentages for the rest of the
123  // entries.
124  UMA_HISTOGRAM_ENUMERATION("GPU.BlacklistTestResultsPerEntry",
125      0, max_entry_id + 1);
126
127  if (blacklisted_features.size() != 0) {
128    std::vector<uint32> flag_entries;
129    blacklist->GetDecisionEntries(&flag_entries, disabled);
130    DCHECK_GT(flag_entries.size(), 0u);
131    for (size_t i = 0; i < flag_entries.size(); ++i) {
132      UMA_HISTOGRAM_ENUMERATION("GPU.BlacklistTestResultsPerEntry",
133          flag_entries[i], max_entry_id + 1);
134    }
135  }
136
137  // This counts how many users are affected by a disabled entry - this allows
138  // us to understand the impact of an entry before enable it.
139  std::vector<uint32> flag_disabled_entries;
140  disabled = true;
141  blacklist->GetDecisionEntries(&flag_disabled_entries, disabled);
142  for (uint32 disabled_entry : flag_disabled_entries) {
143    UMA_HISTOGRAM_ENUMERATION("GPU.BlacklistTestResultsPerDisabledEntry",
144        disabled_entry, max_entry_id + 1);
145  }
146
147  const gpu::GpuFeatureType kGpuFeatures[] = {
148      gpu::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS,
149      gpu::GPU_FEATURE_TYPE_GPU_COMPOSITING, gpu::GPU_FEATURE_TYPE_WEBGL};
150  const std::string kGpuBlacklistFeatureHistogramNames[] = {
151      "GPU.BlacklistFeatureTestResults.Accelerated2dCanvas",
152      "GPU.BlacklistFeatureTestResults.GpuCompositing",
153      "GPU.BlacklistFeatureTestResults.Webgl", };
154  const bool kGpuFeatureUserFlags[] = {
155      command_line.HasSwitch(switches::kDisableAccelerated2dCanvas),
156      command_line.HasSwitch(switches::kDisableGpu),
157      command_line.HasSwitch(switches::kDisableExperimentalWebGL), };
158#if defined(OS_WIN)
159  const std::string kGpuBlacklistFeatureHistogramNamesWin[] = {
160      "GPU.BlacklistFeatureTestResultsWindows.Accelerated2dCanvas",
161      "GPU.BlacklistFeatureTestResultsWindows.GpuCompositing",
162      "GPU.BlacklistFeatureTestResultsWindows.Webgl", };
163#endif
164  const size_t kNumFeatures =
165      sizeof(kGpuFeatures) / sizeof(gpu::GpuFeatureType);
166  for (size_t i = 0; i < kNumFeatures; ++i) {
167    // We can't use UMA_HISTOGRAM_ENUMERATION here because the same name is
168    // expected if the macro is used within a loop.
169    GpuFeatureStatus value = kGpuFeatureEnabled;
170    if (blacklisted_features.count(kGpuFeatures[i]))
171      value = kGpuFeatureBlacklisted;
172    else if (kGpuFeatureUserFlags[i])
173      value = kGpuFeatureDisabled;
174    base::HistogramBase* histogram_pointer = base::LinearHistogram::FactoryGet(
175        kGpuBlacklistFeatureHistogramNames[i],
176        1, kGpuFeatureNumStatus, kGpuFeatureNumStatus + 1,
177        base::HistogramBase::kUmaTargetedHistogramFlag);
178    histogram_pointer->Add(value);
179#if defined(OS_WIN)
180    histogram_pointer = base::LinearHistogram::FactoryGet(
181        kGpuBlacklistFeatureHistogramNamesWin[i],
182        1, kNumWinSubVersions * kGpuFeatureNumStatus,
183        kNumWinSubVersions * kGpuFeatureNumStatus + 1,
184        base::HistogramBase::kUmaTargetedHistogramFlag);
185    histogram_pointer->Add(GetGpuBlacklistHistogramValueWin(value));
186#endif
187  }
188
189  UMA_HISTOGRAM_SPARSE_SLOWLY("GPU.GLResetNotificationStrategy",
190      gpu_info.gl_reset_notification_strategy);
191}
192
193// Combine the integers into a string, seperated by ','.
194std::string IntSetToString(const std::set<int>& list) {
195  std::string rt;
196  for (std::set<int>::const_iterator it = list.begin();
197       it != list.end(); ++it) {
198    if (!rt.empty())
199      rt += ",";
200    rt += base::IntToString(*it);
201  }
202  return rt;
203}
204
205#if defined(OS_MACOSX)
206void DisplayReconfigCallback(CGDirectDisplayID display,
207                             CGDisplayChangeSummaryFlags flags,
208                             void* gpu_data_manager) {
209  if (flags == kCGDisplayBeginConfigurationFlag)
210    return; // This call contains no information about the display change
211
212  GpuDataManagerImpl* manager =
213      reinterpret_cast<GpuDataManagerImpl*>(gpu_data_manager);
214  DCHECK(manager);
215
216  // Display change.
217  bool display_changed = false;
218  uint32_t displayCount;
219  CGGetActiveDisplayList(0, NULL, &displayCount);
220  if (displayCount != manager->GetDisplayCount()) {
221    manager->SetDisplayCount(displayCount);
222    display_changed = true;
223  }
224
225  // Gpu change.
226  bool gpu_changed = false;
227  if (flags & kCGDisplayAddFlag) {
228    uint32 vendor_id, device_id;
229    if (gpu::CollectGpuID(&vendor_id, &device_id) == gpu::kGpuIDSuccess) {
230      gpu_changed = manager->UpdateActiveGpu(vendor_id, device_id);
231    }
232  }
233
234  if (display_changed || gpu_changed)
235    manager->HandleGpuSwitch();
236}
237#endif  // OS_MACOSX
238
239// Block all domains' use of 3D APIs for this many milliseconds if
240// approaching a threshold where system stability might be compromised.
241const int64 kBlockAllDomainsMs = 10000;
242const int kNumResetsWithinDuration = 1;
243
244// Enums for UMA histograms.
245enum BlockStatusHistogram {
246  BLOCK_STATUS_NOT_BLOCKED,
247  BLOCK_STATUS_SPECIFIC_DOMAIN_BLOCKED,
248  BLOCK_STATUS_ALL_DOMAINS_BLOCKED,
249  BLOCK_STATUS_MAX
250};
251
252}  // namespace anonymous
253
254void GpuDataManagerImplPrivate::InitializeForTesting(
255    const std::string& gpu_blacklist_json,
256    const gpu::GPUInfo& gpu_info) {
257  // This function is for testing only, so disable histograms.
258  update_histograms_ = false;
259
260  // Prevent all further initialization.
261  finalized_ = true;
262
263  InitializeImpl(gpu_blacklist_json, std::string(), gpu_info);
264}
265
266bool GpuDataManagerImplPrivate::IsFeatureBlacklisted(int feature) const {
267#if defined(OS_CHROMEOS)
268  if (feature == gpu::GPU_FEATURE_TYPE_PANEL_FITTING &&
269      base::CommandLine::ForCurrentProcess()->HasSwitch(
270          switches::kDisablePanelFitting)) {
271    return true;
272  }
273#endif  // OS_CHROMEOS
274  if (use_swiftshader_) {
275    // Skia's software rendering is probably more efficient than going through
276    // software emulation of the GPU, so use that.
277    if (feature == gpu::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS)
278      return true;
279    return false;
280  }
281
282  return (blacklisted_features_.count(feature) == 1);
283}
284
285bool GpuDataManagerImplPrivate::IsDriverBugWorkaroundActive(int feature) const {
286  return (gpu_driver_bugs_.count(feature) == 1);
287}
288
289size_t GpuDataManagerImplPrivate::GetBlacklistedFeatureCount() const {
290  if (use_swiftshader_)
291    return 1;
292  return blacklisted_features_.size();
293}
294
295void GpuDataManagerImplPrivate::SetDisplayCount(unsigned int display_count) {
296  display_count_ = display_count;
297}
298
299unsigned int GpuDataManagerImplPrivate::GetDisplayCount() const {
300  return display_count_;
301}
302
303gpu::GPUInfo GpuDataManagerImplPrivate::GetGPUInfo() const {
304  return gpu_info_;
305}
306
307void GpuDataManagerImplPrivate::GetGpuProcessHandles(
308    const GpuDataManager::GetGpuProcessHandlesCallback& callback) const {
309  GpuProcessHost::GetProcessHandles(callback);
310}
311
312bool GpuDataManagerImplPrivate::GpuAccessAllowed(
313    std::string* reason) const {
314  if (use_swiftshader_)
315    return true;
316
317  if (!gpu_process_accessible_) {
318    if (reason) {
319      *reason = "GPU process launch failed.";
320    }
321    return false;
322  }
323
324  if (card_blacklisted_) {
325    if (reason) {
326      *reason = "GPU access is disabled ";
327      base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
328      if (command_line->HasSwitch(switches::kDisableGpu))
329        *reason += "through commandline switch --disable-gpu.";
330      else
331        *reason += "in chrome://settings.";
332    }
333    return false;
334  }
335
336  // We only need to block GPU process if more features are disallowed other
337  // than those in the preliminary gpu feature flags because the latter work
338  // through renderer commandline switches.
339  std::set<int> features = preliminary_blacklisted_features_;
340  gpu::MergeFeatureSets(&features, blacklisted_features_);
341  if (features.size() > preliminary_blacklisted_features_.size()) {
342    if (reason) {
343      *reason = "Features are disabled upon full but not preliminary GPU info.";
344    }
345    return false;
346  }
347
348  if (blacklisted_features_.size() == gpu::NUMBER_OF_GPU_FEATURE_TYPES) {
349    // On Linux, we use cached GL strings to make blacklist decsions at browser
350    // startup time. We need to launch the GPU process to validate these
351    // strings even if all features are blacklisted. If all GPU features are
352    // disabled, the GPU process will only initialize GL bindings, create a GL
353    // context, and collect full GPU info.
354#if !defined(OS_LINUX)
355    if (reason) {
356      *reason = "All GPU features are blacklisted.";
357    }
358    return false;
359#endif
360  }
361
362  return true;
363}
364
365void GpuDataManagerImplPrivate::RequestCompleteGpuInfoIfNeeded() {
366  if (complete_gpu_info_already_requested_ || gpu_info_.finalized)
367    return;
368  complete_gpu_info_already_requested_ = true;
369
370  GpuProcessHost::SendOnIO(
371#if defined(OS_WIN)
372      GpuProcessHost::GPU_PROCESS_KIND_UNSANDBOXED,
373#else
374      GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED,
375#endif
376      CAUSE_FOR_GPU_LAUNCH_GPUDATAMANAGER_REQUESTCOMPLETEGPUINFOIFNEEDED,
377      new GpuMsg_CollectGraphicsInfo());
378}
379
380bool GpuDataManagerImplPrivate::IsCompleteGpuInfoAvailable() const {
381  return gpu_info_.finalized;
382}
383
384void GpuDataManagerImplPrivate::RequestVideoMemoryUsageStatsUpdate() const {
385  GpuProcessHost::SendOnIO(
386      GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED,
387      CAUSE_FOR_GPU_LAUNCH_NO_LAUNCH,
388      new GpuMsg_GetVideoMemoryUsageStats());
389}
390
391bool GpuDataManagerImplPrivate::ShouldUseSwiftShader() const {
392  return use_swiftshader_;
393}
394
395void GpuDataManagerImplPrivate::RegisterSwiftShaderPath(
396    const base::FilePath& path) {
397  swiftshader_path_ = path;
398  EnableSwiftShaderIfNecessary();
399}
400
401void GpuDataManagerImplPrivate::AddObserver(GpuDataManagerObserver* observer) {
402  GpuDataManagerImpl::UnlockedSession session(owner_);
403  observer_list_->AddObserver(observer);
404}
405
406void GpuDataManagerImplPrivate::RemoveObserver(
407    GpuDataManagerObserver* observer) {
408  GpuDataManagerImpl::UnlockedSession session(owner_);
409  observer_list_->RemoveObserver(observer);
410}
411
412void GpuDataManagerImplPrivate::UnblockDomainFrom3DAPIs(const GURL& url) {
413  // This method must do two things:
414  //
415  //  1. If the specific domain is blocked, then unblock it.
416  //
417  //  2. Reset our notion of how many GPU resets have occurred recently.
418  //     This is necessary even if the specific domain was blocked.
419  //     Otherwise, if we call Are3DAPIsBlocked with the same domain right
420  //     after unblocking it, it will probably still be blocked because of
421  //     the recent GPU reset caused by that domain.
422  //
423  // These policies could be refined, but at a certain point the behavior
424  // will become difficult to explain.
425  std::string domain = GetDomainFromURL(url);
426
427  blocked_domains_.erase(domain);
428  timestamps_of_gpu_resets_.clear();
429}
430
431void GpuDataManagerImplPrivate::DisableGpuWatchdog() {
432  GpuProcessHost::SendOnIO(
433      GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED,
434      CAUSE_FOR_GPU_LAUNCH_NO_LAUNCH,
435      new GpuMsg_DisableWatchdog);
436}
437
438void GpuDataManagerImplPrivate::SetGLStrings(const std::string& gl_vendor,
439                                             const std::string& gl_renderer,
440                                             const std::string& gl_version) {
441  if (gl_vendor.empty() && gl_renderer.empty() && gl_version.empty())
442    return;
443
444  // If GPUInfo already got GL strings, do nothing.  This is for the rare
445  // situation where GPU process collected GL strings before this call.
446  if (!gpu_info_.gl_vendor.empty() ||
447      !gpu_info_.gl_renderer.empty() ||
448      !gpu_info_.gl_version.empty())
449    return;
450
451  gpu::GPUInfo gpu_info = gpu_info_;
452
453  gpu_info.gl_vendor = gl_vendor;
454  gpu_info.gl_renderer = gl_renderer;
455  gpu_info.gl_version = gl_version;
456
457  gpu::CollectDriverInfoGL(&gpu_info);
458
459  UpdateGpuInfo(gpu_info);
460  UpdateGpuSwitchingManager(gpu_info);
461  UpdatePreliminaryBlacklistedFeatures();
462}
463
464void GpuDataManagerImplPrivate::GetGLStrings(std::string* gl_vendor,
465                                             std::string* gl_renderer,
466                                             std::string* gl_version) {
467  DCHECK(gl_vendor && gl_renderer && gl_version);
468
469  *gl_vendor = gpu_info_.gl_vendor;
470  *gl_renderer = gpu_info_.gl_renderer;
471  *gl_version = gpu_info_.gl_version;
472}
473
474void GpuDataManagerImplPrivate::Initialize() {
475  TRACE_EVENT0("startup", "GpuDataManagerImpl::Initialize");
476  if (finalized_) {
477    DVLOG(0) << "GpuDataManagerImpl marked as finalized; skipping Initialize";
478    return;
479  }
480
481  const base::CommandLine* command_line =
482      base::CommandLine::ForCurrentProcess();
483  if (command_line->HasSwitch(switches::kSkipGpuDataLoading))
484    return;
485
486  gpu::GPUInfo gpu_info;
487  if (command_line->GetSwitchValueASCII(
488          switches::kUseGL) == gfx::kGLImplementationOSMesaName) {
489    // If using the OSMesa GL implementation, use fake vendor and device ids to
490    // make sure it never gets blacklisted. This is better than simply
491    // cancelling GPUInfo gathering as it allows us to proceed with loading the
492    // blacklist below which may have non-device specific entries we want to
493    // apply anyways (e.g., OS version blacklisting).
494    gpu_info.gpu.vendor_id = 0xffff;
495    gpu_info.gpu.device_id = 0xffff;
496
497    // Also declare the driver_vendor to be osmesa to be able to specify
498    // exceptions based on driver_vendor==osmesa for some blacklist rules.
499    gpu_info.driver_vendor = gfx::kGLImplementationOSMesaName;
500  } else {
501    TRACE_EVENT0("startup",
502      "GpuDataManagerImpl::Initialize:CollectBasicGraphicsInfo");
503    gpu::CollectBasicGraphicsInfo(&gpu_info);
504  }
505#if defined(ARCH_CPU_X86_FAMILY)
506  if (!gpu_info.gpu.vendor_id || !gpu_info.gpu.device_id)
507    gpu_info.finalized = true;
508#endif
509
510  std::string gpu_blacklist_string;
511  std::string gpu_driver_bug_list_string;
512  if (!command_line->HasSwitch(switches::kIgnoreGpuBlacklist) &&
513      !command_line->HasSwitch(switches::kUseGpuInTests)) {
514    gpu_blacklist_string = gpu::kSoftwareRenderingListJson;
515  }
516  if (!command_line->HasSwitch(switches::kDisableGpuDriverBugWorkarounds)) {
517    gpu_driver_bug_list_string = gpu::kGpuDriverBugListJson;
518  }
519  InitializeImpl(gpu_blacklist_string,
520                 gpu_driver_bug_list_string,
521                 gpu_info);
522}
523
524void GpuDataManagerImplPrivate::UpdateGpuInfoHelper() {
525  GetContentClient()->SetGpuInfo(gpu_info_);
526
527  if (gpu_blacklist_) {
528    std::set<int> features = gpu_blacklist_->MakeDecision(
529        gpu::GpuControlList::kOsAny, std::string(), gpu_info_);
530    if (update_histograms_)
531      UpdateStats(gpu_info_, gpu_blacklist_.get(), features);
532
533    UpdateBlacklistedFeatures(features);
534  }
535  if (gpu_driver_bug_list_) {
536    gpu_driver_bugs_ = gpu_driver_bug_list_->MakeDecision(
537        gpu::GpuControlList::kOsAny, std::string(), gpu_info_);
538  }
539  gpu::GpuDriverBugList::AppendWorkaroundsFromCommandLine(
540      &gpu_driver_bugs_, *base::CommandLine::ForCurrentProcess());
541
542  // We have to update GpuFeatureType before notify all the observers.
543  NotifyGpuInfoUpdate();
544}
545
546void GpuDataManagerImplPrivate::UpdateGpuInfo(const gpu::GPUInfo& gpu_info) {
547  // No further update of gpu_info if falling back to SwiftShader.
548  if (use_swiftshader_)
549    return;
550
551  gpu::MergeGPUInfo(&gpu_info_, gpu_info);
552  complete_gpu_info_already_requested_ =
553      complete_gpu_info_already_requested_ || gpu_info_.finalized;
554
555  UpdateGpuInfoHelper();
556}
557
558void GpuDataManagerImplPrivate::UpdateVideoMemoryUsageStats(
559    const GPUVideoMemoryUsageStats& video_memory_usage_stats) {
560  GpuDataManagerImpl::UnlockedSession session(owner_);
561  observer_list_->Notify(&GpuDataManagerObserver::OnVideoMemoryUsageStatsUpdate,
562                         video_memory_usage_stats);
563}
564
565void GpuDataManagerImplPrivate::AppendRendererCommandLine(
566    base::CommandLine* command_line) const {
567  DCHECK(command_line);
568
569  if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE) &&
570      !command_line->HasSwitch(switches::kDisableAcceleratedVideoDecode))
571    command_line->AppendSwitch(switches::kDisableAcceleratedVideoDecode);
572#if defined(ENABLE_WEBRTC)
573  if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_ENCODE) &&
574      !command_line->HasSwitch(switches::kDisableWebRtcHWEncoding))
575    command_line->AppendSwitch(switches::kDisableWebRtcHWEncoding);
576#endif
577
578#if defined(USE_AURA)
579  if (!CanUseGpuBrowserCompositor())
580    command_line->AppendSwitch(switches::kDisableGpuCompositing);
581#endif
582}
583
584void GpuDataManagerImplPrivate::AppendGpuCommandLine(
585    base::CommandLine* command_line) const {
586  DCHECK(command_line);
587
588  std::string use_gl =
589      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
590          switches::kUseGL);
591  base::FilePath swiftshader_path =
592      base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
593          switches::kSwiftShaderPath);
594  if (gpu_driver_bugs_.find(gpu::DISABLE_D3D11) != gpu_driver_bugs_.end())
595    command_line->AppendSwitch(switches::kDisableD3D11);
596  if (use_swiftshader_) {
597    command_line->AppendSwitchASCII(switches::kUseGL, "swiftshader");
598    if (swiftshader_path.empty())
599      swiftshader_path = swiftshader_path_;
600  } else if ((IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_WEBGL) ||
601              IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_GPU_COMPOSITING) ||
602              IsFeatureBlacklisted(
603                  gpu::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS)) &&
604             (use_gl == "any")) {
605    command_line->AppendSwitchASCII(
606        switches::kUseGL, gfx::kGLImplementationOSMesaName);
607  } else if (!use_gl.empty()) {
608    command_line->AppendSwitchASCII(switches::kUseGL, use_gl);
609  }
610  if (ui::GpuSwitchingManager::GetInstance()->SupportsDualGpus())
611    command_line->AppendSwitchASCII(switches::kSupportsDualGpus, "true");
612  else
613    command_line->AppendSwitchASCII(switches::kSupportsDualGpus, "false");
614
615  if (!swiftshader_path.empty()) {
616    command_line->AppendSwitchPath(switches::kSwiftShaderPath,
617                                   swiftshader_path);
618  }
619
620  if (!gpu_driver_bugs_.empty()) {
621    command_line->AppendSwitchASCII(switches::kGpuDriverBugWorkarounds,
622                                    IntSetToString(gpu_driver_bugs_));
623  }
624
625  if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE) &&
626      !command_line->HasSwitch(switches::kDisableAcceleratedVideoDecode)) {
627    command_line->AppendSwitch(switches::kDisableAcceleratedVideoDecode);
628  }
629#if defined(ENABLE_WEBRTC)
630  if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_ENCODE) &&
631      !command_line->HasSwitch(switches::kDisableWebRtcHWEncoding)) {
632    command_line->AppendSwitch(switches::kDisableWebRtcHWEncoding);
633  }
634#endif
635
636  // Pass GPU and driver information to GPU process. We try to avoid full GPU
637  // info collection at GPU process startup, but we need gpu vendor_id,
638  // device_id, driver_vendor, driver_version for deciding whether we need to
639  // collect full info (on Linux) and for crash reporting purpose.
640  command_line->AppendSwitchASCII(switches::kGpuVendorID,
641      base::StringPrintf("0x%04x", gpu_info_.gpu.vendor_id));
642  command_line->AppendSwitchASCII(switches::kGpuDeviceID,
643      base::StringPrintf("0x%04x", gpu_info_.gpu.device_id));
644  command_line->AppendSwitchASCII(switches::kGpuDriverVendor,
645      gpu_info_.driver_vendor);
646  command_line->AppendSwitchASCII(switches::kGpuDriverVersion,
647      gpu_info_.driver_version);
648}
649
650void GpuDataManagerImplPrivate::AppendPluginCommandLine(
651    base::CommandLine* command_line) const {
652  DCHECK(command_line);
653
654#if defined(OS_MACOSX)
655  // TODO(jbauman): Add proper blacklist support for core animation plugins so
656  // special-casing this video card won't be necessary. See
657  // http://crbug.com/134015
658  if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_GPU_COMPOSITING)) {
659    if (!command_line->HasSwitch(
660           switches::kDisableCoreAnimationPlugins))
661      command_line->AppendSwitch(
662          switches::kDisableCoreAnimationPlugins);
663  }
664#endif
665}
666
667void GpuDataManagerImplPrivate::UpdateRendererWebPrefs(
668    WebPreferences* prefs) const {
669  DCHECK(prefs);
670
671  if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_WEBGL)) {
672    prefs->experimental_webgl_enabled = false;
673    prefs->pepper_3d_enabled = false;
674  }
675  if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_FLASH3D))
676    prefs->flash_3d_enabled = false;
677  if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_FLASH_STAGE3D)) {
678    prefs->flash_stage3d_enabled = false;
679    prefs->flash_stage3d_baseline_enabled = false;
680  }
681  if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_FLASH_STAGE3D_BASELINE))
682    prefs->flash_stage3d_baseline_enabled = false;
683  if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS))
684    prefs->accelerated_2d_canvas_enabled = false;
685  if (IsDriverBugWorkaroundActive(gpu::DISABLE_MULTISAMPLING) ||
686      (IsDriverBugWorkaroundActive(gpu::DISABLE_MULTIMONITOR_MULTISAMPLING) &&
687          display_count_ > 1))
688    prefs->gl_multisampling_enabled = false;
689
690#if defined(USE_AURA)
691  if (!CanUseGpuBrowserCompositor()) {
692    prefs->accelerated_2d_canvas_enabled = false;
693    prefs->pepper_3d_enabled = false;
694  }
695#endif
696
697  if (!IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE) &&
698      !base::CommandLine::ForCurrentProcess()->HasSwitch(
699          switches::kDisableAcceleratedVideoDecode)) {
700    prefs->pepper_accelerated_video_decode_enabled = true;
701  }
702
703  if (!IsFeatureBlacklisted(
704          gpu::GPU_FEATURE_TYPE_GPU_RASTERIZATION_EXPANDED_HEURISTICS) ||
705      base::FieldTrialList::FindFullName(
706          "GpuRasterizationExpandedContentWhitelist") == "Enabled")
707    prefs->use_expanded_heuristics_for_gpu_rasterization = true;
708}
709
710void GpuDataManagerImplPrivate::DisableHardwareAcceleration() {
711  card_blacklisted_ = true;
712
713  for (int i = 0; i < gpu::NUMBER_OF_GPU_FEATURE_TYPES; ++i)
714    blacklisted_features_.insert(i);
715
716  EnableSwiftShaderIfNecessary();
717  NotifyGpuInfoUpdate();
718}
719
720std::string GpuDataManagerImplPrivate::GetBlacklistVersion() const {
721  if (gpu_blacklist_)
722    return gpu_blacklist_->version();
723  return "0";
724}
725
726std::string GpuDataManagerImplPrivate::GetDriverBugListVersion() const {
727  if (gpu_driver_bug_list_)
728    return gpu_driver_bug_list_->version();
729  return "0";
730}
731
732void GpuDataManagerImplPrivate::GetBlacklistReasons(
733    base::ListValue* reasons) const {
734  if (gpu_blacklist_)
735    gpu_blacklist_->GetReasons(reasons, "disabledFeatures");
736  if (gpu_driver_bug_list_)
737    gpu_driver_bug_list_->GetReasons(reasons, "workarounds");
738}
739
740void GpuDataManagerImplPrivate::GetDriverBugWorkarounds(
741    base::ListValue* workarounds) const {
742  for (std::set<int>::const_iterator it = gpu_driver_bugs_.begin();
743       it != gpu_driver_bugs_.end(); ++it) {
744    workarounds->AppendString(
745        gpu::GpuDriverBugWorkaroundTypeToString(
746            static_cast<gpu::GpuDriverBugWorkaroundType>(*it)));
747  }
748}
749
750void GpuDataManagerImplPrivate::AddLogMessage(
751    int level, const std::string& header, const std::string& message) {
752  log_messages_.push_back(LogMessage(level, header, message));
753}
754
755void GpuDataManagerImplPrivate::ProcessCrashed(
756    base::TerminationStatus exit_code) {
757  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
758    // Unretained is ok, because it's posted to UI thread, the thread
759    // where the singleton GpuDataManagerImpl lives until the end.
760    BrowserThread::PostTask(
761        BrowserThread::UI,
762        FROM_HERE,
763        base::Bind(&GpuDataManagerImpl::ProcessCrashed,
764                   base::Unretained(owner_),
765                   exit_code));
766    return;
767  }
768  {
769    gpu_info_.process_crash_count = GpuProcessHost::gpu_crash_count();
770    GpuDataManagerImpl::UnlockedSession session(owner_);
771    observer_list_->Notify(
772        &GpuDataManagerObserver::OnGpuProcessCrashed, exit_code);
773  }
774}
775
776base::ListValue* GpuDataManagerImplPrivate::GetLogMessages() const {
777  base::ListValue* value = new base::ListValue;
778  for (size_t ii = 0; ii < log_messages_.size(); ++ii) {
779    base::DictionaryValue* dict = new base::DictionaryValue();
780    dict->SetInteger("level", log_messages_[ii].level);
781    dict->SetString("header", log_messages_[ii].header);
782    dict->SetString("message", log_messages_[ii].message);
783    value->Append(dict);
784  }
785  return value;
786}
787
788void GpuDataManagerImplPrivate::HandleGpuSwitch() {
789  GpuDataManagerImpl::UnlockedSession session(owner_);
790  observer_list_->Notify(&GpuDataManagerObserver::OnGpuSwitching);
791}
792
793bool GpuDataManagerImplPrivate::UpdateActiveGpu(
794    uint32 vendor_id, uint32 device_id) {
795  if (gpu_info_.gpu.vendor_id == vendor_id &&
796      gpu_info_.gpu.device_id == device_id) {
797    // The primary GPU is active.
798    if (gpu_info_.gpu.active)
799      return false;
800    gpu_info_.gpu.active = true;
801    for (size_t ii = 0; ii < gpu_info_.secondary_gpus.size(); ++ii)
802      gpu_info_.secondary_gpus[ii].active = false;
803  } else {
804    // A secondary GPU is active.
805    for (size_t ii = 0; ii < gpu_info_.secondary_gpus.size(); ++ii) {
806      if (gpu_info_.secondary_gpus[ii].vendor_id == vendor_id &&
807          gpu_info_.secondary_gpus[ii].device_id == device_id) {
808        if (gpu_info_.secondary_gpus[ii].active)
809          return false;
810        gpu_info_.secondary_gpus[ii].active = true;
811      } else {
812        gpu_info_.secondary_gpus[ii].active = false;
813      }
814    }
815    gpu_info_.gpu.active = false;
816  }
817  UpdateGpuInfoHelper();
818  return true;
819}
820
821bool GpuDataManagerImplPrivate::CanUseGpuBrowserCompositor() const {
822  if (ShouldUseSwiftShader())
823    return false;
824  if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_GPU_COMPOSITING))
825    return false;
826  return true;
827}
828
829void GpuDataManagerImplPrivate::BlockDomainFrom3DAPIs(
830    const GURL& url, GpuDataManagerImpl::DomainGuilt guilt) {
831  BlockDomainFrom3DAPIsAtTime(url, guilt, base::Time::Now());
832}
833
834bool GpuDataManagerImplPrivate::Are3DAPIsBlocked(const GURL& url,
835                                                 int render_process_id,
836                                                 int render_view_id,
837                                                 ThreeDAPIType requester) {
838  bool blocked = Are3DAPIsBlockedAtTime(url, base::Time::Now()) !=
839      GpuDataManagerImpl::DOMAIN_BLOCK_STATUS_NOT_BLOCKED;
840  if (blocked) {
841    // Unretained is ok, because it's posted to UI thread, the thread
842    // where the singleton GpuDataManagerImpl lives until the end.
843    BrowserThread::PostTask(
844        BrowserThread::UI, FROM_HERE,
845        base::Bind(&GpuDataManagerImpl::Notify3DAPIBlocked,
846                   base::Unretained(owner_), url, render_process_id,
847                   render_view_id, requester));
848  }
849
850  return blocked;
851}
852
853void GpuDataManagerImplPrivate::DisableDomainBlockingFor3DAPIsForTesting() {
854  domain_blocking_enabled_ = false;
855}
856
857// static
858GpuDataManagerImplPrivate* GpuDataManagerImplPrivate::Create(
859    GpuDataManagerImpl* owner) {
860  return new GpuDataManagerImplPrivate(owner);
861}
862
863GpuDataManagerImplPrivate::GpuDataManagerImplPrivate(
864    GpuDataManagerImpl* owner)
865    : complete_gpu_info_already_requested_(false),
866      observer_list_(new GpuDataManagerObserverList),
867      use_swiftshader_(false),
868      card_blacklisted_(false),
869      update_histograms_(true),
870      window_count_(0),
871      domain_blocking_enabled_(true),
872      owner_(owner),
873      display_count_(0),
874      gpu_process_accessible_(true),
875      finalized_(false) {
876  DCHECK(owner_);
877  const base::CommandLine* command_line =
878      base::CommandLine::ForCurrentProcess();
879  if (command_line->HasSwitch(switches::kDisableGpu))
880    DisableHardwareAcceleration();
881
882#if defined(OS_MACOSX)
883  CGGetActiveDisplayList (0, NULL, &display_count_);
884  CGDisplayRegisterReconfigurationCallback(DisplayReconfigCallback, owner_);
885#endif  // OS_MACOSX
886
887  // For testing only.
888  if (command_line->HasSwitch(switches::kDisableDomainBlockingFor3DAPIs)) {
889    domain_blocking_enabled_ = false;
890  }
891}
892
893GpuDataManagerImplPrivate::~GpuDataManagerImplPrivate() {
894#if defined(OS_MACOSX)
895  CGDisplayRemoveReconfigurationCallback(DisplayReconfigCallback, owner_);
896#endif
897}
898
899void GpuDataManagerImplPrivate::InitializeImpl(
900    const std::string& gpu_blacklist_json,
901    const std::string& gpu_driver_bug_list_json,
902    const gpu::GPUInfo& gpu_info) {
903  const bool log_gpu_control_list_decisions =
904      base::CommandLine::ForCurrentProcess()->HasSwitch(
905          switches::kLogGpuControlListDecisions);
906
907  if (!gpu_blacklist_json.empty()) {
908    gpu_blacklist_.reset(gpu::GpuBlacklist::Create());
909    if (log_gpu_control_list_decisions)
910      gpu_blacklist_->enable_control_list_logging("gpu_blacklist");
911    bool success = gpu_blacklist_->LoadList(
912        gpu_blacklist_json, gpu::GpuControlList::kCurrentOsOnly);
913    DCHECK(success);
914  }
915  if (!gpu_driver_bug_list_json.empty()) {
916    gpu_driver_bug_list_.reset(gpu::GpuDriverBugList::Create());
917    if (log_gpu_control_list_decisions)
918      gpu_driver_bug_list_->enable_control_list_logging("gpu_driver_bug_list");
919    bool success = gpu_driver_bug_list_->LoadList(
920        gpu_driver_bug_list_json, gpu::GpuControlList::kCurrentOsOnly);
921    DCHECK(success);
922  }
923
924  gpu_info_ = gpu_info;
925  UpdateGpuInfo(gpu_info);
926  UpdateGpuSwitchingManager(gpu_info);
927  UpdatePreliminaryBlacklistedFeatures();
928}
929
930void GpuDataManagerImplPrivate::UpdateBlacklistedFeatures(
931    const std::set<int>& features) {
932  blacklisted_features_ = features;
933
934  // Force disable using the GPU for these features, even if they would
935  // otherwise be allowed.
936  if (card_blacklisted_) {
937    blacklisted_features_.insert(gpu::GPU_FEATURE_TYPE_GPU_COMPOSITING);
938    blacklisted_features_.insert(gpu::GPU_FEATURE_TYPE_WEBGL);
939  }
940
941  EnableSwiftShaderIfNecessary();
942}
943
944void GpuDataManagerImplPrivate::UpdatePreliminaryBlacklistedFeatures() {
945  preliminary_blacklisted_features_ = blacklisted_features_;
946}
947
948void GpuDataManagerImplPrivate::UpdateGpuSwitchingManager(
949    const gpu::GPUInfo& gpu_info) {
950  ui::GpuSwitchingManager::GetInstance()->SetGpuCount(
951      gpu_info.secondary_gpus.size() + 1);
952
953  if (ui::GpuSwitchingManager::GetInstance()->SupportsDualGpus()) {
954    if (gpu_driver_bugs_.count(gpu::FORCE_DISCRETE_GPU) == 1)
955      ui::GpuSwitchingManager::GetInstance()->ForceUseOfDiscreteGpu();
956    else if (gpu_driver_bugs_.count(gpu::FORCE_INTEGRATED_GPU) == 1)
957      ui::GpuSwitchingManager::GetInstance()->ForceUseOfIntegratedGpu();
958  }
959}
960
961void GpuDataManagerImplPrivate::NotifyGpuInfoUpdate() {
962  observer_list_->Notify(&GpuDataManagerObserver::OnGpuInfoUpdate);
963}
964
965void GpuDataManagerImplPrivate::EnableSwiftShaderIfNecessary() {
966  if (!GpuAccessAllowed(NULL) ||
967      blacklisted_features_.count(gpu::GPU_FEATURE_TYPE_WEBGL)) {
968    if (!swiftshader_path_.empty() &&
969        !base::CommandLine::ForCurrentProcess()->HasSwitch(
970             switches::kDisableSoftwareRasterizer))
971      use_swiftshader_ = true;
972  }
973}
974
975std::string GpuDataManagerImplPrivate::GetDomainFromURL(
976    const GURL& url) const {
977  // For the moment, we just use the host, or its IP address, as the
978  // entry in the set, rather than trying to figure out the top-level
979  // domain. This does mean that a.foo.com and b.foo.com will be
980  // treated independently in the blocking of a given domain, but it
981  // would require a third-party library to reliably figure out the
982  // top-level domain from a URL.
983  if (!url.has_host()) {
984    return std::string();
985  }
986
987  return url.host();
988}
989
990void GpuDataManagerImplPrivate::BlockDomainFrom3DAPIsAtTime(
991    const GURL& url,
992    GpuDataManagerImpl::DomainGuilt guilt,
993    base::Time at_time) {
994  if (!domain_blocking_enabled_)
995    return;
996
997  std::string domain = GetDomainFromURL(url);
998
999  DomainBlockEntry& entry = blocked_domains_[domain];
1000  entry.last_guilt = guilt;
1001  timestamps_of_gpu_resets_.push_back(at_time);
1002}
1003
1004GpuDataManagerImpl::DomainBlockStatus
1005GpuDataManagerImplPrivate::Are3DAPIsBlockedAtTime(
1006    const GURL& url, base::Time at_time) const {
1007  if (!domain_blocking_enabled_)
1008    return GpuDataManagerImpl::DOMAIN_BLOCK_STATUS_NOT_BLOCKED;
1009
1010  // Note: adjusting the policies in this code will almost certainly
1011  // require adjusting the associated unit tests.
1012  std::string domain = GetDomainFromURL(url);
1013
1014  DomainBlockMap::const_iterator iter = blocked_domains_.find(domain);
1015  if (iter != blocked_domains_.end()) {
1016    // Err on the side of caution, and assume that if a particular
1017    // domain shows up in the block map, it's there for a good
1018    // reason and don't let its presence there automatically expire.
1019
1020    UMA_HISTOGRAM_ENUMERATION("GPU.BlockStatusForClient3DAPIs",
1021                              BLOCK_STATUS_SPECIFIC_DOMAIN_BLOCKED,
1022                              BLOCK_STATUS_MAX);
1023
1024    return GpuDataManagerImpl::DOMAIN_BLOCK_STATUS_BLOCKED;
1025  }
1026
1027  // Look at the timestamps of the recent GPU resets to see if there are
1028  // enough within the threshold which would cause us to blacklist all
1029  // domains. This doesn't need to be overly precise -- if time goes
1030  // backward due to a system clock adjustment, that's fine.
1031  //
1032  // TODO(kbr): make this pay attention to the TDR thresholds in the
1033  // Windows registry, but make sure it continues to be testable.
1034  {
1035    std::list<base::Time>::iterator iter = timestamps_of_gpu_resets_.begin();
1036    int num_resets_within_timeframe = 0;
1037    while (iter != timestamps_of_gpu_resets_.end()) {
1038      base::Time time = *iter;
1039      base::TimeDelta delta_t = at_time - time;
1040
1041      // If this entry has "expired", just remove it.
1042      if (delta_t.InMilliseconds() > kBlockAllDomainsMs) {
1043        iter = timestamps_of_gpu_resets_.erase(iter);
1044        continue;
1045      }
1046
1047      ++num_resets_within_timeframe;
1048      ++iter;
1049    }
1050
1051    if (num_resets_within_timeframe >= kNumResetsWithinDuration) {
1052      UMA_HISTOGRAM_ENUMERATION("GPU.BlockStatusForClient3DAPIs",
1053                                BLOCK_STATUS_ALL_DOMAINS_BLOCKED,
1054                                BLOCK_STATUS_MAX);
1055
1056      return GpuDataManagerImpl::DOMAIN_BLOCK_STATUS_ALL_DOMAINS_BLOCKED;
1057    }
1058  }
1059
1060  UMA_HISTOGRAM_ENUMERATION("GPU.BlockStatusForClient3DAPIs",
1061                            BLOCK_STATUS_NOT_BLOCKED,
1062                            BLOCK_STATUS_MAX);
1063
1064  return GpuDataManagerImpl::DOMAIN_BLOCK_STATUS_NOT_BLOCKED;
1065}
1066
1067int64 GpuDataManagerImplPrivate::GetBlockAllDomainsDurationInMs() const {
1068  return kBlockAllDomainsMs;
1069}
1070
1071void GpuDataManagerImplPrivate::Notify3DAPIBlocked(const GURL& url,
1072                                                   int render_process_id,
1073                                                   int render_view_id,
1074                                                   ThreeDAPIType requester) {
1075  GpuDataManagerImpl::UnlockedSession session(owner_);
1076  observer_list_->Notify(&GpuDataManagerObserver::DidBlock3DAPIs,
1077                         url, render_process_id, render_view_id, requester);
1078}
1079
1080void GpuDataManagerImplPrivate::OnGpuProcessInitFailure() {
1081  gpu_process_accessible_ = false;
1082  gpu_info_.finalized = true;
1083  complete_gpu_info_already_requested_ = true;
1084  // Some observers might be waiting.
1085  NotifyGpuInfoUpdate();
1086}
1087
1088}  // namespace content
1089