528 lines
17 KiB
JavaScript
528 lines
17 KiB
JavaScript
// Copyright (c) 2011 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.
|
|
|
|
// // Copyright (c) 2011 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.
|
|
cr.define('gpu', function() {
|
|
/**
|
|
* This class provides a 'bridge' for communicating between javascript and the
|
|
* browser. When run outside of WebUI, e.g. as a regular webpage, it provides
|
|
* synthetic data to assist in testing.
|
|
* @constructor
|
|
*/
|
|
function BrowserBridge() {
|
|
// If we are not running inside WebUI, output chrome.send messages
|
|
// to the console to help with quick-iteration debugging.
|
|
this.debugMode_ = (chrome.send === undefined && console.log);
|
|
if (this.debugMode_) {
|
|
var browserBridgeTests = document.createElement('script');
|
|
browserBridgeTests.src = 'browser_bridge_tests.js';
|
|
document.body.appendChild(browserBridgeTests);
|
|
}
|
|
|
|
this.nextRequestId_ = 0;
|
|
this.pendingCallbacks_ = [];
|
|
this.logMessages_ = [];
|
|
|
|
// Tell c++ code that we are ready to receive GPU Info.
|
|
if (!this.debugMode_) {
|
|
chrome.send('browserBridgeInitialized');
|
|
this.beginRequestClientInfo_();
|
|
this.beginRequestLogMessages_();
|
|
}
|
|
}
|
|
|
|
BrowserBridge.prototype = {
|
|
__proto__: cr.EventTarget.prototype,
|
|
|
|
applySimulatedData_: function applySimulatedData(data) {
|
|
// set up things according to the simulated data
|
|
this.gpuInfo_ = data.gpuInfo;
|
|
this.clientInfo_ = data.clientInfo;
|
|
this.logMessages_ = data.logMessages;
|
|
cr.dispatchSimpleEvent(this, 'gpuInfoUpdate');
|
|
cr.dispatchSimpleEvent(this, 'clientInfoChange');
|
|
cr.dispatchSimpleEvent(this, 'logMessagesChange');
|
|
},
|
|
|
|
/**
|
|
* Returns true if the page is hosted inside Chrome WebUI
|
|
* Helps have behavior conditional to emulate_webui.py
|
|
*/
|
|
get debugMode() {
|
|
return this.debugMode_;
|
|
},
|
|
|
|
/**
|
|
* Sends a message to the browser with specified args. The
|
|
* browser will reply asynchronously via the provided callback.
|
|
*/
|
|
callAsync: function(submessage, args, callback) {
|
|
var requestId = this.nextRequestId_;
|
|
this.nextRequestId_ += 1;
|
|
this.pendingCallbacks_[requestId] = callback;
|
|
if (!args) {
|
|
chrome.send('callAsync', [requestId.toString(), submessage]);
|
|
} else {
|
|
var allArgs = [requestId.toString(), submessage].concat(args);
|
|
chrome.send('callAsync', allArgs);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Called by gpu c++ code when client info is ready.
|
|
*/
|
|
onCallAsyncReply: function(requestId, args) {
|
|
if (this.pendingCallbacks_[requestId] === undefined) {
|
|
throw new Error('requestId ' + requestId + ' is not pending');
|
|
}
|
|
var callback = this.pendingCallbacks_[requestId];
|
|
callback(args);
|
|
delete this.pendingCallbacks_[requestId];
|
|
},
|
|
|
|
/**
|
|
* Get gpuInfo data.
|
|
*/
|
|
get gpuInfo() {
|
|
return this.gpuInfo_;
|
|
},
|
|
|
|
/**
|
|
* Called from gpu c++ code when GPU Info is updated.
|
|
*/
|
|
onGpuInfoUpdate: function(gpuInfo) {
|
|
this.gpuInfo_ = gpuInfo;
|
|
cr.dispatchSimpleEvent(this, 'gpuInfoUpdate');
|
|
},
|
|
|
|
/**
|
|
* This function begins a request for the ClientInfo. If it comes back
|
|
* as undefined, then we will issue the request again in 250ms.
|
|
*/
|
|
beginRequestClientInfo_: function() {
|
|
this.callAsync('requestClientInfo', undefined, (function(data) {
|
|
if (data === undefined) { // try again in 250 ms
|
|
window.setTimeout(this.beginRequestClientInfo_.bind(this), 250);
|
|
} else {
|
|
this.clientInfo_ = data;
|
|
cr.dispatchSimpleEvent(this, 'clientInfoChange');
|
|
}
|
|
}).bind(this));
|
|
},
|
|
|
|
/**
|
|
* Returns information about the currently running Chrome build.
|
|
*/
|
|
get clientInfo() {
|
|
return this.clientInfo_;
|
|
},
|
|
|
|
/**
|
|
* This function checks for new GPU_LOG messages.
|
|
* If any are found, a refresh is triggered.
|
|
*/
|
|
beginRequestLogMessages_: function() {
|
|
this.callAsync('requestLogMessages', undefined,
|
|
(function(messages) {
|
|
if (messages.length != this.logMessages_.length) {
|
|
this.logMessages_ = messages;
|
|
cr.dispatchSimpleEvent(this, 'logMessagesChange');
|
|
}
|
|
// check again in 250 ms
|
|
window.setTimeout(this.beginRequestLogMessages_.bind(this), 250);
|
|
}).bind(this));
|
|
},
|
|
|
|
/**
|
|
* Returns an array of log messages issued by the GPU process, if any.
|
|
*/
|
|
get logMessages() {
|
|
return this.logMessages_;
|
|
},
|
|
|
|
/**
|
|
* Returns the value of the "Sandboxed" row.
|
|
*/
|
|
isSandboxedForTesting : function() {
|
|
for (i = 0; i < this.gpuInfo_.basic_info.length; ++i) {
|
|
var info = this.gpuInfo_.basic_info[i];
|
|
if (info.description == "Sandboxed")
|
|
return info.value;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
return {
|
|
BrowserBridge: BrowserBridge
|
|
};
|
|
});
|
|
|
|
// // 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.
|
|
|
|
|
|
/**
|
|
* @fileoverview This view displays information on the current GPU
|
|
* hardware. Its primary usefulness is to allow users to copy-paste
|
|
* their data in an easy to read format for bug reports.
|
|
*/
|
|
cr.define('gpu', function() {
|
|
/**
|
|
* Provides information on the GPU process and underlying graphics hardware.
|
|
* @constructor
|
|
* @extends {cr.ui.TabPanel}
|
|
*/
|
|
var InfoView = cr.ui.define(cr.ui.TabPanel);
|
|
|
|
InfoView.prototype = {
|
|
__proto__: cr.ui.TabPanel.prototype,
|
|
|
|
decorate: function() {
|
|
cr.ui.TabPanel.prototype.decorate.apply(this);
|
|
|
|
browserBridge.addEventListener('gpuInfoUpdate', this.refresh.bind(this));
|
|
browserBridge.addEventListener('logMessagesChange',
|
|
this.refresh.bind(this));
|
|
browserBridge.addEventListener('clientInfoChange',
|
|
this.refresh.bind(this));
|
|
this.refresh();
|
|
},
|
|
|
|
/**
|
|
* Updates the view based on its currently known data
|
|
*/
|
|
refresh: function(data) {
|
|
// Client info
|
|
if (browserBridge.clientInfo) {
|
|
var clientInfo = browserBridge.clientInfo;
|
|
|
|
this.setTable_('client-info', [
|
|
{
|
|
description: 'Data exported',
|
|
value: (new Date()).toLocaleString()
|
|
},
|
|
{
|
|
description: 'Chrome version',
|
|
value: clientInfo.version
|
|
},
|
|
{
|
|
description: 'Operating system',
|
|
value: clientInfo.operating_system
|
|
},
|
|
{
|
|
description: 'Software rendering list version',
|
|
value: clientInfo.blacklist_version
|
|
},
|
|
{
|
|
description: 'Driver bug list version',
|
|
value: clientInfo.driver_bug_list_version
|
|
},
|
|
{
|
|
description: 'ANGLE commit id',
|
|
value: clientInfo.angle_commit_id
|
|
},
|
|
{
|
|
description: '2D graphics backend',
|
|
value: clientInfo.graphics_backend
|
|
},
|
|
{
|
|
description: 'Command Line',
|
|
value: clientInfo.command_line
|
|
}]);
|
|
} else {
|
|
this.setText_('client-info', '... loading...');
|
|
}
|
|
|
|
// Feature map
|
|
var featureLabelMap = {
|
|
'2d_canvas': 'Canvas',
|
|
'gpu_compositing': 'Compositing',
|
|
'webgl': 'WebGL',
|
|
'multisampling': 'WebGL multisampling',
|
|
'flash_3d': 'Flash',
|
|
'flash_stage3d': 'Flash Stage3D',
|
|
'flash_stage3d_baseline': 'Flash Stage3D Baseline profile',
|
|
'texture_sharing': 'Texture Sharing',
|
|
'video_decode': 'Video Decode',
|
|
'video_encode': 'Video Encode',
|
|
'panel_fitting': 'Panel Fitting',
|
|
'rasterization': 'Rasterization',
|
|
'multiple_raster_threads': 'Multiple Raster Threads',
|
|
'native_gpu_memory_buffers': 'Native GpuMemoryBuffers',
|
|
'vpx_decode': 'VPx Video Decode',
|
|
'webgl2': 'WebGL2',
|
|
'checker_imaging': 'CheckerImaging',
|
|
};
|
|
|
|
var statusMap = {
|
|
'disabled_software': {
|
|
'label': 'Software only. Hardware acceleration disabled',
|
|
'class': 'feature-yellow'
|
|
},
|
|
'disabled_off': {
|
|
'label': 'Disabled',
|
|
'class': 'feature-red'
|
|
},
|
|
'disabled_off_ok': {
|
|
'label': 'Disabled',
|
|
'class': 'feature-yellow'
|
|
},
|
|
'unavailable_software': {
|
|
'label': 'Software only, hardware acceleration unavailable',
|
|
'class': 'feature-yellow'
|
|
},
|
|
'unavailable_off': {
|
|
'label': 'Unavailable',
|
|
'class': 'feature-red'
|
|
},
|
|
'unavailable_off_ok': {
|
|
'label': 'Unavailable',
|
|
'class': 'feature-yellow'
|
|
},
|
|
'enabled_readback': {
|
|
'label': 'Hardware accelerated but at reduced performance',
|
|
'class': 'feature-yellow'
|
|
},
|
|
'enabled_force': {
|
|
'label': 'Hardware accelerated on all pages',
|
|
'class': 'feature-green'
|
|
},
|
|
'enabled': {
|
|
'label': 'Hardware accelerated',
|
|
'class': 'feature-green'
|
|
},
|
|
'enabled_on': {
|
|
'label': 'Enabled',
|
|
'class': 'feature-green'
|
|
},
|
|
'enabled_force_on': {
|
|
'label': 'Force enabled',
|
|
'class': 'feature-green'
|
|
},
|
|
};
|
|
|
|
// GPU info, basic
|
|
var diagnosticsDiv = this.querySelector('.diagnostics');
|
|
var diagnosticsLoadingDiv = this.querySelector('.diagnostics-loading');
|
|
var featureStatusList = this.querySelector('.feature-status-list');
|
|
var problemsDiv = this.querySelector('.problems-div');
|
|
var problemsList = this.querySelector('.problems-list');
|
|
var workaroundsDiv = this.querySelector('.workarounds-div');
|
|
var workaroundsList = this.querySelector('.workarounds-list');
|
|
var gpuInfo = browserBridge.gpuInfo;
|
|
var i;
|
|
if (gpuInfo) {
|
|
// Not using jstemplate here for blacklist status because we construct
|
|
// href from data, which jstemplate can't seem to do.
|
|
if (gpuInfo.featureStatus) {
|
|
// feature status list
|
|
featureStatusList.textContent = '';
|
|
for (var featureName in gpuInfo.featureStatus.featureStatus) {
|
|
var featureStatus =
|
|
gpuInfo.featureStatus.featureStatus[featureName];
|
|
var featureEl = document.createElement('li');
|
|
|
|
var nameEl = document.createElement('span');
|
|
if (!featureLabelMap[featureName])
|
|
console.log('Missing featureLabel for', featureName);
|
|
nameEl.textContent = featureLabelMap[featureName] + ': ';
|
|
featureEl.appendChild(nameEl);
|
|
|
|
var statusEl = document.createElement('span');
|
|
var statusInfo = statusMap[featureStatus];
|
|
if (!statusInfo) {
|
|
console.log('Missing status for ', featureStatus);
|
|
statusEl.textContent = 'Unknown';
|
|
statusEl.className = 'feature-red';
|
|
} else {
|
|
statusEl.textContent = statusInfo['label'];
|
|
statusEl.className = statusInfo['class'];
|
|
}
|
|
featureEl.appendChild(statusEl);
|
|
|
|
featureStatusList.appendChild(featureEl);
|
|
}
|
|
|
|
// problems list
|
|
if (gpuInfo.featureStatus.problems.length) {
|
|
problemsDiv.hidden = false;
|
|
problemsList.textContent = '';
|
|
for (i = 0; i < gpuInfo.featureStatus.problems.length; i++) {
|
|
var problem = gpuInfo.featureStatus.problems[i];
|
|
var problemEl = this.createProblemEl_(problem);
|
|
problemsList.appendChild(problemEl);
|
|
}
|
|
} else {
|
|
problemsDiv.hidden = true;
|
|
}
|
|
|
|
// driver bug workarounds list
|
|
if (gpuInfo.featureStatus.workarounds.length) {
|
|
workaroundsDiv.hidden = false;
|
|
workaroundsList.textContent = '';
|
|
for (i = 0; i < gpuInfo.featureStatus.workarounds.length; i++) {
|
|
var workaroundEl = document.createElement('li');
|
|
workaroundEl.textContent = gpuInfo.featureStatus.workarounds[i];
|
|
workaroundsList.appendChild(workaroundEl);
|
|
}
|
|
} else {
|
|
workaroundsDiv.hidden = true;
|
|
}
|
|
|
|
} else {
|
|
featureStatusList.textContent = '';
|
|
problemsList.hidden = true;
|
|
workaroundsList.hidden = true;
|
|
}
|
|
|
|
if (gpuInfo.basic_info)
|
|
this.setTable_('basic-info', gpuInfo.basic_info);
|
|
else
|
|
this.setTable_('basic-info', []);
|
|
|
|
if (gpuInfo.compositorInfo)
|
|
this.setTable_('compositor-info', gpuInfo.compositorInfo);
|
|
else
|
|
this.setTable_('compositor-info', []);
|
|
|
|
if (gpuInfo.gpuMemoryBufferInfo)
|
|
this.setTable_('gpu-memory-buffer-info', gpuInfo.gpuMemoryBufferInfo);
|
|
else
|
|
this.setTable_('gpu-memory-buffer-info', []);
|
|
|
|
if (gpuInfo.diagnostics) {
|
|
diagnosticsDiv.hidden = false;
|
|
diagnosticsLoadingDiv.hidden = true;
|
|
$('diagnostics-table').hidden = false;
|
|
this.setTable_('diagnostics-table', gpuInfo.diagnostics);
|
|
} else if (gpuInfo.diagnostics === null) {
|
|
// gpu_internals.cc sets diagnostics to null when it is being loaded
|
|
diagnosticsDiv.hidden = false;
|
|
diagnosticsLoadingDiv.hidden = false;
|
|
$('diagnostics-table').hidden = true;
|
|
} else {
|
|
diagnosticsDiv.hidden = true;
|
|
}
|
|
} else {
|
|
this.setText_('basic-info', '... loading ...');
|
|
diagnosticsDiv.hidden = true;
|
|
featureStatusList.textContent = '';
|
|
problemsDiv.hidden = true;
|
|
}
|
|
|
|
// Log messages
|
|
jstProcess(new JsEvalContext({values: browserBridge.logMessages}),
|
|
$('log-messages'));
|
|
},
|
|
|
|
createProblemEl_: function(problem) {
|
|
var problemEl;
|
|
problemEl = document.createElement('li');
|
|
|
|
// Description of issue
|
|
var desc = document.createElement('a');
|
|
desc.textContent = problem.description;
|
|
problemEl.appendChild(desc);
|
|
|
|
// Spacing ':' element
|
|
if (problem.crBugs.length > 0) {
|
|
var tmp = document.createElement('span');
|
|
tmp.textContent = ': ';
|
|
problemEl.appendChild(tmp);
|
|
}
|
|
|
|
var nbugs = 0;
|
|
var j;
|
|
|
|
// crBugs
|
|
for (j = 0; j < problem.crBugs.length; ++j) {
|
|
if (nbugs > 0) {
|
|
var tmp = document.createElement('span');
|
|
tmp.textContent = ', ';
|
|
problemEl.appendChild(tmp);
|
|
}
|
|
|
|
var link = document.createElement('a');
|
|
var bugid = parseInt(problem.crBugs[j]);
|
|
link.textContent = bugid;
|
|
link.href = 'http://crbug.com/' + bugid;
|
|
problemEl.appendChild(link);
|
|
nbugs++;
|
|
}
|
|
|
|
if (problem.affectedGpuSettings.length > 0) {
|
|
var brNode = document.createElement('br');
|
|
problemEl.appendChild(brNode);
|
|
|
|
var iNode = document.createElement('i');
|
|
problemEl.appendChild(iNode);
|
|
|
|
var headNode = document.createElement('span');
|
|
if (problem.tag == 'disabledFeatures')
|
|
headNode.textContent = 'Disabled Features: ';
|
|
else // problem.tag == 'workarounds'
|
|
headNode.textContent = 'Applied Workarounds: ';
|
|
iNode.appendChild(headNode);
|
|
for (j = 0; j < problem.affectedGpuSettings.length; ++j) {
|
|
if (j > 0) {
|
|
var separateNode = document.createElement('span');
|
|
separateNode.textContent = ', ';
|
|
iNode.appendChild(separateNode);
|
|
}
|
|
var nameNode = document.createElement('span');
|
|
if (problem.tag == 'disabledFeatures')
|
|
nameNode.classList.add('feature-red');
|
|
else // problem.tag == 'workarounds'
|
|
nameNode.classList.add('feature-yellow');
|
|
nameNode.textContent = problem.affectedGpuSettings[j];
|
|
iNode.appendChild(nameNode);
|
|
}
|
|
}
|
|
|
|
return problemEl;
|
|
},
|
|
|
|
setText_: function(outputElementId, text) {
|
|
var peg = document.getElementById(outputElementId);
|
|
peg.textContent = text;
|
|
},
|
|
|
|
setTable_: function(outputElementId, inputData) {
|
|
var template = jstGetTemplate('info-view-table-template');
|
|
jstProcess(new JsEvalContext({value: inputData}),
|
|
template);
|
|
|
|
var peg = document.getElementById(outputElementId);
|
|
if (!peg)
|
|
throw new Error('Node ' + outputElementId + ' not found');
|
|
|
|
peg.innerHTML = '';
|
|
peg.appendChild(template);
|
|
}
|
|
};
|
|
|
|
return {
|
|
InfoView: InfoView
|
|
};
|
|
});
|
|
|
|
|
|
var browserBridge;
|
|
|
|
/**
|
|
* Main entry point. called once the page has loaded.
|
|
*/
|
|
function onLoad() {
|
|
browserBridge = new gpu.BrowserBridge();
|
|
|
|
// Create the views.
|
|
cr.ui.decorate('#info-view', gpu.InfoView);
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', onLoad);
|