// Copyright (c) 2012 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 "content/browser/gpu/gpu_internals_ui.h" #include #include "base/bind.h" #include "base/bind_helpers.h" #include "base/command_line.h" #include "base/i18n/time_formatting.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/sys_info.h" #include "base/values.h" #include "content/browser/gpu/compositor_util.h" #include "content/browser/gpu/gpu_data_manager_impl.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/gpu_data_manager_observer.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui.h" #include "content/public/browser/web_ui_data_source.h" #include "content/public/browser/web_ui_message_handler.h" #include "content/public/common/content_client.h" #include "content/public/common/content_switches.h" #include "content/public/common/url_constants.h" #include "gpu/config/gpu_feature_type.h" #include "gpu/config/gpu_info.h" #include "grit/content_resources.h" #include "third_party/angle/src/common/version.h" namespace content { namespace { WebUIDataSource* CreateGpuHTMLSource() { WebUIDataSource* source = WebUIDataSource::Create(kChromeUIGpuHost); source->SetJsonPath("strings.js"); source->AddResourcePath("gpu_internals.js", IDR_GPU_INTERNALS_JS); source->SetDefaultResource(IDR_GPU_INTERNALS_HTML); return source; } base::DictionaryValue* NewDescriptionValuePair(const std::string& desc, const std::string& value) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("description", desc); dict->SetString("value", value); return dict; } base::DictionaryValue* NewDescriptionValuePair(const std::string& desc, base::Value* value) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("description", desc); dict->Set("value", value); return dict; } #if defined(OS_WIN) // Output DxDiagNode tree as nested array of {description,value} pairs base::ListValue* DxDiagNodeToList(const gpu::DxDiagNode& node) { base::ListValue* list = new base::ListValue(); for (std::map::const_iterator it = node.values.begin(); it != node.values.end(); ++it) { list->Append(NewDescriptionValuePair(it->first, it->second)); } for (std::map::const_iterator it = node.children.begin(); it != node.children.end(); ++it) { base::ListValue* sublist = DxDiagNodeToList(it->second); list->Append(NewDescriptionValuePair(it->first, sublist)); } return list; } #endif std::string GPUDeviceToString(const gpu::GPUInfo::GPUDevice& gpu) { std::string vendor = base::StringPrintf("0x%04x", gpu.vendor_id); if (!gpu.vendor_string.empty()) vendor += " [" + gpu.vendor_string + "]"; std::string device = base::StringPrintf("0x%04x", gpu.device_id); if (!gpu.device_string.empty()) device += " [" + gpu.device_string + "]"; return base::StringPrintf( "VENDOR = %s, DEVICE= %s", vendor.c_str(), device.c_str()); } base::DictionaryValue* GpuInfoAsDictionaryValue() { gpu::GPUInfo gpu_info = GpuDataManagerImpl::GetInstance()->GetGPUInfo(); base::ListValue* basic_info = new base::ListValue(); basic_info->Append(NewDescriptionValuePair( "Initialization time", base::Int64ToString(gpu_info.initialization_time.InMilliseconds()))); basic_info->Append(NewDescriptionValuePair( "Sandboxed", new base::FundamentalValue(gpu_info.sandboxed))); basic_info->Append(NewDescriptionValuePair( "GPU0", GPUDeviceToString(gpu_info.gpu))); for (size_t i = 0; i < gpu_info.secondary_gpus.size(); ++i) { basic_info->Append(NewDescriptionValuePair( base::StringPrintf("GPU%d", static_cast(i + 1)), GPUDeviceToString(gpu_info.secondary_gpus[i]))); } basic_info->Append(NewDescriptionValuePair( "Optimus", new base::FundamentalValue(gpu_info.optimus))); basic_info->Append(NewDescriptionValuePair( "AMD switchable", new base::FundamentalValue(gpu_info.amd_switchable))); if (gpu_info.lenovo_dcute) { basic_info->Append(NewDescriptionValuePair( "Lenovo dCute", new base::FundamentalValue(true))); } if (gpu_info.display_link_version.IsValid()) { basic_info->Append(NewDescriptionValuePair( "DisplayLink Version", gpu_info.display_link_version.GetString())); } basic_info->Append(NewDescriptionValuePair("Driver vendor", gpu_info.driver_vendor)); basic_info->Append(NewDescriptionValuePair("Driver version", gpu_info.driver_version)); basic_info->Append(NewDescriptionValuePair("Driver date", gpu_info.driver_date)); basic_info->Append(NewDescriptionValuePair("Pixel shader version", gpu_info.pixel_shader_version)); basic_info->Append(NewDescriptionValuePair("Vertex shader version", gpu_info.vertex_shader_version)); basic_info->Append(NewDescriptionValuePair("Machine model", gpu_info.machine_model)); basic_info->Append(NewDescriptionValuePair("GL version", gpu_info.gl_version)); basic_info->Append(NewDescriptionValuePair("GL_VENDOR", gpu_info.gl_vendor)); basic_info->Append(NewDescriptionValuePair("GL_RENDERER", gpu_info.gl_renderer)); basic_info->Append(NewDescriptionValuePair("GL_VERSION", gpu_info.gl_version_string)); basic_info->Append(NewDescriptionValuePair("GL_EXTENSIONS", gpu_info.gl_extensions)); basic_info->Append(NewDescriptionValuePair("Window system binding vendor", gpu_info.gl_ws_vendor)); basic_info->Append(NewDescriptionValuePair("Window system binding version", gpu_info.gl_ws_version)); basic_info->Append(NewDescriptionValuePair("Window system binding extensions", gpu_info.gl_ws_extensions)); std::string reset_strategy = base::StringPrintf("0x%04x", gpu_info.gl_reset_notification_strategy); basic_info->Append(NewDescriptionValuePair( "Reset notification strategy", reset_strategy)); base::DictionaryValue* info = new base::DictionaryValue(); info->Set("basic_info", basic_info); #if defined(OS_WIN) base::ListValue* perf_info = new base::ListValue(); perf_info->Append(NewDescriptionValuePair( "Graphics", base::StringPrintf("%.1f", gpu_info.performance_stats.graphics))); perf_info->Append(NewDescriptionValuePair( "Gaming", base::StringPrintf("%.1f", gpu_info.performance_stats.gaming))); perf_info->Append(NewDescriptionValuePair( "Overall", base::StringPrintf("%.1f", gpu_info.performance_stats.overall))); info->Set("performance_info", perf_info); base::Value* dx_info = gpu_info.dx_diagnostics.children.size() ? DxDiagNodeToList(gpu_info.dx_diagnostics) : base::Value::CreateNullValue(); info->Set("diagnostics", dx_info); #endif return info; } // This class receives javascript messages from the renderer. // Note that the WebUI infrastructure runs on the UI thread, therefore all of // this class's methods are expected to run on the UI thread. class GpuMessageHandler : public WebUIMessageHandler, public base::SupportsWeakPtr, public GpuDataManagerObserver { public: GpuMessageHandler(); virtual ~GpuMessageHandler(); // WebUIMessageHandler implementation. virtual void RegisterMessages() OVERRIDE; // GpuDataManagerObserver implementation. virtual void OnGpuInfoUpdate() OVERRIDE; virtual void OnGpuSwitching() OVERRIDE; // Messages void OnBrowserBridgeInitialized(const base::ListValue* list); void OnCallAsync(const base::ListValue* list); // Submessages dispatched from OnCallAsync base::Value* OnRequestClientInfo(const base::ListValue* list); base::Value* OnRequestLogMessages(const base::ListValue* list); private: // True if observing the GpuDataManager (re-attaching as observer would // DCHECK). bool observing_; DISALLOW_COPY_AND_ASSIGN(GpuMessageHandler); }; //////////////////////////////////////////////////////////////////////////////// // // GpuMessageHandler // //////////////////////////////////////////////////////////////////////////////// GpuMessageHandler::GpuMessageHandler() : observing_(false) { } GpuMessageHandler::~GpuMessageHandler() { GpuDataManagerImpl::GetInstance()->RemoveObserver(this); } /* BrowserBridge.callAsync prepends a requestID to these messages. */ void GpuMessageHandler::RegisterMessages() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); web_ui()->RegisterMessageCallback("browserBridgeInitialized", base::Bind(&GpuMessageHandler::OnBrowserBridgeInitialized, base::Unretained(this))); web_ui()->RegisterMessageCallback("callAsync", base::Bind(&GpuMessageHandler::OnCallAsync, base::Unretained(this))); } void GpuMessageHandler::OnCallAsync(const base::ListValue* args) { DCHECK_GE(args->GetSize(), static_cast(2)); // unpack args into requestId, submessage and submessageArgs bool ok; const base::Value* requestId; ok = args->Get(0, &requestId); DCHECK(ok); std::string submessage; ok = args->GetString(1, &submessage); DCHECK(ok); base::ListValue* submessageArgs = new base::ListValue(); for (size_t i = 2; i < args->GetSize(); ++i) { const base::Value* arg; ok = args->Get(i, &arg); DCHECK(ok); base::Value* argCopy = arg->DeepCopy(); submessageArgs->Append(argCopy); } // call the submessage handler base::Value* ret = NULL; if (submessage == "requestClientInfo") { ret = OnRequestClientInfo(submessageArgs); } else if (submessage == "requestLogMessages") { ret = OnRequestLogMessages(submessageArgs); } else { // unrecognized submessage NOTREACHED(); delete submessageArgs; return; } delete submessageArgs; // call BrowserBridge.onCallAsyncReply with result if (ret) { web_ui()->CallJavascriptFunction("browserBridge.onCallAsyncReply", *requestId, *ret); delete ret; } else { web_ui()->CallJavascriptFunction("browserBridge.onCallAsyncReply", *requestId); } } void GpuMessageHandler::OnBrowserBridgeInitialized( const base::ListValue* args) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // Watch for changes in GPUInfo if (!observing_) GpuDataManagerImpl::GetInstance()->AddObserver(this); observing_ = true; // Tell GpuDataManager it should have full GpuInfo. If the // Gpu process has not run yet, this will trigger its launch. GpuDataManagerImpl::GetInstance()->RequestCompleteGpuInfoIfNeeded(); // Run callback immediately in case the info is ready and no update in the // future. OnGpuInfoUpdate(); } base::Value* GpuMessageHandler::OnRequestClientInfo( const base::ListValue* list) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("version", GetContentClient()->GetProduct()); dict->SetString("command_line", CommandLine::ForCurrentProcess()->GetCommandLineString()); dict->SetString("operating_system", base::SysInfo::OperatingSystemName() + " " + base::SysInfo::OperatingSystemVersion()); dict->SetString("angle_revision", base::UintToString(BUILD_REVISION)); dict->SetString("graphics_backend", "Skia"); dict->SetString("blacklist_version", GpuDataManagerImpl::GetInstance()->GetBlacklistVersion()); dict->SetString("driver_bug_list_version", GpuDataManagerImpl::GetInstance()->GetDriverBugListVersion()); return dict; } base::Value* GpuMessageHandler::OnRequestLogMessages(const base::ListValue*) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); return GpuDataManagerImpl::GetInstance()->GetLogMessages(); } void GpuMessageHandler::OnGpuInfoUpdate() { // Get GPU Info. scoped_ptr gpu_info_val(GpuInfoAsDictionaryValue()); // Add in blacklisting features base::DictionaryValue* feature_status = new DictionaryValue; feature_status->Set("featureStatus", GetFeatureStatus()); feature_status->Set("problems", GetProblems()); feature_status->Set("workarounds", GetDriverBugWorkarounds()); if (feature_status) gpu_info_val->Set("featureStatus", feature_status); // Send GPU Info to javascript. web_ui()->CallJavascriptFunction("browserBridge.onGpuInfoUpdate", *(gpu_info_val.get())); } void GpuMessageHandler::OnGpuSwitching() { GpuDataManagerImpl::GetInstance()->RequestCompleteGpuInfoIfNeeded(); } } // namespace //////////////////////////////////////////////////////////////////////////////// // // GpuInternalsUI // //////////////////////////////////////////////////////////////////////////////// GpuInternalsUI::GpuInternalsUI(WebUI* web_ui) : WebUIController(web_ui) { web_ui->AddMessageHandler(new GpuMessageHandler()); // Set up the chrome://gpu/ source. BrowserContext* browser_context = web_ui->GetWebContents()->GetBrowserContext(); WebUIDataSource::Add(browser_context, CreateGpuHTMLSource()); } } // namespace content