Index: ui/ozone/platform/drm/gpu/screen_manager.cc |
diff --git a/ui/ozone/platform/drm/gpu/screen_manager.cc b/ui/ozone/platform/drm/gpu/screen_manager.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4b4d3f13d75a1e813425e82198ceaa5b333a542a |
--- /dev/null |
+++ b/ui/ozone/platform/drm/gpu/screen_manager.cc |
@@ -0,0 +1,348 @@ |
+// 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); |
+ if (it != window_to_controller_map.end()) |
+ pair.second->SetController(it->second); |
+ else |
+ pair.second->SetController(nullptr); |
+ } |
+} |
+ |
+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 |