Home | History | Annotate | Download | only in gpu
      1 // Copyright (c) 2012 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_internals_ui.h"
      6 
      7 #include <string>
      8 
      9 #include "base/bind.h"
     10 #include "base/bind_helpers.h"
     11 #include "base/command_line.h"
     12 #include "base/i18n/time_formatting.h"
     13 #include "base/strings/string_number_conversions.h"
     14 #include "base/strings/stringprintf.h"
     15 #include "base/sys_info.h"
     16 #include "base/values.h"
     17 #include "content/browser/gpu/compositor_util.h"
     18 #include "content/browser/gpu/gpu_data_manager_impl.h"
     19 #include "content/public/browser/browser_thread.h"
     20 #include "content/public/browser/gpu_data_manager_observer.h"
     21 #include "content/public/browser/web_contents.h"
     22 #include "content/public/browser/web_ui.h"
     23 #include "content/public/browser/web_ui_data_source.h"
     24 #include "content/public/browser/web_ui_message_handler.h"
     25 #include "content/public/common/content_client.h"
     26 #include "content/public/common/content_switches.h"
     27 #include "content/public/common/url_constants.h"
     28 #include "gpu/config/gpu_feature_type.h"
     29 #include "gpu/config/gpu_info.h"
     30 #include "grit/content_resources.h"
     31 #include "third_party/angle/src/common/version.h"
     32 
     33 namespace content {
     34 namespace {
     35 
     36 WebUIDataSource* CreateGpuHTMLSource() {
     37   WebUIDataSource* source = WebUIDataSource::Create(kChromeUIGpuHost);
     38 
     39   source->SetJsonPath("strings.js");
     40   source->AddResourcePath("gpu_internals.js", IDR_GPU_INTERNALS_JS);
     41   source->SetDefaultResource(IDR_GPU_INTERNALS_HTML);
     42   return source;
     43 }
     44 
     45 base::DictionaryValue* NewDescriptionValuePair(const std::string& desc,
     46     const std::string& value) {
     47   base::DictionaryValue* dict = new base::DictionaryValue();
     48   dict->SetString("description", desc);
     49   dict->SetString("value", value);
     50   return dict;
     51 }
     52 
     53 base::DictionaryValue* NewDescriptionValuePair(const std::string& desc,
     54     base::Value* value) {
     55   base::DictionaryValue* dict = new base::DictionaryValue();
     56   dict->SetString("description", desc);
     57   dict->Set("value", value);
     58   return dict;
     59 }
     60 
     61 #if defined(OS_WIN)
     62 // Output DxDiagNode tree as nested array of {description,value} pairs
     63 base::ListValue* DxDiagNodeToList(const gpu::DxDiagNode& node) {
     64   base::ListValue* list = new base::ListValue();
     65   for (std::map<std::string, std::string>::const_iterator it =
     66       node.values.begin();
     67       it != node.values.end();
     68       ++it) {
     69     list->Append(NewDescriptionValuePair(it->first, it->second));
     70   }
     71 
     72   for (std::map<std::string, gpu::DxDiagNode>::const_iterator it =
     73       node.children.begin();
     74       it != node.children.end();
     75       ++it) {
     76     base::ListValue* sublist = DxDiagNodeToList(it->second);
     77     list->Append(NewDescriptionValuePair(it->first, sublist));
     78   }
     79   return list;
     80 }
     81 #endif
     82 
     83 std::string GPUDeviceToString(const gpu::GPUInfo::GPUDevice& gpu) {
     84   std::string vendor = base::StringPrintf("0x%04x", gpu.vendor_id);
     85   if (!gpu.vendor_string.empty())
     86     vendor += " [" + gpu.vendor_string + "]";
     87   std::string device = base::StringPrintf("0x%04x", gpu.device_id);
     88   if (!gpu.device_string.empty())
     89     device += " [" + gpu.device_string + "]";
     90   return base::StringPrintf(
     91       "VENDOR = %s, DEVICE= %s", vendor.c_str(), device.c_str());
     92 }
     93 
     94 base::DictionaryValue* GpuInfoAsDictionaryValue() {
     95   gpu::GPUInfo gpu_info = GpuDataManagerImpl::GetInstance()->GetGPUInfo();
     96   base::ListValue* basic_info = new base::ListValue();
     97   basic_info->Append(NewDescriptionValuePair(
     98       "Initialization time",
     99       base::Int64ToString(gpu_info.initialization_time.InMilliseconds())));
    100   basic_info->Append(NewDescriptionValuePair(
    101       "Sandboxed", new base::FundamentalValue(gpu_info.sandboxed)));
    102   basic_info->Append(NewDescriptionValuePair(
    103       "GPU0", GPUDeviceToString(gpu_info.gpu)));
    104   for (size_t i = 0; i < gpu_info.secondary_gpus.size(); ++i) {
    105     basic_info->Append(NewDescriptionValuePair(
    106         base::StringPrintf("GPU%d", static_cast<int>(i + 1)),
    107         GPUDeviceToString(gpu_info.secondary_gpus[i])));
    108   }
    109   basic_info->Append(NewDescriptionValuePair(
    110       "Optimus", new base::FundamentalValue(gpu_info.optimus)));
    111   basic_info->Append(NewDescriptionValuePair(
    112       "AMD switchable", new base::FundamentalValue(gpu_info.amd_switchable)));
    113   if (gpu_info.lenovo_dcute) {
    114     basic_info->Append(NewDescriptionValuePair(
    115         "Lenovo dCute", new base::FundamentalValue(true)));
    116   }
    117   if (gpu_info.display_link_version.IsValid()) {
    118     basic_info->Append(NewDescriptionValuePair(
    119         "DisplayLink Version", gpu_info.display_link_version.GetString()));
    120   }
    121   basic_info->Append(NewDescriptionValuePair("Driver vendor",
    122                                              gpu_info.driver_vendor));
    123   basic_info->Append(NewDescriptionValuePair("Driver version",
    124                                              gpu_info.driver_version));
    125   basic_info->Append(NewDescriptionValuePair("Driver date",
    126                                              gpu_info.driver_date));
    127   basic_info->Append(NewDescriptionValuePair("Pixel shader version",
    128                                              gpu_info.pixel_shader_version));
    129   basic_info->Append(NewDescriptionValuePair("Vertex shader version",
    130                                              gpu_info.vertex_shader_version));
    131   basic_info->Append(NewDescriptionValuePair("Machine model",
    132                                              gpu_info.machine_model));
    133   basic_info->Append(NewDescriptionValuePair("GL version",
    134                                              gpu_info.gl_version));
    135   basic_info->Append(NewDescriptionValuePair("GL_VENDOR",
    136                                              gpu_info.gl_vendor));
    137   basic_info->Append(NewDescriptionValuePair("GL_RENDERER",
    138                                              gpu_info.gl_renderer));
    139   basic_info->Append(NewDescriptionValuePair("GL_VERSION",
    140                                              gpu_info.gl_version_string));
    141   basic_info->Append(NewDescriptionValuePair("GL_EXTENSIONS",
    142                                              gpu_info.gl_extensions));
    143   basic_info->Append(NewDescriptionValuePair("Window system binding vendor",
    144                                              gpu_info.gl_ws_vendor));
    145   basic_info->Append(NewDescriptionValuePair("Window system binding version",
    146                                              gpu_info.gl_ws_version));
    147   basic_info->Append(NewDescriptionValuePair("Window system binding extensions",
    148                                              gpu_info.gl_ws_extensions));
    149   std::string reset_strategy =
    150       base::StringPrintf("0x%04x", gpu_info.gl_reset_notification_strategy);
    151   basic_info->Append(NewDescriptionValuePair(
    152       "Reset notification strategy", reset_strategy));
    153 
    154   base::DictionaryValue* info = new base::DictionaryValue();
    155   info->Set("basic_info", basic_info);
    156 
    157 #if defined(OS_WIN)
    158   base::ListValue* perf_info = new base::ListValue();
    159   perf_info->Append(NewDescriptionValuePair(
    160       "Graphics",
    161       base::StringPrintf("%.1f", gpu_info.performance_stats.graphics)));
    162   perf_info->Append(NewDescriptionValuePair(
    163       "Gaming",
    164       base::StringPrintf("%.1f", gpu_info.performance_stats.gaming)));
    165   perf_info->Append(NewDescriptionValuePair(
    166       "Overall",
    167       base::StringPrintf("%.1f", gpu_info.performance_stats.overall)));
    168   info->Set("performance_info", perf_info);
    169 
    170   base::Value* dx_info = gpu_info.dx_diagnostics.children.size() ?
    171     DxDiagNodeToList(gpu_info.dx_diagnostics) :
    172     base::Value::CreateNullValue();
    173   info->Set("diagnostics", dx_info);
    174 #endif
    175 
    176   return info;
    177 }
    178 
    179 // This class receives javascript messages from the renderer.
    180 // Note that the WebUI infrastructure runs on the UI thread, therefore all of
    181 // this class's methods are expected to run on the UI thread.
    182 class GpuMessageHandler
    183     : public WebUIMessageHandler,
    184       public base::SupportsWeakPtr<GpuMessageHandler>,
    185       public GpuDataManagerObserver {
    186  public:
    187   GpuMessageHandler();
    188   virtual ~GpuMessageHandler();
    189 
    190   // WebUIMessageHandler implementation.
    191   virtual void RegisterMessages() OVERRIDE;
    192 
    193   // GpuDataManagerObserver implementation.
    194   virtual void OnGpuInfoUpdate() OVERRIDE;
    195   virtual void OnGpuSwitching() OVERRIDE;
    196 
    197   // Messages
    198   void OnBrowserBridgeInitialized(const base::ListValue* list);
    199   void OnCallAsync(const base::ListValue* list);
    200 
    201   // Submessages dispatched from OnCallAsync
    202   base::Value* OnRequestClientInfo(const base::ListValue* list);
    203   base::Value* OnRequestLogMessages(const base::ListValue* list);
    204 
    205  private:
    206   // True if observing the GpuDataManager (re-attaching as observer would
    207   // DCHECK).
    208   bool observing_;
    209 
    210   DISALLOW_COPY_AND_ASSIGN(GpuMessageHandler);
    211 };
    212 
    213 ////////////////////////////////////////////////////////////////////////////////
    214 //
    215 // GpuMessageHandler
    216 //
    217 ////////////////////////////////////////////////////////////////////////////////
    218 
    219 GpuMessageHandler::GpuMessageHandler()
    220     : observing_(false) {
    221 }
    222 
    223 GpuMessageHandler::~GpuMessageHandler() {
    224   GpuDataManagerImpl::GetInstance()->RemoveObserver(this);
    225 }
    226 
    227 /* BrowserBridge.callAsync prepends a requestID to these messages. */
    228 void GpuMessageHandler::RegisterMessages() {
    229   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    230 
    231   web_ui()->RegisterMessageCallback("browserBridgeInitialized",
    232       base::Bind(&GpuMessageHandler::OnBrowserBridgeInitialized,
    233                  base::Unretained(this)));
    234   web_ui()->RegisterMessageCallback("callAsync",
    235       base::Bind(&GpuMessageHandler::OnCallAsync,
    236                  base::Unretained(this)));
    237 }
    238 
    239 void GpuMessageHandler::OnCallAsync(const base::ListValue* args) {
    240   DCHECK_GE(args->GetSize(), static_cast<size_t>(2));
    241   // unpack args into requestId, submessage and submessageArgs
    242   bool ok;
    243   const base::Value* requestId;
    244   ok = args->Get(0, &requestId);
    245   DCHECK(ok);
    246 
    247   std::string submessage;
    248   ok = args->GetString(1, &submessage);
    249   DCHECK(ok);
    250 
    251   base::ListValue* submessageArgs = new base::ListValue();
    252   for (size_t i = 2; i < args->GetSize(); ++i) {
    253     const base::Value* arg;
    254     ok = args->Get(i, &arg);
    255     DCHECK(ok);
    256 
    257     base::Value* argCopy = arg->DeepCopy();
    258     submessageArgs->Append(argCopy);
    259   }
    260 
    261   // call the submessage handler
    262   base::Value* ret = NULL;
    263   if (submessage == "requestClientInfo") {
    264     ret = OnRequestClientInfo(submessageArgs);
    265   } else if (submessage == "requestLogMessages") {
    266     ret = OnRequestLogMessages(submessageArgs);
    267   } else {  // unrecognized submessage
    268     NOTREACHED();
    269     delete submessageArgs;
    270     return;
    271   }
    272   delete submessageArgs;
    273 
    274   // call BrowserBridge.onCallAsyncReply with result
    275   if (ret) {
    276     web_ui()->CallJavascriptFunction("browserBridge.onCallAsyncReply",
    277         *requestId,
    278         *ret);
    279     delete ret;
    280   } else {
    281     web_ui()->CallJavascriptFunction("browserBridge.onCallAsyncReply",
    282         *requestId);
    283   }
    284 }
    285 
    286 void GpuMessageHandler::OnBrowserBridgeInitialized(
    287     const base::ListValue* args) {
    288   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    289 
    290   // Watch for changes in GPUInfo
    291   if (!observing_)
    292     GpuDataManagerImpl::GetInstance()->AddObserver(this);
    293   observing_ = true;
    294 
    295   // Tell GpuDataManager it should have full GpuInfo. If the
    296   // Gpu process has not run yet, this will trigger its launch.
    297   GpuDataManagerImpl::GetInstance()->RequestCompleteGpuInfoIfNeeded();
    298 
    299   // Run callback immediately in case the info is ready and no update in the
    300   // future.
    301   OnGpuInfoUpdate();
    302 }
    303 
    304 base::Value* GpuMessageHandler::OnRequestClientInfo(
    305     const base::ListValue* list) {
    306   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    307 
    308   base::DictionaryValue* dict = new base::DictionaryValue();
    309 
    310   dict->SetString("version", GetContentClient()->GetProduct());
    311   dict->SetString("command_line",
    312       CommandLine::ForCurrentProcess()->GetCommandLineString());
    313   dict->SetString("operating_system",
    314                   base::SysInfo::OperatingSystemName() + " " +
    315                   base::SysInfo::OperatingSystemVersion());
    316   dict->SetString("angle_revision", base::UintToString(BUILD_REVISION));
    317   dict->SetString("graphics_backend", "Skia");
    318   dict->SetString("blacklist_version",
    319       GpuDataManagerImpl::GetInstance()->GetBlacklistVersion());
    320   dict->SetString("driver_bug_list_version",
    321       GpuDataManagerImpl::GetInstance()->GetDriverBugListVersion());
    322 
    323   return dict;
    324 }
    325 
    326 base::Value* GpuMessageHandler::OnRequestLogMessages(const base::ListValue*) {
    327   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    328 
    329   return GpuDataManagerImpl::GetInstance()->GetLogMessages();
    330 }
    331 
    332 void GpuMessageHandler::OnGpuInfoUpdate() {
    333   // Get GPU Info.
    334   scoped_ptr<base::DictionaryValue> gpu_info_val(GpuInfoAsDictionaryValue());
    335 
    336   // Add in blacklisting features
    337   base::DictionaryValue* feature_status = new DictionaryValue;
    338   feature_status->Set("featureStatus", GetFeatureStatus());
    339   feature_status->Set("problems", GetProblems());
    340   feature_status->Set("workarounds", GetDriverBugWorkarounds());
    341   if (feature_status)
    342     gpu_info_val->Set("featureStatus", feature_status);
    343 
    344   // Send GPU Info to javascript.
    345   web_ui()->CallJavascriptFunction("browserBridge.onGpuInfoUpdate",
    346       *(gpu_info_val.get()));
    347 }
    348 
    349 void GpuMessageHandler::OnGpuSwitching() {
    350   GpuDataManagerImpl::GetInstance()->RequestCompleteGpuInfoIfNeeded();
    351 }
    352 
    353 }  // namespace
    354 
    355 
    356 ////////////////////////////////////////////////////////////////////////////////
    357 //
    358 // GpuInternalsUI
    359 //
    360 ////////////////////////////////////////////////////////////////////////////////
    361 
    362 GpuInternalsUI::GpuInternalsUI(WebUI* web_ui)
    363     : WebUIController(web_ui) {
    364   web_ui->AddMessageHandler(new GpuMessageHandler());
    365 
    366   // Set up the chrome://gpu/ source.
    367   BrowserContext* browser_context =
    368       web_ui->GetWebContents()->GetBrowserContext();
    369   WebUIDataSource::Add(browser_context, CreateGpuHTMLSource());
    370 }
    371 
    372 }  // namespace content
    373