[go: up one dir, main page]

blob: 72a14a1f17f7a65de3e9fb9a7a7a5bbadcbd7e61 [file] [log] [blame]
// Copyright 2014 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 "ui/ozone/platform/drm/gpu/screen_manager.h"
#include <xf86drmMode.h>
#include "third_party/skia/include/core/SkCanvas.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/ozone/platform/drm/common/drm_util.h"
#include "ui/ozone/platform/drm/gpu/crtc_controller.h"
#include "ui/ozone/platform/drm/gpu/drm_console_buffer.h"
#include "ui/ozone/platform/drm/gpu/drm_device.h"
#include "ui/ozone/platform/drm/gpu/drm_window.h"
#include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
namespace ui {
namespace {
// Copies the contents of the saved framebuffer from the CRTCs in |controller|
// to the new modeset buffer |buffer|.
void FillModesetBuffer(const scoped_refptr<DrmDevice>& drm,
HardwareDisplayController* controller,
ScanoutBuffer* buffer) {
DrmConsoleBuffer modeset_buffer(drm, buffer->GetFramebufferId());
if (!modeset_buffer.Initialize()) {
VLOG(2) << "Failed to grab framebuffer " << buffer->GetFramebufferId();
return;
}
auto crtcs = controller->crtc_controllers();
DCHECK(!crtcs.empty());
ScopedDrmCrtcPtr saved_crtc(drm->GetCrtc(crtcs[0]->crtc()));
if (!saved_crtc || !saved_crtc->buffer_id) {
VLOG(2) << "Crtc has no saved state or wasn't modeset";
return;
}
// If the display controller is in mirror mode, the CRTCs should be sharing
// the same framebuffer.
DrmConsoleBuffer saved_buffer(drm, saved_crtc->buffer_id);
if (!saved_buffer.Initialize()) {
VLOG(2) << "Failed to grab saved framebuffer " << saved_crtc->buffer_id;
return;
}
// Don't copy anything if the sizes mismatch. This can happen when the user
// changes modes.
if (saved_buffer.canvas()->getBaseLayerSize() !=
modeset_buffer.canvas()->getBaseLayerSize()) {
VLOG(2) << "Previous buffer has a different size than modeset buffer";
return;
}
skia::RefPtr<SkImage> image = saved_buffer.image();
SkPaint paint;
// Copy the source buffer. Do not perform any blending.
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
modeset_buffer.canvas()->drawImage(image.get(), 0, 0, &paint);
}
} // namespace
ScreenManager::ScreenManager(ScanoutBufferGenerator* buffer_generator)
: buffer_generator_(buffer_generator) {
}
ScreenManager::~ScreenManager() {
DCHECK(window_map_.empty());
}
void ScreenManager::AddDisplayController(const scoped_refptr<DrmDevice>& drm,
uint32_t crtc,
uint32_t connector) {
HardwareDisplayControllers::iterator it = FindDisplayController(drm, crtc);
// TODO(dnicoara): Turn this into a DCHECK when async display configuration is
// properly supported. (When there can't be a race between forcing initial
// display configuration in ScreenManager and NativeDisplayDelegate creating
// the display controllers.)
if (it != controllers_.end()) {
LOG(WARNING) << "Display controller (crtc=" << crtc << ") already present.";
return;
}
controllers_.push_back(new HardwareDisplayController(
scoped_ptr<CrtcController>(new CrtcController(drm, crtc, connector)),
gfx::Point()));
}
void ScreenManager::RemoveDisplayController(const scoped_refptr<DrmDevice>& drm,
uint32_t crtc) {
HardwareDisplayControllers::iterator it = FindDisplayController(drm, crtc);
if (it != controllers_.end()) {
bool is_mirrored = (*it)->IsMirrored();
(*it)->RemoveCrtc(drm, crtc);
if (!is_mirrored) {
controllers_.erase(it);
UpdateControllerToWindowMapping();
}
}
}
bool ScreenManager::ConfigureDisplayController(
const scoped_refptr<DrmDevice>& drm,
uint32_t crtc,
uint32_t connector,
const gfx::Point& origin,
const drmModeModeInfo& mode) {
bool status =
ActualConfigureDisplayController(drm, crtc, connector, origin, mode);
if (status)
UpdateControllerToWindowMapping();
return status;
}
bool ScreenManager::ActualConfigureDisplayController(
const scoped_refptr<DrmDevice>& drm,
uint32_t crtc,
uint32_t connector,
const gfx::Point& origin,
const drmModeModeInfo& mode) {
gfx::Rect modeset_bounds(origin.x(), origin.y(), mode.hdisplay,
mode.vdisplay);
HardwareDisplayControllers::iterator it = FindDisplayController(drm, crtc);
DCHECK(controllers_.end() != it) << "Display controller (crtc=" << crtc
<< ") doesn't exist.";
HardwareDisplayController* controller = *it;
// If nothing changed just enable the controller. Note, we perform an exact
// comparison on the mode since the refresh rate may have changed.
if (SameMode(mode, controller->get_mode()) &&
origin == controller->origin()) {
if (controller->IsDisabled()) {
HardwareDisplayControllers::iterator mirror =
FindActiveDisplayControllerByLocation(modeset_bounds);
// If there is an active controller at the same location then start mirror
// mode.
if (mirror != controllers_.end())
return HandleMirrorMode(it, mirror, drm, crtc, connector);
}
// Just re-enable the controller to re-use the current state.
return EnableController(controller, controller->origin(),
controller->get_mode());
}
// Either the mode or the location of the display changed, so exit mirror
// mode and configure the display independently. If the caller still wants
// mirror mode, subsequent calls configuring the other controllers will
// restore mirror mode.
if (controller->IsMirrored()) {
controller = new HardwareDisplayController(
controller->RemoveCrtc(drm, crtc), controller->origin());
controllers_.push_back(controller);
it = controllers_.end() - 1;
}
HardwareDisplayControllers::iterator mirror =
FindActiveDisplayControllerByLocation(modeset_bounds);
// Handle mirror mode.
if (mirror != controllers_.end() && it != mirror)
return HandleMirrorMode(it, mirror, drm, crtc, connector);
return EnableController(controller, origin, mode);
}
bool ScreenManager::DisableDisplayController(
const scoped_refptr<DrmDevice>& drm,
uint32_t crtc) {
HardwareDisplayControllers::iterator it = FindDisplayController(drm, crtc);
if (it != controllers_.end()) {
HardwareDisplayController* controller = *it;
if (controller->IsMirrored()) {
controller = new HardwareDisplayController(
controller->RemoveCrtc(drm, crtc), controller->origin());
controllers_.push_back(controller);
}
controller->Disable();
UpdateControllerToWindowMapping();
return true;
}
LOG(ERROR) << "Failed to find display controller crtc=" << crtc;
return false;
}
HardwareDisplayController* ScreenManager::GetDisplayController(
const gfx::Rect& bounds) {
HardwareDisplayControllers::iterator it =
FindActiveDisplayControllerByLocation(bounds);
if (it != controllers_.end())
return *it;
return nullptr;
}
void ScreenManager::AddWindow(gfx::AcceleratedWidget widget,
scoped_ptr<DrmWindow> window) {
std::pair<WidgetToWindowMap::iterator, bool> result =
window_map_.add(widget, window.Pass());
DCHECK(result.second) << "Window already added.";
UpdateControllerToWindowMapping();
}
scoped_ptr<DrmWindow> ScreenManager::RemoveWindow(
gfx::AcceleratedWidget widget) {
scoped_ptr<DrmWindow> window = window_map_.take_and_erase(widget);
DCHECK(window) << "Attempting to remove non-existing window for " << widget;
UpdateControllerToWindowMapping();
return window.Pass();
}
DrmWindow* ScreenManager::GetWindow(gfx::AcceleratedWidget widget) {
WidgetToWindowMap::iterator it = window_map_.find(widget);
if (it != window_map_.end())
return it->second;
NOTREACHED() << "Attempting to get non-existing window for " << widget;
return nullptr;
}
ScreenManager::HardwareDisplayControllers::iterator
ScreenManager::FindDisplayController(const scoped_refptr<DrmDevice>& drm,
uint32_t crtc) {
for (HardwareDisplayControllers::iterator it = controllers_.begin();
it != controllers_.end(); ++it) {
if ((*it)->HasCrtc(drm, crtc))
return it;
}
return controllers_.end();
}
ScreenManager::HardwareDisplayControllers::iterator
ScreenManager::FindActiveDisplayControllerByLocation(const gfx::Rect& bounds) {
for (HardwareDisplayControllers::iterator it = controllers_.begin();
it != controllers_.end(); ++it) {
gfx::Rect controller_bounds((*it)->origin(), (*it)->GetModeSize());
if (controller_bounds == bounds && !(*it)->IsDisabled())
return it;
}
return controllers_.end();
}
bool ScreenManager::HandleMirrorMode(
HardwareDisplayControllers::iterator original,
HardwareDisplayControllers::iterator mirror,
const scoped_refptr<DrmDevice>& drm,
uint32_t crtc,
uint32_t connector) {
(*mirror)->AddCrtc((*original)->RemoveCrtc(drm, crtc));
if (EnableController(*mirror, (*mirror)->origin(), (*mirror)->get_mode())) {
controllers_.erase(original);
return true;
}
LOG(ERROR) << "Failed to switch to mirror mode";
// When things go wrong revert back to the previous configuration since
// it is expected that the configuration would not have changed if
// things fail.
(*original)->AddCrtc((*mirror)->RemoveCrtc(drm, crtc));
EnableController(*original, (*original)->origin(), (*original)->get_mode());
return false;
}
void ScreenManager::UpdateControllerToWindowMapping() {
std::map<DrmWindow*, HardwareDisplayController*> window_to_controller_map;
// First create a unique mapping between a window and a controller. Note, a
// controller may be associated with at most 1 window.
for (HardwareDisplayController* controller : controllers_) {
if (controller->IsDisabled())
continue;
DrmWindow* window = FindWindowAt(
gfx::Rect(controller->origin(), controller->GetModeSize()));
if (!window)
continue;
window_to_controller_map[window] = controller;
}
// Apply the new mapping to all windows.
for (auto pair : window_map_) {
auto it = window_to_controller_map.find(pair.second);
HardwareDisplayController* controller = nullptr;
if (it != window_to_controller_map.end())
controller = it->second;
bool should_enable =
controller && pair.second->GetController() != controller;
pair.second->SetController(controller);
// If we're moving windows between controllers modeset the controller
// otherwise the controller may be waiting for a page flip while the window
// tries to schedule another buffer.
if (should_enable)
EnableController(controller, controller->origin(),
controller->get_mode());
}
}
bool ScreenManager::EnableController(HardwareDisplayController* controller,
const gfx::Point& origin,
const drmModeModeInfo& mode) {
DCHECK(!controller->crtc_controllers().empty());
gfx::Rect rect(origin, gfx::Size(mode.hdisplay, mode.vdisplay));
controller->set_origin(origin);
DrmWindow* window = FindWindowAt(rect);
if (window) {
const OverlayPlane* primary = window->GetLastModesetBuffer();
if (primary) {
if (!controller->Modeset(*primary, mode)) {
LOG(ERROR) << "Failed to modeset controller";
return false;
}
return true;
}
}
scoped_refptr<DrmDevice> drm = controller->GetAllocationDrmDevice();
scoped_refptr<ScanoutBuffer> buffer =
buffer_generator_->Create(drm, rect.size());
if (!buffer) {
LOG(ERROR) << "Failed to create scanout buffer";
return false;
}
FillModesetBuffer(drm, controller, buffer.get());
if (!controller->Modeset(OverlayPlane(buffer), mode)) {
LOG(ERROR) << "Failed to modeset controller";
return false;
}
return true;
}
DrmWindow* ScreenManager::FindWindowAt(const gfx::Rect& bounds) const {
for (auto pair : window_map_) {
if (pair.second->bounds() == bounds)
return pair.second;
}
return nullptr;
}
} // namespace ui