0

Add "panel_fitting" GPU feature type and use it for mirror mode.

Panel fitting is used by mirror display mode on Chrome OS.
This GPU feature type will tell if it is supported by GPU hardware.
Initially enable it only for Ivy Bridge GPU via blacklisting everything else.
Also add a disable_panel_fitting command flag,
to be able to quickly turn the feature off.

When hardware supports panel fitting,
and there are no common modes between the internal and the external displays,
configure the internal display to the external's resolution,
and let the panel fitter take care of displaying it correctly.

BUG=chromium-os:34442
TEST=Panel Fitting should appear as Hardware Accelerated in about:gpu
     on IVB Chrome OS, Unavailable everywhere else.
TEST=Tested mirror mode on device.

Review URL: https://chromiumcodereview.appspot.com/10909242

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@161980 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
ynovikov@chromium.org
2012-10-15 22:19:31 +00:00
parent 2eb3773566
commit 7158db1b9a
13 changed files with 297 additions and 96 deletions

@ -25,6 +25,9 @@ const char kAshDisableWorkspace2[] = "ash-disable-workspace2";
// Disables boot animation v2, go back to v1.
const char kAshDisableBootAnimation2[] = "ash-disable-boot-animation2";
// Disables panel fitting (used for mirror mode).
const char kAshDisablePanelFitting[] = "ash-disable-panel-fitting";
// Enable advanced gestures (e.g. for window management).
const char kAshEnableAdvancedGestures[] = "ash-enable-advanced-gestures";

@ -19,6 +19,7 @@ ASH_EXPORT extern const char kAshBootAnimationFunction2[];
ASH_EXPORT extern const char kAshBootAnimationFunction3[];
ASH_EXPORT extern const char kAshConstrainPointerToRoot[];
ASH_EXPORT extern const char kAshDebugShortcuts[];
ASH_EXPORT extern const char kAshDisablePanelFitting[];
ASH_EXPORT extern const char kAshDisableWorkspace2[];
ASH_EXPORT extern const char kAshDisableBootAnimation2[];
ASH_EXPORT extern const char kAshEnableAdvancedGestures[];

@ -94,6 +94,8 @@
#include "ash/display/output_configurator_animation.h"
#include "base/message_pump_aurax11.h"
#include "chromeos/display/output_configurator.h"
#include "content/public/browser/gpu_data_manager.h"
#include "content/public/common/gpu_feature_type.h"
#endif // defined(OS_CHROMEOS)
namespace ash {
@ -189,6 +191,14 @@ Shell::Shell(ShellDelegate* delegate)
gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_ALTERNATE, screen_.get());
ui_controls::InstallUIControlsAura(internal::CreateUIControls());
#if defined(OS_CHROMEOS)
content::GpuFeatureType blacklisted_features =
content::GpuDataManager::GetInstance()->GetBlacklistedFeatures();
bool is_panel_fitting_disabled =
(blacklisted_features & content::GPU_FEATURE_TYPE_PANEL_FITTING) ||
CommandLine::ForCurrentProcess()->HasSwitch(
switches::kAshDisablePanelFitting);
output_configurator_->Init(!is_panel_fitting_disabled);
output_configurator_->AddObserver(output_configurator_animation_.get());
base::MessagePumpAuraX11::Current()->AddDispatcherForRootWindow(
output_configurator());

@ -792,6 +792,7 @@ std::string LoginUtilsImpl::GetOffTheRecordCommandLine(
ash::switches::kAshWindowAnimationsDisabled,
ash::switches::kAuraLegacyPowerButton,
ash::switches::kAuraNoShadows,
ash::switches::kAshDisablePanelFitting,
::switches::kUIEnablePartialSwap,
::switches::kUIPrioritizeInGpuProcess,
#if defined(USE_CRAS)

@ -87,6 +87,7 @@ cr.define('gpu', function() {
'video': 'Video',
// GPU Switching
'gpu_switching': 'GPU Switching',
'panel_fitting': 'Panel Fitting',
};
var statusLabelMap = {
'disabled_software': 'Software only. Hardware acceleration disabled.',

@ -6,6 +6,9 @@
#include <string>
#if defined(OS_CHROMEOS)
#include "ash/ash_switches.h"
#endif
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
@ -300,6 +303,18 @@ Value* GetFeatureStatus() {
"Accelerated video presentation has been disabled, either via"
" about:flags or command line.",
true
},
{
"panel_fitting",
flags & content::GPU_FEATURE_TYPE_PANEL_FITTING,
#if defined(OS_CHROMEOS)
command_line.HasSwitch(ash::switches::kAshDisablePanelFitting),
#else
true,
#endif
"Panel fitting is unavailable, either disabled at the command"
" line or not supported by the current system.",
false
}
};
const size_t kNumFeatures = sizeof(kGpuFeatureInfo) / sizeof(GpuFeatureInfo);

@ -28,6 +28,17 @@
namespace chromeos {
struct OutputSnapshot {
RROutput output;
RRCrtc crtc;
RRMode current_mode;
int height;
int y;
RRMode native_mode;
RRMode mirror_mode;
bool is_internal;
};
namespace {
// DPI measurements.
const float kMmInInch = 25.4;
@ -198,97 +209,6 @@ static void CreateFrameBuffer(Display* display,
XRRSetScreenSize(display, window, width, height, mm_width, mm_height);
}
typedef struct OutputSnapshot {
RROutput output;
RRCrtc crtc;
RRMode current_mode;
int height;
int y;
RRMode native_mode;
RRMode mirror_mode;
bool is_internal;
} OutputSnapshot;
static int GetDualOutputs(Display* display,
XRRScreenResources* screen,
OutputSnapshot* one,
OutputSnapshot* two) {
int found_count = 0;
XRROutputInfo* one_info = NULL;
XRROutputInfo* two_info = NULL;
for (int i = 0; (i < screen->noutput) && (found_count < 2); ++i) {
RROutput this_id = screen->outputs[i];
XRROutputInfo* output_info = XRRGetOutputInfo(display, screen, this_id);
bool is_connected = (RR_Connected == output_info->connection);
if (is_connected) {
OutputSnapshot *to_populate = NULL;
if (0 == found_count) {
to_populate = one;
one_info = output_info;
} else {
to_populate = two;
two_info = output_info;
}
to_populate->output = this_id;
// Now, look up the corresponding CRTC and any related info.
to_populate->crtc = output_info->crtc;
if (None != to_populate->crtc) {
XRRCrtcInfo* crtc_info =
XRRGetCrtcInfo(display, screen, to_populate->crtc);
to_populate->current_mode = crtc_info->mode;
to_populate->height = crtc_info->height;
to_populate->y = crtc_info->y;
XRRFreeCrtcInfo(crtc_info);
} else {
to_populate->current_mode = 0;
to_populate->height = 0;
to_populate->y = 0;
}
// Find the native_mode and leave the mirror_mode for the pass after the
// loop.
if (output_info->nmode > 0)
to_populate->native_mode = output_info->modes[0];
to_populate->mirror_mode = 0;
// See if this output refers to an internal display.
to_populate->is_internal =
OutputConfigurator::IsInternalOutputName(
std::string(output_info->name));
VLOG(1) << "Found display #" << found_count
<< " with output " << (int)to_populate->output
<< " crtc " << (int)to_populate->crtc
<< " current mode " << (int)to_populate->current_mode;
++found_count;
} else {
XRRFreeOutputInfo(output_info);
}
}
if (2 == found_count) {
// Find the mirror modes (if there are any).
bool can_mirror = FindMirrorModeForOutputs(display,
screen,
one->output,
two->output,
&one->mirror_mode,
&two->mirror_mode);
if (!can_mirror) {
// We can't mirror so set mirror_mode to 0.
one->mirror_mode = 0;
two->mirror_mode = 0;
}
}
XRRFreeOutputInfo(one_info);
XRRFreeOutputInfo(two_info);
return found_count;
}
static OutputState InferCurrentState(Display* display,
XRRScreenResources* screen,
const OutputSnapshot* outputs,
@ -566,11 +486,18 @@ static bool IsProjecting(const OutputSnapshot* outputs, int output_count) {
OutputConfigurator::OutputConfigurator()
: is_running_on_chrome_os_(base::chromeos::IsRunningOnChromeOS()),
is_panel_fitting_enabled_(false),
connected_output_count_(0),
xrandr_event_base_(0),
output_state_(STATE_INVALID) {
}
void OutputConfigurator::Init(bool is_panel_fitting_enabled) {
if (!is_running_on_chrome_os_)
return;
is_panel_fitting_enabled_ = is_panel_fitting_enabled;
// Cache the initial output state.
Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay();
CHECK(display != NULL);
@ -851,4 +778,172 @@ void OutputConfigurator::NotifyOnDisplayChanged() {
FOR_EACH_OBSERVER(Observer, observers_, OnDisplayModeChanged());
}
int OutputConfigurator::GetDualOutputs(Display* display,
XRRScreenResources* screen,
OutputSnapshot* one,
OutputSnapshot* two) {
int found_count = 0;
XRROutputInfo* one_info = NULL;
XRROutputInfo* two_info = NULL;
for (int i = 0; (i < screen->noutput) && (found_count < 2); ++i) {
RROutput this_id = screen->outputs[i];
XRROutputInfo* output_info = XRRGetOutputInfo(display, screen, this_id);
bool is_connected = (RR_Connected == output_info->connection);
if (is_connected) {
OutputSnapshot *to_populate = NULL;
if (0 == found_count) {
to_populate = one;
one_info = output_info;
} else {
to_populate = two;
two_info = output_info;
}
to_populate->output = this_id;
// Now, look up the corresponding CRTC and any related info.
to_populate->crtc = output_info->crtc;
if (None != to_populate->crtc) {
XRRCrtcInfo* crtc_info =
XRRGetCrtcInfo(display, screen, to_populate->crtc);
to_populate->current_mode = crtc_info->mode;
to_populate->height = crtc_info->height;
to_populate->y = crtc_info->y;
XRRFreeCrtcInfo(crtc_info);
} else {
to_populate->current_mode = 0;
to_populate->height = 0;
to_populate->y = 0;
}
// Find the native_mode and leave the mirror_mode for the pass after the
// loop.
to_populate->native_mode = GetOutputNativeMode(output_info);
to_populate->mirror_mode = 0;
// See if this output refers to an internal display.
to_populate->is_internal = IsInternalOutput(output_info);
VLOG(1) << "Found display #" << found_count
<< " with output " << (int)to_populate->output
<< " crtc " << (int)to_populate->crtc
<< " current mode " << (int)to_populate->current_mode;
++found_count;
} else {
XRRFreeOutputInfo(output_info);
}
}
if (2 == found_count) {
// Find the mirror modes (if there are any).
bool mirror_mode_found = FindMirrorModeForOutputs(display,
screen,
one->output,
two->output,
&one->mirror_mode,
&two->mirror_mode);
if (!mirror_mode_found) {
bool mirror_mode_added = AddMirrorModeToInternalOutput(display,
screen,
one->output,
two->output,
&one->mirror_mode,
&two->mirror_mode);
if (!mirror_mode_added) {
// We can't mirror so set mirror_mode to 0.
one->mirror_mode = 0;
two->mirror_mode = 0;
}
}
}
XRRFreeOutputInfo(one_info);
XRRFreeOutputInfo(two_info);
return found_count;
}
bool OutputConfigurator::AddMirrorModeToInternalOutput(
Display* display,
XRRScreenResources* screen,
RROutput output_one,
RROutput output_two,
RRMode* output_one_mode,
RRMode* output_two_mode) {
// Add new mode only if panel fitting hardware will be able to display it.
if (!is_panel_fitting_enabled_)
return false;
XRROutputInfo* output_one_info =
XRRGetOutputInfo(display, screen, output_one);
XRROutputInfo* output_two_info =
XRRGetOutputInfo(display, screen, output_two);
bool success = false;
// Both outputs should be connected in mirror mode
if (output_one_info->connection == RR_Connected &&
output_two_info->connection == RR_Connected) {
bool one_is_internal = IsInternalOutput(output_one_info);
bool two_is_internal = IsInternalOutput(output_two_info);
XRROutputInfo* internal_info = NULL;
XRROutputInfo* external_info = NULL;
if (one_is_internal) {
internal_info = output_one_info;
external_info = output_two_info;
VLOG_IF(1, two_is_internal) << "Two internal outputs detected.";
DCHECK(!two_is_internal);
} else if (two_is_internal) {
internal_info = output_two_info;
external_info = output_one_info;
}
bool internal_output_found = internal_info != NULL;
if (internal_output_found) {
RRMode internal_native_mode_id = GetOutputNativeMode(internal_info);
RRMode external_native_mode_id = GetOutputNativeMode(external_info);
if (internal_native_mode_id != None && external_native_mode_id != None) {
XRRModeInfo* internal_native_mode =
ModeInfoForID(screen, internal_native_mode_id);
XRRModeInfo* external_native_mode =
ModeInfoForID(screen, external_native_mode_id);
// Panel fitting will not work if the internal output maximal resolution
// is lower than that of the external output
if (internal_native_mode->width >= external_native_mode->width &&
internal_native_mode->height >= external_native_mode->height) {
XRRAddOutputMode(display, one_is_internal ? output_one : output_two,
external_native_mode_id);
*output_one_mode = *output_two_mode = external_native_mode_id;
success = true;
}
}
}
}
XRRFreeOutputInfo(output_one_info);
XRRFreeOutputInfo(output_two_info);
return success;
}
// static
bool OutputConfigurator::IsInternalOutput(const XRROutputInfo* output_info) {
return IsInternalOutputName(std::string(output_info->name));
}
// static
RRMode OutputConfigurator::GetOutputNativeMode(
const XRROutputInfo* output_info) {
if (output_info->nmode <= 0)
return None;
return output_info->modes[0];
}
} // namespace chromeos

@ -21,11 +21,17 @@ typedef XID RROutput;
typedef XID RRCrtc;
typedef XID RRMode;
struct _XDisplay;
typedef struct _XDisplay Display;
struct _XRROutputInfo;
typedef _XRROutputInfo XRROutputInfo;
struct _XRRScreenResources;
typedef _XRRScreenResources XRRScreenResources;
namespace chromeos {
struct OutputSnapshot;
// Used to describe the state of a multi-display configuration.
// TODO(oshima): remove DUAL_SECONDARY_ONLY
enum OutputState {
@ -48,10 +54,10 @@ class CHROMEOS_EXPORT OutputConfigurator : public MessageLoop::Dispatcher {
public:
// Called when the change of the display mode finished. It will usually
// start the fading in the displays.
virtual void OnDisplayModeChanged() {};
virtual void OnDisplayModeChanged() {}
// Called when the change of the display mode is issued but failed.
virtual void OnDisplayModeChangeFailed() {};
virtual void OnDisplayModeChangeFailed() {}
};
OutputConfigurator();
@ -61,6 +67,10 @@ class CHROMEOS_EXPORT OutputConfigurator : public MessageLoop::Dispatcher {
OutputState output_state() const { return output_state_; }
// Initialization, must be called right after constructor.
// |is_panel_fitting_enabled| indicates hardware panel fitting support.
void Init(bool is_panel_fitting_enabled);
// Called when the user hits ctrl-F4 to request a display mode change.
// This method should only return false if it was called in a single-head or
// headless mode.
@ -93,6 +103,37 @@ class CHROMEOS_EXPORT OutputConfigurator : public MessageLoop::Dispatcher {
// Fires OnDisplayModeChanged() event to the observers.
void NotifyOnDisplayChanged();
// Fills output parameters |one| and |two| with properties of
// first two connected outputs found on |display| and |screen|.
int GetDualOutputs(Display* display,
XRRScreenResources* screen,
OutputSnapshot* one,
OutputSnapshot* two);
// Should be called if the internal (built-in) output didn't advertise a mode
// which would be capable to support mirror mode.
// Relies on hardware panel fitting support,
// returns immediately if it is not available.
// Tries to add the native mode of the external output to the internal output,
// assuming panel fitter hardware will take care of scaling and letterboxing.
// The RROutput IDs |output_one| and |output_two| are used
// to look up the modes and configure the internal output,
// |output_one_mode| and |output_two_mode| are the out-parameters
// for the modes on the two outputs which will have same resolution.
// Returns false if it fails to configure the internal output appropriately.
bool AddMirrorModeToInternalOutput(Display* display,
XRRScreenResources* screen,
RROutput output_one,
RROutput output_two,
RRMode* output_one_mode,
RRMode* output_two_mode);
// Tells if the output specified by |output_info| is for internal display.
static bool IsInternalOutput(const XRROutputInfo* output_info);
// Returns output's native mode, None if not found.
static RRMode GetOutputNativeMode(const XRROutputInfo* output_info);
// This is detected by the constructor to determine whether or not we should
// be enabled. If we aren't running on ChromeOS, we can't assume that the
// Xrandr X11 extension is supported.
@ -100,6 +141,10 @@ class CHROMEOS_EXPORT OutputConfigurator : public MessageLoop::Dispatcher {
// configuration to immediately fail without changing the state.
bool is_running_on_chrome_os_;
// This is set externally in Init,
// and is used to enable modes which rely on panel fitting.
bool is_panel_fitting_enabled_;
// The number of outputs that are connected.
int connected_output_count_;

@ -926,6 +926,7 @@ bool GpuBlacklist::GpuBlacklistEntry::SetBlacklistedFeatures(
case content::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE:
case content::GPU_FEATURE_TYPE_3D_CSS:
case content::GPU_FEATURE_TYPE_ACCELERATED_VIDEO:
case content::GPU_FEATURE_TYPE_PANEL_FITTING:
case content::GPU_FEATURE_TYPE_ALL:
feature_type |= type;
break;

@ -30,6 +30,7 @@ const char kGpuFeatureNameTextureSharing[] = "texture_sharing";
const char kGpuFeatureNameAcceleratedVideoDecode[] = "accelerated_video_decode";
const char kGpuFeatureName3dCss[] = "3d_css";
const char kGpuFeatureNameAcceleratedVideo[] = "accelerated_video";
const char kGpuFeatureNamePanelFitting[] = "panel_fitting";
const char kGpuFeatureNameAll[] = "all";
const char kGpuFeatureNameUnknown[] = "unknown";
@ -109,6 +110,8 @@ GpuFeatureType StringToGpuFeatureType(const std::string& feature_string) {
return content::GPU_FEATURE_TYPE_3D_CSS;
if (feature_string == kGpuFeatureNameAcceleratedVideo)
return content::GPU_FEATURE_TYPE_ACCELERATED_VIDEO;
if (feature_string == kGpuFeatureNamePanelFitting)
return content::GPU_FEATURE_TYPE_PANEL_FITTING;
if (feature_string == kGpuFeatureNameAll)
return content::GPU_FEATURE_TYPE_ALL;
return content::GPU_FEATURE_TYPE_UNKNOWN;
@ -139,6 +142,8 @@ std::string GpuFeatureTypeToString(GpuFeatureType type) {
matches.push_back(kGpuFeatureName3dCss);
if (type & content::GPU_FEATURE_TYPE_ACCELERATED_VIDEO)
matches.push_back(kGpuFeatureNameAcceleratedVideo);
if (type & content::GPU_FEATURE_TYPE_PANEL_FITTING)
matches.push_back(kGpuFeatureNamePanelFitting);
if (!matches.size())
matches.push_back(kGpuFeatureNameUnknown);
}

@ -30,6 +30,8 @@ TEST(GpuUtilsTest, GpuFeatureTypFromString) {
content::GPU_FEATURE_TYPE_3D_CSS);
EXPECT_EQ(gpu_util::StringToGpuFeatureType("accelerated_video"),
content::GPU_FEATURE_TYPE_ACCELERATED_VIDEO);
EXPECT_EQ(gpu_util::StringToGpuFeatureType("panel_fitting"),
content::GPU_FEATURE_TYPE_PANEL_FITTING);
EXPECT_EQ(gpu_util::StringToGpuFeatureType("all"),
content::GPU_FEATURE_TYPE_ALL);
EXPECT_EQ(gpu_util::StringToGpuFeatureType("xxx"),
@ -78,6 +80,10 @@ TEST(GpuUtilsTest, GpuFeatureTypeToString) {
gpu_util::GpuFeatureTypeToString(
content::GPU_FEATURE_TYPE_ACCELERATED_VIDEO).c_str(),
"accelerated_video");
EXPECT_STREQ(
gpu_util::GpuFeatureTypeToString(
content::GPU_FEATURE_TYPE_PANEL_FITTING).c_str(),
"panel_fitting");
EXPECT_STREQ(
gpu_util::GpuFeatureTypeToString(
content::GPU_FEATURE_TYPE_ALL).c_str(),

@ -41,7 +41,7 @@
// 19. "blacklist" is a list of gpu feature strings, valid values include
// "accelerated_2d_canvas", "accelerated_compositing", "webgl",
// "multisampling", "flash_3d", "flash_stage3d", "texture_sharing",
// "accelerated_video_decode", and "all".
// "accelerated_video_decode", "panel_fitting", and "all".
// This field is mandatory.
// 20. "description" has the description of the entry.
// 21. "webkit_bugs" is an array of associated webkit bug numbers.
@ -74,7 +74,7 @@
{
"name": "software rendering list",
// Please update the version number whenever you change this file.
"version": "3.8",
"version": "3.9",
"entries": [
{
"id": 1,
@ -855,6 +855,22 @@
"flash_3d",
"flash_stage3d"
]
},
{
"id": 57,
"description": "Enable panel fitting capability on ChromeOS only on Ivy Bridge Graphics Controller.",
"exceptions": [
{
"os": {
"type": "chromeos"
},
"vendor_id": "0x8086",
"device_id": ["0x0166"]
}
],
"blacklist": [
"panel_fitting"
]
}
]
}

@ -21,6 +21,7 @@ enum GpuFeatureType {
GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE = 1 << 7,
GPU_FEATURE_TYPE_3D_CSS = 1 << 8,
GPU_FEATURE_TYPE_ACCELERATED_VIDEO = 1 << 9,
GPU_FEATURE_TYPE_PANEL_FITTING = 1 << 10,
GPU_FEATURE_TYPE_ALL = GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS |
GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING |
GPU_FEATURE_TYPE_WEBGL |
@ -30,7 +31,8 @@ enum GpuFeatureType {
GPU_FEATURE_TYPE_TEXTURE_SHARING |
GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE |
GPU_FEATURE_TYPE_3D_CSS |
GPU_FEATURE_TYPE_ACCELERATED_VIDEO,
GPU_FEATURE_TYPE_ACCELERATED_VIDEO |
GPU_FEATURE_TYPE_PANEL_FITTING,
// All flags initialized to false, i.e., no feature is blacklisted.
GPU_FEATURE_TYPE_UNKNOWN = 0
};